smsc_soap.c fixed - comments?

"Alex Kinch" <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <0a3601c31f23$dd10d490$6601a8c0@alex>
Hi,

After a bit of fiddling I think I've managed to fix smsc_soap.c to take into
account dlr and http changes. I've attached it to this email for your
comments.

It runs ok on my build, but I haven't actually managed to get it process an
XML post yet, still wresting with the template formats.

Alex

PS: This is my first bit of C hacking, so bear with me :-)
smsc_soap.c (application/octet-stream, 68.1 KB)
/*
 * smsc_soap.c - Implementation of SOAP (XML over HTTP) as a Kannel module
 *
 * Oded Arbel, m-Wise inc ([email protected])
 *
 *
 * Changelog:
 *
 * 20/02/2002: started - copied smsc_mam.c for starting
 * 25/02/2002: implemented MT sending
 * 09/05/2002: fixed problem crash when HTTP connection fails.
 *             send message back to bearerbox on HTTP failure instead of local queue
 * 19/05/2002: strip leading + from international numbers
 * 20/05/2002: fixed previous change
 *             changed Transaction Id returned to support 64 bit integers
 * 27/05/2002: changed DLR creation to store the transaction ID instead of timestamp
 *              added parsing of human readable time in DLR
 * 28/05/2002: added multi thread sending support
 * 02/06/2002: changed validity computing to accept minutes instead of seconds
 * 04/06/2002: Changed callbacks to take into account that they might be called while the connection
 *              is dead.
 * 04/06/2002: Started to implement generic parsing engine.
 * 09/06/2002: Removed hardcoded XML generation and parsing
 * 22/07/2002: Removed wrong assignment of charset_convert return code to msg->sms.coding
 * 30/07/2002: fixed wrong format for year in soap_write_date
 *               additional debug and process for invalid charset conversion
 * 04/08/2002: forced chraset_conversion to/from UCS-2 to use big endianity
 *             added curly bracing support to XML data tokens
 *
 * TO-DO:
 *	- add a configuration option to the max number of messages a client can send, and use
 *	  and implement KeepAlive in the clients.
 *	- support XML generation through DTD
 *	- support XML parsing through DTD
 *	
 *
 * Usage: add the following to kannel.conf:
 *
 * group = smsc
 * smsc = soap
 * send-url = <URI>		- URI to send SOAP bubbles at (mandatory)
 * receive-port = <number>	- port number to bind our server on (Default: disabled - MT only)
 * xml-files = "MT.xml;MO.xml;DLR.xml" - XML templates for generation of MT messages and MO and DLR responses
 * xmlspec-files = "MT.spec;MO.spec;DLR.spec" - XML path spec files for parsing of MT response and MO and DLR submission
 * alt-charset = "character map" - charset in which a text message is received (default UTF-8)
 **/


#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <limits.h>

#include "gwlib/gwlib.h"
#include "smscconn.h"
#include "smscconn_p.h"
#include "bb_smscconn_cb.h"
#include "msg.h"
#include "sms.h"
#include "dlr.h"

// libxml include
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

/*
 * Defines and defaults
 **/
#define SOAP_SLEEP_TIME				0.01
#define SOAP_MAX_MESSAGE_PER_ROUND 		1
#define SOAP_DEFAULT_SENDER_STRING		"m-Wise inc."
#define SOAP_DEFAULT_VALIDITY                   60

// URIs for MOs and delivery reports
#define SOAP_MO_URI				"/mo"
#define SOAP_DLR_URI				"/dlr"

// default reponses to HTTP queries
#define SOAP_DEFAULT_MESSAGE				"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>No method by that name</Error>"
#define SOAP_ERROR_NO_DLR_MESSAGE			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Sorry - no DLR for that MT</Error>"
#define SOAP_ERROR_DLR_MESSAGE				"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Fatal error while trying to parse delivery report</Error>"
#define SOAP_ERROR_MO_MESSAGE				"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Fatal error while trying to incoming MO</Error>"
#define SOAP_ERROR_NO_DATA_MESSAGE			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>No data received</Error>"
#define SOAP_ERROR_MALFORMED_DATA_MESSAGE	        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Malformed data received</Error>"

// error codes for HTTP queries
#define SOAP_ERROR_NO_DLR_CODE				405
#define SOAP_DEFAULT_CODE				404
#define SOAP_ERROR_DLR_CODE				500
#define SOAP_ERROR_MO_CODE				500
#define SOAP_ERROR_NO_DATA_CODE				501
#define SOAP_ERROR_MALFORMED_DATA_CODE	                502
#define SOAP_QUERY_OK					200

// compile time configuration defines
#undef HUMAN_TIME

#define MIN_SOAP_CLIENTS            5
#define MAX_SOAP_CLIENTS            50
#define CLIENT_BUSY_TIME            5
#define CLIENT_TEARDOWN_TIME        600
#define CLIENT_BUSY_LOAD            5

// private data store for the SOAP module
typedef struct privdata
{
	List *outgoing_queue;	// queue to hold unsent messages
	
	long listener_thread;	// SOAP HTTP client and module managment
	long server_thread; 	// SOAP HTTP server
	
	int shutdown;		// Internal signal to shut down
	int soap_server;	// internal signal to shut down the server
	long port;    	        // listener port
	int ssl;		// flag whether to use SSL for the server

	Octstr *uri;		// URI to send MTs on

	Octstr *allow_ip, *deny_ip; // connection allowed mask

        List* soap_client;  // list to hold callers
	
	Octstr* name;		// connection name for use in private functions that want to do logging

        // SOAP configurtion
        Octstr* form_variable; // variable name used in post
        int form_urlencoded; // whether to send the data urlencoded or multipart
        Octstr* alt_charset;  // alt-charset to use

        Octstr* mt_xml_file;
        Octstr* mt_spec_file;
        Octstr* mo_xml_file;
        Octstr* mo_spec_file;
        Octstr* dlr_xml_file;
        Octstr* dlr_spec_file;
}
PrivData;

// struct to hold one HTTP client connection (I hope)
typedef struct client_data
{
    time_t last_access;
    unsigned long requests;
    HTTPCaller* caller;
}
ClientData;

// struct useful for the XML mapping routines
typedef struct argument_map {
    Octstr* name;
    Octstr* path;
    Octstr* attribute;
    Octstr* sscan_type;
    void* store;
}
ArgumentMap;

// useful macros go here (some of these were ripped of other modules,
// so maybe its better to put them in a shared file)
#define	O_DESTROY(a)	{ if(a) octstr_destroy(a); a=NULL; }
typedef long long int64;

/*
 * SOAP module public API towards bearerbox
 **/

// module entry point - will also be defined in smsCconn_p.h
int smsc_soap_create(SMSCConn *conn, CfgGroup *cfg);
// callback for bearerbox to add messages to our queue
static int soap_add_msg_cb(SMSCConn *conn, Msg *sms);
// callback for bearerbox to signal a shutdown
static int soap_shutdown_cb(SMSCConn *conn, int finish_sending);
// callback for bearerbox to signal us to start the connection
static void soap_start_cb(SMSCConn *conn);
// callback for bearerbox to signal us to drop the connection
static void soap_stop_cb(SMSCConn *conn);
// callback for bearerbox to query on the number of messages in our queue
static long soap_queued_cb(SMSCConn *conn);

/*
 * SOAP module thread functions (created by smsc_soap_create())
 **/

// SOAP module thread for launching HTTP clients.
static void soap_listener(void *arg);
// SOAP HTTP server thread for incoming MO
static void soap_server(void *arg);

/*
 * SOAP module internal protocol implementation functions
 **/

// start the loop to send all messages in the queue
static void soap_send_loop(SMSCConn *conn);
// function used to send a single MT message
static void soap_send(PrivData* privdata, Octstr* xmlbuffer, Msg* msgid);
// called to retrieve HTTP responses from the HTTP library
static void soap_read_response(SMSCConn *conn);
// format a messages structure as an XML buffer
static Octstr* soap_format_xml(Octstr * xml_file, Msg* msg);
// parse a response from the SOAP server to get the message ID
static int64 soap_parse_response(PrivData* privdata, Octstr *xmlResponse);
// parse an incoming MO xml
static long soap_parse_mo(SMSCConn *conn, Octstr *request, Octstr **response);
// parse an incoming derlivery report
static long soap_parse_dlr(SMSCConn *conn, Octstr *request, Octstr **response);

/*
 * SOAP internal utility functions
 **/
// parse an integer out of an XML node
int soap_xmlnode_get_long(xmlNodePtr cur, long* out);
// parse an int64 out of an XML node
int soap_xmlnode_get_int64(xmlNodePtr cur, int64* out);
// parse a string out of an XML node
int soap_xmlnode_get_octstr(xmlNodePtr cur, Octstr **out);
// convert a one2one date format to epoch time
time_t soap_read_date(Octstr* dateString);
// convert a epoch time to one2one date format
static Octstr* soap_write_date(time_t date);
// start the SOAP server
int soap_server_start(SMSCConn *conn);
// stop the SOAP server
static void soap_server_stop(PrivData* privdata);
// create a new SOAP client caller
static ClientData* soap_create_client_data();
// destroy a SOAP client caller
static void soap_destroy_client_data(void* data);
// start an HTTP query
static void soap_client_init_query(PrivData* privdata, List* headers, Octstr* data, Msg* msg);
// return a caller from the pool that has responses waiting
static ClientData* soap_client_have_response(List* client_list);
// return data from a message according to its name
static Octstr* soap_convert_token(Msg* msg, Octstr* name);
// convert a XML parsing spec file and a list of recognized keywords to an argument map
List* soap_create_map(Octstr* spec, long count, char* keywords[], char* types[], void* storage[]);
// destroy a map structure
void soap_destroy_map(void *item);
// map content in an XML structure to a list of variable using a spec file
int soap_map_xml_data(xmlNodePtr xml, List* maps);

/**************************************************************************************/

/*
 * Implementation
 **/

/*
 * function smsc_soap_create()
 *	called to create and initalize the module's internal data.
 *	if needed also will start the connection threads
 * Input: SMSCConn pointer to connection data, cfgGroup pointer to configuration data
 * Returns: status (0 = OK, -1 = failed)
 */
int smsc_soap_create(SMSCConn *conn, CfgGroup *cfg)
{
        PrivData *privdata;
        Octstr* temp = NULL;
        List* filenames = NULL;
	
	// allocate and init internat data structure
	privdata = gw_malloc(sizeof(PrivData));
	privdata->outgoing_queue = list_create();
//	privdata->pending_ack_queue = list_create();

	privdata->shutdown = 0;
	privdata->soap_client = NULL;
	privdata->soap_server = 0;

	// read configuration data
	if (cfg_get_integer(&(privdata->port), cfg, octstr_imm("receive-port-ssl")) == -1)
		if (cfg_get_integer(&(privdata->port), cfg, octstr_imm("receive-port")) == -1)
			privdata->port = 0;
		else 
			privdata->ssl = 0;
	else
		privdata->ssl = 1;

	privdata->uri = cfg_get(cfg, octstr_imm("send-url"));

	privdata->allow_ip = cfg_get(cfg, octstr_imm("connect-allow-ip"));
	if (privdata->allow_ip)
		privdata->deny_ip = octstr_create("*.*.*.*");
	else
		privdata->deny_ip = NULL;

        // read XML configuration
        privdata->form_variable = cfg_get(cfg, octstr_imm("form-variable"));
        cfg_get_bool(&(privdata->form_urlencoded), cfg, octstr_imm("form-urlencoded"));
        
        privdata->alt_charset = cfg_get(cfg, octstr_imm("alt-charset"));
        if (!privdata->alt_charset)
            privdata->alt_charset = octstr_create("utf-8");

	// check validity of stuff
	if (privdata->port <= 0 || privdata->port > 65535) {
		error(0, "invalid port definition for SOAP server (%ld) - aborting", privdata->port);
		goto error;
	}

	if (!privdata->uri) {
		error(0, "invalid or missing send-url definition for SOAP - aborting.");
		goto error;
	}

        if (!privdata->form_variable) {
		error(0, "invalid or missing form variable name definition for SOAP - aborting.");
		goto error;
        }

        // load XML templates and specs
	filenames = octstr_split(temp = cfg_get(cfg,octstr_imm("xml-files")), octstr_imm(";"));
        octstr_destroy(temp);
        if (list_len(filenames) < 3) {
            error(0,"Not enough template files for XML generation, you need 3 - aborting"); goto error;
        }
        if ( !(privdata->mt_xml_file = octstr_read_file(octstr_get_cstr(temp = list_extract_first(filenames))))) {
            error(0,"Can't load XML template for MT - aborting"); goto error;
        }
        octstr_destroy(temp);
        if ( !(privdata->mo_xml_file = octstr_read_file(octstr_get_cstr(temp = list_extract_first(filenames))))) {
            error(0,"Can't load XML template for MO - aborting"); goto error;
        }
        octstr_destroy(temp);
        if ( !(privdata->dlr_xml_file = octstr_read_file(octstr_get_cstr(temp = list_extract_first(filenames))))) {
            error(0,"Can't load XML template for DLR - aborting"); goto error;
        }
        octstr_destroy(temp);
        list_destroy(filenames, octstr_destroy_item);

	filenames = octstr_split(temp = cfg_get(cfg,octstr_imm("xmlspec-files")), octstr_imm(";"));
        octstr_destroy(temp);
        if (list_len(filenames) < 3) {
            error(0,"Not enough spec files for XML parsing, you need 3 - aborting"); goto error;
        }
        if ( !(privdata->mt_spec_file = octstr_read_file(octstr_get_cstr(temp = list_extract_first(filenames))))) {
            error(0,"Can't load spec for MT parsing - aborting"); goto error;
        }
        octstr_destroy(temp);
        if ( !(privdata->mo_spec_file = octstr_read_file(octstr_get_cstr(temp = list_extract_first(filenames))))) {
            error(0,"Can't load spec for MO parsing - aborting"); goto error;
        }
        octstr_destroy(temp);
        if ( !(privdata->dlr_spec_file = octstr_read_file(octstr_get_cstr(temp = list_extract_first(filenames))))) {
            error(0,"Can't load spec for DLR parsing - aborting"); goto error;
        }
        octstr_destroy(temp);
        list_destroy(filenames, octstr_destroy_item);

        debug("bb.soap.create",0,"Connecting to %s",
                octstr_get_cstr(privdata->uri));
	
	// store private data struct in connection data
	conn->data = privdata;

	// state my name
	conn->name = octstr_format("SOAP: %s", octstr_get_cstr(privdata->uri) );
	privdata->name = octstr_duplicate(conn->id);

	// init status vars
	conn->status = SMSCCONN_CONNECTING;
	conn->connect_time = time(NULL);

	// set up call backs for bearerbox
	conn->shutdown = soap_shutdown_cb;
	conn->queued = soap_queued_cb;
	conn->start_conn = soap_start_cb;
	conn->stop_conn = soap_stop_cb;
	conn->send_msg = soap_add_msg_cb;
	
	privdata->listener_thread = 0;
	privdata->server_thread = 0;
	
	// check whether we can start right away
	if (! conn->is_stopped)
	// yes, we can
		conn->status = SMSCCONN_CONNECTING;
	else
		conn->status = SMSCCONN_DISCONNECTED;
	
	// any which way - start the connection thread
	if ( (privdata->listener_thread = gwthread_create(soap_listener, conn)) == -1)
	{
		error(0, "soap_create failed to spawn thread - aborting");
		goto error;
	}
	
	return 0; // done - ok
	
	
error:
	// oh oh, problems
	error(0, "Failed to create SOAP smsc connection");

	// release stuff
	if (privdata != NULL)
	{
		list_destroy(privdata->outgoing_queue, NULL);
//		list_destroy(privdata->pending_ack_queue, NULL);
	
		O_DESTROY(privdata->uri);
		O_DESTROY(privdata->allow_ip);
		O_DESTROY(privdata->deny_ip);
	}
	
	gw_free(privdata);
 
        octstr_destroy(temp);
        list_destroy(filenames, octstr_destroy_item);
	
	// notify bearerbox
	conn->why_killed = SMSCCONN_KILLED_CANNOT_CONNECT;
	conn->status = SMSCCONN_DEAD;
	
	info(0, "exiting");
	return -1; // I'm dead
}

/*
 * Callbacks
 **/

/*
 * function soap_add_msg_cb()
 *	get a message and copy it to the queue. note that message must be copied
 *	as I don't know what bearerbox wants to do with it after I return
 * Input: SMSCConn connection state data, Msg to send
 * Returns: status - 0 on success, -1 on fail.
 **/
static int soap_add_msg_cb(SMSCConn *conn, Msg *sms)
{
    PrivData *privdata = conn->data;
    Msg *copy;

    // I'm dead and cannot take any calls at the moment, please don't leave a message
    if (conn->status == SMSCCONN_DEAD)
        return -1;

    copy = msg_duplicate(sms); // copy the message
    list_append(privdata->outgoing_queue, copy); // put it in the queue

    debug("bb.soap.add_msg",0,"SOAP[%s]: got a new MT from %s, list has now %ld MTs", octstr_get_cstr(privdata->name),
    	octstr_get_cstr(sms->sms.sender), list_len(privdata->outgoing_queue));

    gwthread_wakeup(privdata->listener_thread);

    return 0;
}

/*
 * function soap_shutdown_cb()
 *	called by bearerbox to signal the module to shutdown. sets the shutdown flags,
 *	wakes up the listener thread and exits (if we add more threads, we need to handle those too)
 * Input: SMSCConn connection state data, flag indicating whether we can finish sending messages
 *	in the queue first.
 * Returns: status - 0 on success, -1 on fail.
 **/
static int soap_shutdown_cb(SMSCConn *conn, int finish_sending)
{
    PrivData *privdata = conn->data;
    long thread;

    // I'm dead, there's really no point in killing me again, is it ?
    if (conn->status == SMSCCONN_DEAD)
        return -1;

    debug("bb.soap.cb", 0, "SOAP[%s]: Shutting down SMSCConn, %s", octstr_get_cstr(privdata->name),
          finish_sending ? "slow" : "instant");

    // Documentation claims this would have been done by smscconn.c, but isn't when this code is being written.
    conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
    // Separate from why_killed to avoid locking, as why_killed may be changed from outside?
    privdata->shutdown = 1;

    if (finish_sending == 0) {
        Msg *msg;
        while ((msg = list_extract_first(privdata->outgoing_queue)) != NULL)
        	bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN);
    }

    thread = privdata->listener_thread;
    gwthread_wakeup(thread);
    gwthread_join(thread);
    return 0;
}

/*
 * function soap_start_cb()
 *	called by bearerbox when the module is allowed to start working
 * Input: SMSCConn connection state data
 * Returns: status - 0 on success, -1 on fail.
 **/
static void soap_start_cb(SMSCConn *conn)
{
    PrivData *privdata = conn->data;
 	
 	debug("smsc.soap.start", 0, "SOAP[%s]: start called", octstr_get_cstr(privdata->name));

	// set the status so that connection_thread will know what to do	
	conn->status = SMSCCONN_CONNECTING;

	// start connection_thread, in case its not started.
	if ( (! privdata->listener_thread) &&
		( (privdata->listener_thread = gwthread_create(soap_listener, conn)) == -1 ) )
	{
		error(0, "soap_start failed to spawn thread - aborting");
		conn->why_killed = SMSCCONN_KILLED_CANNOT_CONNECT;
		conn->status = SMSCCONN_DEAD;
		privdata->shutdown = 1;
		return;
	}
//    gwthread_wakeup(privdata->listener_thread);
    debug ("smsc.soap.start",0,"SOAP[%s]: starting OK", octstr_get_cstr(privdata->name));
}

/*
 * function soap_stop_cb()
 *	this function may be used to 'pause' the module. it should cause the connection
 *	to logout, but not to be destroyed, so it will be restarted later.
 * Input: SMSCConn connection state data
 **/
static void soap_stop_cb(SMSCConn *conn)
{
    PrivData *privdata = conn->data;

    // I'm dead, its really too late to take a break now
    if (conn->status == SMSCCONN_DEAD)
        return;

 	debug("smsc.soap.stop", 0, "SOAP[%s]: stop called", octstr_get_cstr(privdata->name));
 	
 	// make connection thread disconnect
 	conn->status = SMSCCONN_DISCONNECTED;
}

/*
 * function soap_queued_cb()
 *	called by bearerbox to query the number of messages pending send.
 *	the number returned includes the number of messages sent, but for which no ACK was yet received.
 * Input: SMSCConn connection state data
 * Returns: number of messages still waiting to be sent
 **/
static long soap_queued_cb(SMSCConn *conn)
{
    PrivData *privdata = conn->data;
    long ret;

    // I'm dead, so I have no queues - well there !
    if (conn->status == SMSCCONN_DEAD)
        return -1;

    ret = list_len(privdata->outgoing_queue); // + list_len(privdata->pending_ack_queue);

    // use internal queue as load, maybe something else later
    conn->load = ret;

    return ret;
}


/*
 * SOAP module thread functions (created by smsc_soap_create())
 **/

/*
 * function soap_listener()
 *	entry point to the listenr thread. this thread listenes on the MO port (if
 *	needed, and is also reposnsible for invoking "MT threads" (HTTP clients) to
 *	to send MTs.
 * Input: SMSCConn connection state data
 **/
static void soap_listener(void *arg)
{
	SMSCConn *conn = arg;
	PrivData *privdata = conn->data;
	Msg *msg = NULL;
	debug("bb.soap.listener",0,"SOAP[%s]: listener entering", octstr_get_cstr(privdata->name));

	while (!privdata->shutdown)
	{
		// check connection status
		switch (conn->status)
		{
		case SMSCCONN_RECONNECTING:
		case SMSCCONN_CONNECTING:
			if (privdata->soap_server) {
				soap_server_stop(privdata);
			}
		
			if (soap_server_start(conn)) {
				privdata->shutdown = 1;
				error(0, "SOAP[%s]: failed to start HTTP server!", octstr_get_cstr(privdata->name));
				break;
			}

			mutex_lock(conn->flow_mutex);
			conn->status = SMSCCONN_ACTIVE;
			mutex_unlock(conn->flow_mutex);
			
			bb_smscconn_connected(conn);
			break;
		case SMSCCONN_DISCONNECTED:
			if (privdata->soap_server)
				soap_server_stop(privdata);
			break;
		case SMSCCONN_ACTIVE:
			if (!privdata->soap_server) {
				mutex_lock(conn->flow_mutex);
				conn->status = SMSCCONN_RECONNECTING;
				mutex_unlock(conn->flow_mutex);
				break;
			}
			
			// run the normal send/receive loop
			if (list_len(privdata->outgoing_queue) > 0) { // we have messages to send
				soap_send_loop(conn); // send any messages in queue
			}
			break;
		case SMSCCONN_DEAD:
			// this shouldn't happen here - I'm the only one allowed to set SMSCCONN_DEAD
		default:
			break;
		}

		soap_read_response(conn); // collect HTTP responses
	
		gwthread_sleep(SOAP_SLEEP_TIME); // sleep for a while so I wont busy-loop
    	
	}

	debug("bb.soap.connection",0,"SOAP[%s]: connection shutting down", octstr_get_cstr(privdata->name));

	soap_server_stop(privdata);

	// send all queued messages to bearerbox for recycling
	debug("bb.soap.connection",0,"SOAP[%s]: sending messages back to bearerbox", octstr_get_cstr(privdata->name));
	while ((msg = list_extract_first(privdata->outgoing_queue)) != NULL)
		bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN);

	// lock module public state data
	mutex_lock(conn->flow_mutex);

	debug("bb.soap.connection",0,"SOAP[%s]: playing dead", octstr_get_cstr(privdata->name));
	conn->status = SMSCCONN_DEAD; // set state

	// destroy lists
	debug("bb.soap.connection",0,"SOAP[%s]: don't need the queue anymore", octstr_get_cstr(privdata->name));
	list_destroy(privdata->outgoing_queue, NULL);
	//list_destroy(privdata->pending_ack_queue, NULL);

	// clear the soap client collection
	debug("bb.soap.connection",0,"SOAP[%s]: tell caller to stop", octstr_get_cstr(privdata->name));
	if (privdata->soap_client)
        list_destroy(privdata->soap_client, soap_destroy_client_data);
	
    // destroy private data stores
	debug("bb.soap.connection",0,"SOAP[%s]: done with privdata", octstr_get_cstr(privdata->name));
	O_DESTROY(privdata->uri);
	O_DESTROY(privdata->allow_ip);
	O_DESTROY(privdata->deny_ip);

	gw_free(privdata);
	conn->data = NULL;

	mutex_unlock(conn->flow_mutex);

	debug("bb.soap.connection", 0, "SOAP: module has completed shutdown.");
	bb_smscconn_killed();
}

/*
 * function soap_server()
 *	server thread - accepts incoming MOs
 * Input: SMSCConn connection state data
 **/
static void soap_server(void* arg)
{
	SMSCConn* conn = (SMSCConn*)arg;
	PrivData* privdata = conn->data;
//	PrivData* privdata = (PrivData*)arg;
	HTTPClient* remote_client = NULL;
	List *request_headers = NULL, *response_headers = NULL;
	List *cgivars = NULL;
	Octstr *client_ip = NULL, *request_uri = NULL, *request_body = NULL;
	Octstr *response_body = NULL;
	Octstr *timebuf = NULL;
	int http_response_status;
	
	debug("bb.soap.server",0,"SOAP[%s]: Server starting", octstr_get_cstr(privdata->name));
	
	// create basic headers
	response_headers = http_create_empty_headers();
	http_header_add(response_headers, "Content-type","text/xml");
	//http_header_add(response_headers, "Content-type","application/x-www-form-urlencoded");
	//http_header_add(response_headers,"Connection", "Close");
	http_header_add(response_headers, "Server","Kannel");
			
	while (privdata->soap_server) {
		if ( (remote_client = http_accept_request(privdata->port, 
			&client_ip, &request_uri, &request_headers, &request_body, &cgivars)) ) {
			
			debug("bb.soap.server",0,"SOAP[%s]: server got a request for %s from %s, with body <%s>", octstr_get_cstr(privdata->name),
				octstr_get_cstr(request_uri),octstr_get_cstr(client_ip), 
				request_body?octstr_get_cstr(request_body):"<null>");

			// parse request
			if (!octstr_compare(request_uri,octstr_imm(SOAP_MO_URI))) {
				// this is an incoming MO
				if ((http_response_status = soap_parse_mo(conn,request_body, &response_body)) == -1) { 
					// fatal error parsing MO
					error(0,"SOAP[%s]: fatal error parsing MO", octstr_get_cstr(privdata->name));
					response_body = octstr_create(SOAP_ERROR_MO_MESSAGE);
					http_response_status = SOAP_ERROR_MO_CODE;
				}
			} else if (!octstr_compare(request_uri,octstr_imm(SOAP_DLR_URI))) {
				// a delivery report
				if ((http_response_status = soap_parse_dlr(conn,request_body, &response_body)) == -1) { 
					// fatal error parsing MO
					error(0,"SOAP[%s]: fatal error parsing DLR", octstr_get_cstr(privdata->name));
					response_body = octstr_create(SOAP_ERROR_DLR_MESSAGE);
					http_response_status = SOAP_ERROR_DLR_CODE;
				}
			} else {
				// unknown command send default message
				response_body = octstr_create(SOAP_DEFAULT_MESSAGE);
				http_response_status = SOAP_DEFAULT_CODE;
			}
			
			// create response
			/*
			response_body = octstr_create("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
			"<!DOCTYPE SMSCACCESS_REPLY SYSTEM \"http://superion/~oded/smsc_reply-1_0.dtd\">\n"
			"<SMSCACCESS_REPLY>\n"
			"   <SUBSCRIBER>447951718145</SUBSCRIBER>\n"
			"   <DATE_RECEIVED>22/01/2002:15:12</DATE_RECEIVED>\n"
			"   <RETURN_CODE>00</RETURN_CODE>\n"
			"</SMSCACCESS_REPLY>\n");
			*/
			
			// encode date in headers
			timebuf = date_format_http(time(NULL));
			http_header_add(response_headers, "Date", octstr_get_cstr(timebuf));
			O_DESTROY(timebuf);
//			http_header_dump(response_headers);
			
			// send response back to client
			http_send_reply(remote_client,http_response_status,response_headers, response_body);
									
			// destroy response data
			//http_destroy_headers(response_headers);
			O_DESTROY(response_body);
			
			// destroy request data
			O_DESTROY(request_uri);
			O_DESTROY(request_body);
			O_DESTROY(client_ip);
			http_destroy_headers(request_headers);
			list_destroy(cgivars, NULL);
		}
		
		gwthread_sleep(SOAP_SLEEP_TIME);
	}
	
	debug("bb.soap.server",0,"SOAP[%s]: server going down", octstr_get_cstr(privdata->name));
//	privdata->server_thread = 0;
}

/*
 * SOAP module internal protocol implementation functions
 **/

/*
 * function soap_send_loop()
 *	called when there are messages in the queue waiting to be sent
 * Input: SMSCConn connection state data
 **/
static void soap_send_loop(SMSCConn* conn)
{
	PrivData* privdata = conn->data;
	Msg *msg;
	Octstr* xmldata = NULL;
	int counter = 0;
  size_t ret;
  
	debug("bb.soap.client",0,"SOAP[%s]: client - entering", octstr_get_cstr(privdata->name));

	while ( (counter < SOAP_MAX_MESSAGE_PER_ROUND) && (msg = list_extract_first(privdata->outgoing_queue)) )
	{ // as long as we have some messages
		++counter;

                if (!msg->sms.id)
                    /* generate a message id */
                    msg->sms.id = gw_generate_id();
                    
                /* convert message data to target encoding */
                if (msg->sms.coding == DC_UCS2) {
                    debug("bb.soap.send_loop", 0, "converting from USC-2 to %s", octstr_get_cstr(privdata->alt_charset));
                    ret = charset_convert(msg->sms.msgdata, "UCS-2BE", octstr_get_cstr(privdata->alt_charset));
                }    
                else  {
                    debug("bb.soap.send_loop", 0, "converting from ISO-8859-1 to %s", octstr_get_cstr(privdata->alt_charset));
                    ret = charset_convert(msg->sms.msgdata, "ISO-8859-1", octstr_get_cstr(privdata->alt_charset));
		            }
                if (ret == -1) {
		                error(0, "charset_convert failed");
                    octstr_dump(msg->sms.msgdata, 0);
                    bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_MALFORMED);
			              continue;
                }
                
                    /* format the messages as a character buffer to send */
		if (!(xmldata = soap_format_xml(privdata->mt_xml_file, msg)))
		{
			debug("bb.soap.client",1,"SOAP[%s]: client - failed to format message for sending", octstr_get_cstr(privdata->name));
			bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_MALFORMED);
			continue;
		}

		debug("bb.soap.client",0,"SOAP[%s]: client - Sending message <%s>", octstr_get_cstr(privdata->name),
			octstr_get_cstr(msg->sms.msgdata));
		debug("bb.soap.client",0,"SOAP[%s]: data dump: <%s>", octstr_get_cstr(privdata->name), octstr_get_cstr(xmldata));

		// send to the server
		soap_send(privdata, xmldata, msg);
		
		// store in the second queue so that soap_read_response will know what to do
		//list_append(privdata->pending_ack_queue,msg);
	
		// don't need this anymore
		O_DESTROY(xmldata);
	}
}

/*
 * function soap_format_xml()
 *	fill in the fields in an XML template with data from a message
 * Input: Octstr containing an XML template,  Msg structure
 * Returns: Octstr xml formated data or NULL on error
 **/
static Octstr* soap_format_xml(Octstr* xml_file, Msg* msg)
{
    Octstr* xml;
    long t;
    long start = -1;
    int curly_enclose = 0;
	
    xml = octstr_create("");

    for (t = 0; t < octstr_len(xml_file); ++t) {
        unsigned char c;
            
        if ((c = octstr_get_char(xml_file,t)) == '%') {
             // found start of token
             start = t+1;
             continue;
        }

        if (c == '{' && start == t) { // the token is enclosed in curlys
	    ++start; // make sure the token is read from the next char
	    curly_enclose=1;
        }
                
        if (start < 0)
            octstr_append_char(xml,c);
        else if (
	    (curly_enclose && (c == '}')) // end of token in case of curly enclosure
		||
            (!curly_enclose && !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'))
                ) {
            // found end of token
            Octstr *data, *token;
                                        
            token = octstr_copy(xml_file,start,(t-start));
            if ((data = soap_convert_token(msg, token))) {
                octstr_append(xml, data);
                octstr_destroy(data);
            } else 
                error(0,"SOAP: format_xml - failed to format token %s using message",octstr_get_cstr(token));
            octstr_destroy(token);
            start = -1;
            if (!curly_enclose)
		--t; // I want to get that char again, to let the normal behaviour deal with it - only if it's not the ending curly
            else
		curly_enclose = 0;
        }
    }

    return xml;
}

/*
 * function soap_send()
 *	send an XML buffer using POST to the SOAP server.
 * Input: PrivData connection state, Octstr XML formatted data buffer, Message pointer to store with request
 **/
static void soap_send(PrivData* privdata, Octstr* xmlbuffer, Msg* msg)
{
	List *requestHeaders;
	Octstr* postdata;
	
	// create request headers
	requestHeaders = http_create_empty_headers();
	http_header_add(requestHeaders, "User-Agent", "Kannel " VERSION);

        if (privdata->form_urlencoded) {
                http_header_add(requestHeaders, "Content-Type", "application/x-www-form-urlencoded");
                postdata = octstr_format("%S=%E", privdata->form_variable, xmlbuffer);
        } else {
            http_header_add(requestHeaders, "Content-Type", "multipart/form-data, boundary=AaB03x");
            postdata = octstr_format("--AaB03x\r\n"
                "content-disposition: form-data; name=\"%S\"\r\n\r\n%S",
                privdata->form_variable, xmlbuffer);
        }

	// send the request along
        soap_client_init_query(privdata, requestHeaders, postdata, msg);

	O_DESTROY(postdata);
	
	// done with that
	http_destroy_headers(requestHeaders);
	return;
}

/*
 * function soap_read_response()
 *	check my HTTP caller for responses and act on them
 * Input: PrivData connection state
 **/
static void soap_read_response(SMSCConn *conn)
{
	PrivData *privdata = conn->data;
	Msg* msg;
	Octstr *responseBody, *responseURL;
	List* responseHeaders;
	int responseStatus;
	int64 msgID;
    ClientData* cd;
	
	// don't get in here unless I have some callers
    // (I shouldn't have one before I start sending messages)
	if (!list_len(privdata->soap_client))
		return;
	
	// see if we have any responses pending
	if (!(cd = soap_client_have_response(privdata->soap_client)))
		return;
	
    cd->requests--;
	msg = http_receive_result(cd->caller, &responseStatus, &responseURL, &responseHeaders, &responseBody);
	
	if (!msg) // no responses here
	{
		debug("bb.soap.read_response",0,"SOAP[%s]: sorry, no response", octstr_get_cstr(privdata->name));
		return;
	}

    if (responseStatus == -1) {
        debug("bb.soap.read_response",0,"SOAP[%s]: HTTP connection failed - blame the server (requeing msg)",
                       octstr_get_cstr(privdata->name));
        bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_TEMPORARILY);
/*      list_append(privdata->outgoing_queue, msg); */
        return;
    }
	
	debug("bb.soap.read_response",0,"SOAP[%s]: got a response %d=<%s>",
            octstr_get_cstr(privdata->name), responseStatus, responseBody?octstr_get_cstr(responseBody):octstr_get_cstr(octstr_imm("NULL")));

	// got a message from HTTP, parse it
	if ( (msgID = soap_parse_response(privdata, responseBody)) != -1)
	{ // ack
		
		char tmpid[30];
			 	
		sprintf(tmpid,"%lld",msgID);
		debug("bb.soap.read_response",0,"SOAP[%s]: ACK - id: %lld", octstr_get_cstr(privdata->name), msgID);

		dlr_add(octstr_get_cstr(conn->id),
				tmpid,
                                octstr_get_cstr(msg->sms.sender),
                                octstr_get_cstr(msg->sms.receiver),
                                octstr_get_cstr(msg->sms.service),
                        	octstr_get_cstr(msg->sms.dlr_url),
                                msg->sms.dlr_mask,
                                octstr_get_cstr(msg->sms.boxc_id));
   
 
	// generate SMSC success DLR
		if (msg->sms.dlr_mask & DLR_SUCCESS)
		{
			Msg* dlrmsg;

			dlrmsg = dlr_find(octstr_get_cstr(conn->id),tmpid,
				octstr_get_cstr(msg->sms.receiver), /* destination */
				DLR_SUCCESS);

			if (dlrmsg)
			{
				debug("bb.soap.read_response",0,"SOAP[%s]: sending DLR success", octstr_get_cstr(privdata->name));
				octstr_insert_data(dlrmsg->sms.msgdata, 0, "Success/",8);
				bb_smscconn_receive(conn, dlrmsg);
			}
			else
				error(0,"SOAP[%s]: Got SMSC_ACK but couldnt find message", octstr_get_cstr(privdata->name));
		}
		
   		// send msg back to bearerbox for recycling
	    	bb_smscconn_sent(conn, msg);
	} else { // nack
		debug("bb.soap.read_response",0,"SOAP[%s]: NACK", octstr_get_cstr(privdata->name));
		if (msg->sms.dlr_mask & DLR_SMSC_FAIL) {
			Msg* dlrmsg;
			char tmpid[30];
			 	
			sprintf(tmpid,"%lld",msgID);

				dlr_add(octstr_get_cstr(conn->id),
                                tmpid,
                                octstr_get_cstr(msg->sms.sender),
                                octstr_get_cstr(msg->sms.receiver),
                                octstr_get_cstr(msg->sms.service),
                                octstr_get_cstr(msg->sms.dlr_url),
                                msg->sms.dlr_mask,
                                octstr_get_cstr(msg->sms.boxc_id));


			dlrmsg = dlr_find(octstr_get_cstr(conn->id),tmpid,
				octstr_get_cstr(msg->sms.receiver), /* destination */
				DLR_SMSC_FAIL);
					
			if (dlrmsg) {
				debug("bb.soap.read_response",0,"SOAP[%s]: sending DLR failed", octstr_get_cstr(privdata->name));
				octstr_insert_data(dlrmsg->sms.msgdata, 0, "Failed/",7);
				bb_smscconn_receive(conn, dlrmsg);
			} else
				error(0,"SOAP[%s]: Got SMSC_NACK but couldnt find DLR message", octstr_get_cstr(privdata->name));
		}
   		
   		// send msg back to bearerbox for recycling
        	bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_MALFORMED);
	}
	
	http_destroy_headers(responseHeaders);
	O_DESTROY(responseBody);
	O_DESTROY(responseURL);
}

/*
 * function soap_parse_response()
 *	parse the response from the server to find the message ID
 * Input: Connection session data, Octstr xml buffer
 * Returns: message ID parsed or -1 if parsing failed (for example - a NACK received)
 *
 * Possible bug : I use list_get() liberaly here, after checking that I have enough items,
 *                but if list_get() returns NULL for an empty item, things might break - and
 *                not in a nice way.
 **/
static int64 soap_parse_response(PrivData* privdata, Octstr* xmlResponse)
{
    int64 msgID = -1;
    long responseStatus = -1;
    xmlDocPtr responseDoc;
    xmlNodePtr root;
    List* maps;
    char* keywords[] = { "id", "result" };
    char* sscans[] = { "%lld", "%ld" };
    void* pointers[] = { &msgID, &responseStatus };
    
    if (!xmlResponse)
         return -1;
         // FIXME: do something here
	
    // parse XML
    if ( !(responseDoc = xmlParseDoc(octstr_get_cstr(xmlResponse))) ) {
        error(0,"SOAP[%s]: couldn't parse XML response <%s> in MT parsing", 
            octstr_get_cstr(privdata->name), octstr_get_cstr(xmlResponse));		
       	return -1;
    }
	
    // get root element
    if ( ! (root = xmlDocGetRootElement(responseDoc)) ) {
	error(0,"SOAP[%s]: couldn't get XML root element in MT parsing", 
            octstr_get_cstr(privdata->name));		
	xmlFreeDoc(responseDoc);
	return -1;
    }
    
    // create the argument map
    maps = soap_create_map(privdata->mt_spec_file, 2, keywords, sscans, pointers);
        
    // run the map and the xml through the parser
    if (soap_map_xml_data(root, maps) < 2) {
        error(0,"SOAP[%s]: failed to map all the arguments from the XML data",
            octstr_get_cstr(privdata->name));
    }
	
    list_destroy(maps, soap_destroy_map);
	
    // done with the document
    xmlFreeDoc(responseDoc);
	
    if (msgID == -1) {
        error(0,"SOAP: parse_response - failed to parse response");
	return -1;
    }
	
    if (responseStatus != 0) {
       	error(0,"SOAP: parse_response - response code isn't 0 ! (%ld)", responseStatus);
	return -1;
    }
	
    return msgID;
}

/*
 * function soap_parse_mo()
 *	parse an incoming MO xml request, build a message from it and sent it.
 *	also generate the reponse text and status code
 * Input: module public state data, request body
 * Output: response body
 * Returns: HTTP status code on successful parse or -1 on failure
 **/
static long soap_parse_mo(SMSCConn *conn, Octstr *request, Octstr **response)
{
    PrivData *privdata = conn->data;
    xmlDocPtr requestDoc;
    xmlNodePtr root;
    Msg* msg;
    int pos = 0;
    
    List* maps;
    char receiver[30], sender[30], msgtype[30], msgdata[255], date[30];
    int64 msgid = -1;
    char* keywords[] = { "receiver", "sender", "msgtype", "msgdata", "date", "id" };
    char* sscans[] = { "%s", "%s", "%s", "%s", "%s", "%lld" };
    void* pointers[] = { &receiver, &sender, &msgtype, &msgdata, &date, &msgid };

    receiver[0] = sender[0] = msgtype[0] = msgdata[0] = date[0] = '\0';
    
    if (!response) // how am I supposed to return a response now ?
  	return -1;
		
    if (!request) {
      	*response = octstr_create(SOAP_ERROR_NO_DATA_MESSAGE);
	return SOAP_ERROR_NO_DATA_CODE;
    }
	
    // find the POST parameter name
    if ( (pos = octstr_search_char(request,'=',0)) < 0) {
      	// didn't find it - 
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    // cut of the parameter name - I'm not really interested in it
    octstr_delete(request,0,pos+1);
	
    // decode the URL encoded data
    if (octstr_url_decode(request) < 0) {
	// probably not URL encoded
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    debug("bb.soap.parse_mo",0,"SOAP[%s]: parse_mo  - MO request dump <%s>", octstr_get_cstr(privdata->name),octstr_get_cstr(request));
	
    // parse XML
    if ( !(requestDoc = xmlParseDoc(octstr_get_cstr(request))) ) {
	error(0,"SOAP[%s]: parse_mo couldn't parse XML response", octstr_get_cstr(privdata->name));		
	return -1;
    }
	
    // get root element
    if ( ! (root = xmlDocGetRootElement(requestDoc)) ) {
	error(0,"SOAP[%s]: parse_mo couldn't get XML root element for request", octstr_get_cstr(privdata->name));		
	xmlFreeDoc(requestDoc);
	return -1;
    }
    
    // create the argument map
    maps = soap_create_map(privdata->mo_spec_file, 6, keywords, sscans, pointers);
    
    // run the map and the xml through the parser
    if (soap_map_xml_data(root, maps) < list_len(maps)) {
        error(0,"SOAP[%s]: parse_mo failed to map all the arguments from the XML data",
            octstr_get_cstr(privdata->name));
    }
	
    list_destroy(maps, soap_destroy_map);
	
    // done with the document
    xmlFreeDoc(requestDoc);
	
    if (msgid == -1) {
        error(0,"SOAP: parse_mo - failed to get message ID");
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    if (strlen(receiver) == 0) {
       	error(0,"SOAP: parse_mo - failed to get receiver");
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    if (strlen(sender) == 0) {
       	error(0,"SOAP: parse_mo - failed to get sender");
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    if (strlen(msgtype) == 0) {
        // not an error = ENCODING should be considered implicit.
        strcpy(msgtype,"A");        
    } 
	
    if (strlen(msgdata) == 0) {
       	error(0,"SOAP: parse_mo - failed to get message content");
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    // create me a message to store data in it
    msg = msg_create(sms);
	
    // fill in the fields from the parsed arguments
    msg->sms.sender = octstr_create(sender);
    msg->sms.receiver = octstr_create(receiver);
    msg->sms.id = msgid;
    msg->sms.msgdata = octstr_create(msgdata);
    
    // fill in the date
    if (strlen(date)) {
        struct universaltime tm;
        Octstr* temp = octstr_create(date);
        if (date_parse_iso(&tm, temp)) 
            /* failed to parse the date */
            msg->sms.time = time(NULL);
        else 
            msg->sms.time = date_convert_universal(&tm);
        octstr_destroy(temp);
    } else
        msg->sms.time = time(NULL);
        
    // check message data type - B stands for "base 64 encoded" in Team Mobile
    if (!strcmp(msgtype, "B")) {
        octstr_base64_to_binary(msg->sms.msgdata);
        msg->sms.coding = DC_8BIT;
    } else if (!strcmp(msgtype, "binary")) {
        octstr_hex_to_binary(msg->sms.msgdata);
        msg->sms.coding = DC_8BIT;
    } else {
    	
/* not gonna play this game - just convert from whatever alt_charset is set to, to UCS2 		
    // scan message for unicode chars (utf-8 encoded)	
    pos = 0;
    while (pos < octstr_len(msg->sms.msgdata)) {
	if (octstr_get_char(msg->sms.msgdata,pos) & 128)
	    break;
	++pos;
    }
    
    if (pos < octstr_len(msg->sms.msgdata)) {
        // message has some unicode - we need to convert to UCS-2 first
        Octstr* temp = msg->sms.msgdata;
        msg->sms.coding = DC_UCS2;
   	if (charset_from_utf8(temp, &(msg->sms.msgdata), octstr_imm("UCS-2")) < 0) { 
            error(0,"SOAP[%s]: parse_mo couldn't convert msg text from UTF-8 to UCS-2. leaving as is.", octstr_get_cstr(privdata->name));
            O_DESTROY(msg->sms.msgdata);
            msg->sms.msgdata = octstr_duplicate(temp);
	    // set coding to 8bit and hope for the best
	    msg->sms.coding = DC_8BIT;
        }
        octstr_destroy(temp);
    } else 
         // not unicode : 7bit
         msg->sms.coding = DC_7BIT; 
*/
        /* if it's not binary, then assume unicode and convert from alt_charset to UCS2 */
        msg->sms.coding = DC_UCS2;

        if (!octstr_case_compare(privdata->alt_charset, octstr_imm("UCS-2"))) 
            charset_convert(msg->sms.msgdata, octstr_get_cstr(privdata->alt_charset), "UCS-2BE");
        msg->sms.charset = octstr_create("UCS-2");
    }
    
   
    debug("bb.soap.parse_mo",0,"SOAP[%s]: message decoded -", octstr_get_cstr(privdata->name));
    octstr_dump(msg->sms.msgdata,0);
	
    // check that we have all the fields necessary
    if (!(msg->sms.sender)
	||
	!(msg->sms.msgdata)) {
	// generate error message
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    // setup defaults
    if (msg->sms.time <= 0)
	msg->sms.time = time(NULL);
	
    if (!msg->sms.receiver)
	msg->sms.receiver = octstr_create(SOAP_DEFAULT_SENDER_STRING);
	
    if (!msg->sms.smsc_id)
	msg->sms.smsc_id = octstr_duplicate(conn->id);
	
    *response = soap_format_xml(privdata->mo_xml_file,msg);
    debug("bb.soap.reponse_dlr",0,"SOAP[%s]: data dump: <%s>", octstr_get_cstr(privdata->name), octstr_get_cstr(*response));
	
    bb_smscconn_receive(conn,msg);
	
    return SOAP_QUERY_OK;
}

/*
 * function soap_parse_dlr()
 *	parse an incoming DLR xml request, build a message from it and sent it.
 *	also generate the reponse text and status code
 * Input: module public state data, request body
 * Output: response body
 * Returns: HTTP status code on successful parse or -1 on failure
 **/
static long soap_parse_dlr(SMSCConn *conn, Octstr *request, Octstr **response)
{
    PrivData *privdata = conn->data;
    xmlDocPtr requestDoc;
    xmlNodePtr root;
    Msg* dlrmsg = NULL;
    long dlrtype;
    int pos;
	
    List* maps;
    char receiver[30], soapdate[30], msgid[30];
    long result = -1;
    char* keywords[] = { "receiver", "soapdate", "msgid", "result" };
    char* sscans[] = { "%s", "%s", "%s", "%ld" };
    void* pointers[] = { &receiver, &soapdate, &msgid, &result };

    receiver[0] = soapdate[0] = msgid[0] = '\0';
    
    if (!response) // how am I supposed to return a response now ?
  	return -1;
		
    if (!request) {
      	*response = octstr_create(SOAP_ERROR_NO_DATA_MESSAGE);
	return SOAP_ERROR_NO_DATA_CODE;
    }
	
    // find the POST parameter name
    if ( (pos = octstr_search_char(request,'=',0)) < 0) {
      	// didn't find it - 
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    // cut of the parameter name - I'm not really interested in it
    octstr_delete(request,0,pos+1);
	
    // decode the URL encoded data
    if (octstr_url_decode(request) < 0) {
	// probably not URL encoded
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr  - DLR request dump <%s>", octstr_get_cstr(privdata->name),octstr_get_cstr(request));
	
    // parse XML
    if ( !(requestDoc = xmlParseDoc(octstr_get_cstr(request))) ) {
	error(0,"SOAP[%s]: parse_dlr couldn't parse XML response", octstr_get_cstr(privdata->name));		
	return -1;
    }
	
    // get root element
    if ( ! (root = xmlDocGetRootElement(requestDoc)) ) {
	error(0,"SOAP[%s]: parse_dlr couldn't get XML root element for request", octstr_get_cstr(privdata->name));		
	xmlFreeDoc(requestDoc);
	return -1;
    }
    
    // create the argument map
    maps = soap_create_map(privdata->dlr_spec_file, 4, keywords, sscans, pointers);
    
    // run the map and the xml through the parser
    if (soap_map_xml_data(root, maps) < 4) {
        error(0,"SOAP[%s]: parse_dlr failed to map all the arguments from the XML data",
            octstr_get_cstr(privdata->name));
    }
	
    list_destroy(maps, soap_destroy_map);
	
    // done with the document
    xmlFreeDoc(requestDoc);
	
    if (strlen(msgid) == 0) {
        error(0,"SOAP: parse_dlr - failed to get message ID");
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    if (result == -1) {
        error(0,"SOAP: parse_dlr - failed to get delivery code");
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }
	
    if (strlen(receiver) == 0) {
       	error(0,"SOAP: parse_mo - failed to get receiver");
	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
	return SOAP_ERROR_MALFORMED_DATA_CODE;
    }

    // log the delivery code - this could be used to determine dlrtype (or so I hope)
    debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr DELIVERY_CODE : %ld", octstr_get_cstr(privdata->name),result);
    if (result == 0)
        dlrtype = DLR_SUCCESS;
    else 
        dlrtype = DLR_FAIL;
	
    // fetch the DLR
    dlrmsg = dlr_find(octstr_get_cstr(conn->id),msgid, receiver, /* destination */
			dlrtype);
			
    if (!dlrmsg) {
	error(0,"SOAP[%s]: parse_dlr invoked (%ld), but no DLR found for MsgID %s", octstr_get_cstr(privdata->name),dlrtype,msgid);		
	*response = octstr_create(SOAP_ERROR_NO_DLR_MESSAGE);
	return SOAP_ERROR_NO_DLR_CODE;
    }
	
    debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr found dlr", octstr_get_cstr(privdata->name));
    switch (dlrtype) { // change message according to DLR type
	case DLR_SUCCESS:
		octstr_insert_data(dlrmsg->sms.msgdata, 0, "Delivered/",10);
		break;
	case DLR_BUFFERED:
		octstr_insert_data(dlrmsg->sms.msgdata, 0, "Buffered/",9);
		break;
	case DLR_FAIL:
		octstr_insert_data(dlrmsg->sms.msgdata, 0, "Failed/",7);
		break;
	default:
		break;
    }
    
    if (dlrmsg->sms.receiver) {
        octstr_destroy(dlrmsg->sms.sender);
        dlrmsg->sms.sender = dlrmsg->sms.receiver;
    }
    dlrmsg->sms.receiver = octstr_create(receiver);
    dlrmsg->sms.id = strtol(msgid, NULL, 10); 
		
    debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr sent dlr <%s>", octstr_get_cstr(privdata->name),octstr_get_cstr(dlrmsg->sms.msgdata));
	
    *response = soap_format_xml(privdata->dlr_xml_file, dlrmsg);
    debug("bb.soap.reponse_dlr",0,"SOAP[%s]: data dump: <%s>", octstr_get_cstr(privdata->name), octstr_get_cstr(*response));
		
    // send to bearerbox
    bb_smscconn_receive(conn, dlrmsg);
  	
    return SOAP_QUERY_OK;
}


/*
 * SOAP internal utility functions
 **/
 
/*
 * function soap_xmlnode_get_long()
 *	parse the content of an XML node and return it as an integer
 * Input: xmlNodePtr to node
 * Output: long parsed
 * Returns: 0 on success, -1 on failure
 **/
int soap_xmlnode_get_long(xmlNodePtr cur, long* out)
{
	xmlChar* nodeContent;
	char* endPointer;
	
	if (!out)  // sanity check
		return -1;
	
	// get content of tag
	if (!(nodeContent = xmlNodeGetContent(cur))) {
		error(0,"get_long - xml Node has content !");
		return -1;
	}
	
	// read the content into output
	*out = strtol(nodeContent,&endPointer,10);
	xmlFree(nodeContent);
	
	if (endPointer == (char*)nodeContent) {
		error(0,"get_long - node has non-numeric content <%s>", nodeContent);
		return -1;
	}		 		
			 		
	return 0;
}

/*
 * function soap_xmlnode_get_int64()
 *	parse the content of an XML node and return it as an int64
 * Input: xmlNodePtr to node
 * Output: long parsed
 * Returns: 0 on success, -1 on failure
 **/
int soap_xmlnode_get_int64(xmlNodePtr cur, int64* out)
{
	xmlChar* nodeContent;
	char* endPointer;
	
	if (!out)  // sanity check
		return -1;
	
	// get content of tag
	if (!(nodeContent = xmlNodeGetContent(cur))) {
		error(0,"get_long - xml Node has content !");
		return -1;
	}
	
	// read the content into output
	*out = strtoll(nodeContent,&endPointer,10);
	xmlFree(nodeContent);
	
	if (endPointer == (char*)nodeContent) {
		error(0,"get_long - node has non-numeric content <%s>", nodeContent);
		return -1;
	}		 		
			 		
	return 0;
}

/*
 * function soap_xmlnode_get_octstr()
 *	parse the content of an XML node and return it as an Octstr*
 * Input: xmlNodePtr to node
 * Output: Octstr to feel with data
 * Returns: 0 on success, -1 on failure
 **/
int soap_xmlnode_get_octstr(xmlNodePtr cur, Octstr **out) 
{
	xmlChar* nodeContent;
	
	if (!out)  // sanity check
		return -1;
	
	// get content of tag
	if (!(nodeContent = xmlNodeGetContent(cur))) {
		error(0,"get_octstr - xml Node has content !");
		return -1;
	}
	
	// store the content into output
	*out = octstr_create(nodeContent);
	xmlFree(nodeContent);
	
	if (*out)
		return 0;
	else
		return -1;
}

/*
 * function soap_read_date()
 *	convert a date string in one2one obiquis format (%Y/%M/%d:%h:%m) to epoch time
 * Input: Octstr date
 * Returns: epoch time on success or -1 on failure
 **/
time_t soap_read_date(Octstr* dateString)
{
	int pos, count;
	struct universaltime stTime; 
	long arTime[5];
	
	if (!dateString) // sanity check
		return -1;

	pos = count = 0;
	// tricky control structures are my favourite among complicated expressions ;-)
	while (count < 5 && pos < octstr_len(dateString) && 
		   (pos = octstr_parse_long(&(arTime[count++]),dateString, pos,10)) && pos != -1)
		++pos;
					   
	if (count < 5) {
		// error parsing the date
		debug("bb.soap.read_date",0,"read_date failed parsing the date value <%s>", octstr_get_cstr(dateString));
		return -1;
	}
		
	stTime.day = arTime[0];
	stTime.month = arTime[1];
	stTime.year = arTime[2];
	stTime.hour = arTime[3];
	stTime.minute = arTime[4];
	stTime.second = 0;
	return date_convert_universal(&stTime);
}

/*
 * function soap_write_date()
 *	convert an epoch time value to a date string in one2one obiquis format (%Y/%M/%d:%h:%m)
 * Input: time_t epoch time
 * Returns: an Octstr containing the date - this must be freed by the caller
 **/
static Octstr* soap_write_date(time_t date)
{
	struct tm date_parts;
	Octstr* out;
	
	if (date < 0) 
	// sanity check - I don't think it should ever happen, but I don't want to get 
	// support calls at 2am because some gateway in the UK went bananas.
		return octstr_create("ERROR"); 
	
	// split up epoch time to elements
	gmtime_r(&date, &date_parts);
	
	out = octstr_format("%d/%02d/%02d:%02d:%02d",
		date_parts.tm_year + 1900, date_parts.tm_mon + 1, date_parts.tm_mday, date_parts.tm_hour, date_parts.tm_min);
	
	// again
	if (out)
		return out;
	else
		return octstr_create("ERROR"); 
	// assuming octstr_create never fails, unlike octstr_format. this is not the case currently (both cannot fail), but it may change
}

/*
 * function soap_server_start()
 *	init and start the SOAP HTTP server
 * Input: Module public connection state data
 * Returns: 0 on success, -1 on failure
 **/
int soap_server_start(SMSCConn *conn)
{
	PrivData* privdata = conn->data;
	
	debug("bb.soap.server_stop",0,"SOAP[%s]: Starting HTTP server", octstr_get_cstr(privdata->name));
	// start the HTTP server
	if (http_open_port(privdata->port,privdata->ssl)) {
		return -1;
	}
	
	// raise server flag
	privdata->soap_server = 1; 
	
	if ( (privdata->server_thread = gwthread_create(soap_server, conn)) == -1)
	{
		error(0, "SOAP[%s]: server_start failed to create server thread!", octstr_get_cstr(privdata->name));
		http_close_port(privdata->port);
		return -1;
	}
	
	return 0;
}

/*
 * function soap_server_stop()
 *	tears down and stops the SOAP HTTP server
 * Input: Module connection state data
 **/
static void soap_server_stop(PrivData* privdata)
{
//    time_t start = time(NULL);
	
    debug("bb.soap.server_stop",0,"SOAP[%s]: Stopping HTTP server", octstr_get_cstr(privdata->name));
    // signal the server thread to stop
    privdata->soap_server = 0;
    
    // close the http server thread
    http_close_port(privdata->port);
	
    if (privdata->server_thread) {
	gwthread_wakeup(privdata->server_thread);
        gwthread_join(privdata->server_thread);
        privdata->server_thread = 0;
    }
	
    /*
    // wait upto 5 minutes for our server thread to shutdown
    while (privdata->server_thread &&  (start + 300 > time(NULL)))
	gwthread_sleep(SOAP_SLEEP_TIME);
		
    if (privdata->server_thread) {
	error(0,"SOAP[%s]: our server refuses to die!", octstr_get_cstr(privdata->name));
	privdata->server_thread = 0; // dump it either way
    }*/
    
    debug("bb.soap.server_stop",0,"SOAP[%s]: Done stopping HTTP server", octstr_get_cstr(privdata->name));
}

/*
 * function soap_client_init_query()
 *	start an HTTP query, load balance callers, and manage caller pool
 * Input: Module state, list of headers to send, data to send, message to store
 **/
static void soap_client_init_query(PrivData* privdata, List* headers, Octstr* data, Msg* msg)
{
    ClientData *cur_client = NULL;
    long index;
    

    // no list yet, generate one
    if (!privdata->soap_client)
        privdata->soap_client = list_create();

    // I'm going to change the list, so lock it
    list_lock(privdata->soap_client);

    // find the next live caller
    for (index = list_len(privdata->soap_client) - 1 ; index >= 0; --index) {
        cur_client = list_get(privdata->soap_client, index);
        if (
            cur_client->last_access + CLIENT_BUSY_TIME < time(NULL)
            &&
            cur_client->requests < CLIENT_BUSY_LOAD
            ) {
	    debug("bb.soap.init_query",0,"SOAP[%s]: init_query getting a client",octstr_get_cstr(privdata->name));
            // client is not busy - get it
            list_delete(privdata->soap_client, index, 1);
            break;
        }
        cur_client = NULL;
    }

    if (!cur_client) {
        if (list_len(privdata->soap_client) > MAX_SOAP_CLIENTS) {
	    debug("bb.soap.init_query",0,"SOAP[%s]: init_query all clients are busy, getting the first client",octstr_get_cstr(privdata->name));
            // query not dispatched, and we have the max number of callers -
            // grab the first caller (least used) from the list
            cur_client = list_extract_first(privdata->soap_client);
        } else {
            // query not dispatched, and we don't have enough callers -
            // start a new one
	    debug("bb.soap.init_query",0,"SOAP[%s]: init_query creates a new client",octstr_get_cstr(privdata->name));
            cur_client = soap_create_client_data();
        }
    }

    // dispatch query to selected client
    http_start_request(cur_client->caller, HTTP_METHOD_GET, privdata->uri, headers, data, 1, msg, NULL);
    cur_client->requests++;
    cur_client->last_access = time(NULL);
    list_append(privdata->soap_client, cur_client);
    list_unlock(privdata->soap_client);
}

/*
 * function soap_create_client_data()
 *	creates a new SOAP client data structure and caller
 * Returns: an initialized client data structure with a live caller
 **/
static ClientData* soap_create_client_data()
{
    ClientData *cd = gw_malloc(sizeof(ClientData));

    cd->last_access = 0;
    cd->requests = 0;
    cd->caller = http_caller_create();

    return cd;
}

/*
 * function soap_destroy_client_data()
 *	destroy a SOAP client caller
 * Input: pointer to a client data structure with a live caller
 **/
static void soap_destroy_client_data(void* data)
{
    ClientData *cd = (ClientData*) data;

	// signal the caller to stop and then kill it
    if (cd->caller) {
        http_caller_signal_shutdown(cd->caller);
        http_caller_destroy(cd->caller);
    }	
}

/*
 * function soap_client_have_response()
 *	return a caller from the pool that has responses waiting
 * Input: ClientData pool
 * Returns: a client data structure that has a caller with responses waiting,
 *  or NULL if none are found
 **/
static ClientData* soap_client_have_response(List* client_list)
{
    long index;
    ClientData* cd;

    if (!client_list)
        return NULL;

    // lock the list so nobody removes or adds clients while I'm looping on the list
    list_lock(client_list);

    for (index = list_len(client_list) - 1; index >= 0; --index) {
        cd = list_get(client_list,index);
        if (list_len(cd->caller)) {
            list_unlock(client_list);
            return list_get(client_list, index);
        }
    }

    list_unlock(client_list);
    return NULL;
}

/*
 * function soap_convert_token()
 *	convert a member of the message structure and return it as octstr
 * Input: member name
 * Returns: an Octstr containing the content of the data member from the message structure
 *  or NULL if an error occured.
 **/
static Octstr* soap_convert_token(Msg* msg, Octstr* name)
{
        char buf[20];
        
        // first check for special tokens :
        if (msg->type == sms && !octstr_str_compare(name,"validity30"))
            // validity in 30 minutes increment
            return octstr_format("%ld",(msg->sms.validity?msg->sms.validity:SOAP_DEFAULT_VALIDITY) / 30);
        else if (msg->type == sms && !octstr_str_compare(name, "validity_date"))
            /* date on which the message's validity expires */
            return date_create_iso(msg->sms.time+(60*msg->sms.validity));
        else if (msg->type == sms && !octstr_str_compare(name, "dlrmask_smsc_yn"))
                 // "Y" for any of the SMSC generated DLRs, "N" otherwise
            return octstr_create(msg->sms.dlr_mask & (DLR_FAIL | DLR_SUCCESS | DLR_BUFFERED) ? "Y" : "N");
        else if (msg->type == sms && !octstr_str_compare(name, "121date"))
            return soap_write_date(msg->sms.time);
        else if (msg->type == sms && !octstr_str_compare(name, "date"))
            return date_create_iso(msg->sms.time);
        else if (msg->type == sms && !octstr_str_compare(name, "rand"))
            return octstr_format("%d",gw_rand());
        else if (msg->type == sms && !octstr_str_compare(name, "dlrmask_success_01"))
                 // "1" for any of the SMSC generated DLRs, "0" otherwise
            return octstr_create(msg->sms.dlr_mask & (DLR_SUCCESS) ? "0" : "1");

#define INTEGER(fieldname) \
        if (!octstr_str_compare(name, #fieldname)) { \
                sprintf(buf,"%ld", p->fieldname); \
                return octstr_create(buf); \
        }
#define INT64(fieldname) \
        if (!octstr_str_compare(name, #fieldname)) { \
                sprintf(buf,"%lld", p->fieldname); \
                return octstr_create(buf); \
        }
#define OCTSTR(fieldname) \
        if (!octstr_str_compare(name, #fieldname)) \
                return octstr_duplicate(p->fieldname);
#define MSG(type, stmt) \
        case type: { struct type *p = &msg->type; stmt } break;

   switch (msg->type) {
#include "msg-decl.h"
    default:
         error(0, "Internal error: unknown message type %d",
              msg->type);
         return NULL;
    }

    return NULL;
}

/*
 * function soap_create_map()
 *	convert a XML parsing spec file and a list of recognized keywords to an argument map
 * Input: XML parsing spec buffer and lists of keywords, types and pointers
 * Returns: number of variables successfuly mapped
 **/
List* soap_create_map(Octstr* spec, long count, char* keywords[], char* types[], void* storage[])
{
    List *parse_items, *out;
    
    out = list_create();
    
    // read the list of items from the spec file
    parse_items = octstr_split(spec, octstr_imm("\n"));

    while (list_len(parse_items)) {
        ArgumentMap* map;
        int index;
        Octstr* temp = list_extract_first(parse_items);
        List* item = octstr_split_words(temp);
        
        // make sure we have at least two things in the item : a keyword and a path
        if (list_len(item) < 2) {
            debug("bb.soap.parse_create_map",0,"SOAP: broken spec file line <%s> in soap_create_map",
                octstr_get_cstr(temp));
            octstr_destroy(temp);
            list_destroy(item, octstr_destroy_item);
            continue;
        }
        
        // check that the keyword matches something in the list of keywords
        for (index = 0; index < count; ++index) {
            if (!octstr_str_compare(list_get(item,0), keywords[index])) {
                // allocate the structure
                map = gw_malloc(sizeof(ArgumentMap));
                map->name = list_extract_first(item);
                map->path = list_extract_first(item);
                map->attribute = list_extract_first(item); // could be NULL, but that is ok
                map->sscan_type = octstr_create(types[index]);
                map->store = storage[index];
                list_append(out, map);
                break;
            }
        }
        
        // destroy temporary variables;
        list_destroy(item, octstr_destroy_item);
        octstr_destroy(temp);
    }
    
    list_destroy(parse_items, octstr_destroy_item);
    
    return out;
}

/*
 * function soap_destroy_map()
 *	destroy a map structure. used in list_destroy(calls);
 * Input: pointer to a map structure;
 **/
void soap_destroy_map(void *item)
{
    ArgumentMap* map = item;
    octstr_destroy(map->name);
    octstr_destroy(map->path);
    octstr_destroy(map->attribute);
    octstr_destroy(map->sscan_type);
    gw_free(map);
}

/*
 * function soap_map_xml_data()
 *	maps content of an XML structure to a list of variables using a map
 * Input: XML document and an argument map
 * Returns: number of variables successfuly mapped
 **/
int soap_map_xml_data(xmlNodePtr xml, List* maps)
{
    int mapindex = 0, args = 0;
    xmlNodePtr node, parent;
    
    // step through the items on the map
    while (mapindex < list_len(maps)) {
        Octstr* temp;
        int index = 0;
        ArgumentMap* map = list_get(maps,mapindex);
        // split the path elements
        List* path_elements = octstr_split(map->path, octstr_imm("/"));
                
        // walk the message tree down the path
        parent = NULL;
        node = xml;
        while (index < list_len(path_elements)) {
            int found = 0;
            // get the next path element
            temp = list_get(path_elements, index);
            do {
                if (!octstr_str_compare(temp,node->name)) {
                    // found what we're looking for
                    if (!(node->xmlChildrenNode) && index < list_len(path_elements)) {
                        // while this is indeed the item we are looking for, it's not the end
                        // of the path, and this item has no children
                        debug("bb.soap.map_xml_data",0,"SOAP: error parsing XML, looking for <%s>, but element <%s> has no children",
                            octstr_get_cstr(map->path), octstr_get_cstr(temp));
                    } else {
                        ++index; // go down the path
                        parent = node; // remember where I came from
                        node = node->xmlChildrenNode; // trace into the node
                        ++found;
                        break; // escape to the next level
                    }
                } 
            } while ((node = node->next));
            
            if (!found) { 
                // didn't find anything - back track
                node = parent;
                parent = node->parent;
                if (--index < 0) 
                    // I backtracked too much up the tree, nowhere to go to
                    break;
                    
                if (!(node = node->next)) 
                    // no more childs under the main tree to look under, abort
                    break;
            }
        }
            
        if (index < list_len(path_elements)) {
            // didn't find the full path
            debug("bb.soap.map_xml_data",0,"SOAP: didn't find element for keyword <%s> in XML data",
                octstr_get_cstr(map->name));
            list_destroy(path_elements, octstr_destroy_item);
            ++mapindex;
            continue;
        }
            
        // found the correct node (it's stored in parent)
        if (map->attribute) {
           // The user wants to get an attribute
           xmlChar* content;
           content = xmlGetProp(parent, octstr_get_cstr(map->attribute));
           if (content)
               temp = octstr_create(content);
           else // dont treat an empty or non-existant attribute as an error right away
               temp = octstr_create(""); 
           xmlFree(content);
        } else {
           // the user wants to get the content
           xmlChar* content;
           content = xmlNodeGetContent(parent);
           if (content)
               temp = octstr_create(content);
           else // don't treat an empty tag an error right away
               temp = octstr_create("");
           xmlFree(content);
        }
        
        // parse the content using sscan_type from the map
        octstr_strip_blanks(temp);
        if (!octstr_str_compare(map->sscan_type,"%s")) { 
            // special processing of %s - this means the whole string, while sscanf stops at spaces
            strcpy(map->store,octstr_get_cstr(temp));
            ++args;
        } else {
            if (!sscanf(octstr_get_cstr(temp), octstr_get_cstr(map->sscan_type), map->store)) {
                debug("bb.soap.map_xml_data",0,"SOAP: failed to scan content '%s' for '%s' in xml parsing",
                    octstr_get_cstr(temp), octstr_get_cstr(map->sscan_type));
            } else {
                ++args;
            }
        }
        
        // done for this item
        octstr_destroy(temp);
        list_destroy(path_elements, octstr_destroy_item);
        ++mapindex;
    }

    return args;
}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.