/* -------------------------------------------------------------------- */
/* 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 <stdio.h>
#include <string.h>

#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 <RETURN> 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 <RETURN> 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
};

