Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

smsc_soap.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * smsc_soap.c - Implementation of SOAP (XML over HTTP) as a Kannel module
00059  *
00060  * Oded Arbel,     m-Wise inc (oded@m-wise.com)
00061  * Dima Milentiev, m-Wise inc (dima@m-wise.com)
00062  *
00063  * ChangeLog:
00064  *
00065  * 20/02/2002: started - copied smsc_mam.c for starting
00066  * 25/02/2002: implemented MT sending
00067  * 09/05/2002: fixed problem crash when HTTP connection fails.
00068  *             send message back to bearerbox on HTTP failure instead of local queue
00069  * 19/05/2002: stripped leading + from international numbers
00070  * 20/05/2002: fixed previous change
00071  *             changed Transaction Id returned to support 64 bit integers
00072  * 27/05/2002: changed DLR creation to store the transaction ID instead of timestamp
00073  *              added parsing of human readable time in DLR
00074  * 28/05/2002: added multi thread sending support
00075  * 02/06/2002: changed validity computing to accept minutes instead of seconds
00076  * 04/06/2002: Changed callbacks to take into account that they might be called while the connection
00077  *              is dead.
00078 
00079  * 04/06/2002: Started to implement generic parsing engine.
00080  * 09/06/2002: Removed hardcoded XML generation and parsing
00081  * 22/07/2002: Removed wrong assignment of charset_convert return code to msg->sms.coding
00082  * 30/07/2002: fixed wrong format for year in soap_write_date
00083  *               additional debug and process for invalid charset conversion
00084  * 04/08/2002: forced chraset_conversion to/from UCS-2 to use big endianity
00085  *             added curly bracing support to XML data tokens
00086  * 04/09/2002: Added some debugging info
00087  * 05/09/2002: Changed dlr_add and http_start_request calls to support current CVS
00088  * 26/09/2002: Added soap_fetch_xml_data
00089  * 29/09/2002: Changed Ack/Nack to process case when Ack not return msg ID, move declaration to fix worning
00090  * 01/10/2002: started to change MO general
00091  * 07/10/2002: MT generalization
00092  * 
00093  * TODOs:
00094  *  - add a configuration option to the max number of messages a client can send, and use
00095  *    and implement KeepAlive in the clients.
00096  *  - support XML generation through DTD
00097  *  - support XML parsing through DTD
00098  *
00099  *
00100  * Usage: add the following to kannel.conf:
00101  *
00102  *   group = smsc
00103  *   smsc = soap
00104  *   send-url = <URI>       - URI to send SOAP bubbles at (mandatory)
00105  *   receive-port = <number>    - port number to bind our server on (Default: disabled - MT only)
00106  *   xml-files = "MT.xml;MO.xml;DLR.xml" - XML templates for generation of MT messages 
00107  *                                         and MO and DLR responses
00108  *   xmlspec-files = "MT.spec;MO.spec;DLR.spec" - XML path spec files for parsing of MT ´
00109  *                                                response and MO and DLR submission
00110  *   alt-charset = "character map" - charset in which a text message is received (default UTF-8)
00111  *
00112  */
00113 
00114 #include <sys/types.h>
00115 #include <sys/socket.h>
00116 #include <unistd.h>
00117 #include <errno.h>
00118 #include <time.h>
00119 #include <limits.h>
00120 
00121 #include "gwlib/gwlib.h"
00122 #include "gwlib/http.h"
00123 #include "smscconn.h"
00124 #include "smscconn_p.h"
00125 #include "bb_smscconn_cb.h"
00126 #include "msg.h"
00127 #include "sms.h"
00128 #include "dlr.h"
00129 
00130 /* libxml include */
00131 #include <libxml/xmlmemory.h>
00132 #include <libxml/parser.h>
00133 
00134 /* Defines and defaults */
00135 #define SOAP_SLEEP_TIME             0.01
00136 #define SOAP_MAX_MESSAGE_PER_ROUND      1
00137 #define SOAP_DEFAULT_SENDER_STRING      "Kannel"
00138 #define SOAP_DEFAULT_VALIDITY           60
00139 
00140 /* URIs for MOs and delivery reports */
00141 #define SOAP_MO_URI         "/mo"
00142 #define SOAP_DLR_URI        "/dlr"
00143 
00144 /* default reponses to HTTP queries */
00145 #define SOAP_DEFAULT_MESSAGE                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>No method by that name</Error>"
00146 #define SOAP_ERROR_NO_DLR_MESSAGE           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Sorry - no DLR for that MT</Error>"
00147 #define SOAP_ERROR_DLR_MESSAGE              "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Fatal error while trying to parse delivery report</Error>"
00148 #define SOAP_ERROR_MO_MESSAGE               "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Fatal error while trying to parse incoming MO</Error>"
00149 #define SOAP_ERROR_NO_DATA_MESSAGE          "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>No data received</Error>"
00150 #define SOAP_ERROR_MALFORMED_DATA_MESSAGE           "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Malformed data received</Error>"
00151 
00152 /* map HTTP status codes to SOAP HTTP reply codes */
00153 #define SOAP_ERROR_NO_DLR_CODE              HTTP_BAD_METHOD
00154 #define SOAP_DEFAULT_CODE               HTTP_NOT_FOUND
00155 #define SOAP_ERROR_DLR_CODE             HTTP_INTERNAL_SERVER_ERROR
00156 #define SOAP_ERROR_MO_CODE              HTTP_INTERNAL_SERVER_ERROR
00157 #define SOAP_ERROR_NO_DATA_CODE             HTTP_NOT_IMPLEMENTED
00158 #define SOAP_ERROR_MALFORMED_DATA_CODE                  HTTP_BAD_GATEWAY
00159 #define SOAP_QUERY_OK                   HTTP_OK
00160 
00161 /* compile time configuration defines */
00162 #undef HUMAN_TIME
00163 
00164 #define MIN_SOAP_CLIENTS            5
00165 #define MAX_SOAP_CLIENTS            50
00166 #define CLIENT_BUSY_TIME            5
00167 #define CLIENT_TEARDOWN_TIME        600
00168 #define CLIENT_BUSY_LOAD            5
00169 
00170 #define SPEC_DEFAULT                "default"
00171 
00172 /* private data store for the SOAP module */
00173 typedef struct privdata {
00174     List *outgoing_queue;   /* queue to hold unsent messages */
00175 
00176     long listener_thread;   /* SOAP HTTP client and module managment */
00177     long server_thread;     /* SOAP HTTP server */
00178 
00179     int shutdown;       /* Internal signal to shut down */
00180     int soap_server;    /* internal signal to shut down the server */
00181 
00182     long port;              /* listener port */
00183     int ssl;        /* flag whether to use SSL for the server */
00184 
00185     Octstr *uri;        /* URI to send MTs on */
00186 
00187     Octstr *allow_ip, *deny_ip; /* connection allowed mask */
00188 
00189     List* soap_client;  /* list to hold callers */
00190 
00191     Octstr* name;       /* connection name for use in private functions that want to do logging */
00192 
00193     /* SOAP configurtion */
00194     Octstr* form_variable; /* variable name used in post */
00195     int form_urlencoded; /* whether to send the data urlencoded or multipart */
00196     Octstr* alt_charset;   /* alt-charset to use */
00197 
00198     Octstr* mt_xml_file;
00199     Octstr* mt_spec_file;
00200     Octstr* mo_xml_file;
00201     Octstr* mo_spec_file;
00202     Octstr* dlr_xml_file;
00203     Octstr* dlr_spec_file;
00204     Octstr* mo_deps_file;
00205 } PrivData;
00206 
00207 /* struct to hold one HTTP client connection (I hope) */
00208 typedef struct client_data {
00209     time_t last_access;
00210     unsigned long requests;
00211     HTTPCaller* caller;
00212 } ClientData;
00213 
00214 /* struct useful for the XML mapping routines */
00215 typedef struct argument_map {
00216     Octstr* name;
00217     Octstr* path;
00218     Octstr* attribute;
00219     Octstr* sscan_type;
00220     void* store;
00221 } ArgumentMap;
00222 
00223 /* useful macros go here (some of these were ripped of other modules,
00224    so maybe its better to put them in a shared file) */
00225 #define O_DESTROY(a)    { if(a) octstr_destroy(a); a=NULL; }
00226 
00227 /*
00228  * SOAP module public API towards bearerbox
00229  */
00230 
00231 /* module entry point - will also be defined in smscconn_p.h */
00232 int smsc_soap_create(SMSCConn *conn, CfgGroup *cfg);
00233 /* callback for bearerbox to add messages to our queue */
00234 static int soap_add_msg_cb(SMSCConn *conn, Msg *sms);
00235 /* callback for bearerbox to signal a shutdown */
00236 static int soap_shutdown_cb(SMSCConn *conn, int finish_sending);
00237 /* callback for bearerbox to signal us to start the connection */
00238 static void soap_start_cb(SMSCConn *conn);
00239 /* callback for bearerbox to signal us to drop the connection */
00240 static void soap_stop_cb(SMSCConn *conn);
00241 /* callback for bearerbox to query on the number of messages in our queue */
00242 static long soap_queued_cb(SMSCConn *conn);
00243  
00244 /*
00245  * SOAP module thread functions (created by smsc_soap_create())
00246  */
00247 
00248 /* SOAP module thread for launching HTTP clients. */
00249 static void soap_listener(void *arg);
00250 /* SOAP HTTP server thread for incoming MO */
00251 static void soap_server(void *arg);
00252  
00253 /*
00254  * SOAP module internal protocol implementation functions
00255  */
00256 
00257 /* start the loop to send all messages in the queue */
00258 static void soap_send_loop(SMSCConn *conn);
00259 /* function used to send a single MT message */
00260 static void soap_send(PrivData *privdata, Octstr *xmlbuffer, Msg *msgid);
00261 /* called to retrieve HTTP responses from the HTTP library */
00262 static void soap_read_response(SMSCConn *conn);
00263 /* format a messages structure as an XML buffer */
00264 static Octstr *soap_format_xml(Octstr *xml_file, Msg *msg, PrivData *privdata);
00265 /* parse a response from the SOAP server to get the message ID */
00266 
00267 static long long soap_parse_response(PrivData *privdata, Octstr *xmlResponse);
00268 /* parse an incoming MO xml */
00269 static long soap_parse_mo(SMSCConn *conn, Octstr *request, Octstr **response);
00270 /* parse an incoming derlivery report */
00271 static long soap_parse_dlr(SMSCConn *conn, Octstr *request, Octstr **response);
00272 
00273 /*
00274  * SOAP internal utility functions
00275  */
00276 /* parse an integer out of a XML node */
00277 int soap_xmlnode_get_long(xmlNodePtr cur, long *out);
00278 /* parse an int64 out of a XML node */
00279 int soap_xmlnode_get_int64(xmlNodePtr cur, long long *out);
00280 /* parse a string out of a XML node */
00281 int soap_xmlnode_get_octstr(xmlNodePtr cur, Octstr **out);
00282 /* convert a one2one date format to epoch time */
00283 time_t soap_read_date(Octstr *dateString);
00284 /* convert a epoch time to one2one date format */
00285 static Octstr *soap_write_date(time_t date);
00286 /* start the SOAP server */
00287 int soap_server_start(SMSCConn *conn);
00288 /* stop the SOAP server */
00289 static void soap_server_stop(PrivData *privdata);
00290 /* create a new SOAP client caller */
00291 static ClientData *soap_create_client_data();
00292 /* destroy a SOAP client caller */
00293 static void soap_destroy_client_data(void *data);
00294 /* start an HTTP query */
00295 static void soap_client_init_query(PrivData *privdata, List *headers, Octstr *data, Msg *msg);
00296 /* return a caller from the pool that has responses waiting */
00297 static ClientData *soap_client_have_response(List *client_list);
00298 /* return data from a message according to its name */
00299 static Octstr *soap_convert_token(Msg *msg, Octstr *name, PrivData *privdata);
00300 /* convert a XML parsing spec file and a list of recognized keywords to an argument map */
00301 List *soap_create_map(Octstr* spec, long count, char* keywords[], char* types[], void* storage[]);
00302 /* destroy a map structure */
00303 void soap_destroy_map(void *item);
00304 /* map content in a XML structure to a list of variable using a spec file */
00305 int soap_map_xml_data(xmlNodePtr xml, List* maps);
00306 /* fetch content from the XML */
00307 Octstr* soap_fetch_xml_data(xmlNodePtr xml, Octstr* path);
00308 
00309 /* MO */
00310 /* search and release dependences for keys */
00311 long soap_release_dependences(Octstr* deps, List* lstmaps, Msg* msg, PrivData *privdata);
00312 /* for appropriate <key> call function referenced by key_func_ind */
00313 int soap_process_deps(int key_index, int key_func_ind, Msg* msg, PrivData *privdata);
00314 
00315 /* <key>s specific functions */
00316 int soap_msgtype_deps(int key_func_index, Msg* msg);
00317 int soap_msgdata_deps(int key_func_index, Msg* msg, PrivData *privdata);
00318  
00319 /* MT */
00320 /* return index of functions alias in array of function aliases */
00321 int soap_lookup_function(Octstr* funcname);
00322 
00323 /* select function by index */
00324 Octstr* soap_select_function(int index, Msg* msg, PrivData* privdata);
00325 
00326 Octstr* soap_bouyg_content_attribute(Msg* msg);
00327 Octstr* soap_mobitai_content_attribute(Msg* msg);
00328 Octstr* soap_o2o_msgdata_attribute(Msg* msg, PrivData *privdata);
00329 Octstr* soap_msgdata_attribute(Msg* msg, PrivData* privdata);
00330 Octstr* soap_o2o_validity30_attribute(Msg* msg);
00331 Octstr* soap_mobitai_validity_date_attribute(Msg* msg);
00332 Octstr* soap_bouyg_validity_attribute(Msg* msg);
00333 Octstr* soap_o2o_date_attribute(Msg* msg);
00334 Octstr* soap_mobitai_date_attribute(Msg* msg);
00335 Octstr* soap_rand_attribute(Msg* msg);
00336 Octstr* soap_o2o_dlrmask_smsc_yn_attribute(Msg* msg);
00337 Octstr* soap_o2o_dlrmask_success_01_attribute(Msg* msg);
00338 
00339 /* searching 'key' in 'where' and return index of element or -1 */
00340 int soap_get_index(List* where, Octstr* key, int map_index);
00341 
00342 
00343 /**************************************************************************************
00344  * Implementation
00345  */
00346 
00347 /*
00348  * function smsc_soap_create()
00349  *  called to create and initalize the module's internal data.
00350  *  if needed also will start the connection threads
00351  * Input: SMSCConn pointer to connection data, cfgGroup pointer to configuration data
00352  * Returns: status (0 = OK, -1 = failed)
00353  */
00354 int smsc_soap_create(SMSCConn *conn, CfgGroup *cfg)
00355 {
00356     PrivData *privdata;
00357     Octstr* temp = NULL;
00358     List* filenames = NULL;
00359 
00360     /* allocate and init internat data structure */
00361     privdata = gw_malloc(sizeof(PrivData));
00362     privdata->outgoing_queue = gwlist_create();
00363     /* privdata->pending_ack_queue = gwlist_create(); */
00364 
00365     privdata->shutdown = 0;
00366     privdata->soap_client = NULL;
00367     privdata->soap_server = 0;
00368 
00369     /* read configuration data */
00370     if (cfg_get_integer(&(privdata->port), cfg, octstr_imm("receive-port-ssl")) == -1)
00371         if (cfg_get_integer(&(privdata->port), cfg, octstr_imm("receive-port")) == -1)
00372             privdata->port = 0;
00373         else
00374             privdata->ssl = 0;
00375     else
00376 
00377         privdata->ssl = 1;
00378 
00379     privdata->uri = cfg_get(cfg, octstr_imm("send-url"));
00380 
00381     privdata->allow_ip = cfg_get(cfg, octstr_imm("connect-allow-ip"));
00382     if (privdata->allow_ip)
00383         privdata->deny_ip = octstr_create("*.*.*.*");
00384     else
00385         privdata->deny_ip = NULL;
00386 
00387     /* read XML configuration */
00388     privdata->form_variable = cfg_get(cfg, octstr_imm("form-variable"));
00389     cfg_get_bool(&(privdata->form_urlencoded), cfg, octstr_imm("form-urlencoded"));
00390 
00391     privdata->alt_charset = cfg_get(cfg, octstr_imm("alt-charset"));
00392     if (!privdata->alt_charset)
00393         privdata->alt_charset = octstr_create("utf-8");
00394 
00395     /* check validity of stuff */
00396     if (privdata->port <= 0 || privdata->port > 65535) {
00397         error(0, "invalid port definition for SOAP server (%ld) - aborting", 
00398               privdata->port);
00399         goto error;
00400     }
00401 
00402     if (!privdata->uri) {
00403         error(0, "invalid or missing send-url definition for SOAP - aborting.");
00404         goto error;
00405     }
00406 
00407     if (!privdata->form_variable) {
00408         error(0, "invalid or missing form variable name definition for SOAP - aborting.");
00409         goto error;
00410     }
00411 
00412     /* load XML templates and specs */
00413     filenames = octstr_split(temp = cfg_get(cfg,octstr_imm("xml-files")), 
00414                              octstr_imm(";"));
00415     octstr_destroy(temp);
00416     if (gwlist_len(filenames) < 3) {
00417         error(0,"SOAP: Not enough template files for XML generation, you need 3 - aborting"); 
00418         goto error;
00419     }
00420     if ( !(privdata->mt_xml_file = octstr_read_file(
00421             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
00422         error(0,"SOAP: Can't load XML template for MT - aborting"); 
00423         goto error;
00424 
00425     }
00426     octstr_destroy(temp);
00427     if ( !(privdata->mo_xml_file = octstr_read_file(
00428             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
00429         error(0,"SOAP: Can't load XML template for MO - aborting"); 
00430         goto error;
00431     }
00432     octstr_destroy(temp);
00433     if ( !(privdata->dlr_xml_file = octstr_read_file(
00434             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
00435 
00436         error(0,"SOAP: Can't load XML template for DLR - aborting"); 
00437         goto error;
00438     }
00439     octstr_destroy(temp);
00440     gwlist_destroy(filenames, octstr_destroy_item);
00441 
00442     filenames = octstr_split(temp = cfg_get(cfg,octstr_imm("xmlspec-files")), 
00443                              octstr_imm(";"));
00444     octstr_destroy(temp);
00445     if (gwlist_len(filenames) < 4) {
00446         error(0,"Not enough spec files for XML parsing, you need 4 - aborting"); 
00447         goto error;
00448     }
00449     if ( !(privdata->mt_spec_file = octstr_read_file(
00450             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
00451         error(0,"Can't load spec for MT parsing - aborting"); 
00452         goto error;
00453     }
00454     octstr_destroy(temp);
00455     if ( !(privdata->mo_spec_file = octstr_read_file(
00456             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
00457         error(0,"SOAP: Can't load spec for MO parsing - aborting"); 
00458         goto error;
00459     }
00460     octstr_destroy(temp);
00461     if ( !(privdata->dlr_spec_file = octstr_read_file(
00462             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
00463         error(0,"SOAP: Can't load spec for DLR parsing - aborting"); 
00464         goto error;
00465     }
00466     octstr_destroy(temp);
00467 
00468     if ( !(privdata->mo_deps_file = octstr_read_file(
00469             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
00470         error(0,"SOAP: Can't load 'deps' file for MO processing - aborting"); 
00471         goto error;
00472     }
00473     octstr_destroy(temp);
00474 
00475     gwlist_destroy(filenames, octstr_destroy_item);
00476 
00477     debug("bb.soap.create",0,"Connecting to %s",
00478           octstr_get_cstr(privdata->uri));
00479 
00480     /* store private data struct in connection data */
00481     conn->data = privdata;
00482 
00483     /* state my name */
00484     conn->name = octstr_format("SOAP: %s", octstr_get_cstr(privdata->uri) );
00485     privdata->name = octstr_duplicate(conn->id);
00486 
00487     /* init status vars */
00488     conn->status = SMSCCONN_CONNECTING;
00489     conn->connect_time = time(NULL);
00490 
00491     /* set up call backs for bearerbox */
00492     conn->shutdown = soap_shutdown_cb;
00493     conn->queued = soap_queued_cb;
00494     conn->start_conn = soap_start_cb;
00495     conn->stop_conn = soap_stop_cb;
00496     conn->send_msg = soap_add_msg_cb;
00497   
00498     privdata->listener_thread = 0;
00499     privdata->server_thread = 0;
00500   
00501     /* check whether we can start right away */
00502     if (!conn->is_stopped)
00503         /* yes, we can */
00504         conn->status = SMSCCONN_CONNECTING;
00505     else
00506         conn->status = SMSCCONN_DISCONNECTED;
00507 
00508     /* any which way - start the connection thread */
00509     if ((privdata->listener_thread = gwthread_create(soap_listener, conn)) == -1) {
00510         error(0, "SOAP: soap_create, failed to spawn thread - aborting");
00511         goto error;
00512     }
00513 
00514     return 0; /* done - ok */
00515 
00516 error:
00517     /* oh oh, problems */
00518     error(0, "SOAP: Failed to create SOAP smsc connection");
00519 
00520     /* release stuff */
00521     if (privdata != NULL) {
00522         gwlist_destroy(privdata->outgoing_queue, NULL);
00523         /* gwlist_destroy(privdata->pending_ack_queue, NULL); */
00524 
00525         O_DESTROY(privdata->uri);
00526         O_DESTROY(privdata->allow_ip);
00527         O_DESTROY(privdata->deny_ip);
00528         O_DESTROY(privdata->form_variable);
00529         O_DESTROY(privdata->alt_charset);
00530         O_DESTROY(privdata->name);
00531         O_DESTROY(privdata->mo_xml_file);
00532         O_DESTROY(privdata->dlr_xml_file);
00533         O_DESTROY(privdata->mt_xml_file);
00534         O_DESTROY(privdata->mo_spec_file);
00535         O_DESTROY(privdata->dlr_spec_file);
00536         O_DESTROY(privdata->mt_spec_file);
00537         O_DESTROY(privdata->mo_deps_file);
00538     }
00539     gw_free(privdata);
00540     octstr_destroy(temp);
00541     gwlist_destroy(filenames, octstr_destroy_item);
00542 
00543     /* notify bearerbox */
00544     conn->why_killed = SMSCCONN_KILLED_CANNOT_CONNECT;
00545     conn->status = SMSCCONN_DEAD;
00546 
00547     info(0, "exiting");
00548     return -1; /* I'm dead */
00549 }
00550 
00551 
00552 /**************************************************************************************
00553  * Callbacks
00554  */
00555 
00556 /*
00557  * function soap_add_msg_cb()
00558  *  get a message and copy it to the queue. note that message must be copied
00559  *  as I don't know what bearerbox wants to do with it after I return
00560  * Input: SMSCConn connection state data, Msg to send
00561  * Returns: status - 0 on success, -1 on fail.
00562  */
00563 static int soap_add_msg_cb(SMSCConn *conn, Msg *sms)
00564 {
00565 
00566     PrivData *privdata = conn->data;
00567     Msg *copy;
00568 
00569     /* I'm dead and cannot take any calls at the moment, please don't leave a message */
00570     if (conn->status == SMSCCONN_DEAD)
00571         return -1;
00572 
00573     copy = msg_duplicate(sms); /* copy the message */
00574     gwlist_append(privdata->outgoing_queue, copy); /* put it in the queue */
00575 
00576     debug("bb.soap.add_msg",0,"SOAP[%s]: got a new MT from %s, list has now %ld MTs", 
00577           octstr_get_cstr(privdata->name), octstr_get_cstr(sms->sms.sender), 
00578           gwlist_len(privdata->outgoing_queue));
00579 
00580     gwthread_wakeup(privdata->listener_thread);
00581 
00582     return 0;
00583 }
00584 
00585 
00586 /*
00587  * function soap_shutdown_cb()
00588  *  called by bearerbox to signal the module to shutdown. sets the shutdown flags,
00589  *  wakes up the listener thread and exits (if we add more threads, we need to handle those too)
00590  * Input: SMSCConn connection state data, flag indicating whether we can finish sending messages
00591  *  in the queue first.
00592  * Returns: status - 0 on success, -1 on fail.
00593  */
00594 static int soap_shutdown_cb(SMSCConn *conn, int finish_sending)
00595 {
00596     PrivData *privdata = conn->data;
00597     long thread;
00598 
00599     /* I'm dead, there's really no point in killing me again, is it ? */
00600     if (conn->status == SMSCCONN_DEAD)
00601         return -1;
00602 
00603     debug("bb.soap.cb", 0, "SOAP[%s]: Shutting down SMSCConn, %s", 
00604           octstr_get_cstr(privdata->name), finish_sending ? "slow" : "instant");
00605 
00606     /* Documentation claims this would have been done by smscconn.c, 
00607      * but isn't when this code is being written.*/
00608     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
00609     /* Separate from why_killed to avoid locking, 
00610      * as why_killed may be changed from outside? */
00611     privdata->shutdown = 1;
00612 
00613     if (finish_sending == 0) {
00614         Msg *msg;
00615         while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL)
00616             bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN, NULL);
00617     }
00618 
00619     thread = privdata->listener_thread;
00620 
00621     gwthread_wakeup(thread);
00622     gwthread_join(thread);
00623 
00624     return 0;
00625 }
00626 
00627 
00628 /*
00629  * function soap_start_cb()
00630  *  called by bearerbox when the module is allowed to start working
00631  * Input: SMSCConn connection state data
00632  * Returns: status - 0 on success, -1 on fail.
00633  */
00634 static void soap_start_cb(SMSCConn *conn)
00635 {
00636     PrivData *privdata = conn->data;
00637 
00638     debug("smsc.soap.start", 0, "SOAP[%s]: start called", 
00639           octstr_get_cstr(privdata->name));
00640 
00641     /* set the status so that connection_thread will know what to do */
00642     conn->status = SMSCCONN_CONNECTING;
00643 
00644     /* start connection_thread, in case its not started. */
00645     if ((!privdata->listener_thread) &&
00646         ((privdata->listener_thread = gwthread_create(soap_listener, conn)) == -1)) {
00647         error(0, "SOAP: soap_start, failed to spawn thread - aborting");
00648         conn->why_killed = SMSCCONN_KILLED_CANNOT_CONNECT;
00649         conn->status = SMSCCONN_DEAD;
00650         privdata->shutdown = 1;
00651         return;
00652     }
00653     /* gwthread_wakeup(privdata->listener_thread); */
00654     debug("smsc.soap.start",0,"SOAP[%s]: starting OK", 
00655           octstr_get_cstr(privdata->name));
00656 }
00657 
00658 
00659 /*
00660  * function soap_stop_cb()
00661  *  this function may be used to 'pause' the module. it should cause the connection
00662  *  to logout, but not to be destroyed, so it will be restarted later.
00663  * Input: SMSCConn connection state data
00664  */
00665 static void soap_stop_cb(SMSCConn *conn)
00666 {
00667     PrivData *privdata = conn->data;
00668 
00669     /* I'm dead, its really too late to take a break now */
00670     if (conn->status == SMSCCONN_DEAD)
00671         return;
00672 
00673     debug("smsc.soap.stop", 0, "SOAP[%s]: stop called", 
00674           octstr_get_cstr(privdata->name));
00675 
00676     /* make connection thread disconnect */
00677     conn->status = SMSCCONN_DISCONNECTED;
00678 }
00679 
00680 
00681 /*
00682  * function soap_queued_cb()
00683  *  called by bearerbox to query the number of messages pending send.
00684  *  the number returned includes the number of messages sent, but for which no ACK was yet received.
00685  * Input: SMSCConn connection state data
00686  * Returns: number of messages still waiting to be sent
00687  */
00688 static long soap_queued_cb(SMSCConn *conn)
00689 {
00690     PrivData *privdata = conn->data;
00691     long ret;
00692 
00693     /* I'm dead, so I have no queues - well there ! */
00694     if (conn->status == SMSCCONN_DEAD)
00695         return -1;
00696 
00697     ret = gwlist_len(privdata->outgoing_queue); 
00698     /* + gwlist_len(privdata->pending_ack_queue); */
00699 
00700     /* use internal queue as load, maybe something else later */
00701     conn->load = ret;
00702 
00703     return ret;
00704 }
00705 
00706 
00707 /**************************************************************************************
00708  * SOAP module thread functions (created by smsc_soap_create())
00709  */
00710 
00711 /*
00712  * function soap_listener()
00713  *  entry point to the listenr thread. this thread listenes on the MO port (if
00714  *  needed, and is also reposnsible for invoking "MT threads" (HTTP clients) to
00715  *  to send MTs.
00716  * Input: SMSCConn connection state data
00717  */
00718 static void soap_listener(void *arg)
00719 {
00720     SMSCConn *conn = arg;
00721     PrivData *privdata = conn->data;
00722     Msg *msg = NULL;
00723     debug("bb.soap.listener",0,"SOAP[%s]: listener entering", 
00724           octstr_get_cstr(privdata->name));
00725 
00726     while (!privdata->shutdown) {
00727 
00728         /* check connection status */
00729         switch (conn->status) {
00730             case SMSCCONN_RECONNECTING:
00731             case SMSCCONN_CONNECTING:
00732                 if (privdata->soap_server) {
00733                     soap_server_stop(privdata);
00734                 }
00735 
00736                 if (soap_server_start(conn)) {
00737                     privdata->shutdown = 1;
00738                     error(0, "SOAP[%s]: failed to start HTTP server!", 
00739                           octstr_get_cstr(privdata->name));
00740                     break;
00741                 }
00742 
00743                 mutex_lock(conn->flow_mutex);
00744                 conn->status = SMSCCONN_ACTIVE;
00745                 mutex_unlock(conn->flow_mutex);
00746 
00747                 bb_smscconn_connected(conn);
00748                 break;
00749 
00750             case SMSCCONN_DISCONNECTED:
00751                 if (privdata->soap_server)
00752                     soap_server_stop(privdata);
00753                 break;
00754 
00755             case SMSCCONN_ACTIVE:
00756                 if (!privdata->soap_server) {
00757                     mutex_lock(conn->flow_mutex);
00758                     conn->status = SMSCCONN_RECONNECTING;
00759                     mutex_unlock(conn->flow_mutex);
00760                     break;
00761                 }
00762 
00763                 /* run the normal send/receive loop */
00764                 if (gwlist_len(privdata->outgoing_queue) > 0) { /* we have messages to send */
00765                     soap_send_loop(conn); /* send any messages in queue */
00766                 }
00767                 break;
00768 
00769             case SMSCCONN_DEAD:
00770                 /* this shouldn't happen here - 
00771                  * I'm the only one allowed to set SMSCCONN_DEAD */
00772 
00773             default:
00774                 break;
00775         }
00776 
00777         soap_read_response(conn); /* collect HTTP responses */
00778 
00779         /* sleep for a while so I wont busy-loop */
00780         gwthread_sleep(SOAP_SLEEP_TIME); 
00781     }
00782 
00783     debug("bb.soap.connection",0,"SOAP[%s]: connection shutting down", 
00784           octstr_get_cstr(privdata->name));
00785 
00786     soap_server_stop(privdata);
00787 
00788     /* send all queued messages to bearerbox for recycling */
00789     debug("bb.soap.connection",0,"SOAP[%s]: sending messages back to bearerbox", 
00790           octstr_get_cstr(privdata->name));
00791 
00792     while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL)
00793         bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN, NULL);
00794 
00795     /* lock module public state data */
00796     mutex_lock(conn->flow_mutex);
00797 
00798     debug("bb.soap.connection",0,"SOAP[%s]: playing dead", 
00799           octstr_get_cstr(privdata->name));
00800     conn->status = SMSCCONN_DEAD; /* set state */
00801 
00802     /* destroy lists */
00803     debug("bb.soap.connection",0,"SOAP[%s]: don't need the queue anymore", 
00804           octstr_get_cstr(privdata->name));
00805 
00806     gwlist_destroy(privdata->outgoing_queue, NULL);
00807     /* gwlist_destroy(privdata->pending_ack_queue, NULL); */
00808 
00809     /* clear the soap client collection */
00810     debug("bb.soap.connection",0,"SOAP[%s]: tell caller to stop", 
00811           octstr_get_cstr(privdata->name));
00812     if (privdata->soap_client)
00813         gwlist_destroy(privdata->soap_client, soap_destroy_client_data);
00814 
00815     /* destroy private data stores */
00816     debug("bb.soap.connection",0,"SOAP[%s]: done with privdata", 
00817           octstr_get_cstr(privdata->name));
00818     O_DESTROY(privdata->uri);
00819     O_DESTROY(privdata->allow_ip);
00820     O_DESTROY(privdata->deny_ip);
00821 
00822     O_DESTROY(privdata->form_variable);
00823 
00824     O_DESTROY(privdata->alt_charset);
00825     O_DESTROY(privdata->name);
00826     O_DESTROY(privdata->mo_xml_file);
00827 
00828     O_DESTROY(privdata->dlr_xml_file);
00829     O_DESTROY(privdata->mt_xml_file);
00830     O_DESTROY(privdata->mo_spec_file);
00831     O_DESTROY(privdata->dlr_spec_file);
00832     O_DESTROY(privdata->mt_spec_file);
00833     O_DESTROY(privdata->mo_deps_file);
00834 
00835     gw_free(privdata);
00836     conn->data = NULL;
00837 
00838     mutex_unlock(conn->flow_mutex);
00839 
00840     debug("bb.soap.connection", 0, "SOAP: module has completed shutdown.");
00841     bb_smscconn_killed();
00842 }
00843 
00844 
00845 /*
00846  * function soap_server()
00847  *  server thread - accepts incoming MOs
00848  * Input: SMSCConn connection state data
00849  */
00850 static void soap_server(void* arg)
00851 {
00852     SMSCConn* conn = (SMSCConn*)arg;
00853     PrivData* privdata = conn->data;
00854     /* PrivData* privdata = (PrivData*)arg; */
00855 
00856     HTTPClient* remote_client = NULL;
00857     List *request_headers = NULL, *response_headers = NULL;
00858     List *cgivars = NULL;
00859     Octstr *client_ip = NULL, *request_uri = NULL, *request_body = NULL;
00860     Octstr *response_body = NULL;
00861     Octstr *timebuf = NULL;
00862     int http_response_status;
00863 
00864     debug("bb.soap.server",0,"SOAP[%s]: Server starting", 
00865           octstr_get_cstr(privdata->name));
00866 
00867     /* create basic headers */
00868     response_headers = http_create_empty_headers();
00869     http_header_add(response_headers, "Content-type","text/xml");
00870     /* http_header_add(response_headers, "Content-type","application/x-www-form-urlencoded"); */
00871     /* http_header_add(response_headers,"Connection", "Close"); */
00872     http_header_add(response_headers, "Server","Kannel");
00873 
00874     while (privdata->soap_server) {
00875         if ((remote_client = http_accept_request(privdata->port,
00876                               &client_ip, &request_uri, &request_headers, 
00877                               &request_body, &cgivars))) {
00878 
00879             debug("bb.soap.server",0,"SOAP[%s]: server got a request for "
00880                   "%s from %s, with body <%s>", octstr_get_cstr(privdata->name),
00881                   octstr_get_cstr(request_uri),octstr_get_cstr(client_ip),
00882                   request_body ? octstr_get_cstr(request_body) : "<null>");
00883 
00884             /* parse request */
00885             if (!octstr_compare(request_uri,octstr_imm(SOAP_MO_URI))) {
00886                 /* this is an incoming MO */
00887                 if ((http_response_status = 
00888                         soap_parse_mo(conn,request_body, &response_body)) == -1) {
00889                     /* fatal error parsing MO */
00890                     error(0,"SOAP[%s]: fatal error parsing MO", 
00891                           octstr_get_cstr(privdata->name));
00892                     response_body = octstr_create(SOAP_ERROR_MO_MESSAGE);
00893                     http_response_status = SOAP_ERROR_MO_CODE;
00894                 }
00895             } else if (!octstr_compare(request_uri,octstr_imm(SOAP_DLR_URI))) {
00896                 /* a delivery report */
00897                 if ((http_response_status = 
00898                         soap_parse_dlr(conn,request_body, &response_body)) == -1) {
00899                     /* fatal error parsing MO */
00900                     error(0,"SOAP[%s]: fatal error parsing DLR", 
00901                           octstr_get_cstr(privdata->name));
00902                     response_body = octstr_create(SOAP_ERROR_DLR_MESSAGE);
00903                     http_response_status = SOAP_ERROR_DLR_CODE;
00904                 }
00905             } else {
00906                 /* unknown command send default message */
00907                 response_body = octstr_create(SOAP_DEFAULT_MESSAGE);
00908                 http_response_status = SOAP_DEFAULT_CODE;
00909             }
00910 
00911             /* create response */
00912             /*
00913             response_body = octstr_create("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
00914             "<!DOCTYPE SMSCACCESS_REPLY SYSTEM \"http://superion/~oded/smsc_reply-1_0.dtd\">\n"
00915             "<SMSCACCESS_REPLY>\n"
00916             "   <SUBSCRIBER>447951718145</SUBSCRIBER>\n"
00917             "   <DATE_RECEIVED>22/01/2002:15:12</DATE_RECEIVED>\n"
00918             "   <RETURN_CODE>00</RETURN_CODE>\n"
00919             "</SMSCACCESS_REPLY>\n");
00920             */
00921 
00922             /* encode date in headers */
00923 
00924             timebuf = date_format_http(time(NULL));
00925             /* http_header_add(response_headers, "Date", octstr_get_cstr(timebuf)); */
00926             O_DESTROY(timebuf);
00927             /* http_header_dump(response_headers); */
00928 
00929             /* send response back to client */
00930             http_send_reply(remote_client,http_response_status,response_headers, response_body);
00931 
00932             /* destroy response data */
00933             /* http_destroy_headers(response_headers); */
00934             O_DESTROY(response_body);
00935 
00936             /* destroy request data */
00937             O_DESTROY(request_uri);
00938             O_DESTROY(request_body);
00939             O_DESTROY(client_ip);
00940 
00941             http_destroy_headers(request_headers);
00942             gwlist_destroy(cgivars, NULL);
00943         }
00944 
00945         gwthread_sleep(SOAP_SLEEP_TIME);
00946     }
00947 
00948     debug("bb.soap.server",0,"SOAP[%s]: server going down", 
00949           octstr_get_cstr(privdata->name));
00950     /* privdata->server_thread = 0; */
00951 }
00952 
00953 
00954 /**************************************************************************************
00955  * SOAP module internal protocol implementation functions
00956  */
00957 
00958 /*
00959  * function soap_send_loop()
00960  *  called when there are messages in the queue waiting to be sent
00961  * Input: SMSCConn connection state data
00962  */
00963 static void soap_send_loop(SMSCConn* conn)
00964 {
00965     PrivData* privdata = conn->data;
00966     Msg *msg;
00967     Octstr* xmldata = NULL;
00968     int counter = 0;
00969  
00970     debug("bb.soap.client",0,"SOAP[%s]: client - entering", 
00971           octstr_get_cstr(privdata->name));
00972 
00973     while ((counter < SOAP_MAX_MESSAGE_PER_ROUND) && 
00974             (msg = gwlist_extract_first(privdata->outgoing_queue))) { 
00975         /* as long as we have some messages */
00976         ++counter;
00977 
00978         if (uuid_is_null(msg->sms.id))   /* generate a message id */
00979             uuid_generate(msg->sms.id);
00980 
00981         /* format the messages as a character buffer to send */
00982         if (!(xmldata = soap_format_xml(privdata->mt_xml_file, msg, privdata))) {
00983             debug("bb.soap.client",0,"SOAP[%s]: client - failed to format message for sending", 
00984                   octstr_get_cstr(privdata->name));
00985             bb_smscconn_send_failed(conn, msg,
00986                     SMSCCONN_FAILED_MALFORMED, octstr_create("MALFORMED"));
00987             continue;
00988         }
00989 
00990         debug("bb.soap.client",0,"SOAP[%s]: client - Sending message <%s>",
00991               octstr_get_cstr(privdata->name), octstr_get_cstr(msg->sms.msgdata));
00992         if (xmldata)
00993             debug("bb.soap.client",0,"SOAP[%s]: data dump: %s",
00994                   octstr_get_cstr(privdata->name), octstr_get_cstr(xmldata));
00995 
00996         /* send to the server */
00997         soap_send(privdata, xmldata, msg);
00998 
00999         /* store in the second queue so that soap_read_response will know what to do */
01000         /* gwlist_append(privdata->pending_ack_queue,msg); */
01001 
01002         /* don't need this anymore */
01003         O_DESTROY(xmldata);
01004     }
01005 }
01006 
01007 
01008 /*
01009  * function soap_format_xml()
01010  *  fill in the fields in a XML template with data from a message
01011  * Input: Octstr containing an XML template,  Msg structure
01012  * Returns: Octstr xml formated data or NULL on error
01013  */
01014 static Octstr *soap_format_xml(Octstr *xml_file, Msg *msg, PrivData *privdata)
01015 {
01016     Octstr *xml;
01017     long t;
01018     long start = -1;
01019     int curly_enclose = 0;
01020 
01021     xml = octstr_create("");
01022 
01023     for (t = 0; t < octstr_len(xml_file); ++t) {
01024         unsigned char c;
01025          
01026         if ((c = octstr_get_char(xml_file,t)) == '%') {
01027             /* found start of token */
01028             start = t+1;
01029             continue;
01030         }
01031 
01032         if (c == '{' && start == t) { /* the token is enclosed in curlys */
01033             ++start; /* make sure the token is read from the next char */
01034             curly_enclose=1;
01035         }
01036 
01037         if (start < 0)
01038             octstr_append_char(xml,c);
01039         else if (
01040             (curly_enclose && (c == '}')) /* end of token in case of curly enclosure */
01041             ||
01042             (!curly_enclose && !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || 
01043                                  (c >= '0' && c <= '9') || c == '_'))) {
01044             /* found end of token */
01045             Octstr *data, *token;
01046           
01047             token = octstr_copy(xml_file,start,(t-start));
01048             if ((data = soap_convert_token(msg, token, privdata))) {
01049                 octstr_append(xml, data);
01050                 octstr_destroy(data);
01051             } else {
01052                 error(0,"SOAP: format_xml - failed to format token %s using message",
01053                       octstr_get_cstr(token));
01054                 octstr_destroy(token);
01055                 octstr_destroy(xml);
01056                 return NULL;
01057 
01058             }
01059             octstr_destroy(token);
01060             start = -1;
01061             if (!curly_enclose)
01062                 /* I want to get that char again, to let the normal behaviour 
01063                  * deal with it - only if it's not the ending curly */
01064                 --t; 
01065             else
01066                 curly_enclose = 0;
01067         }
01068     }
01069 
01070     return xml;
01071 }
01072 
01073 
01074 /*
01075  * function soap_send()
01076  *  send an XML buffer using POST to the SOAP server.
01077  * Input: PrivData connection state, Octstr XML formatted data buffer, 
01078  *        Message pointer to store with request
01079  */
01080 static void soap_send(PrivData* privdata, Octstr* xmlbuffer, Msg* msg)
01081 {
01082     List *requestHeaders;
01083     Octstr* postdata;
01084 
01085     /* create request headers */
01086     requestHeaders = http_create_empty_headers();
01087     http_header_add(requestHeaders, "User-Agent", "Kannel " GW_VERSION);
01088 
01089     if (privdata->form_urlencoded) {
01090         http_header_add(requestHeaders, "Content-Type", "application/x-www-form-urlencoded");
01091         postdata = octstr_format("%S=%E", privdata->form_variable, xmlbuffer);
01092     } else {
01093         http_header_add(requestHeaders, "Content-Type", "multipart/form-data, boundary=AaB03x");
01094         postdata = octstr_format("--AaB03x\r\n"
01095                                  "content-disposition: form-data; name=\"%S\"\r\n\r\n%S",
01096                                  privdata->form_variable, xmlbuffer);
01097     }
01098 
01099     /* send the request along */
01100     soap_client_init_query(privdata, requestHeaders, postdata, msg);
01101 
01102     O_DESTROY(postdata);
01103 
01104     /* done with that */
01105     http_destroy_headers(requestHeaders);
01106 
01107     return;
01108 
01109 }
01110 
01111 
01112 /*
01113  * function soap_read_response()
01114  *  check my HTTP caller for responses and act on them
01115 
01116  * Input: PrivData connection state
01117  **/
01118 static void soap_read_response(SMSCConn *conn)
01119 {
01120     PrivData *privdata = conn->data;
01121     Msg* msg;
01122     Octstr *responseBody, *responseURL;
01123     List* responseHeaders;
01124     int responseStatus;
01125     long long msgID;
01126     ClientData* cd;
01127 
01128     /* don't get in here unless I have some callers */
01129     /* (I shouldn't have one before I start sending messages) */
01130     if (!gwlist_len(privdata->soap_client))
01131         return;
01132 
01133 
01134     /* see if we have any responses pending */
01135     if (!(cd = soap_client_have_response(privdata->soap_client)))
01136         return;
01137 
01138     cd->requests--;
01139     msg = http_receive_result(cd->caller, &responseStatus, &responseURL, &responseHeaders, &responseBody);
01140 
01141     if (!msg) /* no responses here */
01142     {
01143         debug("bb.soap.read_response",0,"SOAP[%s]: sorry, no response", octstr_get_cstr(privdata->name));
01144         return;
01145     }
01146 
01147     if (responseStatus == -1) {
01148         debug("bb.soap.read_response",0,"SOAP[%s]: HTTP connection failed - blame the server (requeing msg)",
01149               octstr_get_cstr(privdata->name));
01150         bb_smscconn_send_failed(conn, msg,
01151                 SMSCCONN_FAILED_MALFORMED, octstr_create("MALFORMED"));
01152         /*    bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_TEMPORARILY); */
01153         /*      gwlist_append(privdata->outgoing_queue, msg); */
01154         return;
01155     }
01156 
01157     debug("bb.soap.read_response",0,"SOAP[%s]: got a response %d= %s",
01158           octstr_get_cstr(privdata->name), responseStatus, responseBody?octstr_get_cstr(responseBody):octstr_get_cstr(octstr_imm("NULL")));
01159 
01160 
01161     /* got a message from HTTP, parse it */
01162     if ( (msgID = soap_parse_response(privdata, responseBody)) >= 0)
01163     { /* ack with msg ID */
01164         char tmpid[30];
01165 
01166         /*
01167          * XXX UUID is used, fix this. 
01168 
01169         if (msgID == 0)
01170             msgID = msg->sms.id;
01171         */
01172 
01173         sprintf(tmpid,"%lld",msgID);
01174         debug("bb.soap.read_response",0,"SOAP[%s]: ACK - id: %lld", octstr_get_cstr(privdata->name), msgID);
01175 
01176         dlr_add(conn->id, octstr_imm(tmpid), msg);
01177 
01178         /* send msg back to bearerbox for recycling */
01179         bb_smscconn_sent(conn, msg, NULL);
01180     }
01181 
01182     else { /* nack */
01183         debug("bb.soap.read_response",0,"SOAP[%s]: NACK", octstr_get_cstr(privdata->name));
01184 
01185         /* send msg back to bearerbox for recycling */
01186         bb_smscconn_send_failed(conn, msg,
01187                 SMSCCONN_FAILED_MALFORMED, octstr_create("MALFORMED"));
01188     }
01189 
01190     http_destroy_headers(responseHeaders);
01191     O_DESTROY(responseBody);
01192     O_DESTROY(responseURL);
01193 }
01194 
01195 /*
01196  * function soap_parse_response()
01197  *  parse the response from the server to find the message ID
01198  * Input: Connection session data, Octstr xml buffer
01199  * Returns: message ID parsed or -1 if parsing failed (for example - a NACK received)
01200  *
01201  * Possible bug : I use gwlist_get() liberaly here, after checking that I have enough items,
01202  *                but if gwlist_get() returns NULL for an empty item, things might break - and
01203  *                not in a nice way.
01204  **/
01205 static long long soap_parse_response(PrivData* privdata, Octstr* xmlResponse)
01206 {
01207     long long msgID = -1;
01208     long responseStatus = -1;
01209     xmlDocPtr responseDoc;
01210     xmlNodePtr root;
01211     List* maps;
01212     char* keywords[] = { "id", "result" };
01213     char* sscans[] = { "%lld", "%ld" };
01214     void* pointers[] = { &msgID, &responseStatus };
01215 
01216     if (!xmlResponse)
01217         return -1;
01218     /* FIXME: do something here */
01219 
01220     /* parse XML */
01221     if ( !(responseDoc = xmlParseDoc((xmlChar *)octstr_get_cstr(xmlResponse))) ) {
01222         error(0,"SOAP[%s]: couldn't parse XML response [ %s ] in MT parsing",
01223               octstr_get_cstr(privdata->name), octstr_get_cstr(xmlResponse));
01224         return -1;
01225     }
01226 
01227     /* get root element */
01228     if ( ! (root = xmlDocGetRootElement(responseDoc)) ) {
01229         error(0,"SOAP[%s]: couldn't get XML root element in MT parsing",
01230               octstr_get_cstr(privdata->name));
01231         xmlFreeDoc(responseDoc);
01232         return -1;
01233     }
01234 
01235     /* create the argument map */
01236     maps = soap_create_map(privdata->mt_spec_file, 2, keywords, sscans, pointers);
01237 
01238     /* run the map and the xml through the parser */
01239     if (soap_map_xml_data(root, maps) < 2) {
01240         error(0,"SOAP[%s]: failed to map all the arguments from the XML data",
01241               octstr_get_cstr(privdata->name));
01242     }
01243 
01244     gwlist_destroy(maps, soap_destroy_map);
01245 
01246     /* done with the document */
01247     xmlFreeDoc(responseDoc);
01248 
01249     if (msgID == -1) {
01250         if (responseStatus == 0) {   /* success without msg ID */
01251             warning(0, "SOAP[%s]: parse_response - the protocol does not support message ID",
01252                     octstr_get_cstr(privdata->name));
01253             return 0;
01254         }
01255         else {
01256             error(0,"SOAP[%s]: parse_response - response code isn't 0 ! (%ld)",
01257                   octstr_get_cstr(privdata->name), responseStatus);
01258             return -1;  /* Nack */
01259         }
01260     }
01261     else
01262         return msgID;   /* success with msg ID */
01263 
01264 }
01265 
01266 
01267 /*
01268  * function soap_parse_mo()
01269  *  parse an incoming MO xml request, build a message from it and sent it.
01270  *  also generate the reponse text and status code
01271  * Input: module public state data, request body
01272  * Output: response body
01273  * Returns: HTTP status code on successful parse or -1 on failure
01274  **/
01275 static long soap_parse_mo(SMSCConn *conn, Octstr *request, Octstr **response)
01276 {
01277     PrivData *privdata = conn->data;
01278     xmlDocPtr requestDoc;
01279     xmlNodePtr root;
01280 
01281     Msg* msg;
01282     int pos = 0;
01283 
01284     long res = -1;
01285 
01286     List* maps;
01287     char receiver[30], sender[30], msgtype[30], msgdata[255], date[30];
01288     long long msgid = -1;
01289     char* keywords[] = { "receiver", "sender", "msgtype", "msgdata", "date", "id" };
01290     char* sscans[] = { "%s", "%s", "%s", "%s", "%s", "%lld" };
01291     void* pointers[] = { &receiver, &sender, &msgtype, &msgdata, &date, &msgid };
01292 
01293     receiver[0] = sender[0] = msgtype[0] = msgdata[0] = date[0] = '\0';
01294 
01295     if (!response) /* how am I supposed to return a response now ? */
01296         return -1;
01297 
01298     if (!request) {
01299         *response = octstr_create(SOAP_ERROR_NO_DATA_MESSAGE);
01300         return SOAP_ERROR_NO_DATA_CODE;
01301     }
01302 
01303     /* find the POST parameter name */
01304     if ( (pos = octstr_search_char(request,'=',0)) < 0) {
01305         /* didn't find it - */
01306         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01307         return SOAP_ERROR_MALFORMED_DATA_CODE;
01308     }
01309 
01310     /* cut of the parameter name - I'm not really interested in it */
01311     octstr_delete(request,0,pos+1);
01312 
01313     /* decode the URL encoded data */
01314     if (octstr_url_decode(request) < 0) {
01315         /* probably not URL encoded */
01316 
01317         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01318         return SOAP_ERROR_MALFORMED_DATA_CODE;
01319     }
01320 
01321     debug("bb.soap.parse_mo",0,"SOAP[%s]: parse_mo  - MO request dump <%s>", octstr_get_cstr(privdata->name),octstr_get_cstr(request));
01322 
01323     /* parse XML */
01324     if ( !(requestDoc = xmlParseDoc((xmlChar *)octstr_get_cstr(request))) ) {
01325         error(0,"SOAP[%s]: parse_mo couldn't parse XML response", octstr_get_cstr(privdata->name));
01326         return -1;
01327     }
01328 
01329     /* get root element */
01330     if ( ! (root = xmlDocGetRootElement(requestDoc)) ) {
01331         error(0,"SOAP[%s]: parse_mo couldn't get XML root element for request", octstr_get_cstr(privdata->name));
01332         xmlFreeDoc(requestDoc);
01333         return -1;
01334     }
01335 
01336     /* create the argument map */
01337     maps = soap_create_map(privdata->mo_spec_file, 6, keywords, sscans, pointers);
01338 
01339 
01340     /* run the map and the xml through the parser */
01341     if (soap_map_xml_data(root, maps) < gwlist_len(maps)) {
01342         error(0,"SOAP[%s]: parse_mo failed to map all the arguments from the XML data",
01343               octstr_get_cstr(privdata->name));
01344     }
01345 
01346     /* done with the document */
01347     xmlFreeDoc(requestDoc);
01348 
01349     if (strlen(receiver) == 0) {
01350         error(0,"SOAP: parse_mo - failed to get receiver");
01351         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01352         return SOAP_ERROR_MALFORMED_DATA_CODE;
01353     }
01354 
01355     if (strlen(sender) == 0) {
01356         error(0,"SOAP: parse_mo - failed to get sender");
01357         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01358         return SOAP_ERROR_MALFORMED_DATA_CODE;
01359     }
01360 
01361     if (strlen(msgdata) == 0) {
01362         error(0,"SOAP: parse_mo - failed to get message content");
01363         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01364 
01365         return SOAP_ERROR_MALFORMED_DATA_CODE;
01366     }
01367 
01368     /* create me a message to store data in it */
01369 
01370     msg = msg_create(sms);
01371 
01372     /*
01373      * XXX UUID is used, fix this. 
01374 
01375     if (msgid == -1) {
01376         error(0,"SOAP: parse_mo - failed to get message ID, generate by itself");
01377         msg->sms.id = gw_generate_id(); 
01378     }
01379     */
01380 
01381     /* fill in the fields from the parsed arguments */
01382     msg->sms.sender = octstr_create(sender);
01383     msg->sms.receiver = octstr_create(receiver);
01384     /*
01385      * XXX UUID is used, fix this. 
01386     msg->sms.id = msgid;
01387     */
01388     msg->sms.msgdata = octstr_create(msgdata);
01389 
01390     /* special processing and refill appropriate fields */
01391     if (privdata->mo_deps_file) {
01392         if ((res = soap_release_dependences(privdata->mo_deps_file, maps, msg, privdata))!=0)
01393             error(0,"SOAP: parse_mo - failed to release all dependences");
01394     }
01395     gwlist_destroy(maps, soap_destroy_map);
01396 
01397     /* fill in the date */
01398     if (strlen(date)) {
01399         struct universaltime tm;
01400         Octstr* temp = octstr_create(date);
01401         if (date_parse_iso(&tm, temp))
01402             /* failed to parse the date */
01403             msg->sms.time = time(NULL);
01404         else
01405             msg->sms.time = date_convert_universal(&tm);
01406         octstr_destroy(temp);
01407     } else
01408         msg->sms.time = time(NULL);
01409 
01410     /*
01411         / * check message data type - B stands for "base 64 encoded" in Team Mobile * /
01412         if (!strcmp(msgtype, "B")) {
01413             octstr_base64_to_binary(msg->sms.msgdata);
01414             msg->sms.coding = DC_8BIT;
01415 
01416         } else if (!strcmp(msgtype, "binary")) {
01417             octstr_hex_to_binary(msg->sms.msgdata);
01418             msg->sms.coding = DC_8BIT;
01419         } else {
01420     */
01421 
01422     /* not gonna play this game - just convert from whatever alt_charset is set to, to UCS-2
01423         / * scan message for unicode chars (UTF-8 encoded) * /
01424         pos = 0;
01425         while (pos < octstr_len(msg->sms.msgdata)) {
01426         if (octstr_get_char(msg->sms.msgdata,pos) & 128)
01427             break;
01428         ++pos;
01429         }
01430 
01431         if (pos < octstr_len(msg->sms.msgdata)) {
01432             / * message has some unicode - we need to convert to UCS-2 first * /
01433             Octstr* temp = msg->sms.msgdata;
01434 
01435             msg->sms.coding = DC_UCS2;
01436         if (charset_from_utf8(temp, &(msg->sms.msgdata), octstr_imm("UCS-2")) < 0) {
01437                 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));
01438                 O_DESTROY(msg->sms.msgdata);
01439                 msg->sms.msgdata = octstr_duplicate(temp);
01440             / *  set coding to 8bit and hope for the best * /
01441             msg->sms.coding = DC_8BIT;
01442 
01443             }
01444             octstr_destroy(temp);
01445         } else
01446              / * not unicode : 7bit * /
01447              msg->sms.coding = DC_7BIT;
01448     */
01449 
01450     /* if it's not binary, then assume unicode and convert from alt_charset to UCS-2 */
01451     /*        msg->sms.coding = DC_UCS2;
01452 
01453 
01454 
01455             if (!octstr_case_compare(privdata->alt_charset, octstr_imm("UCS-2")))  {
01456                 int ret = 0;
01457                 debug("bb.soap.parse_mo",0,"SOAP[%s]: converting from %s to UCS-2BE",
01458                         octstr_get_cstr(privdata->name), octstr_get_cstr(privdata->alt_charset));
01459                 ret = charset_convert(msg->sms.msgdata, octstr_get_cstr(privdata->alt_charset), "UCS-2BE");
01460 
01461 
01462                 if (ret == -1) {
01463                     error(2,"SOAP[%s]: Error converting MO data from %s to unicode",
01464                             octstr_get_cstr(privdata->name),  octstr_get_cstr(privdata->alt_charset));
01465                 } else if (ret != 0) {
01466                     debug("bb.soap.parse_mo",1,"SOAP[%s]: charset_convert made %d irreversable transformations",
01467                             octstr_get_cstr(privdata->name), ret);
01468                 }
01469             }
01470             msg->sms.charset = octstr_create("UCS-2");
01471         }
01472         debug("bb.soap.parse_mo",0,"SOAP[%s]: message decoded -", octstr_get_cstr(privdata->name));
01473         octstr_dump(msg->sms.msgdata,0);
01474     */
01475 
01476     /* check that we have all the fields necessary */
01477     if (!(msg->sms.sender)
01478             ||
01479             !(msg->sms.msgdata)) {
01480         /* generate error message */
01481         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01482         return SOAP_ERROR_MALFORMED_DATA_CODE;
01483     }
01484 
01485     /* setup defaults */
01486     if (msg->sms.time <= 0)
01487         msg->sms.time = time(NULL);
01488 
01489     if (!msg->sms.receiver)
01490         msg->sms.receiver = octstr_create(SOAP_DEFAULT_SENDER_STRING);
01491 
01492     if (!msg->sms.smsc_id)
01493         msg->sms.smsc_id = octstr_duplicate(conn->id);
01494 
01495     *response = soap_format_xml(privdata->mo_xml_file,msg,privdata);
01496     if (*response)
01497         debug("bb.soap.reponse_dlr",0,"SOAP[%s]: data dump: %s", octstr_get_cstr(privdata->name), octstr_get_cstr(*response));
01498 
01499     bb_smscconn_receive(conn,msg);
01500 
01501     return SOAP_QUERY_OK;
01502 }
01503 
01504 /*
01505  * function soap_parse_dlr()
01506  *  parse an incoming DLR xml request, build a message from it and sent it.
01507  *  also generate the reponse text and status code
01508  * Input: module public state data, request body
01509  * Output: response body
01510  * Returns: HTTP status code on successful parse or -1 on failure
01511  **/
01512 static long soap_parse_dlr(SMSCConn *conn, Octstr *request, Octstr **response)
01513 {
01514     PrivData *privdata = conn->data;
01515     xmlDocPtr requestDoc;
01516     xmlNodePtr root;
01517     Msg* dlrmsg = NULL;
01518     long dlrtype;
01519     int pos;
01520 
01521     List* maps;
01522     char receiver[30], soapdate[30], msgid[30];
01523     long result = -1;
01524     char* keywords[] = { "receiver", "soapdate", "id", "result" };
01525 
01526     char* sscans[] = { "%s", "%s", "%s", "%ld" };
01527     void* pointers[] = { &receiver, &soapdate, &msgid, &result };
01528 
01529     receiver[0] = soapdate[0] = msgid[0] = '\0';
01530 
01531     if (!response) /* how am I supposed to return a response now ? */
01532         return -1;
01533 
01534     if (!request) {
01535         *response = octstr_create(SOAP_ERROR_NO_DATA_MESSAGE);
01536         return SOAP_ERROR_NO_DATA_CODE;
01537     }
01538 
01539     /* find the POST parameter name */
01540     if ( (pos = octstr_search_char(request,'=',0)) < 0) {
01541         /* didn't find it - */
01542         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01543         return SOAP_ERROR_MALFORMED_DATA_CODE;
01544     }
01545 
01546     /* cut of the parameter name - I'm not really interested in it */
01547     octstr_delete(request,0,pos+1);
01548 
01549     /* decode the URL encoded data */
01550     if (octstr_url_decode(request) < 0) {
01551         /* probably not URL encoded */
01552         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01553         return SOAP_ERROR_MALFORMED_DATA_CODE;
01554     }
01555 
01556     debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr  - DLR request dump <%s>", octstr_get_cstr(privdata->name),octstr_get_cstr(request));
01557 
01558     /* parse XML */
01559 
01560     if ( !(requestDoc = xmlParseDoc((xmlChar *)octstr_get_cstr(request))) ) {
01561         error(0,"SOAP[%s]: parse_dlr couldn't parse XML response", octstr_get_cstr(privdata->name));
01562         return -1;
01563     }
01564 
01565     /* get root element */
01566     if ( ! (root = xmlDocGetRootElement(requestDoc)) ) {
01567 
01568         error(0,"SOAP[%s]: parse_dlr couldn't get XML root element for request", octstr_get_cstr(privdata->name));
01569         xmlFreeDoc(requestDoc);
01570         return -1;
01571     }
01572 
01573     /* create the argument map */
01574 
01575     maps = soap_create_map(privdata->dlr_spec_file, 4, keywords, sscans, pointers);
01576 
01577     /* run the map and the xml through the parser */
01578     if (soap_map_xml_data(root, maps) < 4) {
01579         error(0,"SOAP[%s]: parse_dlr failed to map all the arguments from the XML data",
01580 
01581               octstr_get_cstr(privdata->name));
01582     }
01583 
01584     gwlist_destroy(maps, soap_destroy_map);
01585 
01586     /* done with the document */
01587     xmlFreeDoc(requestDoc);
01588 
01589     if (strlen(msgid) == 0) {
01590         error(0,"SOAP: parse_dlr - failed to get message ID");
01591         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01592         return SOAP_ERROR_MALFORMED_DATA_CODE;
01593     }
01594 
01595     if (result == -1) {
01596 
01597         error(0,"SOAP: parse_dlr - failed to get delivery code");
01598         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01599         return SOAP_ERROR_MALFORMED_DATA_CODE;
01600     }
01601 
01602     /* we not need it because receiver now is constant string "receiver"
01603       if (strlen(receiver) == 0) {
01604             error(0,"SOAP: parse_dlr - failed to get receiver");
01605         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
01606         return SOAP_ERROR_MALFORMED_DATA_CODE;
01607         }
01608     */
01609     /* log the delivery code - this could be used to determine dlrtype (or so I hope) */
01610     debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr DELIVERY_CODE : %ld", octstr_get_cstr(privdata->name),result);
01611     if (result == 0)
01612         dlrtype = DLR_SUCCESS;
01613     else
01614         dlrtype = DLR_FAIL;
01615 
01616     /* fetch the DLR */
01617 
01618     dlrmsg = dlr_find(conn->id, octstr_imm(msgid), octstr_imm("receiver"), /* destination */
01619                       dlrtype);
01620 
01621     if (!dlrmsg) {
01622         error(0,"SOAP[%s]: parse_dlr invoked (%ld), but no DLR found for MsgID %s", octstr_get_cstr(privdata->name),dlrtype,msgid);
01623         *response = octstr_create(SOAP_ERROR_NO_DLR_MESSAGE);
01624         return SOAP_ERROR_NO_DLR_CODE;
01625     }
01626 
01627     debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr found dlr", octstr_get_cstr(privdata->name));
01628     octstr_destroy(dlrmsg->sms.msgdata);
01629     switch (dlrtype) { /* change message according to DLR type */
01630         case DLR_SUCCESS:
01631             dlrmsg->sms.msgdata = octstr_create("Delivered");
01632             break;
01633         case DLR_BUFFERED:
01634             dlrmsg->sms.msgdata = octstr_create("Buffered");
01635             break;
01636         case DLR_FAIL:
01637             dlrmsg->sms.msgdata = octstr_create("Failed");
01638             break;
01639         default:
01640             break;
01641 
01642     }
01643 
01644     /*
01645     if (dlrmsg->sms.receiver) {
01646         octstr_destroy(dlrmsg->sms.sender);
01647         dlrmsg->sms.sender = dlrmsg->sms.receiver;
01648     }
01649     dlrmsg->sms.receiver = octstr_create(receiver);
01650     dlrmsg->sms.id = strtol(msgid, NULL, 10);
01651     */
01652 
01653     debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr sent dlr <%s>", octstr_get_cstr(privdata->name),octstr_get_cstr(dlrmsg->sms.msgdata));
01654 
01655 
01656     *response = soap_format_xml(privdata->dlr_xml_file, dlrmsg, privdata);
01657     if (*response)
01658         debug("bb.soap.reponse_dlr",0,"SOAP[%s]: data dump: %s", octstr_get_cstr(privdata->name), octstr_get_cstr(*response));
01659 
01660     /* send to bearerbox */
01661     bb_smscconn_receive(conn, dlrmsg);
01662 
01663     return SOAP_QUERY_OK;
01664 
01665 }
01666 
01667 
01668 /*
01669  * SOAP internal utility functions
01670  **/
01671 
01672 /*
01673  * function soap_xmlnode_get_long()
01674  *  parse the content of an XML node and return it as an integer
01675  * Input: xmlNodePtr to node
01676  * Output: long parsed
01677  * Returns: 0 on success, -1 on failure
01678  **/
01679 int soap_xmlnode_get_long(xmlNodePtr cur, long* out)
01680 {
01681     xmlChar* nodeContent;
01682     char* endPointer;
01683 
01684     if (!out)  /* sanity check */
01685         return -1;
01686 
01687     /* get content of tag */
01688     if (!(nodeContent = xmlNodeGetContent(cur))) {
01689         error(0,"SOAP: get_long - xml Node has content !");
01690         return -1;
01691     }
01692 
01693     /* read the content into output */
01694     *out = strtol((char *)nodeContent,&endPointer,10);
01695     xmlFree(nodeContent);
01696 
01697     if (endPointer == (char*)nodeContent) {
01698         error(0,"SOAP: get_long - node has non-numeric content <%s>", nodeContent);
01699         return -1;
01700     }
01701 
01702     return 0;
01703 }
01704 
01705 /*
01706  * function soap_xmlnode_get_int64()
01707  *  parse the content of an XML node and return it as an long long
01708  * Input: xmlNodePtr to node
01709  * Output: long parsed
01710  * Returns: 0 on success, -1 on failure
01711  **/
01712 int soap_xmlnode_get_int64(xmlNodePtr cur, long long* out)
01713 {
01714     xmlChar* nodeContent;
01715     char* endPointer;
01716 
01717     if (!out)  /* sanity check */
01718         return -1;
01719 
01720     /* get content of tag */
01721     if (!(nodeContent = xmlNodeGetContent(cur))) {
01722         error(0,"SOAP: get_long - xml Node has content !");
01723         return -1;
01724     }
01725 
01726 
01727     /* read the content into output */
01728     *out = strtoll((char *)nodeContent,&endPointer,10);
01729     xmlFree(nodeContent);
01730 
01731     if (endPointer == (char*)nodeContent) {
01732         error(0,"SOAP: get_long - node has non-numeric content <%s>", nodeContent);
01733         return -1;
01734     }
01735 
01736     return 0;
01737 }
01738 
01739 /*
01740  * function soap_xmlnode_get_octstr()
01741  *  parse the content of an XML node and return it as an Octstr*
01742  * Input: xmlNodePtr to node
01743  * Output: Octstr to feel with data
01744  * Returns: 0 on success, -1 on failure
01745  **/
01746 int soap_xmlnode_get_octstr(xmlNodePtr cur, Octstr **out)
01747 {
01748     xmlChar* nodeContent;
01749 
01750     if (!out)  /* sanity check */
01751         return -1;
01752 
01753     /* get content of tag */
01754     if (!(nodeContent = xmlNodeGetContent(cur))) {
01755         error(0,"SOAP: get_octstr - xml Node has content !");
01756         return -1;
01757     }
01758 
01759     /* store the content into output */
01760     *out = octstr_create((char *)nodeContent);
01761     xmlFree(nodeContent);
01762 
01763     if (*out)
01764         return 0;
01765     else
01766         return -1;
01767 
01768 
01769 }
01770 
01771 /*
01772  * function soap_read_date()
01773  *  convert a date string in one2one obiquis format (%Y/%M/%d:%h:%m) to epoch time
01774  * Input: Octstr date
01775  * Returns: epoch time on success or -1 on failure
01776  **/
01777 time_t soap_read_date(Octstr* dateString)
01778 {
01779     int pos, count;
01780     struct universaltime stTime;
01781     long arTime[5];
01782 
01783 
01784     if (!dateString) /* sanity check */
01785         return -1;
01786 
01787     pos = count = 0;
01788     /* tricky control structures are my favourite among complicated expressions ;-) */
01789     while (count < 5 && pos < octstr_len(dateString) &&
01790             (pos = octstr_parse_long(&(arTime[count++]),dateString, pos,10)) && pos != -1)
01791         ++pos;
01792 
01793     if (count < 5) {
01794         /* error parsing the date */
01795         debug("bb.soap.read_date",0,"read_date failed parsing the date value <%s>", octstr_get_cstr(dateString));
01796         return -1;
01797     }
01798 
01799     stTime.day = arTime[0];
01800     stTime.month = arTime[1];
01801     stTime.year = arTime[2];
01802     stTime.hour = arTime[3];
01803     stTime.minute = arTime[4];
01804     stTime.second = 0;
01805     return date_convert_universal(&stTime);
01806 }
01807 
01808 /*
01809  * function soap_write_date()
01810  *  convert an epoch time value to a date string in one2one obiquis format (%Y/%M/%d:%h:%m)
01811  * Input: time_t epoch time
01812  * Returns: an Octstr containing the date - this must be freed by the caller
01813  **/
01814 static Octstr* soap_write_date(time_t date)
01815 {
01816     struct tm date_parts;
01817     Octstr* out;
01818 
01819     if (date < 0)
01820         /* sanity check - I don't think it should ever happen, but I don't want to get
01821            support calls at 2am because some gateway in the UK went bananas. */
01822         return octstr_create("ERROR");
01823 
01824     /* split up epoch time to elements */
01825     gmtime_r(&date, &date_parts);
01826 
01827     out = octstr_format("%d/%02d/%02d:%02d:%02d",
01828                         date_parts.tm_year + 1900, date_parts.tm_mon + 1, date_parts.tm_mday, date_parts.tm_hour, date_parts.tm_min);
01829 
01830     /* again */
01831     if (out)
01832         return out;
01833     else
01834         return octstr_create("ERROR");
01835     /* assuming octstr_create never fails, unlike octstr_format. this is not the case currently (both cannot fail), but it may change */
01836 }
01837 
01838 
01839 /*
01840  * function soap_server_start()
01841  *  init and start the SOAP HTTP server
01842  * Input: Module public connection state data
01843  * Returns: 0 on success, -1 on failure
01844  **/
01845 int soap_server_start(SMSCConn *conn)
01846 {
01847     PrivData* privdata = conn->data;
01848 
01849     debug("bb.soap.server_stop",0,"SOAP[%s]: Starting HTTP server", octstr_get_cstr(privdata->name));
01850     /* start the HTTP server */
01851     if (http_open_port(privdata->port,privdata->ssl)) {
01852         return -1;
01853     }
01854 
01855 
01856     /* raise server flag */
01857     privdata->soap_server = 1;
01858 
01859     if ( (privdata->server_thread = gwthread_create(soap_server, conn)) == -1)
01860     {
01861         error(0, "SOAP[%s]: server_start failed to create server thread!", octstr_get_cstr(privdata->name));
01862         http_close_port(privdata->port);
01863         return -1;
01864     }
01865 
01866 
01867     return 0;
01868 }
01869 
01870 /*
01871  * function soap_server_stop()
01872  *  tears down and stops the SOAP HTTP server
01873  * Input: Module connection state data
01874  **/
01875 static void soap_server_stop(PrivData* privdata)
01876 {
01877     /*    time_t start = time(NULL); */
01878 
01879     debug("bb.soap.server_stop",0,"SOAP[%s]: Stopping HTTP server", octstr_get_cstr(privdata->name));
01880     /* signal the server thread to stop */
01881     privdata->soap_server = 0;
01882 
01883     /* close the http server thread */
01884     http_close_port(privdata->port);
01885 
01886     if (privdata->server_thread) {
01887         gwthread_wakeup(privdata->server_thread);
01888         gwthread_join(privdata->server_thread);
01889         privdata->server_thread = 0;
01890     }
01891 
01892 
01893     /*
01894     / * wait upto 5 minutes for our server thread to shutdown * /
01895     while (privdata->server_thread &&  (start + 300 > time(NULL)))
01896     gwthread_sleep(SOAP_SLEEP_TIME);
01897 
01898 
01899     if (privdata->server_thread) {
01900     error(0,"SOAP[%s]: our server refuses to die!", octstr_get_cstr(privdata->name));
01901     privdata->server_thread = 0; / * dump it either way * /
01902 
01903     }*/
01904 
01905     debug("bb.soap.server_stop",0,"SOAP[%s]: Done stopping HTTP server", octstr_get_cstr(privdata->name));
01906 }
01907 
01908 
01909 /*
01910  * function soap_create_client_data()
01911  *  creates a new SOAP client data structure and caller
01912  * Returns: an initialized client data structure with a live caller
01913 
01914  **/
01915 static ClientData* soap_create_client_data()
01916 {
01917 
01918     ClientData *cd = gw_malloc(sizeof(ClientData));
01919 
01920     cd->last_access = 0;
01921     cd->requests = 0;
01922     cd->caller = http_caller_create();
01923 
01924     return cd;
01925 }
01926 
01927 
01928 /*
01929  * function soap_client_init_query()
01930  *  start an HTTP query, load balance callers, and manage caller pool
01931  * Input: Module state, list of headers to send, data to send, message to store
01932  **/
01933 static void soap_client_init_query(PrivData* privdata, List* headers, Octstr* data, Msg* msg)
01934 {
01935     ClientData *cur_client = NULL;
01936     long index;
01937 
01938 
01939     /* no list yet, generate one */
01940     if (!privdata->soap_client)
01941         privdata->soap_client = gwlist_create();
01942 
01943     /* I'm going to change the list, so lock it */
01944     gwlist_lock(privdata->soap_client);
01945 
01946     /* find the next live caller */
01947     for (index = gwlist_len(privdata->soap_client) - 1 ; index >= 0; --index) {
01948         cur_client = gwlist_get(privdata->soap_client, index);
01949         if (
01950             cur_client->last_access + CLIENT_BUSY_TIME < time(NULL)
01951             &&
01952             cur_client->requests < CLIENT_BUSY_LOAD
01953         ) {
01954             debug("bb.soap.init_query",0,"SOAP[%s]: init_query getting a client",octstr_get_cstr(privdata->name));
01955 
01956             /* client is not busy - get it */
01957             gwlist_delete(privdata->soap_client, index, 1);
01958             break;
01959         }
01960         cur_client = NULL;
01961     }
01962 
01963     if (!cur_client) {
01964         if (gwlist_len(privdata->soap_client) > MAX_SOAP_CLIENTS) {
01965             debug("bb.soap.init_query",0,"SOAP[%s]: init_query all clients are busy, getting the first client",octstr_get_cstr(privdata->name));
01966             /* query not dispatched, and we have the max number of callers -
01967                grab the first caller (least used) from the list */
01968             cur_client = gwlist_extract_first(privdata->soap_client);
01969         } else {
01970             /* query not dispatched, and we don't have enough callers -
01971                start a new one */
01972             debug("bb.soap.init_query",0,"SOAP[%s]: init_query creates a new client",octstr_get_cstr(privdata->name));
01973             cur_client = soap_create_client_data();
01974         }
01975     }
01976 
01977     /* dispatch query to selected client */
01978     http_start_request(cur_client->caller, HTTP_METHOD_POST, privdata->uri, headers, data, 1, msg, NULL);
01979     cur_client->requests++;
01980     cur_client->last_access = time(NULL);
01981     gwlist_append(privdata->soap_client, cur_client);
01982     gwlist_unlock(privdata->soap_client);
01983 }
01984 
01985 
01986 /*
01987  * function soap_destroy_client_data()
01988  *  destroy a SOAP client caller
01989  * Input: pointer to a client data structure with a live caller
01990  **/
01991 static void soap_destroy_client_data(void* data)
01992 {
01993     ClientData *cd = (ClientData*) data;
01994 
01995     /* signal the caller to stop and then kill it */
01996     if (cd->caller) {
01997         http_caller_signal_shutdown(cd->caller);
01998         http_caller_destroy(cd->caller);
01999     }
02000 }
02001 
02002 /*
02003  * function soap_client_have_response()
02004  *  return a caller from the pool that has responses waiting
02005  * Input: ClientData pool
02006  * Returns: a client data structure that has a caller with responses waiting,
02007  *  or NULL if none are found
02008  **/
02009 static ClientData* soap_client_have_response(List* client_list)
02010 {
02011     long index;
02012     ClientData* cd;
02013 
02014     if (!client_list)
02015         return NULL;
02016 
02017     /* lock the list so nobody removes or adds clients while I'm looping on the list */
02018     gwlist_lock(client_list);
02019 
02020     for (index = gwlist_len(client_list) - 1; index >= 0; --index) {
02021         cd = gwlist_get(client_list,index);
02022         if (gwlist_len(cd->caller)) {
02023 
02024             gwlist_unlock(client_list);
02025             return gwlist_get(client_list, index);
02026         }
02027     }
02028 
02029     gwlist_unlock(client_list);
02030     return NULL;
02031 }
02032 
02033 /*
02034  * function soap_convert_token()
02035  *  convert a member of the message structure and return it as octstr
02036  * Input: member name
02037  * Returns: an Octstr containing the content of the data member from the message structure
02038  *  or NULL if an error occured.
02039  **/
02040 static Octstr* soap_convert_token(Msg* msg, Octstr* name, PrivData* privdata)
02041 
02042 {
02043     char buf[20];
02044     int index;
02045 
02046     if ( (index=soap_lookup_function(name)) >= 0 )
02047         return soap_select_function(index, msg, privdata);
02048 
02049 
02050 #define INTEGER(fieldname) \
02051         if (!octstr_str_compare(name, #fieldname)) { \
02052                 sprintf(buf,"%ld", p->fieldname); \
02053                 return octstr_create(buf); \
02054         }
02055 #define INT64(fieldname) \
02056         if (!octstr_str_compare(name, #fieldname)) { \
02057                 sprintf(buf,"%lld", p->fieldname); \
02058                 return octstr_create(buf); \
02059         }
02060 #define OCTSTR(fieldname) \
02061         if (!octstr_str_compare(name, #fieldname)) \
02062                 return octstr_duplicate(p->fieldname);
02063 #define UUID(fieldname) 
02064 #define VOID(fieldname)
02065 
02066 #define MSG(type, stmt) \
02067         case type: { struct type *p = &msg->type; stmt } break;
02068 
02069     switch (msg->type) {
02070 #include "msg-decl.h"
02071         default:
02072 
02073 
02074             error(0, "SOAP: Internal error: unknown message type %d", msg->type);
02075             return NULL;
02076     }
02077 
02078     error(0,"SOAP: soap_convert_token, can't find token named <%s>", octstr_get_cstr(name));
02079     return NULL;
02080 }
02081 
02082 /*
02083  * function soap_create_map()
02084  *  convert a XML parsing spec file and a list of recognized keywords to an argument map
02085  * Input: XML parsing spec buffer and lists of keywords, types and pointers
02086  * Returns: number of variables successfuly mapped
02087  **/
02088 List* soap_create_map(Octstr* spec, long count, char* keywords[], char* types[], void* storage[])
02089 {
02090     List *parse_items, *out;
02091 
02092     out = gwlist_create();
02093 
02094     /* read the list of items from the spec file */
02095     parse_items = octstr_split(spec, octstr_imm("\n"));
02096 
02097     while (gwlist_len(parse_items)) {
02098         ArgumentMap* map;
02099         int index;
02100         Octstr* temp = gwlist_extract_first(parse_items);
02101         List* item = octstr_split_words(temp);
02102 
02103 
02104         /* make sure we have at least two things in the item : a keyword and a path */
02105         if (gwlist_len(item) < 2) {
02106             debug("bb.soap.parse_create_map",0,"SOAP: broken spec file line <%s> in soap_create_map",
02107                   octstr_get_cstr(temp));
02108             octstr_destroy(temp);
02109             gwlist_destroy(item, octstr_destroy_item);
02110             continue;
02111         }
02112 
02113         /* check that the keyword matches something in the list of keywords */
02114         for (index = 0; index < count; ++index) {
02115             if (!octstr_str_compare(gwlist_get(item,0), keywords[index])) {
02116                 /* allocate the structure */
02117                 map = gw_malloc(sizeof(ArgumentMap));
02118                 map->name = gwlist_extract_first(item);
02119                 map->path = gwlist_extract_first(item);
02120                 map->attribute = gwlist_extract_first(item); /* could be NULL, but that is ok */
02121                 map->sscan_type = octstr_create(types[index]);
02122                 map->store = storage[index];
02123                 gwlist_append(out, map);
02124                 break;
02125             }
02126         }
02127 
02128         /* destroy temporary variables; */
02129         gwlist_destroy(item, octstr_destroy_item);
02130         octstr_destroy(temp);
02131     }
02132 
02133     gwlist_destroy(parse_items, octstr_destroy_item);
02134 
02135     return out;
02136 }
02137 
02138 /*
02139  * function soap_destroy_map()
02140  *  destroy a map structure. used in gwlist_destroy(calls);
02141  * Input: pointer to a map structure;
02142  **/
02143 void soap_destroy_map(void *item)
02144 {
02145     ArgumentMap* map = item;
02146     octstr_destroy(map->name);
02147     octstr_destroy(map->path);
02148     octstr_destroy(map->attribute);
02149     octstr_destroy(map->sscan_type);
02150     gw_free(map);
02151 }
02152 
02153 /*
02154  * function soap_fetch_xml_data()
02155  *      return the value of an XML element.
02156  * Input: pointer to root of XML to search under, path specified in one of three forms
02157  *      a) <path to tag> - will return the content of this tag
02158  *      b) <path to tag>,<attribute name> - will return the value of this attribute
02159  *      c) "<fixed value>" - will return the given value as it is
02160  * Returns: content if found or NULL
02161  **/
02162 Octstr* soap_fetch_xml_data(xmlNodePtr xml, Octstr* path)
02163 {
02164     Octstr *temp, *xml_path, *attr_name = NULL;
02165     List* path_elements;
02166     unsigned char c;
02167     xmlNodePtr parent, node;
02168     int index;
02169 
02170 
02171     /* sanity check */
02172     if (!octstr_len(path) || !xml)
02173         return NULL;
02174 
02175 
02176     /* stop here for case (c) */
02177     if (((c = octstr_get_char(path, 0)) == '"' || c == '\'') &&
02178             (octstr_get_char(path, octstr_len(path)-1) == c))
02179         return octstr_copy(path, 1, octstr_len(path) - 2);
02180 
02181     /* split into XML path and attribute name */
02182     path_elements = octstr_split(path, octstr_imm(","));
02183     xml_path = gwlist_get(path_elements,0);
02184     if (gwlist_len(path_elements) > 1) /* case (b), we have an attribute */
02185         attr_name = gwlist_get(path_elements,1);
02186     gwlist_destroy(path_elements, NULL);
02187 
02188     /* split path into parts */
02189     path_elements = octstr_split(xml_path, octstr_imm("/"));
02190 
02191     /* walk the message tree down the path */
02192     parent = NULL;
02193     node = xml;
02194     index = 0;
02195     while (index < gwlist_len(path_elements)) {
02196         int found = 0;
02197         /* get the next path element */
02198         temp = gwlist_get(path_elements, index);
02199         do {
02200             if (!octstr_str_compare(temp,(char *)node->name)) {
02201                 /* found what we're looking for */
02202                 if (!(node->xmlChildrenNode) && index < (gwlist_len(path_elements)-1)) {
02203                     /* while this is indeed the item we are looking for, it's not the end
02204                      * of the path, and this item has no children */
02205                     debug("bb.soap.fetch_xml_data",0,"SOAP: fetch_xml - error parsing XML, "
02206                           "looking for <%s>, but element <%s> has no children",
02207                           octstr_get_cstr(xml_path), octstr_get_cstr(temp));
02208                 } else {
02209                     ++index; /* go down the path */
02210                     parent = node; /* remember where I came from */
02211                     node = node->xmlChildrenNode; /* trace into the node */
02212                     ++found; /* remember that I found it */
02213                     break; /* escape to the next level */
02214                 }
02215             }
02216             /* get the next node on this level - this runs if the current node is not in the path */
02217         } while ((node = node->next));
02218 
02219         if (!found) {
02220             /* didn't find anything - back track */
02221             node = parent;
02222             parent = node->parent;
02223             if (--index < 0)
02224                 /* I backtracked too much up the tree, nowhere to go to */
02225                 break; /* out of the main loop with nothing to show for it */
02226 
02227             if (!(node = node->next))
02228 
02229                 /* after back tracking, go over to the next sibling of the node I just
02230                  * finished searching under, or bail out if no more siblings */
02231                 break;
02232         }
02233     }
02234 
02235     /* coming here there are two options:
02236      * 1) we looped over all the tree, but did not succeed in traveling the
02237      *    requested path - index not pointing past the list of path elements - */
02238     if (index < gwlist_len(path_elements)) {
02239         /* didn't find the full path */
02240         debug("bb.soap.map_xml_data",0,"SOAP: fetch_xml - path <%s> cannot be traveled in input XML",
02241               octstr_get_cstr(xml_path));
02242         gwlist_destroy(path_elements, octstr_destroy_item);
02243         octstr_destroy(xml_path);
02244         octstr_destroy(attr_name);
02245         return NULL;
02246     }
02247 
02248 
02249     /* 2) index is pointing past the end of the path and the correct node
02250      * is stored in parent */
02251     if (attr_name) { /* The caller wants to get an attribute */
02252         xmlChar* content;
02253         content = xmlGetProp(parent, (xmlChar *)octstr_get_cstr(attr_name));
02254         if (content)
02255             temp = octstr_create((char *)content);
02256         else /* dont treat an empty or non-existant attribute as an error right away */
02257             temp = octstr_create("");
02258         xmlFree(content);
02259     } else { /* the caller wants to get the content */
02260         xmlChar* content;
02261         content = xmlNodeGetContent(parent);
02262         if (content)
02263             temp = octstr_create((char *)content);
02264         else /* don't treat an empty tag an error right away */
02265             temp = octstr_create("");
02266         xmlFree(content);
02267     }
02268 
02269     gwlist_destroy(path_elements, octstr_destroy_item);
02270     octstr_destroy(xml_path);
02271     octstr_destroy(attr_name);
02272 
02273     return temp;
02274 }
02275 
02276 /*
02277  * function soap_map_xml_data()
02278  *  maps content of an XML structure to a list of variables using a map
02279  * Input: XML document and an argument map
02280  * Returns: number of variables successfuly mapped
02281  **/
02282 int soap_map_xml_data(xmlNodePtr xml, List* maps)
02283 {
02284     int mapindex = 0, args = 0;
02285     xmlNodePtr node, parent;
02286 
02287     /* step through the items on the map */
02288     while (mapindex < gwlist_len(maps)) {
02289 
02290         Octstr* temp;
02291 
02292         int index = 0;
02293         ArgumentMap* map = gwlist_get(maps,mapindex);
02294         /* split the path elements */
02295         List* path_elements = octstr_split(map->path, octstr_imm("/"));
02296 
02297         /* walk the message tree down the path */
02298         parent = NULL;
02299         node = xml;
02300         while (index < gwlist_len(path_elements)) {
02301             int found = 0;
02302             /* get the next path element */
02303             temp = gwlist_get(path_elements, index);
02304             do {
02305                 if (!octstr_str_compare(temp,(char *)node->name)) {
02306                     /* found what we're looking for */
02307                     if (!(node->xmlChildrenNode) && index < (gwlist_len(path_elements)-1)) {
02308                         /* while this is indeed the item we are looking for, it's not the end
02309                            of the path, and this item has no children */
02310                         debug("bb.soap.map_xml_data",0,"SOAP: error parsing XML, looking for <%s>, but element <%s> has no children",
02311                               octstr_get_cstr(map->path), octstr_get_cstr(temp));
02312                     } else {
02313                         ++index; /* go down the path */
02314                         parent = node; /* remember where I came from */
02315                         node = node->xmlChildrenNode; /* trace into the node */
02316                         ++found;
02317                         break; /* escape to the next level */
02318                     }
02319                 }
02320             } while ((node = node->next));
02321 
02322             if (!found) {
02323                 /* didn't find anything - back track */
02324                 node = parent;
02325                 if (parent==NULL) /* first tag not found, quickly go out ! */
02326                     return 0;
02327 
02328                 parent = node->parent;
02329                 if (--index < 0)
02330                     /* I backtracked too much up the tree, nowhere to go to */
02331                     break;
02332 
02333                 if (!(node = node->next))
02334                     /* no more childs under the main tree to look under, abort */
02335                     break;
02336 
02337             }
02338         }
02339 
02340 
02341         if (index < gwlist_len(path_elements)) {
02342             /* didn't find the full path */
02343             debug("bb.soap.map_xml_data",0,"SOAP: didn't find element for keyword <%s> in XML data",
02344                   octstr_get_cstr(map->name));
02345             gwlist_destroy(path_elements, octstr_destroy_item);
02346             ++mapindex;
02347             continue;
02348         }
02349 
02350         /* found the correct node (it's stored in parent) */
02351         if (map->attribute) {
02352 
02353             /* The user wants to get an attribute */
02354             xmlChar* content;
02355             content = xmlGetProp(parent, (xmlChar *)octstr_get_cstr(map->attribute));
02356             if (content)
02357                 temp = octstr_create((char *)content);
02358             else /* dont treat an empty or non-existant attribute as an error right away */
02359                 temp = octstr_create("");
02360             xmlFree(content);
02361         } else {
02362             /* the user wants to get the content */
02363             xmlChar* content;
02364             content = xmlNodeGetContent(parent);
02365             if (content)
02366                 temp = octstr_create((char *)content);
02367             else /*  don't treat an empty tag an error right away */
02368 
02369                 temp = octstr_create("");
02370             xmlFree(content);
02371         }
02372 
02373         /* parse the content using sscan_type from the map */
02374         octstr_strip_blanks(temp);
02375         if (!octstr_str_compare(map->sscan_type,"%s")) {
02376             /* special processing of %s - this means the whole string, while sscanf stops at spaces */
02377             strcpy(map->store,octstr_get_cstr(temp));
02378 
02379             ++args;
02380         } else {
02381             if (!sscanf(octstr_get_cstr(temp), octstr_get_cstr(map->sscan_type), map->store)) {
02382                 debug("bb.soap.map_xml_data",0,"SOAP: failed to scan content '%s' for '%s' in xml parsing",
02383                       octstr_get_cstr(temp), octstr_get_cstr(map->sscan_type));
02384             } else {
02385                 ++args;
02386             }
02387         }
02388 
02389 
02390         /* done for this item */
02391         octstr_destroy(temp);
02392         gwlist_destroy(path_elements, octstr_destroy_item);
02393         ++mapindex;
02394     }
02395     return args;
02396 }
02397 
02398 /*
02399  * function soap_release_dependences()
02400  * check for each key if we need specific convertation and do it
02401  * Input:  specification of dependences to convert, map with all keys, msg structure to change values
02402  * Returns: error code or 0 on success
02403  **/
02404 long soap_release_dependences(Octstr* file_deps, List* lstmaps, Msg* msg, PrivData *privdata)
02405 {
02406     List *issues;
02407     long i, j, key_index, key_deps_index, map_index;
02408     int res, k;
02409     List *issue_items, *header_item;
02410     int key_func_index;
02411     ArgumentMap* map;
02412     Octstr *header, *key, *key_deps;
02413     Octstr *func_alias = NULL, *block;
02414 
02415     /* follows  keys and funcs identifiers must be
02416      * the same as in a 'deps' file
02417      **/
02418 
02419 
02420     /*  structure of file_deps;
02421      *
02422      *  <key> <key_deps>
02423      *  <key_deps_value> <function_alias>
02424      *  <key_deps_value> <function_alias>
02425      *  ;
02426      */
02427 
02428     /* ADD HERE: */
02429 
02430     char* funcs[][5] = {    /* functions aliasis used in mo.deps file */
02431                            {"text","binary","unicode","default"},                     /*  msgtype */
02432                            {"set_iso","64_binary","hex_binary","unicode","default"}  /* msgdata */
02433                        };
02434 
02435 
02436     issues = octstr_split(file_deps, octstr_imm(";"));                  /* get paragraphs */
02437 
02438     if (gwlist_len(issues) == 0) {
02439         error(0, "SOAP: soap_release_dependences, empty or broken 'deps' file");
02440         return -1;
02441     }
02442 
02443     for (i=0; i<gwlist_len(issues); ++i)                                    /* loop paragraphs */
02444     {
02445         block = gwlist_get(issues, i);
02446         octstr_strip_crlfs(block);
02447         octstr_strip_blanks(block);
02448 
02449         issue_items = octstr_split(block, octstr_imm("\n"));
02450         if (gwlist_len(issue_items) < 2) {
02451             error(0, "SOAP: soap_release_dependences, broken file 'deps' can't find any definition for <key>");
02452             gwlist_destroy(issue_items, octstr_destroy_item);
02453             gwlist_destroy(issues, octstr_destroy_item);
02454             return -1;
02455         }
02456 
02457 
02458         header = gwlist_extract_first(issue_items);
02459         header_item = octstr_split_words(header);               /* header content */
02460         O_DESTROY(header);
02461 
02462         if (gwlist_len(header_item) < 2) {
02463             error(0, "SOAP: soap_release_dependences, broken 'deps' file in <key> <key_deps> part");
02464             gwlist_destroy(header_item, octstr_destroy_item);
02465             gwlist_destroy(issue_items, octstr_destroy_item);
02466             gwlist_destroy(issues, octstr_destroy_item);
02467             return -1;
02468         }
02469 
02470         key      = gwlist_get(header_item, 0);
02471         key_deps = gwlist_get(header_item, 1);
02472         key_index      = soap_get_index(lstmaps, key, 0);      /* search key_index */
02473         key_deps_index = soap_get_index(lstmaps, key_deps, 0); /* search key_deps_index, from what depends */
02474 
02475         if (key_index == -1 || key_deps_index == -1) {
02476             gwlist_destroy(header_item, octstr_destroy_item);
02477             gwlist_destroy(issue_items, octstr_destroy_item);
02478             gwlist_destroy(issues, octstr_destroy_item);
02479             return -1;
02480         }
02481 
02482         map_index = soap_get_index(lstmaps, key_deps, 1); /* get index for map->name==key_deps */
02483         map = gwlist_get(lstmaps, map_index);
02484 
02485         /* search <function_identifier> and if not found try to set default */
02486         for (j=0; j < gwlist_len(issue_items); ++j) {
02487 
02488             Octstr *tmp = gwlist_get(issue_items, j);
02489             List *row = octstr_split_words(tmp);
02490 
02491             if (!octstr_str_compare(gwlist_get(row, 0), map->store)) {
02492                 func_alias = octstr_duplicate(gwlist_get(row, 1));
02493                 gwlist_destroy(row, octstr_destroy_item);
02494                 break;
02495             }
02496 
02497             if (j==gwlist_len(issue_items)-1) {
02498                 error(0, "SOAP: soap_release_dependences, \
02499                       can't find function_alias for <%s> in 'deps' file, set default", (char*)map->store);
02500                 func_alias = octstr_create(SPEC_DEFAULT);
02501             }
02502             gwlist_destroy(row, octstr_destroy_item);
02503         }
02504 
02505         key_func_index = -1;
02506         /* searching index of function by its alias */
02507         for (k=0; k < sizeof(funcs[key_index])/sizeof(funcs[key_index][0]); ++k)
02508         {
02509             if (!octstr_str_compare(func_alias, funcs[key_index][k])) {
02510                 key_func_index = k;
02511                 break;
02512             }
02513         }
02514         if (key_func_index==-1)
02515             error(0, "SOAP: soap_release_dependences, can't find function for alias <%s>", octstr_get_cstr(func_alias));
02516 
02517         O_DESTROY(func_alias);
02518 
02519         gwlist_destroy(header_item, octstr_destroy_item);
02520         gwlist_destroy(issue_items, octstr_destroy_item);
02521 
02522         /* which field has deps, which func need be called, msg need be changed */
02523         if ((res=soap_process_deps(key_index, key_func_index, msg, privdata)) < 0)
02524             error(0, "SOAP: soap_release_dependences, error processing dependent value");
02525     }
02526     gwlist_destroy(issues, octstr_destroy_item);
02527 
02528 
02529     return 0; /* OK */
02530 }
02531 
02532 /*
02533  * function soap_process_deps
02534  * select function for selected <key>
02535  **/
02536 int soap_process_deps(int key_index, int key_func_ind, Msg* msg, PrivData *privdata)
02537 {
02538     /* ADD HERE: MO 'key' functions */
02539     switch (key_index)
02540     {
02541         case 0:
02542             return soap_msgtype_deps(key_func_ind, msg);
02543         case 1:
02544             return soap_msgdata_deps(key_func_ind, msg, privdata);
02545         default:
02546             return -1;
02547     }
02548     return -1;
02549 }
02550 
02551 /*
02552 * function soap_msgtype_deps
02553 * release dependences fot all types of specific coding
02554 **/
02555 int soap_msgtype_deps(int key_func_index, Msg* msg)
02556 {
02557     /* {"text","binary","unicode","default"}    msgtype  */
02558 
02559     /* ADD HERE: see order in funcs[][] */
02560     switch (key_func_index)
02561     {
02562         case 0:                                 /* "text" */
02563             msg->sms.coding = DC_7BIT;
02564             break;
02565         case 1:                                 /* "binary" */
02566             msg->sms.coding = DC_8BIT;
02567             break;
02568 
02569         case 2:                                 /* "unicode" */
02570         case 3:                                 /* "default" == unicode */
02571             msg->sms.coding = DC_UCS2;
02572             break;
02573         default:
02574             /* out of range */
02575             error(0, "SOAP: soap_msgtype_deps, unknown index %d", key_func_index);
02576             return -1;
02577     }
02578     return 0;
02579 }
02580 
02581 int soap_msgdata_deps(int key_func_index, Msg* msg, PrivData *privdata)
02582 {
02583     int ret = 0;
02584     /* {"set_iso","64_binary","hex_binary","unicode","default"}  msgdata */
02585 
02586     /* ADD HERE: */
02587     switch (key_func_index)
02588     {
02589         case 0:       /* "set_iso" */
02590             msg->sms.charset = octstr_create("ISO-8859-1");
02591             break;
02592         case 1:      /* "64_binary" */
02593             octstr_base64_to_binary(msg->sms.msgdata);
02594             break;
02595         case 2:          /* "hex_binary" */
02596             octstr_hex_to_binary(msg->sms.msgdata);
02597             break;
02598 
02599         case 3:         /* "unicode" */
02600         case 4:         /* "default" */
02601 
02602             if (!octstr_case_compare(privdata->alt_charset, octstr_imm("UCS-2"))) {
02603                 debug("bb.soap.msgdata_deps",0,"SOAP[%s]: converting from %s to UCS-2BE",
02604                       octstr_get_cstr(privdata->name), octstr_get_cstr(privdata->alt_charset));
02605                 ret = charset_convert(msg->sms.msgdata, octstr_get_cstr(privdata->alt_charset), "UCS-2BE");
02606 
02607                 if (ret == -1) {
02608 
02609                     error(2,"SOAP[%s]: Error converting MO data from %s to unicode",
02610                           octstr_get_cstr(privdata->name), octstr_get_cstr(privdata->alt_charset));
02611                 }
02612             }
02613             else if (ret != 0) {
02614                 debug("bb.soap.parse_mo",1,"SOAP[%s]: charset_convert made %d irreversable transformations",
02615                       octstr_get_cstr(privdata->name), ret);
02616             }
02617             msg->sms.charset = octstr_create("UCS-2");
02618 
02619             debug("bb.soap.parse_mo",0,"SOAP[%s]: message decoded -", octstr_get_cstr(privdata->name));
02620             octstr_dump(msg->sms.msgdata, 0);
02621             break;
02622 
02623         default:
02624             /* out of range */
02625 
02626             error(0, "SOAP: soap_msgdata_deps, unknown index %d", key_func_index);
02627             return -1;
02628     }
02629     return 0;
02630 }
02631 
02632 
02633 /* MT spec processing */
02634 /* return index of function alias in array of aliasis for functions
02635 *  used in MT XML file to fill up specific parameters in XML doc
02636 */
02637 int soap_lookup_function(Octstr* funcname)
02638 {
02639     int i;
02640 
02641     /* ADD HERE: XML functions aliasis */
02642     char *aliasis[] = {
02643                           "bouyg_content", "mobitai_content",
02644                           "o2o_msgdata", "msgdata",
02645                           "o2o_validity30", "mobitai_validity_date", "bouyg_validity",
02646                           "o2o_date", "mobitai_date", "rand",
02647                           "o2o_dlrmask_smsc_yn", "o2o_dlrmask_success_01"
02648                       };
02649 
02650     for (i=0; i<sizeof(aliasis)/sizeof(aliasis[0]); ++i)
02651     {
02652         if (!octstr_str_compare(funcname, aliasis[i]))
02653             return i;
02654     }
02655     return -1;
02656 }
02657 
02658 /*
02659 * select function by index
02660 * follow the order defined in array of aliasis in look_up_function()
02661 * return Octstr value
02662 */
02663 Octstr* soap_select_function(int index, Msg* msg, PrivData* privdata)
02664 {
02665     /* ADD HERE: XML functions  */
02666     switch (index)
02667     {
02668         case 0:
02669             return soap_bouyg_content_attribute(msg);
02670         case 1:
02671             return soap_mobitai_content_attribute(msg);
02672         case 2:
02673             return soap_o2o_msgdata_attribute(msg, privdata);
02674         case 3:
02675             return soap_msgdata_attribute(msg, privdata);           /* mobitai/bouyg */
02676         case 4:
02677             return soap_o2o_validity30_attribute(msg);
02678         case 5:
02679             return soap_mobitai_validity_date_attribute(msg);
02680         case 6:
02681             return soap_bouyg_validity_attribute(msg);
02682         case 7:
02683             return soap_o2o_date_attribute(msg);
02684         case 8:
02685             return soap_mobitai_date_attribute(msg);
02686         case 9:
02687             return soap_rand_attribute(msg);
02688         case 10:
02689             return soap_o2o_dlrmask_smsc_yn_attribute(msg);
02690         case 11:
02691             return soap_o2o_dlrmask_success_01_attribute(msg);
02692         default:
02693             error(0,"SOAP: soap_select_function can't find function");
02694             return NULL;
02695     }
02696 }
02697 
02698 /* set of MT msg structure to XML specific converting functions */
02699 
02700 Octstr* soap_bouyg_content_attribute(Msg* msg)
02701 {
02702 
02703     if (msg->sms.coding == DC_8BIT)
02704         return octstr_create("D");
02705     else
02706         return octstr_create("A");
02707 }
02708 
02709 Octstr* soap_mobitai_content_attribute(Msg* msg)
02710 {
02711     if (msg->sms.coding == DC_8BIT)
02712         return octstr_create("binary");
02713     else
02714         return octstr_create("text");
02715 
02716 }
02717 
02718 Octstr* soap_o2o_msgdata_attribute(Msg* msg, PrivData *privdata)
02719 {
02720     Octstr *data, *res, *udhres;
02721     int ret;
02722 
02723     data = octstr_duplicate(msg->sms.msgdata);
02724 
02725     if (msg->sms.coding == DC_8BIT) {
02726         debug("bb.soap.o2o_msgdata_attribute",0,"SOAP: base 64 encoding");
02727         octstr_binary_to_base64(data);
02728         res = octstr_format("<Control_Data>%S</Control_Data>", data);
02729 
02730         if (octstr_len(msg->sms.udhdata) > 0) { /* add UDH */
02731             O_DESTROY(data);
02732             data = octstr_duplicate(msg->sms.udhdata);
02733             debug("bb.soap.o2o_msgdata_attribute",0,"SOAP: UDH base 64 encoding");
02734             octstr_binary_to_base64(data);
02735             udhres = octstr_format("<UDH>%S</UDH>", data);
02736             octstr_append(res, udhres);
02737             O_DESTROY(udhres);
02738         }
02739         else {
02740             error(0, "SOAP: o2o_msgdata_attribute, UDH not defined");
02741             udhres = octstr_create("<UDH></UDH>");
02742             octstr_append(res, udhres);
02743             O_DESTROY(udhres);
02744         }
02745         O_DESTROY(data);
02746         return res;
02747     }
02748     else if (msg->sms.coding == DC_7BIT || msg->sms.coding == DC_UNDEF) {
02749         /* convert message data to target encoding */
02750         debug("bb.soap.o2o_msgdata_attribute", 0, "SOAP: converting from UTF-8 to %s", octstr_get_cstr(privdata->alt_charset));
02751         ret = charset_convert(data, "UTF-8", octstr_get_cstr(privdata->alt_charset));
02752         if (ret == -1) {
02753             error(0,"SOAP: soap_o2o_msgdata_attribute, charset_convert failed");
02754             octstr_dump(msg->sms.msgdata, 0);
02755             O_DESTROY(data);
02756             return NULL;
02757         }
02758         debug("bb.soap.o2o_msgdata_attribute",0,"SOAP: converting to HTML entities");
02759         octstr_convert_to_html_entities(data);
02760         res = octstr_format("<Message_Text>%S</Message_Text>", data);
02761         O_DESTROY(data);
02762         return res;
02763     }
02764     else if (msg->sms.coding == DC_UCS2) {
02765         /* convert message data to target encoding */
02766         debug("bb.soap.o2o_msgdata_attribute", 0, "converting from USC-2 to %s", octstr_get_cstr(privdata->alt_charset));
02767         ret = charset_convert(msg->sms.msgdata, "UCS-2BE", octstr_get_cstr(privdata->alt_charset));
02768         if (ret == -1) {
02769             error(0,"SOAP: soap_o2o_msgdata_attribute, charset_convert failed");
02770 
02771             octstr_dump(msg->sms.msgdata, 0);
02772             O_DESTROY(data);
02773             return NULL;
02774         }
02775         res = octstr_format("<Message_Text>%s</Message_Text>", data);
02776         O_DESTROY(data);
02777         return res;
02778     }
02779 
02780     else {
02781         error(0,"SOAP: soap_o2o_msgdata_attribute, unknown coding: %ld", msg->sms.coding);
02782         O_DESTROY(data);
02783         return NULL;
02784 
02785     }
02786 }
02787 
02788 /* mobitai/bouyg */
02789 Octstr* soap_msgdata_attribute(Msg* msg, PrivData* privdata)
02790 {
02791     Octstr *data, *udhdata;
02792     int ret;
02793 
02794 
02795     data = octstr_duplicate(msg->sms.msgdata);
02796 
02797     if (msg->sms.coding == DC_8BIT) {
02798         udhdata = octstr_duplicate(msg->sms.udhdata);
02799         octstr_append(udhdata, data);
02800         octstr_binary_to_hex(udhdata, 1);
02801         O_DESTROY(data);
02802         return udhdata;
02803     }
02804     else if (msg->sms.coding == DC_7BIT || msg->sms.coding == DC_UNDEF) {
02805         /* convert message data to target encoding */
02806         debug("bb.soap.msgdata_attribute", 0, "SOAP: converting from UTF-8 to %s", octstr_get_cstr(privdata->alt_charset));
02807         ret = charset_convert(data, "UTF-8", octstr_get_cstr(privdata->alt_charset));
02808         if (ret == -1) {
02809             error(0,"SOAP: soap_msgdata_attribute, charset_convert failed");
02810             octstr_dump(msg->sms.msgdata, 0);
02811             O_DESTROY(data);
02812             return NULL;
02813         }
02814         debug("bb.soap.msgdata_attribute",0,"SOAP: converting to HTML entities");
02815         octstr_convert_to_html_entities(data);
02816         return data;
02817     }
02818     else if (msg->sms.coding == DC_UCS2) {
02819         /* convert message data to target encoding */
02820         debug("bb.soap.msgdata_attribute", 0, "converting from USC-2 to %s", octstr_get_cstr(privdata->alt_charset));
02821         ret = charset_convert(data, "UCS-2BE", octstr_get_cstr(privdata->alt_charset));
02822         if (ret == -1) {
02823             error(0,"SOAP: soap_msgdata_attribute, charset_convert failed");
02824 
02825             octstr_dump(data, 0);
02826             O_DESTROY(data);
02827             return NULL;
02828         }
02829         return data;
02830     }
02831     else {
02832         error(0,"SOAP: soap_msgdata_attribute, unknown coding: %ld", msg->sms.coding);
02833         O_DESTROY(data);
02834         return NULL;
02835     }
02836 }
02837 
02838 /* validity in 30 minutes increment */
02839 Octstr* soap_o2o_validity30_attribute(Msg* msg)
02840 {
02841     return octstr_format("%ld",(msg->sms.validity != SMS_PARAM_UNDEFINED ? 
02842              msg->sms.validity : SOAP_DEFAULT_VALIDITY) / 30);
02843 }
02844 
02845 /* date on which the message's validity expires */
02846 Octstr* soap_mobitai_validity_date_attribute(Msg* msg)
02847 {
02848     return date_create_iso(msg->sms.time+(60*msg->sms.validity));
02849 }
02850 
02851 /* validity in seconds */
02852 Octstr* soap_bouyg_validity_attribute(Msg* msg)
02853 {
02854     if(msg->sms.validity >= 0)
02855     return octstr_format("%d", 60*msg->sms.validity);
02856     else
02857     return octstr_format("%d", 0);
02858 }
02859 
02860 Octstr* soap_o2o_date_attribute(Msg* msg)
02861 {
02862     return soap_write_date(msg->sms.time);
02863 }
02864 
02865 
02866 /* TIMESTAMP */
02867 Octstr* soap_mobitai_date_attribute(Msg* msg)
02868 {
02869     return date_create_iso(msg->sms.time);
02870 }
02871 
02872 Octstr* soap_rand_attribute(Msg* msg)
02873 {
02874     return octstr_format("%d",gw_rand());
02875 }
02876 
02877 /* "Y" for any of the SMSC generated DLRs, "N" otherwise */
02878 Octstr* soap_o2o_dlrmask_smsc_yn_attribute(Msg* msg)
02879 {
02880     return octstr_create(DLR_IS_ENABLED_SMSC(msg->sms.dlr_mask) ? "Y" : "N");
02881 }
02882 
02883 /* "1" for any of the SMSC generated DLRs, "0" otherwise */
02884 Octstr* soap_o2o_dlrmask_success_01_attribute(Msg* msg)
02885 {
02886     return octstr_create( DLR_IS_SUCCESS(msg->sms.dlr_mask) ? "0" : "1");
02887 }
02888 
02889 /*
02890  * looking for key in the map names collection to find key_index
02891  * if map_index==1 i like to know index in the map 'where' for name 'key'
02892  */
02893 int soap_get_index(List* where, Octstr* key, int map_index)
02894 {
02895     int i, j;
02896     ArgumentMap* map;
02897 
02898     /* ADD HERE: */
02899     /* key and key_deps values as defined in mo.deps */
02900     char* funcs_deps[] = {
02901                              "msgtype", "msgdata"
02902                          };
02903 
02904     for (i=0; i < gwlist_len(where); ++i) {
02905         map = gwlist_get(where, i);
02906         if (!octstr_compare(map->name, key)) {
02907             if (map_index==1) /* return index from the list where found name */
02908                 return i;
02909 
02910             for (j=0; j < sizeof(funcs_deps)/sizeof(funcs_deps[0]); ++j) {
02911                 if (!octstr_str_compare(map->name, funcs_deps[j]))
02912                     return j;
02913             }
02914         }
02915     }
02916     error(0, "SOAP: soap_get_index, broken 'deps' file, can't find key <%s> ", octstr_get_cstr(key));
02917     return -1;
02918 }
02919 
02920 
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.