/* -------------------------------------------------------------------- */ /* SMS Client, send messages to mobile phones and pagers */ /* */ /* kpn.c */ /* */ /* Copyright (C) 1998,1999 Harold "Bo" Baur */ /* */ /* This library is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU Library General Public */ /* License as published by the Free Software Foundation; either */ /* version 2 of the License, or (at your option) any later version. */ /* */ /* This library is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ /* Library General Public License for more details. */ /* */ /* You should have received a copy of the GNU Library General Public */ /* License along with this library; if not, write to the Free */ /* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* */ /* You can contact the author at this e-mail address: */ /* */ /* bo@bikerider.com */ /* */ /* -------------------------------------------------------------------- */ #include #include #include "common/common.h" #include "logfile/logfile.h" #include "driver.h" #include "error.h" #include "comms/comms.h" #include "resource/resource.h" /* -------------------------------------------------------------------- */ static struct kpn_env { DRIVER_DEFAULT_ENV def; /* Place any extended driver */ /* variables here */ } driver_env; /* -------------------------------------------------------------------- */ static RESOURCE resource_list[] = { { RESOURCE_STRING, "SMS_comms_params", 0, 1, NULL, 0, "8N1", 0, &(driver_env.def.comms_params) }, { RESOURCE_STRING, "SMS_centre_number", 0, 1, NULL, 0, NULL, 0, &(driver_env.def.centre_number) }, { RESOURCE_NUMERIC, "SMS_baud", 0, 1, NULL, 0, NULL, 9600, &(driver_env.def.baud) }, { RESOURCE_NUMERIC, "SMS_deliver_timeout", 0, 0, NULL, 0, NULL, 30, &(driver_env.def.deliver_timeout) }, { RESOURCE_NUMERIC, "SMS_timeout", 0, 0, NULL, 0, NULL, 20, &(driver_env.def.timeout) }, { RESOURCE_NUMERIC, "SMS_write_timeout", 0, 0, NULL, 0, NULL, 10, &(driver_env.def.write_timeout) }, { RESOURCE_NUMERIC, "SMS_max_deliver", 0, 0, NULL, 0, NULL, 2, &(driver_env.def.max_deliver) }, { RESOURCE_NULL, NULL, 0, 1, NULL, 0, NULL, 0, NULL } }; /* -------------------------------------------------------------------- */ #define DELIVERTIMEOUT (driver_env.def.deliver_timeout) #define TIMEOUT (driver_env.def.timeout) #define WRITETIMEOUT (driver_env.def.write_timeout) /* -------------------------------------------------------------------- */ #define FD (driver_env.def.fd) /* -------------------------------------------------------------------- */ static void KPN_hangup(void); static int found(char *description, char *match); static int KPN_login(void); static int KPN_sendmessage(char *msisdn, char *message); static int KPN_send_disconnect(void); /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ static void KPN_hangup(void) { default_hangup((DRIVER_DEFAULT_ENV *)(&driver_env)); } /* -------------------------------------------------------------------- */ /* This function is here to keep the functions readable. */ /* Timeout and buffersize settings are constants anyway. */ /* -------------------------------------------------------------------- */ static int found(char *description, char *match) { static char buf[MAX_RESPONSE_BUFSIZE]; lprintf(LOG_VERBOSE, "Waiting for %s\n", description ); if (expstr(FD, buf, match, MAX_RESPONSE_BUFSIZE, TIMEOUT) == 0) { lprintf(LOG_VERYVERBOSE, "Ok, got '%s'\n", match ); return 1; } lprintf(LOG_ERROR, "Failed to match '%s'\n", match ); KPN_hangup(); return 0; } /* -------------------------------------------------------------------- */ /* Now follow the functions that implement the actual chat sequence. */ /* Rather than just prompting for a message and a phonenumber to */ /* sent it to, they present you with all kinds of menus which you */ /* have to traverse in order to get your message sent. If you */ /* are wondering why this is, here's a clue: the SMS service number */ /* charges you by the second, so the longer they can hold you on */ /* line, the more money they make. This is typical KPN behaviour. */ /* They also support sending "multiple" messages within one session, */ /* with a maximum of - yes - two. Count your blessings. It's not that */ /* they drop the connection after this, no, they still allow you to */ /* specify a phone number and a text, but after this you realise that */ /* the menu has undergone a subtle change. The option to actually SEND */ /* it has suddenly disappeared. You have to disconnect and call again */ /* so they can charge you the initial connection fee again. Are we */ /* having fun yet? Can you say "monopoly". End of rant, thank you for */ /* listening, I will shut up now, and talk about how this is coded. */ /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ /* This function performs the "login" before the actual message can */ /* be sent. Right after connecting we first have to choose a terminal */ /* type (I'm using "VT100"), then send a to get to Menu 2, */ /* wherethen select the (only available) option "1" to get to Menu 3 */ /* (yes, it really is absurd) where we can finally get some message */ /* sending done (but that's is handled by KPN_sendmessage). */ /* -------------------------------------------------------------------- */ static int KPN_login(void) { if ( !found("Welcome","KPN Telecom") ) return EKPN_NOWELCOME; if ( !found("Menu 1 Prompt",">: ") ) return EKPN_NOMENU1; /* select option 1 : VT100 */ twrite(FD, "1\r\n", strlen("1\r\n"), TIMEOUT); if ( !found("Press RETURN","verbreken.") ) return EKPN_NORETURN; /* just send a return */ twrite(FD, "\r\n", strlen("\r\n"), TIMEOUT); if ( !found("Menu 2 Prompt",">: ") ) return EKPN_NOMENU2; /* select option 1 : send a message */ twrite(FD, "1\r\n", strlen("1\r\n"), TIMEOUT); /* once we get the 3rd prompt, the "login" has succeeded */ if ( !found("Menu 3 Prompt",">: ") ) return EKPN_NOMENU3; return 0; } /* -------------------------------------------------------------------- */ /* The function that actually sends a message picks up were KPN_login */ /* ended, assuming that we are now in Menu 3. This one allows you to */ /* specify a phone number, a message, and an option to actually send */ /* send it (yaaaay!). Note that we specify the message only once (by */ /* checking wether driver_env.def.num_sent equals 0), even though the */ /* function is called again for each subsequent recipient. */ /* -------------------------------------------------------------------- */ static int KPN_sendmessage(char *msisdn, char *message) { /* specify message only if this is the first recipient */ if ( ! driver_env.def.num_sent ) { /* select option 7 : specify the message text */ twrite(FD, "7\r\n", strlen("7\r\n"), TIMEOUT); if ( !found("Message Prompt","karakters)") ) return EKPN_NOMESSAGE; /* send the message text */ twrite(FD, message, strlen(message), TIMEOUT); twrite(FD, "\r\n", strlen("\r\n"), TIMEOUT); if ( !found("Menu 3 Prompt",">: ") ) return EKPN_NOMENU3; } /* select option 5 : specify the phone number */ twrite(FD, "5\r\n", strlen("5\r\n"), TIMEOUT); if ( !found("Number Prompt"," : ") ) return EKPN_NONUMBER; /* send the phone number */ twrite(FD, msisdn, strlen(msisdn), TIMEOUT); twrite(FD, "\r\n", strlen("\r\n"), TIMEOUT); if ( !found("Menu 3 Prompt",">: ") ) return EKPN_NOMENU3; /* select option 10 : send the message */ twrite(FD, "10\r\n", strlen("10\r\n"), TIMEOUT); if ( !found("Confirm Send","N>: ") ) return EKPN_NOCONFIRM; /* send a "j" (for "yes" in dutch) */ twrite(FD, "j\r\n", strlen("j\r\n"), TIMEOUT); if ( !found("Message Sent","Bericht verzonden") ) return EKPN_NOSEND; if ( !found("Press Text","gaan.") ) return EKPN_NORETURN; /* just send a return */ twrite(FD, "\r\n", strlen("\r\n"), TIMEOUT); /* after this we should get the menu prompt back */ if ( !found("Menu 3 Prompt",">: ") ) return EKPN_NOMENU3; /* we leave the routine in "Menu 3" state. If another */ /* message needs to be sent, it's called all over again */ /* with the selection of option 5 (specify number). If */ /* the last message has been sent, we fall through to */ /* KPN_send_disconnect (implements the exit sequence). */ return 0; } /* ------------------------------------------------------------ */ /* We can bail out immediately after the message is sent, */ /* without attempting to traverse back through the menu */ /* answering questions like "do you want to disconnect?". */ /* This function implements the latter, but if you prefer */ /* to just drop the carrier, modify the DEVICE_ENTRY to use */ /* default_send_disconnect rather than KPN_send_disconnect. */ /* It could be argued they don't deserve better :-) */ /* ------------------------------------------------------------ */ static int KPN_send_disconnect(void) { /* last message - send a CTRL-Z to exit this menu */ twrite(FD, "", strlen(""), TIMEOUT); if ( !found("Menu 2 Prompt",">: ") ) return EKPN_NOMENU2; /* send a CTRL-Z to exit this menu */ twrite(FD, "", strlen(""), TIMEOUT); if ( !found("Confirm Disconnect",">: ") ) return EKPN_NOCONFIRM; /* send a "j" (for "yes" in dutch) */ twrite(FD, "j\r\n", strlen("j\r\n"), TIMEOUT); return 0; } /* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */ DEVICE_ENTRY kpn_device = { "KPN", "1.0", resource_list, (DRIVER_DEFAULT_ENV *)(&driver_env), default_init, default_main, default_validate_numeric_id, default_dial, default_hangup, KPN_send_disconnect, default_multiple_counted_deliver, KPN_sendmessage, KPN_login };