1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2014 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  *    if any, must include the following acknowledgment:
22  *       "This product includes software developed by the
23  *        Kannel Group (http://www.kannel.org/)."
24  *    Alternately, this acknowledgment may appear in the software itself,
25  *    if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  *    endorse or promote products derived from this software without
29  *    prior written permission. For written permission, please
30  *    contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  *    nor may "Kannel" appear in their name, without prior written
34  *    permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group.  For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * smsc_soap.c - Implementation of SOAP (XML over HTTP) as a Kannel module
59  *
60  * Oded Arbel,     m-Wise inc (oded@m-wise.com)
61  * Dima Milentiev, m-Wise inc (dima@m-wise.com)
62  *
63  * ChangeLog:
64  *
65  * 20/02/2002: started - copied smsc_mam.c for starting
66  * 25/02/2002: implemented MT sending
67  * 09/05/2002: fixed problem crash when HTTP connection fails.
68  *             send message back to bearerbox on HTTP failure instead of local queue
69  * 19/05/2002: stripped leading + from international numbers
70  * 20/05/2002: fixed previous change
71  *             changed Transaction Id returned to support 64 bit integers
72  * 27/05/2002: changed DLR creation to store the transaction ID instead of timestamp
73  *              added parsing of human readable time in DLR
74  * 28/05/2002: added multi thread sending support
75  * 02/06/2002: changed validity computing to accept minutes instead of seconds
76  * 04/06/2002: Changed callbacks to take into account that they might be called while the connection
77  *              is dead.
78 
79  * 04/06/2002: Started to implement generic parsing engine.
80  * 09/06/2002: Removed hardcoded XML generation and parsing
81  * 22/07/2002: Removed wrong assignment of charset_convert return code to msg->sms.coding
82  * 30/07/2002: fixed wrong format for year in soap_write_date
83  *               additional debug and process for invalid charset conversion
84  * 04/08/2002: forced chraset_conversion to/from UCS-2 to use big endianity
85  *             added curly bracing support to XML data tokens
86  * 04/09/2002: Added some debugging info
87  * 05/09/2002: Changed dlr_add and http_start_request calls to support current CVS
88  * 26/09/2002: Added soap_fetch_xml_data
89  * 29/09/2002: Changed Ack/Nack to process case when Ack not return msg ID, move declaration to fix worning
90  * 01/10/2002: started to change MO general
91  * 07/10/2002: MT generalization
92  *
93  * TODOs:
94  *	- add a configuration option to the max number of messages a client can send, and use
95  *	  and implement KeepAlive in the clients.
96  *	- support XML generation through DTD
97  *	- support XML parsing through DTD
98  *
99  *
100  * Usage: add the following to kannel.conf:
101  *
102  *   group = smsc
103  *   smsc = soap
104  *   send-url = <URI>		- URI to send SOAP bubbles at (mandatory)
105  *   receive-port = <number>	- port number to bind our server on (Default: disabled - MT only)
106  *   xml-files = "MT.xml;MO.xml;DLR.xml" - XML templates for generation of MT messages
107  *                                         and MO and DLR responses
108  *   xmlspec-files = "MT.spec;MO.spec;DLR.spec" - XML path spec files for parsing of MT ¥
109  *                                                response and MO and DLR submission
110  *   alt-charset = "character map" - charset in which a text message is received (default UTF-8)
111  *
112  */
113 
114 #include <sys/types.h>
115 #include <sys/socket.h>
116 #include <unistd.h>
117 #include <errno.h>
118 #include <time.h>
119 #include <limits.h>
120 
121 #include "gwlib/gwlib.h"
122 #include "gwlib/http.h"
123 #include "smscconn.h"
124 #include "smscconn_p.h"
125 #include "bb_smscconn_cb.h"
126 #include "msg.h"
127 #include "sms.h"
128 #include "dlr.h"
129 
130 /* libxml include */
131 #include <libxml/xmlmemory.h>
132 #include <libxml/parser.h>
133 
134 /* Defines and defaults */
135 #define SOAP_SLEEP_TIME				0.01
136 #define SOAP_MAX_MESSAGE_PER_ROUND 		1
137 #define SOAP_DEFAULT_SENDER_STRING		"Kannel"
138 #define SOAP_DEFAULT_VALIDITY           60
139 
140 /* URIs for MOs and delivery reports */
141 #define SOAP_MO_URI	        "/mo"
142 #define SOAP_DLR_URI		"/dlr"
143 
144 /* default reponses to HTTP queries */
145 #define SOAP_DEFAULT_MESSAGE				"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>No method by that name</Error>"
146 #define SOAP_ERROR_NO_DLR_MESSAGE			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Sorry - no DLR for that MT</Error>"
147 #define SOAP_ERROR_DLR_MESSAGE				"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Fatal error while trying to parse delivery report</Error>"
148 #define SOAP_ERROR_MO_MESSAGE				"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Fatal error while trying to parse incoming MO</Error>"
149 #define SOAP_ERROR_NO_DATA_MESSAGE			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>No data received</Error>"
150 #define SOAP_ERROR_MALFORMED_DATA_MESSAGE	        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<Error>Malformed data received</Error>"
151 
152 /* map HTTP status codes to SOAP HTTP reply codes */
153 #define SOAP_ERROR_NO_DLR_CODE				HTTP_BAD_METHOD
154 #define SOAP_DEFAULT_CODE				HTTP_NOT_FOUND
155 #define SOAP_ERROR_DLR_CODE				HTTP_INTERNAL_SERVER_ERROR
156 #define SOAP_ERROR_MO_CODE				HTTP_INTERNAL_SERVER_ERROR
157 #define SOAP_ERROR_NO_DATA_CODE				HTTP_NOT_IMPLEMENTED
158 #define SOAP_ERROR_MALFORMED_DATA_CODE	                HTTP_BAD_GATEWAY
159 #define SOAP_QUERY_OK					HTTP_OK
160 
161 /* compile time configuration defines */
162 #undef HUMAN_TIME
163 
164 #define MIN_SOAP_CLIENTS            5
165 #define MAX_SOAP_CLIENTS            50
166 #define CLIENT_BUSY_TIME            5
167 #define CLIENT_TEARDOWN_TIME        600
168 #define CLIENT_BUSY_LOAD            5
169 
170 #define SPEC_DEFAULT				"default"
171 
172 /* private data store for the SOAP module */
173 typedef struct privdata {
174     List *outgoing_queue;	/* queue to hold unsent messages */
175 
176     long listener_thread;	/* SOAP HTTP client and module managment */
177     long server_thread; 	/* SOAP HTTP server */
178 
179     int shutdown;		/* Internal signal to shut down */
180     int soap_server;	/* internal signal to shut down the server */
181 
182     long port;    	        /* listener port */
183     int ssl;		/* flag whether to use SSL for the server */
184 
185     Octstr *uri;		/* URI to send MTs on */
186 
187     Octstr *allow_ip, *deny_ip; /* connection allowed mask */
188 
189     List* soap_client;  /* list to hold callers */
190 
191     Octstr* name;		/* connection name for use in private functions that want to do logging */
192 
193     /* SOAP configurtion */
194     Octstr* form_variable; /* variable name used in post */
195     int form_urlencoded; /* whether to send the data urlencoded or multipart */
196     Octstr* alt_charset;   /* alt-charset to use */
197 
198     Octstr* mt_xml_file;
199     Octstr* mt_spec_file;
200     Octstr* mo_xml_file;
201     Octstr* mo_spec_file;
202     Octstr* dlr_xml_file;
203     Octstr* dlr_spec_file;
204     Octstr* mo_deps_file;
205 } PrivData;
206 
207 /* struct to hold one HTTP client connection (I hope) */
208 typedef struct client_data {
209     time_t last_access;
210     unsigned long requests;
211     HTTPCaller* caller;
212 } ClientData;
213 
214 /* struct useful for the XML mapping routines */
215 typedef struct argument_map {
216     Octstr* name;
217     Octstr* path;
218     Octstr* attribute;
219     Octstr* sscan_type;
220     void* store;
221 } ArgumentMap;
222 
223 /* useful macros go here (some of these were ripped of other modules,
224    so maybe its better to put them in a shared file) */
225 #define	O_DESTROY(a)	{ if(a) octstr_destroy(a); a=NULL; }
226 
227 /*
228  * SOAP module public API towards bearerbox
229  */
230 
231 /* module entry point - will also be defined in smscconn_p.h */
232 int smsc_soap_create(SMSCConn *conn, CfgGroup *cfg);
233 /* callback for bearerbox to add messages to our queue */
234 static int soap_add_msg_cb(SMSCConn *conn, Msg *sms);
235 /* callback for bearerbox to signal a shutdown */
236 static int soap_shutdown_cb(SMSCConn *conn, int finish_sending);
237 /* callback for bearerbox to signal us to start the connection */
238 static void soap_start_cb(SMSCConn *conn);
239 /* callback for bearerbox to signal us to drop the connection */
240 static void soap_stop_cb(SMSCConn *conn);
241 /* callback for bearerbox to query on the number of messages in our queue */
242 static long soap_queued_cb(SMSCConn *conn);
243 
244 /*
245  * SOAP module thread functions (created by smsc_soap_create())
246  */
247 
248 /* SOAP module thread for launching HTTP clients. */
249 static void soap_listener(void *arg);
250 /* SOAP HTTP server thread for incoming MO */
251 static void soap_server(void *arg);
252 
253 /*
254  * SOAP module internal protocol implementation functions
255  */
256 
257 /* start the loop to send all messages in the queue */
258 static void soap_send_loop(SMSCConn *conn);
259 /* function used to send a single MT message */
260 static void soap_send(PrivData *privdata, Octstr *xmlbuffer, Msg *msgid);
261 /* called to retrieve HTTP responses from the HTTP library */
262 static void soap_read_response(SMSCConn *conn);
263 /* format a messages structure as an XML buffer */
264 static Octstr *soap_format_xml(Octstr *xml_file, Msg *msg, PrivData *privdata);
265 /* parse a response from the SOAP server to get the message ID */
266 
267 static long long soap_parse_response(PrivData *privdata, Octstr *xmlResponse);
268 /* parse an incoming MO xml */
269 static long soap_parse_mo(SMSCConn *conn, Octstr *request, Octstr **response);
270 /* parse an incoming derlivery report */
271 static long soap_parse_dlr(SMSCConn *conn, Octstr *request, Octstr **response);
272 
273 /*
274  * SOAP internal utility functions
275  */
276 /* parse an integer out of a XML node */
277 int soap_xmlnode_get_long(xmlNodePtr cur, long *out);
278 /* parse an int64 out of a XML node */
279 int soap_xmlnode_get_int64(xmlNodePtr cur, long long *out);
280 /* parse a string out of a XML node */
281 int soap_xmlnode_get_octstr(xmlNodePtr cur, Octstr **out);
282 /* convert a one2one date format to epoch time */
283 time_t soap_read_date(Octstr *dateString);
284 /* convert a epoch time to one2one date format */
285 static Octstr *soap_write_date(time_t date);
286 /* start the SOAP server */
287 int soap_server_start(SMSCConn *conn);
288 /* stop the SOAP server */
289 static void soap_server_stop(PrivData *privdata);
290 /* create a new SOAP client caller */
291 static ClientData *soap_create_client_data();
292 /* destroy a SOAP client caller */
293 static void soap_destroy_client_data(void *data);
294 /* start an HTTP query */
295 static void soap_client_init_query(PrivData *privdata, List *headers, Octstr *data, Msg *msg);
296 /* return a caller from the pool that has responses waiting */
297 static ClientData *soap_client_have_response(List *client_list);
298 /* return data from a message according to its name */
299 static Octstr *soap_convert_token(Msg *msg, Octstr *name, PrivData *privdata);
300 /* convert a XML parsing spec file and a list of recognized keywords to an argument map */
301 List *soap_create_map(Octstr* spec, long count, char* keywords[], char* types[], void* storage[]);
302 /* destroy a map structure */
303 void soap_destroy_map(void *item);
304 /* map content in a XML structure to a list of variable using a spec file */
305 int soap_map_xml_data(xmlNodePtr xml, List* maps);
306 /* fetch content from the XML */
307 Octstr* soap_fetch_xml_data(xmlNodePtr xml, Octstr* path);
308 
309 /* MO */
310 /* search and release dependences for keys */
311 long soap_release_dependences(Octstr* deps, List* lstmaps, Msg* msg, PrivData *privdata);
312 /* for appropriate <key> call function referenced by key_func_ind */
313 int soap_process_deps(int key_index, int key_func_ind, Msg* msg, PrivData *privdata);
314 
315 /* <key>s specific functions */
316 int soap_msgtype_deps(int key_func_index, Msg* msg);
317 int soap_msgdata_deps(int key_func_index, Msg* msg, PrivData *privdata);
318 
319 /* MT */
320 /* return index of functions alias in array of function aliases */
321 int soap_lookup_function(Octstr* funcname);
322 
323 /* select function by index */
324 Octstr* soap_select_function(int index, Msg* msg, PrivData* privdata);
325 
326 Octstr* soap_bouyg_content_attribute(Msg* msg);
327 Octstr* soap_mobitai_content_attribute(Msg* msg);
328 Octstr* soap_o2o_msgdata_attribute(Msg* msg, PrivData *privdata);
329 Octstr* soap_msgdata_attribute(Msg* msg, PrivData* privdata);
330 Octstr* soap_o2o_validity30_attribute(Msg* msg);
331 Octstr* soap_mobitai_validity_date_attribute(Msg* msg);
332 Octstr* soap_bouyg_validity_attribute(Msg* msg);
333 Octstr* soap_o2o_date_attribute(Msg* msg);
334 Octstr* soap_mobitai_date_attribute(Msg* msg);
335 Octstr* soap_rand_attribute(Msg* msg);
336 Octstr* soap_o2o_dlrmask_smsc_yn_attribute(Msg* msg);
337 Octstr* soap_o2o_dlrmask_success_01_attribute(Msg* msg);
338 
339 /* searching 'key' in 'where' and return index of element or -1 */
340 int soap_get_index(List* where, Octstr* key, int map_index);
341 
342 
343 /**************************************************************************************
344  * Implementation
345  */
346 
347 /*
348  * function smsc_soap_create()
349  *	called to create and initalize the module's internal data.
350  *	if needed also will start the connection threads
351  * Input: SMSCConn pointer to connection data, cfgGroup pointer to configuration data
352  * Returns: status (0 = OK, -1 = failed)
353  */
smsc_soap_create(SMSCConn * conn,CfgGroup * cfg)354 int smsc_soap_create(SMSCConn *conn, CfgGroup *cfg)
355 {
356     PrivData *privdata;
357     Octstr* temp = NULL;
358     List* filenames = NULL;
359 
360     /* allocate and init internat data structure */
361     privdata = gw_malloc(sizeof(PrivData));
362     privdata->outgoing_queue = gwlist_create();
363     /* privdata->pending_ack_queue = gwlist_create(); */
364 
365     privdata->shutdown = 0;
366     privdata->soap_client = NULL;
367     privdata->soap_server = 0;
368 
369     /* read configuration data */
370     if (cfg_get_integer(&(privdata->port), cfg, octstr_imm("receive-port-ssl")) == -1)
371         if (cfg_get_integer(&(privdata->port), cfg, octstr_imm("receive-port")) == -1)
372             privdata->port = 0;
373         else
374             privdata->ssl = 0;
375     else
376 
377         privdata->ssl = 1;
378 
379     privdata->uri = cfg_get(cfg, octstr_imm("send-url"));
380 
381     privdata->allow_ip = cfg_get(cfg, octstr_imm("connect-allow-ip"));
382     if (privdata->allow_ip)
383         privdata->deny_ip = octstr_create("*.*.*.*");
384     else
385         privdata->deny_ip = NULL;
386 
387     /* read XML configuration */
388     privdata->form_variable = cfg_get(cfg, octstr_imm("form-variable"));
389     cfg_get_bool(&(privdata->form_urlencoded), cfg, octstr_imm("form-urlencoded"));
390 
391     privdata->alt_charset = cfg_get(cfg, octstr_imm("alt-charset"));
392     if (!privdata->alt_charset)
393         privdata->alt_charset = octstr_create("utf-8");
394 
395     /* check validity of stuff */
396     if (privdata->port <= 0 || privdata->port > 65535) {
397         error(0, "invalid port definition for SOAP server (%ld) - aborting",
398               privdata->port);
399         goto error;
400     }
401 
402     if (!privdata->uri) {
403         error(0, "invalid or missing send-url definition for SOAP - aborting.");
404         goto error;
405     }
406 
407     if (!privdata->form_variable) {
408         error(0, "invalid or missing form variable name definition for SOAP - aborting.");
409         goto error;
410     }
411 
412     /* load XML templates and specs */
413     filenames = octstr_split(temp = cfg_get(cfg,octstr_imm("xml-files")),
414                              octstr_imm(";"));
415     octstr_destroy(temp);
416     if (gwlist_len(filenames) < 3) {
417         error(0,"SOAP: Not enough template files for XML generation, you need 3 - aborting");
418         goto error;
419     }
420     if ( !(privdata->mt_xml_file = octstr_read_file(
421             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
422         error(0,"SOAP: Can't load XML template for MT - aborting");
423         goto error;
424 
425     }
426     octstr_destroy(temp);
427     if ( !(privdata->mo_xml_file = octstr_read_file(
428             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
429         error(0,"SOAP: Can't load XML template for MO - aborting");
430         goto error;
431     }
432     octstr_destroy(temp);
433     if ( !(privdata->dlr_xml_file = octstr_read_file(
434             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
435 
436         error(0,"SOAP: Can't load XML template for DLR - aborting");
437         goto error;
438     }
439     octstr_destroy(temp);
440     gwlist_destroy(filenames, octstr_destroy_item);
441 
442     filenames = octstr_split(temp = cfg_get(cfg,octstr_imm("xmlspec-files")),
443                              octstr_imm(";"));
444     octstr_destroy(temp);
445     if (gwlist_len(filenames) < 4) {
446         error(0,"Not enough spec files for XML parsing, you need 4 - aborting");
447         goto error;
448     }
449     if ( !(privdata->mt_spec_file = octstr_read_file(
450             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
451         error(0,"Can't load spec for MT parsing - aborting");
452         goto error;
453     }
454     octstr_destroy(temp);
455     if ( !(privdata->mo_spec_file = octstr_read_file(
456             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
457         error(0,"SOAP: Can't load spec for MO parsing - aborting");
458         goto error;
459     }
460     octstr_destroy(temp);
461     if ( !(privdata->dlr_spec_file = octstr_read_file(
462             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
463         error(0,"SOAP: Can't load spec for DLR parsing - aborting");
464         goto error;
465     }
466     octstr_destroy(temp);
467 
468     if ( !(privdata->mo_deps_file = octstr_read_file(
469             octstr_get_cstr(temp = gwlist_extract_first(filenames))))) {
470         error(0,"SOAP: Can't load 'deps' file for MO processing - aborting");
471         goto error;
472     }
473     octstr_destroy(temp);
474 
475     gwlist_destroy(filenames, octstr_destroy_item);
476 
477     debug("bb.soap.create",0,"Connecting to %s",
478           octstr_get_cstr(privdata->uri));
479 
480     /* store private data struct in connection data */
481     conn->data = privdata;
482 
483     /* state my name */
484     conn->name = octstr_format("SOAP: %s", octstr_get_cstr(privdata->uri) );
485     privdata->name = octstr_duplicate(conn->id);
486 
487     /* init status vars */
488     conn->status = SMSCCONN_CONNECTING;
489     conn->connect_time = time(NULL);
490 
491     /* set up call backs for bearerbox */
492     conn->shutdown = soap_shutdown_cb;
493     conn->queued = soap_queued_cb;
494     conn->start_conn = soap_start_cb;
495     conn->stop_conn = soap_stop_cb;
496     conn->send_msg = soap_add_msg_cb;
497 
498     privdata->listener_thread = 0;
499     privdata->server_thread = 0;
500 
501     /* check whether we can start right away */
502     if (!conn->is_stopped)
503         /* yes, we can */
504         conn->status = SMSCCONN_CONNECTING;
505     else
506         conn->status = SMSCCONN_DISCONNECTED;
507 
508     /* any which way - start the connection thread */
509     if ((privdata->listener_thread = gwthread_create(soap_listener, conn)) == -1) {
510         error(0, "SOAP: soap_create, failed to spawn thread - aborting");
511         goto error;
512     }
513 
514     return 0; /* done - ok */
515 
516 error:
517     /* oh oh, problems */
518     error(0, "SOAP: Failed to create SOAP smsc connection");
519 
520     /* release stuff */
521     if (privdata != NULL) {
522         gwlist_destroy(privdata->outgoing_queue, NULL);
523         /* gwlist_destroy(privdata->pending_ack_queue, NULL); */
524 
525         O_DESTROY(privdata->uri);
526         O_DESTROY(privdata->allow_ip);
527         O_DESTROY(privdata->deny_ip);
528         O_DESTROY(privdata->form_variable);
529         O_DESTROY(privdata->alt_charset);
530         O_DESTROY(privdata->name);
531         O_DESTROY(privdata->mo_xml_file);
532         O_DESTROY(privdata->dlr_xml_file);
533         O_DESTROY(privdata->mt_xml_file);
534         O_DESTROY(privdata->mo_spec_file);
535         O_DESTROY(privdata->dlr_spec_file);
536         O_DESTROY(privdata->mt_spec_file);
537         O_DESTROY(privdata->mo_deps_file);
538     }
539     gw_free(privdata);
540     octstr_destroy(temp);
541     gwlist_destroy(filenames, octstr_destroy_item);
542 
543     /* notify bearerbox */
544     conn->why_killed = SMSCCONN_KILLED_CANNOT_CONNECT;
545     conn->status = SMSCCONN_DEAD;
546 
547     info(0, "exiting");
548     return -1; /* I'm dead */
549 }
550 
551 
552 /**************************************************************************************
553  * Callbacks
554  */
555 
556 /*
557  * function soap_add_msg_cb()
558  *	get a message and copy it to the queue. note that message must be copied
559  *	as I don't know what bearerbox wants to do with it after I return
560  * Input: SMSCConn connection state data, Msg to send
561  * Returns: status - 0 on success, -1 on fail.
562  */
soap_add_msg_cb(SMSCConn * conn,Msg * sms)563 static int soap_add_msg_cb(SMSCConn *conn, Msg *sms)
564 {
565 
566     PrivData *privdata = conn->data;
567     Msg *copy;
568 
569     /* I'm dead and cannot take any calls at the moment, please don't leave a message */
570     if (conn->status == SMSCCONN_DEAD)
571         return -1;
572 
573     copy = msg_duplicate(sms); /* copy the message */
574     gwlist_append(privdata->outgoing_queue, copy); /* put it in the queue */
575 
576     debug("bb.soap.add_msg",0,"SOAP[%s]: got a new MT from %s, list has now %ld MTs",
577           octstr_get_cstr(privdata->name), octstr_get_cstr(sms->sms.sender),
578           gwlist_len(privdata->outgoing_queue));
579 
580     gwthread_wakeup(privdata->listener_thread);
581 
582     return 0;
583 }
584 
585 
586 /*
587  * function soap_shutdown_cb()
588  *	called by bearerbox to signal the module to shutdown. sets the shutdown flags,
589  *	wakes up the listener thread and exits (if we add more threads, we need to handle those too)
590  * Input: SMSCConn connection state data, flag indicating whether we can finish sending messages
591  *	in the queue first.
592  * Returns: status - 0 on success, -1 on fail.
593  */
soap_shutdown_cb(SMSCConn * conn,int finish_sending)594 static int soap_shutdown_cb(SMSCConn *conn, int finish_sending)
595 {
596     PrivData *privdata = conn->data;
597     long thread;
598 
599     /* I'm dead, there's really no point in killing me again, is it ? */
600     if (conn->status == SMSCCONN_DEAD)
601         return -1;
602 
603     debug("bb.soap.cb", 0, "SOAP[%s]: Shutting down SMSCConn, %s",
604           octstr_get_cstr(privdata->name), finish_sending ? "slow" : "instant");
605 
606     /* Documentation claims this would have been done by smscconn.c,
607      * but isn't when this code is being written.*/
608     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
609     /* Separate from why_killed to avoid locking,
610      * as why_killed may be changed from outside? */
611     privdata->shutdown = 1;
612 
613     if (finish_sending == 0) {
614         Msg *msg;
615         while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL)
616             bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN, NULL);
617     }
618 
619     thread = privdata->listener_thread;
620 
621     gwthread_wakeup(thread);
622     gwthread_join(thread);
623 
624     return 0;
625 }
626 
627 
628 /*
629  * function soap_start_cb()
630  *	called by bearerbox when the module is allowed to start working
631  * Input: SMSCConn connection state data
632  * Returns: status - 0 on success, -1 on fail.
633  */
soap_start_cb(SMSCConn * conn)634 static void soap_start_cb(SMSCConn *conn)
635 {
636     PrivData *privdata = conn->data;
637 
638     debug("smsc.soap.start", 0, "SOAP[%s]: start called",
639           octstr_get_cstr(privdata->name));
640 
641     /* set the status so that connection_thread will know what to do */
642     conn->status = SMSCCONN_CONNECTING;
643 
644     /* start connection_thread, in case its not started. */
645     if ((!privdata->listener_thread) &&
646         ((privdata->listener_thread = gwthread_create(soap_listener, conn)) == -1)) {
647         error(0, "SOAP: soap_start, failed to spawn thread - aborting");
648         conn->why_killed = SMSCCONN_KILLED_CANNOT_CONNECT;
649         conn->status = SMSCCONN_DEAD;
650         privdata->shutdown = 1;
651         return;
652     }
653     /* gwthread_wakeup(privdata->listener_thread); */
654     debug("smsc.soap.start",0,"SOAP[%s]: starting OK",
655           octstr_get_cstr(privdata->name));
656 }
657 
658 
659 /*
660  * function soap_stop_cb()
661  *	this function may be used to 'pause' the module. it should cause the connection
662  *	to logout, but not to be destroyed, so it will be restarted later.
663  * Input: SMSCConn connection state data
664  */
soap_stop_cb(SMSCConn * conn)665 static void soap_stop_cb(SMSCConn *conn)
666 {
667     PrivData *privdata = conn->data;
668 
669     /* I'm dead, its really too late to take a break now */
670     if (conn->status == SMSCCONN_DEAD)
671         return;
672 
673     debug("smsc.soap.stop", 0, "SOAP[%s]: stop called",
674           octstr_get_cstr(privdata->name));
675 
676     /* make connection thread disconnect */
677     conn->status = SMSCCONN_DISCONNECTED;
678 }
679 
680 
681 /*
682  * function soap_queued_cb()
683  *	called by bearerbox to query the number of messages pending send.
684  *	the number returned includes the number of messages sent, but for which no ACK was yet received.
685  * Input: SMSCConn connection state data
686  * Returns: number of messages still waiting to be sent
687  */
soap_queued_cb(SMSCConn * conn)688 static long soap_queued_cb(SMSCConn *conn)
689 {
690     PrivData *privdata = conn->data;
691     long ret;
692 
693     /* I'm dead, so I have no queues - well there ! */
694     if (conn->status == SMSCCONN_DEAD)
695         return -1;
696 
697     ret = gwlist_len(privdata->outgoing_queue);
698     /* + gwlist_len(privdata->pending_ack_queue); */
699 
700     /* use internal queue as load, maybe something else later */
701     conn->load = ret;
702 
703     return ret;
704 }
705 
706 
707 /**************************************************************************************
708  * SOAP module thread functions (created by smsc_soap_create())
709  */
710 
711 /*
712  * function soap_listener()
713  *	entry point to the listenr thread. this thread listenes on the MO port (if
714  *	needed, and is also reposnsible for invoking "MT threads" (HTTP clients) to
715  *	to send MTs.
716  * Input: SMSCConn connection state data
717  */
soap_listener(void * arg)718 static void soap_listener(void *arg)
719 {
720     SMSCConn *conn = arg;
721     PrivData *privdata = conn->data;
722     Msg *msg = NULL;
723     debug("bb.soap.listener",0,"SOAP[%s]: listener entering",
724           octstr_get_cstr(privdata->name));
725 
726     while (!privdata->shutdown) {
727 
728         /* check connection status */
729         switch (conn->status) {
730             case SMSCCONN_RECONNECTING:
731             case SMSCCONN_CONNECTING:
732                 if (privdata->soap_server) {
733                     soap_server_stop(privdata);
734                 }
735 
736                 if (soap_server_start(conn)) {
737                     privdata->shutdown = 1;
738                     error(0, "SOAP[%s]: failed to start HTTP server!",
739                           octstr_get_cstr(privdata->name));
740                     break;
741                 }
742 
743                 mutex_lock(conn->flow_mutex);
744                 conn->status = SMSCCONN_ACTIVE;
745                 mutex_unlock(conn->flow_mutex);
746 
747                 bb_smscconn_connected(conn);
748                 break;
749 
750             case SMSCCONN_DISCONNECTED:
751                 if (privdata->soap_server)
752                     soap_server_stop(privdata);
753                 break;
754 
755             case SMSCCONN_ACTIVE:
756                 if (!privdata->soap_server) {
757                     mutex_lock(conn->flow_mutex);
758                     conn->status = SMSCCONN_RECONNECTING;
759                     mutex_unlock(conn->flow_mutex);
760                     break;
761                 }
762 
763                 /* run the normal send/receive loop */
764                 if (gwlist_len(privdata->outgoing_queue) > 0) { /* we have messages to send */
765                     soap_send_loop(conn); /* send any messages in queue */
766                 }
767                 break;
768 
769             case SMSCCONN_DEAD:
770                 /* this shouldn't happen here -
771                  * I'm the only one allowed to set SMSCCONN_DEAD */
772 
773             default:
774                 break;
775         }
776 
777         soap_read_response(conn); /* collect HTTP responses */
778 
779         /* sleep for a while so I wont busy-loop */
780         gwthread_sleep(SOAP_SLEEP_TIME);
781     }
782 
783     debug("bb.soap.connection",0,"SOAP[%s]: connection shutting down",
784           octstr_get_cstr(privdata->name));
785 
786     soap_server_stop(privdata);
787 
788     /* send all queued messages to bearerbox for recycling */
789     debug("bb.soap.connection",0,"SOAP[%s]: sending messages back to bearerbox",
790           octstr_get_cstr(privdata->name));
791 
792     while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != NULL)
793         bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN, NULL);
794 
795     /* lock module public state data */
796     mutex_lock(conn->flow_mutex);
797 
798     debug("bb.soap.connection",0,"SOAP[%s]: playing dead",
799           octstr_get_cstr(privdata->name));
800     conn->status = SMSCCONN_DEAD; /* set state */
801 
802     /* destroy lists */
803     debug("bb.soap.connection",0,"SOAP[%s]: don't need the queue anymore",
804           octstr_get_cstr(privdata->name));
805 
806     gwlist_destroy(privdata->outgoing_queue, NULL);
807     /* gwlist_destroy(privdata->pending_ack_queue, NULL); */
808 
809     /* clear the soap client collection */
810     debug("bb.soap.connection",0,"SOAP[%s]: tell caller to stop",
811           octstr_get_cstr(privdata->name));
812     if (privdata->soap_client)
813         gwlist_destroy(privdata->soap_client, soap_destroy_client_data);
814 
815     /* destroy private data stores */
816     debug("bb.soap.connection",0,"SOAP[%s]: done with privdata",
817           octstr_get_cstr(privdata->name));
818     O_DESTROY(privdata->uri);
819     O_DESTROY(privdata->allow_ip);
820     O_DESTROY(privdata->deny_ip);
821 
822     O_DESTROY(privdata->form_variable);
823 
824     O_DESTROY(privdata->alt_charset);
825     O_DESTROY(privdata->name);
826     O_DESTROY(privdata->mo_xml_file);
827 
828     O_DESTROY(privdata->dlr_xml_file);
829     O_DESTROY(privdata->mt_xml_file);
830     O_DESTROY(privdata->mo_spec_file);
831     O_DESTROY(privdata->dlr_spec_file);
832     O_DESTROY(privdata->mt_spec_file);
833     O_DESTROY(privdata->mo_deps_file);
834 
835     gw_free(privdata);
836     conn->data = NULL;
837 
838     mutex_unlock(conn->flow_mutex);
839 
840     debug("bb.soap.connection", 0, "SOAP: module has completed shutdown.");
841     bb_smscconn_killed();
842 }
843 
844 
845 /*
846  * function soap_server()
847  *	server thread - accepts incoming MOs
848  * Input: SMSCConn connection state data
849  */
soap_server(void * arg)850 static void soap_server(void* arg)
851 {
852     SMSCConn* conn = (SMSCConn*)arg;
853     PrivData* privdata = conn->data;
854     /* PrivData* privdata = (PrivData*)arg; */
855 
856     HTTPClient* remote_client = NULL;
857     List *request_headers = NULL, *response_headers = NULL;
858     List *cgivars = NULL;
859     Octstr *client_ip = NULL, *request_uri = NULL, *request_body = NULL;
860     Octstr *response_body = NULL;
861     Octstr *timebuf = NULL;
862     int http_response_status;
863 
864     debug("bb.soap.server",0,"SOAP[%s]: Server starting",
865           octstr_get_cstr(privdata->name));
866 
867     /* create basic headers */
868     response_headers = http_create_empty_headers();
869     http_header_add(response_headers, "Content-type","text/xml");
870     /* http_header_add(response_headers, "Content-type","application/x-www-form-urlencoded"); */
871     /* http_header_add(response_headers,"Connection", "Close"); */
872     http_header_add(response_headers, "Server","Kannel");
873 
874     while (privdata->soap_server) {
875         if ((remote_client = http_accept_request(privdata->port,
876                               &client_ip, &request_uri, &request_headers,
877                               &request_body, &cgivars))) {
878 
879             debug("bb.soap.server",0,"SOAP[%s]: server got a request for "
880                   "%s from %s, with body <%s>", octstr_get_cstr(privdata->name),
881                   octstr_get_cstr(request_uri),octstr_get_cstr(client_ip),
882                   request_body ? octstr_get_cstr(request_body) : "<null>");
883 
884             /* parse request */
885             if (!octstr_compare(request_uri,octstr_imm(SOAP_MO_URI))) {
886                 /* this is an incoming MO */
887                 if ((http_response_status =
888                         soap_parse_mo(conn,request_body, &response_body)) == -1) {
889                     /* fatal error parsing MO */
890                     error(0,"SOAP[%s]: fatal error parsing MO",
891                           octstr_get_cstr(privdata->name));
892                     response_body = octstr_create(SOAP_ERROR_MO_MESSAGE);
893                     http_response_status = SOAP_ERROR_MO_CODE;
894                 }
895             } else if (!octstr_compare(request_uri,octstr_imm(SOAP_DLR_URI))) {
896                 /* a delivery report */
897                 if ((http_response_status =
898                         soap_parse_dlr(conn,request_body, &response_body)) == -1) {
899                     /* fatal error parsing MO */
900                     error(0,"SOAP[%s]: fatal error parsing DLR",
901                           octstr_get_cstr(privdata->name));
902                     response_body = octstr_create(SOAP_ERROR_DLR_MESSAGE);
903                     http_response_status = SOAP_ERROR_DLR_CODE;
904                 }
905             } else {
906                 /* unknown command send default message */
907                 response_body = octstr_create(SOAP_DEFAULT_MESSAGE);
908                 http_response_status = SOAP_DEFAULT_CODE;
909             }
910 
911             /* create response */
912             /*
913             response_body = octstr_create("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
914             "<!DOCTYPE SMSCACCESS_REPLY SYSTEM \"http://superion/~oded/smsc_reply-1_0.dtd\">\n"
915             "<SMSCACCESS_REPLY>\n"
916             "   <SUBSCRIBER>447951718145</SUBSCRIBER>\n"
917             "   <DATE_RECEIVED>22/01/2002:15:12</DATE_RECEIVED>\n"
918             "   <RETURN_CODE>00</RETURN_CODE>\n"
919             "</SMSCACCESS_REPLY>\n");
920             */
921 
922             /* encode date in headers */
923 
924             timebuf = date_format_http(time(NULL));
925             /* http_header_add(response_headers, "Date", octstr_get_cstr(timebuf)); */
926             O_DESTROY(timebuf);
927             /* http_header_dump(response_headers); */
928 
929             /* send response back to client */
930             http_send_reply(remote_client,http_response_status,response_headers, response_body);
931 
932             /* destroy response data */
933             /* http_destroy_headers(response_headers); */
934             O_DESTROY(response_body);
935 
936             /* destroy request data */
937             O_DESTROY(request_uri);
938             O_DESTROY(request_body);
939             O_DESTROY(client_ip);
940 
941             http_destroy_headers(request_headers);
942             gwlist_destroy(cgivars, NULL);
943         }
944 
945         gwthread_sleep(SOAP_SLEEP_TIME);
946     }
947 
948     debug("bb.soap.server",0,"SOAP[%s]: server going down",
949           octstr_get_cstr(privdata->name));
950     /* privdata->server_thread = 0; */
951 }
952 
953 
954 /**************************************************************************************
955  * SOAP module internal protocol implementation functions
956  */
957 
958 /*
959  * function soap_send_loop()
960  *	called when there are messages in the queue waiting to be sent
961  * Input: SMSCConn connection state data
962  */
soap_send_loop(SMSCConn * conn)963 static void soap_send_loop(SMSCConn* conn)
964 {
965     PrivData* privdata = conn->data;
966     Msg *msg;
967     Octstr* xmldata = NULL;
968     int counter = 0;
969 
970     debug("bb.soap.client",0,"SOAP[%s]: client - entering",
971           octstr_get_cstr(privdata->name));
972 
973     while ((counter < SOAP_MAX_MESSAGE_PER_ROUND) &&
974             (msg = gwlist_extract_first(privdata->outgoing_queue))) {
975         /* as long as we have some messages */
976         ++counter;
977 
978         if (uuid_is_null(msg->sms.id))   /* generate a message id */
979             uuid_generate(msg->sms.id);
980 
981         /* format the messages as a character buffer to send */
982         if (!(xmldata = soap_format_xml(privdata->mt_xml_file, msg, privdata))) {
983             debug("bb.soap.client",0,"SOAP[%s]: client - failed to format message for sending",
984                   octstr_get_cstr(privdata->name));
985             bb_smscconn_send_failed(conn, msg,
986 	                SMSCCONN_FAILED_MALFORMED, octstr_create("MALFORMED"));
987             continue;
988         }
989 
990         debug("bb.soap.client",0,"SOAP[%s]: client - Sending message <%s>",
991               octstr_get_cstr(privdata->name), octstr_get_cstr(msg->sms.msgdata));
992         if (xmldata)
993             debug("bb.soap.client",0,"SOAP[%s]: data dump: %s",
994                   octstr_get_cstr(privdata->name), octstr_get_cstr(xmldata));
995 
996         /* send to the server */
997         soap_send(privdata, xmldata, msg);
998 
999         /* store in the second queue so that soap_read_response will know what to do */
1000         /* gwlist_append(privdata->pending_ack_queue,msg); */
1001 
1002         /* don't need this anymore */
1003         O_DESTROY(xmldata);
1004     }
1005 }
1006 
1007 
1008 /*
1009  * function soap_format_xml()
1010  *	fill in the fields in a XML template with data from a message
1011  * Input: Octstr containing an XML template,  Msg structure
1012  * Returns: Octstr xml formated data or NULL on error
1013  */
soap_format_xml(Octstr * xml_file,Msg * msg,PrivData * privdata)1014 static Octstr *soap_format_xml(Octstr *xml_file, Msg *msg, PrivData *privdata)
1015 {
1016     Octstr *xml;
1017     long t;
1018     long start = -1;
1019     int curly_enclose = 0;
1020 
1021     xml = octstr_create("");
1022 
1023     for (t = 0; t < octstr_len(xml_file); ++t) {
1024         unsigned char c;
1025 
1026         if ((c = octstr_get_char(xml_file,t)) == '%') {
1027             /* found start of token */
1028             start = t+1;
1029             continue;
1030         }
1031 
1032         if (c == '{' && start == t) { /* the token is enclosed in curlys */
1033             ++start; /* make sure the token is read from the next char */
1034             curly_enclose=1;
1035         }
1036 
1037         if (start < 0)
1038             octstr_append_char(xml,c);
1039         else if (
1040             (curly_enclose && (c == '}')) /* end of token in case of curly enclosure */
1041             ||
1042             (!curly_enclose && !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
1043                                  (c >= '0' && c <= '9') || c == '_'))) {
1044             /* found end of token */
1045             Octstr *data, *token;
1046 
1047             token = octstr_copy(xml_file,start,(t-start));
1048             if ((data = soap_convert_token(msg, token, privdata))) {
1049                 octstr_append(xml, data);
1050                 octstr_destroy(data);
1051             } else {
1052                 error(0,"SOAP: format_xml - failed to format token %s using message",
1053                       octstr_get_cstr(token));
1054                 octstr_destroy(token);
1055                 octstr_destroy(xml);
1056                 return NULL;
1057 
1058             }
1059             octstr_destroy(token);
1060             start = -1;
1061             if (!curly_enclose)
1062                 /* I want to get that char again, to let the normal behaviour
1063                  * deal with it - only if it's not the ending curly */
1064                 --t;
1065             else
1066                 curly_enclose = 0;
1067         }
1068     }
1069 
1070     return xml;
1071 }
1072 
1073 
1074 /*
1075  * function soap_send()
1076  *	send an XML buffer using POST to the SOAP server.
1077  * Input: PrivData connection state, Octstr XML formatted data buffer,
1078  *        Message pointer to store with request
1079  */
soap_send(PrivData * privdata,Octstr * xmlbuffer,Msg * msg)1080 static void soap_send(PrivData* privdata, Octstr* xmlbuffer, Msg* msg)
1081 {
1082     List *requestHeaders;
1083     Octstr* postdata;
1084 
1085     /* create request headers */
1086     requestHeaders = http_create_empty_headers();
1087     http_header_add(requestHeaders, "User-Agent", "Kannel " GW_VERSION);
1088 
1089     if (privdata->form_urlencoded) {
1090         http_header_add(requestHeaders, "Content-Type", "application/x-www-form-urlencoded");
1091         postdata = octstr_format("%S=%E", privdata->form_variable, xmlbuffer);
1092     } else {
1093         http_header_add(requestHeaders, "Content-Type", "multipart/form-data, boundary=AaB03x");
1094         postdata = octstr_format("--AaB03x\r\n"
1095                                  "content-disposition: form-data; name=\"%S\"\r\n\r\n%S",
1096                                  privdata->form_variable, xmlbuffer);
1097     }
1098 
1099     /* send the request along */
1100     soap_client_init_query(privdata, requestHeaders, postdata, msg);
1101 
1102     O_DESTROY(postdata);
1103 
1104     /* done with that */
1105     http_destroy_headers(requestHeaders);
1106 
1107     return;
1108 
1109 }
1110 
1111 
1112 /*
1113  * function soap_read_response()
1114  *	check my HTTP caller for responses and act on them
1115 
1116  * Input: PrivData connection state
1117  **/
soap_read_response(SMSCConn * conn)1118 static void soap_read_response(SMSCConn *conn)
1119 {
1120     PrivData *privdata = conn->data;
1121     Msg* msg;
1122     Octstr *responseBody, *responseURL;
1123     List* responseHeaders;
1124     int responseStatus;
1125     long long msgID;
1126     ClientData* cd;
1127 
1128     /* don't get in here unless I have some callers */
1129     /* (I shouldn't have one before I start sending messages) */
1130     if (!gwlist_len(privdata->soap_client))
1131         return;
1132 
1133 
1134     /* see if we have any responses pending */
1135     if (!(cd = soap_client_have_response(privdata->soap_client)))
1136         return;
1137 
1138     cd->requests--;
1139     msg = http_receive_result(cd->caller, &responseStatus, &responseURL, &responseHeaders, &responseBody);
1140 
1141     if (!msg) /* no responses here */
1142     {
1143         debug("bb.soap.read_response",0,"SOAP[%s]: sorry, no response", octstr_get_cstr(privdata->name));
1144         return;
1145     }
1146 
1147     if (responseStatus == -1) {
1148         debug("bb.soap.read_response",0,"SOAP[%s]: HTTP connection failed - blame the server (requeing msg)",
1149               octstr_get_cstr(privdata->name));
1150         bb_smscconn_send_failed(conn, msg,
1151 	            SMSCCONN_FAILED_MALFORMED, octstr_create("MALFORMED"));
1152         /*    bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_TEMPORARILY); */
1153         /*      gwlist_append(privdata->outgoing_queue, msg); */
1154         return;
1155     }
1156 
1157     debug("bb.soap.read_response",0,"SOAP[%s]: got a response %d= %s",
1158           octstr_get_cstr(privdata->name), responseStatus, responseBody?octstr_get_cstr(responseBody):octstr_get_cstr(octstr_imm("NULL")));
1159 
1160 
1161     /* got a message from HTTP, parse it */
1162     if ( (msgID = soap_parse_response(privdata, responseBody)) >= 0)
1163     { /* ack with msg ID */
1164         char tmpid[30];
1165 
1166         /*
1167          * XXX UUID is used, fix this.
1168 
1169         if (msgID == 0)
1170             msgID = msg->sms.id;
1171         */
1172 
1173         sprintf(tmpid,"%lld",msgID);
1174         debug("bb.soap.read_response",0,"SOAP[%s]: ACK - id: %lld", octstr_get_cstr(privdata->name), msgID);
1175 
1176         dlr_add(conn->id, octstr_imm(tmpid), msg, 0);
1177 
1178         /* send msg back to bearerbox for recycling */
1179         bb_smscconn_sent(conn, msg, NULL);
1180     }
1181 
1182     else { /* nack */
1183         debug("bb.soap.read_response",0,"SOAP[%s]: NACK", octstr_get_cstr(privdata->name));
1184 
1185         /* send msg back to bearerbox for recycling */
1186         bb_smscconn_send_failed(conn, msg,
1187 	            SMSCCONN_FAILED_MALFORMED, octstr_create("MALFORMED"));
1188     }
1189 
1190     http_destroy_headers(responseHeaders);
1191     O_DESTROY(responseBody);
1192     O_DESTROY(responseURL);
1193 }
1194 
1195 /*
1196  * function soap_parse_response()
1197  *	parse the response from the server to find the message ID
1198  * Input: Connection session data, Octstr xml buffer
1199  * Returns: message ID parsed or -1 if parsing failed (for example - a NACK received)
1200  *
1201  * Possible bug : I use gwlist_get() liberaly here, after checking that I have enough items,
1202  *                but if gwlist_get() returns NULL for an empty item, things might break - and
1203  *                not in a nice way.
1204  **/
soap_parse_response(PrivData * privdata,Octstr * xmlResponse)1205 static long long soap_parse_response(PrivData* privdata, Octstr* xmlResponse)
1206 {
1207     long long msgID = -1;
1208     long responseStatus = -1;
1209     xmlDocPtr responseDoc;
1210     xmlNodePtr root;
1211     List* maps;
1212     char* keywords[] = { "id", "result" };
1213     char* sscans[] = { "%lld", "%ld" };
1214     void* pointers[] = { &msgID, &responseStatus };
1215 
1216     if (!xmlResponse)
1217         return -1;
1218     /* FIXME: do something here */
1219 
1220     /* parse XML */
1221     if ( !(responseDoc = xmlParseDoc((xmlChar *)octstr_get_cstr(xmlResponse))) ) {
1222         error(0,"SOAP[%s]: couldn't parse XML response [ %s ] in MT parsing",
1223               octstr_get_cstr(privdata->name), octstr_get_cstr(xmlResponse));
1224         return -1;
1225     }
1226 
1227     /* get root element */
1228     if ( ! (root = xmlDocGetRootElement(responseDoc)) ) {
1229         error(0,"SOAP[%s]: couldn't get XML root element in MT parsing",
1230               octstr_get_cstr(privdata->name));
1231         xmlFreeDoc(responseDoc);
1232         return -1;
1233     }
1234 
1235     /* create the argument map */
1236     maps = soap_create_map(privdata->mt_spec_file, 2, keywords, sscans, pointers);
1237 
1238     /* run the map and the xml through the parser */
1239     if (soap_map_xml_data(root, maps) < 2) {
1240         error(0,"SOAP[%s]: failed to map all the arguments from the XML data",
1241               octstr_get_cstr(privdata->name));
1242     }
1243 
1244     gwlist_destroy(maps, soap_destroy_map);
1245 
1246     /* done with the document */
1247     xmlFreeDoc(responseDoc);
1248 
1249     if (msgID == -1) {
1250         if (responseStatus == 0) {   /* success without msg ID */
1251             warning(0, "SOAP[%s]: parse_response - the protocol does not support message ID",
1252                     octstr_get_cstr(privdata->name));
1253             return 0;
1254         }
1255         else {
1256             error(0,"SOAP[%s]: parse_response - response code isn't 0 ! (%ld)",
1257                   octstr_get_cstr(privdata->name), responseStatus);
1258             return -1;  /* Nack */
1259         }
1260     }
1261     else
1262         return msgID;   /* success with msg ID */
1263 
1264 }
1265 
1266 
1267 /*
1268  * function soap_parse_mo()
1269  *	parse an incoming MO xml request, build a message from it and sent it.
1270  *	also generate the reponse text and status code
1271  * Input: module public state data, request body
1272  * Output: response body
1273  * Returns: HTTP status code on successful parse or -1 on failure
1274  **/
soap_parse_mo(SMSCConn * conn,Octstr * request,Octstr ** response)1275 static long soap_parse_mo(SMSCConn *conn, Octstr *request, Octstr **response)
1276 {
1277     PrivData *privdata = conn->data;
1278     xmlDocPtr requestDoc;
1279     xmlNodePtr root;
1280 
1281     Msg* msg;
1282     int pos = 0;
1283 
1284     long res = -1;
1285 
1286     List* maps;
1287     char receiver[30], sender[30], msgtype[30], msgdata[255], date[30];
1288     long long msgid = -1;
1289     char* keywords[] = { "receiver", "sender", "msgtype", "msgdata", "date", "id" };
1290     char* sscans[] = { "%s", "%s", "%s", "%s", "%s", "%lld" };
1291     void* pointers[] = { &receiver, &sender, &msgtype, &msgdata, &date, &msgid };
1292 
1293     receiver[0] = sender[0] = msgtype[0] = msgdata[0] = date[0] = '\0';
1294 
1295     if (!response) /* how am I supposed to return a response now ? */
1296         return -1;
1297 
1298     if (!request) {
1299         *response = octstr_create(SOAP_ERROR_NO_DATA_MESSAGE);
1300         return SOAP_ERROR_NO_DATA_CODE;
1301     }
1302 
1303     /* find the POST parameter name */
1304     if ( (pos = octstr_search_char(request,'=',0)) < 0) {
1305         /* didn't find it - */
1306         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1307         return SOAP_ERROR_MALFORMED_DATA_CODE;
1308     }
1309 
1310     /* cut of the parameter name - I'm not really interested in it */
1311     octstr_delete(request,0,pos+1);
1312 
1313     /* decode the URL encoded data */
1314     if (octstr_url_decode(request) < 0) {
1315         /* probably not URL encoded */
1316 
1317         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1318         return SOAP_ERROR_MALFORMED_DATA_CODE;
1319     }
1320 
1321     debug("bb.soap.parse_mo",0,"SOAP[%s]: parse_mo  - MO request dump <%s>", octstr_get_cstr(privdata->name),octstr_get_cstr(request));
1322 
1323     /* parse XML */
1324     if ( !(requestDoc = xmlParseDoc((xmlChar *)octstr_get_cstr(request))) ) {
1325         error(0,"SOAP[%s]: parse_mo couldn't parse XML response", octstr_get_cstr(privdata->name));
1326         return -1;
1327     }
1328 
1329     /* get root element */
1330     if ( ! (root = xmlDocGetRootElement(requestDoc)) ) {
1331         error(0,"SOAP[%s]: parse_mo couldn't get XML root element for request", octstr_get_cstr(privdata->name));
1332         xmlFreeDoc(requestDoc);
1333         return -1;
1334     }
1335 
1336     /* create the argument map */
1337     maps = soap_create_map(privdata->mo_spec_file, 6, keywords, sscans, pointers);
1338 
1339 
1340     /* run the map and the xml through the parser */
1341     if (soap_map_xml_data(root, maps) < gwlist_len(maps)) {
1342         error(0,"SOAP[%s]: parse_mo failed to map all the arguments from the XML data",
1343               octstr_get_cstr(privdata->name));
1344     }
1345 
1346     /* done with the document */
1347     xmlFreeDoc(requestDoc);
1348 
1349     if (strlen(receiver) == 0) {
1350         error(0,"SOAP: parse_mo - failed to get receiver");
1351         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1352         return SOAP_ERROR_MALFORMED_DATA_CODE;
1353     }
1354 
1355     if (strlen(sender) == 0) {
1356         error(0,"SOAP: parse_mo - failed to get sender");
1357         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1358         return SOAP_ERROR_MALFORMED_DATA_CODE;
1359     }
1360 
1361     if (strlen(msgdata) == 0) {
1362         error(0,"SOAP: parse_mo - failed to get message content");
1363         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1364 
1365         return SOAP_ERROR_MALFORMED_DATA_CODE;
1366     }
1367 
1368     /* create me a message to store data in it */
1369 
1370     msg = msg_create(sms);
1371 
1372     /*
1373      * XXX UUID is used, fix this.
1374 
1375     if (msgid == -1) {
1376         error(0,"SOAP: parse_mo - failed to get message ID, generate by itself");
1377         msg->sms.id = gw_generate_id();
1378     }
1379     */
1380 
1381     /* fill in the fields from the parsed arguments */
1382     msg->sms.sender = octstr_create(sender);
1383     msg->sms.receiver = octstr_create(receiver);
1384     /*
1385      * XXX UUID is used, fix this.
1386     msg->sms.id = msgid;
1387     */
1388     msg->sms.msgdata = octstr_create(msgdata);
1389 
1390     /* special processing and refill appropriate fields */
1391     if (privdata->mo_deps_file) {
1392         if ((res = soap_release_dependences(privdata->mo_deps_file, maps, msg, privdata))!=0)
1393             error(0,"SOAP: parse_mo - failed to release all dependences");
1394     }
1395     gwlist_destroy(maps, soap_destroy_map);
1396 
1397     /* fill in the date */
1398     if (strlen(date)) {
1399         struct universaltime tm;
1400         Octstr* temp = octstr_create(date);
1401         if (date_parse_iso(&tm, temp))
1402             /* failed to parse the date */
1403             msg->sms.time = time(NULL);
1404         else
1405             msg->sms.time = date_convert_universal(&tm);
1406         octstr_destroy(temp);
1407     } else
1408         msg->sms.time = time(NULL);
1409 
1410     /*
1411         / * check message data type - B stands for "base 64 encoded" in Team Mobile * /
1412         if (!strcmp(msgtype, "B")) {
1413             octstr_base64_to_binary(msg->sms.msgdata);
1414             msg->sms.coding = DC_8BIT;
1415 
1416         } else if (!strcmp(msgtype, "binary")) {
1417             octstr_hex_to_binary(msg->sms.msgdata);
1418             msg->sms.coding = DC_8BIT;
1419         } else {
1420     */
1421 
1422     /* not gonna play this game - just convert from whatever alt_charset is set to, to UCS-2
1423         / * scan message for unicode chars (UTF-8 encoded) * /
1424         pos = 0;
1425         while (pos < octstr_len(msg->sms.msgdata)) {
1426     	if (octstr_get_char(msg->sms.msgdata,pos) & 128)
1427     	    break;
1428     	++pos;
1429         }
1430 
1431         if (pos < octstr_len(msg->sms.msgdata)) {
1432             / * message has some unicode - we need to convert to UCS-2 first * /
1433             Octstr* temp = msg->sms.msgdata;
1434 
1435             msg->sms.coding = DC_UCS2;
1436        	if (charset_from_utf8(temp, &(msg->sms.msgdata), octstr_imm("UCS-2")) < 0) {
1437                 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));
1438                 O_DESTROY(msg->sms.msgdata);
1439                 msg->sms.msgdata = octstr_duplicate(temp);
1440     	    / *  set coding to 8bit and hope for the best * /
1441     	    msg->sms.coding = DC_8BIT;
1442 
1443             }
1444             octstr_destroy(temp);
1445         } else
1446              / * not unicode : 7bit * /
1447              msg->sms.coding = DC_7BIT;
1448     */
1449 
1450     /* if it's not binary, then assume unicode and convert from alt_charset to UCS-2 */
1451     /*        msg->sms.coding = DC_UCS2;
1452 
1453 
1454 
1455             if (!octstr_case_compare(privdata->alt_charset, octstr_imm("UCS-2")))  {
1456                 int ret = 0;
1457                 debug("bb.soap.parse_mo",0,"SOAP[%s]: converting from %s to UCS-2BE",
1458                         octstr_get_cstr(privdata->name), octstr_get_cstr(privdata->alt_charset));
1459                 ret = charset_convert(msg->sms.msgdata, octstr_get_cstr(privdata->alt_charset), "UCS-2BE");
1460 
1461 
1462                 if (ret == -1) {
1463                     error(2,"SOAP[%s]: Error converting MO data from %s to unicode",
1464                             octstr_get_cstr(privdata->name),  octstr_get_cstr(privdata->alt_charset));
1465                 } else if (ret != 0) {
1466                     debug("bb.soap.parse_mo",1,"SOAP[%s]: charset_convert made %d irreversable transformations",
1467                             octstr_get_cstr(privdata->name), ret);
1468                 }
1469             }
1470             msg->sms.charset = octstr_create("UCS-2");
1471         }
1472         debug("bb.soap.parse_mo",0,"SOAP[%s]: message decoded -", octstr_get_cstr(privdata->name));
1473         octstr_dump(msg->sms.msgdata,0);
1474     */
1475 
1476     /* check that we have all the fields necessary */
1477     if (!(msg->sms.sender)
1478             ||
1479             !(msg->sms.msgdata)) {
1480         /* generate error message */
1481         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1482         return SOAP_ERROR_MALFORMED_DATA_CODE;
1483     }
1484 
1485     /* setup defaults */
1486     if (msg->sms.time <= 0)
1487         msg->sms.time = time(NULL);
1488 
1489     if (!msg->sms.receiver)
1490         msg->sms.receiver = octstr_create(SOAP_DEFAULT_SENDER_STRING);
1491 
1492     if (!msg->sms.smsc_id)
1493         msg->sms.smsc_id = octstr_duplicate(conn->id);
1494 
1495     *response = soap_format_xml(privdata->mo_xml_file,msg,privdata);
1496     if (*response)
1497         debug("bb.soap.reponse_dlr",0,"SOAP[%s]: data dump: %s", octstr_get_cstr(privdata->name), octstr_get_cstr(*response));
1498 
1499     bb_smscconn_receive(conn,msg);
1500 
1501     return SOAP_QUERY_OK;
1502 }
1503 
1504 /*
1505  * function soap_parse_dlr()
1506  *	parse an incoming DLR xml request, build a message from it and sent it.
1507  *	also generate the reponse text and status code
1508  * Input: module public state data, request body
1509  * Output: response body
1510  * Returns: HTTP status code on successful parse or -1 on failure
1511  **/
soap_parse_dlr(SMSCConn * conn,Octstr * request,Octstr ** response)1512 static long soap_parse_dlr(SMSCConn *conn, Octstr *request, Octstr **response)
1513 {
1514     PrivData *privdata = conn->data;
1515     xmlDocPtr requestDoc;
1516     xmlNodePtr root;
1517     Msg* dlrmsg = NULL;
1518     long dlrtype;
1519     int pos;
1520 
1521     List* maps;
1522     char receiver[30], soapdate[30], msgid[30];
1523     long result = -1;
1524     char* keywords[] = { "receiver", "soapdate", "id", "result" };
1525 
1526     char* sscans[] = { "%s", "%s", "%s", "%ld" };
1527     void* pointers[] = { &receiver, &soapdate, &msgid, &result };
1528 
1529     receiver[0] = soapdate[0] = msgid[0] = '\0';
1530 
1531     if (!response) /* how am I supposed to return a response now ? */
1532         return -1;
1533 
1534     if (!request) {
1535         *response = octstr_create(SOAP_ERROR_NO_DATA_MESSAGE);
1536         return SOAP_ERROR_NO_DATA_CODE;
1537     }
1538 
1539     /* find the POST parameter name */
1540     if ( (pos = octstr_search_char(request,'=',0)) < 0) {
1541         /* didn't find it - */
1542         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1543         return SOAP_ERROR_MALFORMED_DATA_CODE;
1544     }
1545 
1546     /* cut of the parameter name - I'm not really interested in it */
1547     octstr_delete(request,0,pos+1);
1548 
1549     /* decode the URL encoded data */
1550     if (octstr_url_decode(request) < 0) {
1551         /* probably not URL encoded */
1552         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1553         return SOAP_ERROR_MALFORMED_DATA_CODE;
1554     }
1555 
1556     debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr  - DLR request dump <%s>", octstr_get_cstr(privdata->name),octstr_get_cstr(request));
1557 
1558     /* parse XML */
1559 
1560     if ( !(requestDoc = xmlParseDoc((xmlChar *)octstr_get_cstr(request))) ) {
1561         error(0,"SOAP[%s]: parse_dlr couldn't parse XML response", octstr_get_cstr(privdata->name));
1562         return -1;
1563     }
1564 
1565     /* get root element */
1566     if ( ! (root = xmlDocGetRootElement(requestDoc)) ) {
1567 
1568         error(0,"SOAP[%s]: parse_dlr couldn't get XML root element for request", octstr_get_cstr(privdata->name));
1569         xmlFreeDoc(requestDoc);
1570         return -1;
1571     }
1572 
1573     /* create the argument map */
1574 
1575     maps = soap_create_map(privdata->dlr_spec_file, 4, keywords, sscans, pointers);
1576 
1577     /* run the map and the xml through the parser */
1578     if (soap_map_xml_data(root, maps) < 4) {
1579         error(0,"SOAP[%s]: parse_dlr failed to map all the arguments from the XML data",
1580 
1581               octstr_get_cstr(privdata->name));
1582     }
1583 
1584     gwlist_destroy(maps, soap_destroy_map);
1585 
1586     /* done with the document */
1587     xmlFreeDoc(requestDoc);
1588 
1589     if (strlen(msgid) == 0) {
1590         error(0,"SOAP: parse_dlr - failed to get message ID");
1591         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1592         return SOAP_ERROR_MALFORMED_DATA_CODE;
1593     }
1594 
1595     if (result == -1) {
1596 
1597         error(0,"SOAP: parse_dlr - failed to get delivery code");
1598         *response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1599         return SOAP_ERROR_MALFORMED_DATA_CODE;
1600     }
1601 
1602     /* we not need it because receiver now is constant string "receiver"
1603       if (strlen(receiver) == 0) {
1604            	error(0,"SOAP: parse_dlr - failed to get receiver");
1605     	*response = octstr_create(SOAP_ERROR_MALFORMED_DATA_MESSAGE);
1606     	return SOAP_ERROR_MALFORMED_DATA_CODE;
1607         }
1608     */
1609     /* log the delivery code - this could be used to determine dlrtype (or so I hope) */
1610     debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr DELIVERY_CODE : %ld", octstr_get_cstr(privdata->name),result);
1611     if (result == 0)
1612         dlrtype = DLR_SUCCESS;
1613     else
1614         dlrtype = DLR_FAIL;
1615 
1616     /* fetch the DLR */
1617 
1618     dlrmsg = dlr_find(conn->id, octstr_imm(msgid), octstr_imm("receiver"), /* destination */
1619                       dlrtype, 0);
1620 
1621     if (!dlrmsg) {
1622         error(0,"SOAP[%s]: parse_dlr invoked (%ld), but no DLR found for MsgID %s", octstr_get_cstr(privdata->name),dlrtype,msgid);
1623         *response = octstr_create(SOAP_ERROR_NO_DLR_MESSAGE);
1624         return SOAP_ERROR_NO_DLR_CODE;
1625     }
1626 
1627     debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr found dlr", octstr_get_cstr(privdata->name));
1628     octstr_destroy(dlrmsg->sms.msgdata);
1629     switch (dlrtype) { /* change message according to DLR type */
1630         case DLR_SUCCESS:
1631             dlrmsg->sms.msgdata = octstr_create("Delivered");
1632             break;
1633         case DLR_BUFFERED:
1634             dlrmsg->sms.msgdata = octstr_create("Buffered");
1635             break;
1636         case DLR_FAIL:
1637             dlrmsg->sms.msgdata = octstr_create("Failed");
1638             break;
1639         default:
1640             break;
1641 
1642     }
1643 
1644     /*
1645     if (dlrmsg->sms.receiver) {
1646         octstr_destroy(dlrmsg->sms.sender);
1647         dlrmsg->sms.sender = dlrmsg->sms.receiver;
1648     }
1649     dlrmsg->sms.receiver = octstr_create(receiver);
1650     dlrmsg->sms.id = strtol(msgid, NULL, 10);
1651     */
1652 
1653     debug("bb.soap.parse_dlr",0,"SOAP[%s]: parse_dlr sent dlr <%s>", octstr_get_cstr(privdata->name),octstr_get_cstr(dlrmsg->sms.msgdata));
1654 
1655 
1656     *response = soap_format_xml(privdata->dlr_xml_file, dlrmsg, privdata);
1657     if (*response)
1658         debug("bb.soap.reponse_dlr",0,"SOAP[%s]: data dump: %s", octstr_get_cstr(privdata->name), octstr_get_cstr(*response));
1659 
1660     /* send to bearerbox */
1661     bb_smscconn_receive(conn, dlrmsg);
1662 
1663     return SOAP_QUERY_OK;
1664 
1665 }
1666 
1667 
1668 /*
1669  * SOAP internal utility functions
1670  **/
1671 
1672 /*
1673  * function soap_xmlnode_get_long()
1674  *	parse the content of an XML node and return it as an integer
1675  * Input: xmlNodePtr to node
1676  * Output: long parsed
1677  * Returns: 0 on success, -1 on failure
1678  **/
soap_xmlnode_get_long(xmlNodePtr cur,long * out)1679 int soap_xmlnode_get_long(xmlNodePtr cur, long* out)
1680 {
1681     xmlChar* nodeContent;
1682     char* endPointer;
1683 
1684     if (!out)  /* sanity check */
1685         return -1;
1686 
1687     /* get content of tag */
1688     if (!(nodeContent = xmlNodeGetContent(cur))) {
1689         error(0,"SOAP: get_long - xml Node has content !");
1690         return -1;
1691     }
1692 
1693     /* read the content into output */
1694     *out = strtol((char *)nodeContent,&endPointer,10);
1695     xmlFree(nodeContent);
1696 
1697     if (endPointer == (char*)nodeContent) {
1698         error(0,"SOAP: get_long - node has non-numeric content <%s>", nodeContent);
1699         return -1;
1700     }
1701 
1702     return 0;
1703 }
1704 
1705 /*
1706  * function soap_xmlnode_get_int64()
1707  *	parse the content of an XML node and return it as an long long
1708  * Input: xmlNodePtr to node
1709  * Output: long parsed
1710  * Returns: 0 on success, -1 on failure
1711  **/
soap_xmlnode_get_int64(xmlNodePtr cur,long long * out)1712 int soap_xmlnode_get_int64(xmlNodePtr cur, long long* out)
1713 {
1714     xmlChar* nodeContent;
1715     char* endPointer;
1716 
1717     if (!out)  /* sanity check */
1718         return -1;
1719 
1720     /* get content of tag */
1721     if (!(nodeContent = xmlNodeGetContent(cur))) {
1722         error(0,"SOAP: get_long - xml Node has content !");
1723         return -1;
1724     }
1725 
1726 
1727     /* read the content into output */
1728     *out = strtoll((char *)nodeContent,&endPointer,10);
1729     xmlFree(nodeContent);
1730 
1731     if (endPointer == (char*)nodeContent) {
1732         error(0,"SOAP: get_long - node has non-numeric content <%s>", nodeContent);
1733         return -1;
1734     }
1735 
1736     return 0;
1737 }
1738 
1739 /*
1740  * function soap_xmlnode_get_octstr()
1741  *	parse the content of an XML node and return it as an Octstr*
1742  * Input: xmlNodePtr to node
1743  * Output: Octstr to feel with data
1744  * Returns: 0 on success, -1 on failure
1745  **/
soap_xmlnode_get_octstr(xmlNodePtr cur,Octstr ** out)1746 int soap_xmlnode_get_octstr(xmlNodePtr cur, Octstr **out)
1747 {
1748     xmlChar* nodeContent;
1749 
1750     if (!out)  /* sanity check */
1751         return -1;
1752 
1753     /* get content of tag */
1754     if (!(nodeContent = xmlNodeGetContent(cur))) {
1755         error(0,"SOAP: get_octstr - xml Node has content !");
1756         return -1;
1757     }
1758 
1759     /* store the content into output */
1760     *out = octstr_create((char *)nodeContent);
1761     xmlFree(nodeContent);
1762 
1763     if (*out)
1764         return 0;
1765     else
1766         return -1;
1767 
1768 
1769 }
1770 
1771 /*
1772  * function soap_read_date()
1773  *	convert a date string in one2one obiquis format (%Y/%M/%d:%h:%m) to epoch time
1774  * Input: Octstr date
1775  * Returns: epoch time on success or -1 on failure
1776  **/
soap_read_date(Octstr * dateString)1777 time_t soap_read_date(Octstr* dateString)
1778 {
1779     int pos, count;
1780     struct universaltime stTime;
1781     long arTime[5];
1782 
1783 
1784     if (!dateString) /* sanity check */
1785         return -1;
1786 
1787     pos = count = 0;
1788     /* tricky control structures are my favourite among complicated expressions ;-) */
1789     while (count < 5 && pos < octstr_len(dateString) &&
1790             (pos = octstr_parse_long(&(arTime[count++]),dateString, pos,10)) && pos != -1)
1791         ++pos;
1792 
1793     if (count < 5) {
1794         /* error parsing the date */
1795         debug("bb.soap.read_date",0,"read_date failed parsing the date value <%s>", octstr_get_cstr(dateString));
1796         return -1;
1797     }
1798 
1799     stTime.day = arTime[0];
1800     stTime.month = arTime[1];
1801     stTime.year = arTime[2];
1802     stTime.hour = arTime[3];
1803     stTime.minute = arTime[4];
1804     stTime.second = 0;
1805     return date_convert_universal(&stTime);
1806 }
1807 
1808 /*
1809  * function soap_write_date()
1810  *	convert an epoch time value to a date string in one2one obiquis format (%Y/%M/%d:%h:%m)
1811  * Input: time_t epoch time
1812  * Returns: an Octstr containing the date - this must be freed by the caller
1813  **/
soap_write_date(time_t date)1814 static Octstr* soap_write_date(time_t date)
1815 {
1816     struct tm date_parts;
1817     Octstr* out;
1818 
1819     if (date < 0)
1820         /* sanity check - I don't think it should ever happen, but I don't want to get
1821            support calls at 2am because some gateway in the UK went bananas. */
1822         return octstr_create("ERROR");
1823 
1824     /* split up epoch time to elements */
1825     gmtime_r(&date, &date_parts);
1826 
1827     out = octstr_format("%d/%02d/%02d:%02d:%02d",
1828                         date_parts.tm_year + 1900, date_parts.tm_mon + 1, date_parts.tm_mday, date_parts.tm_hour, date_parts.tm_min);
1829 
1830     /* again */
1831     if (out)
1832         return out;
1833     else
1834         return octstr_create("ERROR");
1835     /* assuming octstr_create never fails, unlike octstr_format. this is not the case currently (both cannot fail), but it may change */
1836 }
1837 
1838 
1839 /*
1840  * function soap_server_start()
1841  *	init and start the SOAP HTTP server
1842  * Input: Module public connection state data
1843  * Returns: 0 on success, -1 on failure
1844  **/
soap_server_start(SMSCConn * conn)1845 int soap_server_start(SMSCConn *conn)
1846 {
1847     PrivData* privdata = conn->data;
1848 
1849     debug("bb.soap.server_stop",0,"SOAP[%s]: Starting HTTP server", octstr_get_cstr(privdata->name));
1850     /* start the HTTP server */
1851     if (http_open_port(privdata->port,privdata->ssl)) {
1852         return -1;
1853     }
1854 
1855 
1856     /* raise server flag */
1857     privdata->soap_server = 1;
1858 
1859     if ( (privdata->server_thread = gwthread_create(soap_server, conn)) == -1)
1860     {
1861         error(0, "SOAP[%s]: server_start failed to create server thread!", octstr_get_cstr(privdata->name));
1862         http_close_port(privdata->port);
1863         return -1;
1864     }
1865 
1866 
1867     return 0;
1868 }
1869 
1870 /*
1871  * function soap_server_stop()
1872  *	tears down and stops the SOAP HTTP server
1873  * Input: Module connection state data
1874  **/
soap_server_stop(PrivData * privdata)1875 static void soap_server_stop(PrivData* privdata)
1876 {
1877     /*    time_t start = time(NULL); */
1878 
1879     debug("bb.soap.server_stop",0,"SOAP[%s]: Stopping HTTP server", octstr_get_cstr(privdata->name));
1880     /* signal the server thread to stop */
1881     privdata->soap_server = 0;
1882 
1883     /* close the http server thread */
1884     http_close_port(privdata->port);
1885 
1886     if (privdata->server_thread) {
1887         gwthread_wakeup(privdata->server_thread);
1888         gwthread_join(privdata->server_thread);
1889         privdata->server_thread = 0;
1890     }
1891 
1892 
1893     /*
1894     / * wait upto 5 minutes for our server thread to shutdown * /
1895     while (privdata->server_thread &&  (start + 300 > time(NULL)))
1896     gwthread_sleep(SOAP_SLEEP_TIME);
1897 
1898 
1899     if (privdata->server_thread) {
1900     error(0,"SOAP[%s]: our server refuses to die!", octstr_get_cstr(privdata->name));
1901     privdata->server_thread = 0; / * dump it either way * /
1902 
1903     }*/
1904 
1905     debug("bb.soap.server_stop",0,"SOAP[%s]: Done stopping HTTP server", octstr_get_cstr(privdata->name));
1906 }
1907 
1908 
1909 /*
1910  * function soap_create_client_data()
1911  *	creates a new SOAP client data structure and caller
1912  * Returns: an initialized client data structure with a live caller
1913 
1914  **/
soap_create_client_data()1915 static ClientData* soap_create_client_data()
1916 {
1917 
1918     ClientData *cd = gw_malloc(sizeof(ClientData));
1919 
1920     cd->last_access = 0;
1921     cd->requests = 0;
1922     cd->caller = http_caller_create();
1923 
1924     return cd;
1925 }
1926 
1927 
1928 /*
1929  * function soap_client_init_query()
1930  *	start an HTTP query, load balance callers, and manage caller pool
1931  * Input: Module state, list of headers to send, data to send, message to store
1932  **/
soap_client_init_query(PrivData * privdata,List * headers,Octstr * data,Msg * msg)1933 static void soap_client_init_query(PrivData* privdata, List* headers, Octstr* data, Msg* msg)
1934 {
1935     ClientData *cur_client = NULL;
1936     long index;
1937 
1938 
1939     /* no list yet, generate one */
1940     if (!privdata->soap_client)
1941         privdata->soap_client = gwlist_create();
1942 
1943     /* I'm going to change the list, so lock it */
1944     gwlist_lock(privdata->soap_client);
1945 
1946     /* find the next live caller */
1947     for (index = gwlist_len(privdata->soap_client) - 1 ; index >= 0; --index) {
1948         cur_client = gwlist_get(privdata->soap_client, index);
1949         if (
1950             cur_client->last_access + CLIENT_BUSY_TIME < time(NULL)
1951             &&
1952             cur_client->requests < CLIENT_BUSY_LOAD
1953         ) {
1954             debug("bb.soap.init_query",0,"SOAP[%s]: init_query getting a client",octstr_get_cstr(privdata->name));
1955 
1956             /* client is not busy - get it */
1957             gwlist_delete(privdata->soap_client, index, 1);
1958             break;
1959         }
1960         cur_client = NULL;
1961     }
1962 
1963     if (!cur_client) {
1964         if (gwlist_len(privdata->soap_client) > MAX_SOAP_CLIENTS) {
1965             debug("bb.soap.init_query",0,"SOAP[%s]: init_query all clients are busy, getting the first client",octstr_get_cstr(privdata->name));
1966             /* query not dispatched, and we have the max number of callers -
1967                grab the first caller (least used) from the list */
1968             cur_client = gwlist_extract_first(privdata->soap_client);
1969         } else {
1970             /* query not dispatched, and we don't have enough callers -
1971                start a new one */
1972             debug("bb.soap.init_query",0,"SOAP[%s]: init_query creates a new client",octstr_get_cstr(privdata->name));
1973             cur_client = soap_create_client_data();
1974         }
1975     }
1976 
1977     /* dispatch query to selected client */
1978     http_start_request(cur_client->caller, HTTP_METHOD_POST, privdata->uri, headers, data, 1, msg, NULL);
1979     cur_client->requests++;
1980     cur_client->last_access = time(NULL);
1981     gwlist_append(privdata->soap_client, cur_client);
1982     gwlist_unlock(privdata->soap_client);
1983 }
1984 
1985 
1986 /*
1987  * function soap_destroy_client_data()
1988  *	destroy a SOAP client caller
1989  * Input: pointer to a client data structure with a live caller
1990  **/
soap_destroy_client_data(void * data)1991 static void soap_destroy_client_data(void* data)
1992 {
1993     ClientData *cd = (ClientData*) data;
1994 
1995     /* signal the caller to stop and then kill it */
1996     if (cd->caller) {
1997         http_caller_signal_shutdown(cd->caller);
1998         http_caller_destroy(cd->caller);
1999     }
2000 }
2001 
2002 /*
2003  * function soap_client_have_response()
2004  *	return a caller from the pool that has responses waiting
2005  * Input: ClientData pool
2006  * Returns: a client data structure that has a caller with responses waiting,
2007  *  or NULL if none are found
2008  **/
soap_client_have_response(List * client_list)2009 static ClientData* soap_client_have_response(List* client_list)
2010 {
2011     long index;
2012     ClientData* cd;
2013 
2014     if (!client_list)
2015         return NULL;
2016 
2017     /* lock the list so nobody removes or adds clients while I'm looping on the list */
2018     gwlist_lock(client_list);
2019 
2020     for (index = gwlist_len(client_list) - 1; index >= 0; --index) {
2021         cd = gwlist_get(client_list,index);
2022         if (gwlist_len(cd->caller)) {
2023 
2024             gwlist_unlock(client_list);
2025             return gwlist_get(client_list, index);
2026         }
2027     }
2028 
2029     gwlist_unlock(client_list);
2030     return NULL;
2031 }
2032 
2033 /*
2034  * function soap_convert_token()
2035  *	convert a member of the message structure and return it as octstr
2036  * Input: member name
2037  * Returns: an Octstr containing the content of the data member from the message structure
2038  *  or NULL if an error occured.
2039  **/
soap_convert_token(Msg * msg,Octstr * name,PrivData * privdata)2040 static Octstr* soap_convert_token(Msg* msg, Octstr* name, PrivData* privdata)
2041 
2042 {
2043     char buf[20];
2044     int index;
2045 
2046     if ( (index=soap_lookup_function(name)) >= 0 )
2047         return soap_select_function(index, msg, privdata);
2048 
2049 
2050 #define INTEGER(fieldname) \
2051         if (!octstr_str_compare(name, #fieldname)) { \
2052                 sprintf(buf,"%ld", p->fieldname); \
2053                 return octstr_create(buf); \
2054         }
2055 #define INT64(fieldname) \
2056         if (!octstr_str_compare(name, #fieldname)) { \
2057                 sprintf(buf,"%lld", p->fieldname); \
2058                 return octstr_create(buf); \
2059         }
2060 #define OCTSTR(fieldname) \
2061         if (!octstr_str_compare(name, #fieldname)) \
2062                 return octstr_duplicate(p->fieldname);
2063 #define UUID(fieldname)
2064 #define VOID(fieldname)
2065 
2066 #define MSG(type, stmt) \
2067         case type: { struct type *p = &msg->type; stmt } break;
2068 
2069     switch (msg->type) {
2070 #include "msg-decl.h"
2071         default:
2072 
2073 
2074             error(0, "SOAP: Internal error: unknown message type %d", msg->type);
2075             return NULL;
2076     }
2077 
2078     error(0,"SOAP: soap_convert_token, can't find token named <%s>", octstr_get_cstr(name));
2079     return NULL;
2080 }
2081 
2082 /*
2083  * function soap_create_map()
2084  *	convert a XML parsing spec file and a list of recognized keywords to an argument map
2085  * Input: XML parsing spec buffer and lists of keywords, types and pointers
2086  * Returns: number of variables successfuly mapped
2087  **/
soap_create_map(Octstr * spec,long count,char * keywords[],char * types[],void * storage[])2088 List* soap_create_map(Octstr* spec, long count, char* keywords[], char* types[], void* storage[])
2089 {
2090     List *parse_items, *out;
2091 
2092     out = gwlist_create();
2093 
2094     /* read the list of items from the spec file */
2095     parse_items = octstr_split(spec, octstr_imm("\n"));
2096 
2097     while (gwlist_len(parse_items)) {
2098         ArgumentMap* map;
2099         int index;
2100         Octstr* temp = gwlist_extract_first(parse_items);
2101         List* item = octstr_split_words(temp);
2102 
2103 
2104         /* make sure we have at least two things in the item : a keyword and a path */
2105         if (gwlist_len(item) < 2) {
2106             debug("bb.soap.parse_create_map",0,"SOAP: broken spec file line <%s> in soap_create_map",
2107                   octstr_get_cstr(temp));
2108             octstr_destroy(temp);
2109             gwlist_destroy(item, octstr_destroy_item);
2110             continue;
2111         }
2112 
2113         /* check that the keyword matches something in the list of keywords */
2114         for (index = 0; index < count; ++index) {
2115             if (!octstr_str_compare(gwlist_get(item,0), keywords[index])) {
2116                 /* allocate the structure */
2117                 map = gw_malloc(sizeof(ArgumentMap));
2118                 map->name = gwlist_extract_first(item);
2119                 map->path = gwlist_extract_first(item);
2120                 map->attribute = gwlist_extract_first(item); /* could be NULL, but that is ok */
2121                 map->sscan_type = octstr_create(types[index]);
2122                 map->store = storage[index];
2123                 gwlist_append(out, map);
2124                 break;
2125             }
2126         }
2127 
2128         /* destroy temporary variables; */
2129         gwlist_destroy(item, octstr_destroy_item);
2130         octstr_destroy(temp);
2131     }
2132 
2133     gwlist_destroy(parse_items, octstr_destroy_item);
2134 
2135     return out;
2136 }
2137 
2138 /*
2139  * function soap_destroy_map()
2140  *	destroy a map structure. used in gwlist_destroy(calls);
2141  * Input: pointer to a map structure;
2142  **/
soap_destroy_map(void * item)2143 void soap_destroy_map(void *item)
2144 {
2145     ArgumentMap* map = item;
2146     octstr_destroy(map->name);
2147     octstr_destroy(map->path);
2148     octstr_destroy(map->attribute);
2149     octstr_destroy(map->sscan_type);
2150     gw_free(map);
2151 }
2152 
2153 /*
2154  * function soap_fetch_xml_data()
2155  *      return the value of an XML element.
2156  * Input: pointer to root of XML to search under, path specified in one of three forms
2157  *      a) <path to tag> - will return the content of this tag
2158  *      b) <path to tag>,<attribute name> - will return the value of this attribute
2159  *      c) "<fixed value>" - will return the given value as it is
2160  * Returns: content if found or NULL
2161  **/
soap_fetch_xml_data(xmlNodePtr xml,Octstr * path)2162 Octstr* soap_fetch_xml_data(xmlNodePtr xml, Octstr* path)
2163 {
2164     Octstr *temp, *xml_path, *attr_name = NULL;
2165     List* path_elements;
2166     unsigned char c;
2167     xmlNodePtr parent, node;
2168     int index;
2169 
2170 
2171     /* sanity check */
2172     if (!octstr_len(path) || !xml)
2173         return NULL;
2174 
2175 
2176     /* stop here for case (c) */
2177     if (((c = octstr_get_char(path, 0)) == '"' || c == '\'') &&
2178             (octstr_get_char(path, octstr_len(path)-1) == c))
2179         return octstr_copy(path, 1, octstr_len(path) - 2);
2180 
2181     /* split into XML path and attribute name */
2182     path_elements = octstr_split(path, octstr_imm(","));
2183     xml_path = gwlist_get(path_elements,0);
2184     if (gwlist_len(path_elements) > 1) /* case (b), we have an attribute */
2185         attr_name = gwlist_get(path_elements,1);
2186     gwlist_destroy(path_elements, NULL);
2187 
2188     /* split path into parts */
2189     path_elements = octstr_split(xml_path, octstr_imm("/"));
2190 
2191     /* walk the message tree down the path */
2192     parent = NULL;
2193     node = xml;
2194     index = 0;
2195     while (index < gwlist_len(path_elements)) {
2196         int found = 0;
2197         /* get the next path element */
2198         temp = gwlist_get(path_elements, index);
2199         do {
2200             if (!octstr_str_compare(temp,(char *)node->name)) {
2201                 /* found what we're looking for */
2202                 if (!(node->xmlChildrenNode) && index < (gwlist_len(path_elements)-1)) {
2203                     /* while this is indeed the item we are looking for, it's not the end
2204                      * of the path, and this item has no children */
2205                     debug("bb.soap.fetch_xml_data",0,"SOAP: fetch_xml - error parsing XML, "
2206                           "looking for <%s>, but element <%s> has no children",
2207                           octstr_get_cstr(xml_path), octstr_get_cstr(temp));
2208                 } else {
2209                     ++index; /* go down the path */
2210                     parent = node; /* remember where I came from */
2211                     node = node->xmlChildrenNode; /* trace into the node */
2212                     ++found; /* remember that I found it */
2213                     break; /* escape to the next level */
2214                 }
2215             }
2216             /* get the next node on this level - this runs if the current node is not in the path */
2217         } while ((node = node->next));
2218 
2219         if (!found) {
2220             /* didn't find anything - back track */
2221             node = parent;
2222             parent = node->parent;
2223             if (--index < 0)
2224                 /* I backtracked too much up the tree, nowhere to go to */
2225                 break; /* out of the main loop with nothing to show for it */
2226 
2227             if (!(node = node->next))
2228 
2229                 /* after back tracking, go over to the next sibling of the node I just
2230                  * finished searching under, or bail out if no more siblings */
2231                 break;
2232         }
2233     }
2234 
2235     /* coming here there are two options:
2236      * 1) we looped over all the tree, but did not succeed in traveling the
2237      *    requested path - index not pointing past the list of path elements - */
2238     if (index < gwlist_len(path_elements)) {
2239         /* didn't find the full path */
2240         debug("bb.soap.map_xml_data",0,"SOAP: fetch_xml - path <%s> cannot be traveled in input XML",
2241               octstr_get_cstr(xml_path));
2242         gwlist_destroy(path_elements, octstr_destroy_item);
2243         octstr_destroy(xml_path);
2244         octstr_destroy(attr_name);
2245         return NULL;
2246     }
2247 
2248 
2249     /* 2) index is pointing past the end of the path and the correct node
2250      * is stored in parent */
2251     if (attr_name) { /* The caller wants to get an attribute */
2252         xmlChar* content;
2253         content = xmlGetProp(parent, (xmlChar *)octstr_get_cstr(attr_name));
2254         if (content)
2255             temp = octstr_create((char *)content);
2256         else /* dont treat an empty or non-existant attribute as an error right away */
2257             temp = octstr_create("");
2258         xmlFree(content);
2259     } else { /* the caller wants to get the content */
2260         xmlChar* content;
2261         content = xmlNodeGetContent(parent);
2262         if (content)
2263             temp = octstr_create((char *)content);
2264         else /* don't treat an empty tag an error right away */
2265             temp = octstr_create("");
2266         xmlFree(content);
2267     }
2268 
2269     gwlist_destroy(path_elements, octstr_destroy_item);
2270     octstr_destroy(xml_path);
2271     octstr_destroy(attr_name);
2272 
2273     return temp;
2274 }
2275 
2276 /*
2277  * function soap_map_xml_data()
2278  *	maps content of an XML structure to a list of variables using a map
2279  * Input: XML document and an argument map
2280  * Returns: number of variables successfuly mapped
2281  **/
soap_map_xml_data(xmlNodePtr xml,List * maps)2282 int soap_map_xml_data(xmlNodePtr xml, List* maps)
2283 {
2284     int mapindex = 0, args = 0;
2285     xmlNodePtr node, parent;
2286 
2287     /* step through the items on the map */
2288     while (mapindex < gwlist_len(maps)) {
2289 
2290         Octstr* temp;
2291 
2292         int index = 0;
2293         ArgumentMap* map = gwlist_get(maps,mapindex);
2294         /* split the path elements */
2295         List* path_elements = octstr_split(map->path, octstr_imm("/"));
2296 
2297         /* walk the message tree down the path */
2298         parent = NULL;
2299         node = xml;
2300         while (index < gwlist_len(path_elements)) {
2301             int found = 0;
2302             /* get the next path element */
2303             temp = gwlist_get(path_elements, index);
2304             do {
2305                 if (!octstr_str_compare(temp,(char *)node->name)) {
2306                     /* found what we're looking for */
2307                     if (!(node->xmlChildrenNode) && index < (gwlist_len(path_elements)-1)) {
2308                         /* while this is indeed the item we are looking for, it's not the end
2309                            of the path, and this item has no children */
2310                         debug("bb.soap.map_xml_data",0,"SOAP: error parsing XML, looking for <%s>, but element <%s> has no children",
2311                               octstr_get_cstr(map->path), octstr_get_cstr(temp));
2312                     } else {
2313                         ++index; /* go down the path */
2314                         parent = node; /* remember where I came from */
2315                         node = node->xmlChildrenNode; /* trace into the node */
2316                         ++found;
2317                         break; /* escape to the next level */
2318                     }
2319                 }
2320             } while ((node = node->next));
2321 
2322             if (!found) {
2323                 /* didn't find anything - back track */
2324                 node = parent;
2325                 if (parent==NULL) /* first tag not found, quickly go out ! */
2326                     return 0;
2327 
2328                 parent = node->parent;
2329                 if (--index < 0)
2330                     /* I backtracked too much up the tree, nowhere to go to */
2331                     break;
2332 
2333                 if (!(node = node->next))
2334                     /* no more childs under the main tree to look under, abort */
2335                     break;
2336 
2337             }
2338         }
2339 
2340 
2341         if (index < gwlist_len(path_elements)) {
2342             /* didn't find the full path */
2343             debug("bb.soap.map_xml_data",0,"SOAP: didn't find element for keyword <%s> in XML data",
2344                   octstr_get_cstr(map->name));
2345             gwlist_destroy(path_elements, octstr_destroy_item);
2346             ++mapindex;
2347             continue;
2348         }
2349 
2350         /* found the correct node (it's stored in parent) */
2351         if (map->attribute) {
2352 
2353             /* The user wants to get an attribute */
2354             xmlChar* content;
2355             content = xmlGetProp(parent, (xmlChar *)octstr_get_cstr(map->attribute));
2356             if (content)
2357                 temp = octstr_create((char *)content);
2358             else /* dont treat an empty or non-existant attribute as an error right away */
2359                 temp = octstr_create("");
2360             xmlFree(content);
2361         } else {
2362             /* the user wants to get the content */
2363             xmlChar* content;
2364             content = xmlNodeGetContent(parent);
2365             if (content)
2366                 temp = octstr_create((char *)content);
2367             else /*  don't treat an empty tag an error right away */
2368 
2369                 temp = octstr_create("");
2370             xmlFree(content);
2371         }
2372 
2373         /* parse the content using sscan_type from the map */
2374         octstr_strip_blanks(temp);
2375         if (!octstr_str_compare(map->sscan_type,"%s")) {
2376             /* special processing of %s - this means the whole string, while sscanf stops at spaces */
2377             strcpy(map->store,octstr_get_cstr(temp));
2378 
2379             ++args;
2380         } else {
2381             if (!sscanf(octstr_get_cstr(temp), octstr_get_cstr(map->sscan_type), map->store)) {
2382                 debug("bb.soap.map_xml_data",0,"SOAP: failed to scan content '%s' for '%s' in xml parsing",
2383                       octstr_get_cstr(temp), octstr_get_cstr(map->sscan_type));
2384             } else {
2385                 ++args;
2386             }
2387         }
2388 
2389 
2390         /* done for this item */
2391         octstr_destroy(temp);
2392         gwlist_destroy(path_elements, octstr_destroy_item);
2393         ++mapindex;
2394     }
2395     return args;
2396 }
2397 
2398 /*
2399  * function soap_release_dependences()
2400  * check for each key if we need specific convertation and do it
2401  * Input:  specification of dependences to convert, map with all keys, msg structure to change values
2402  * Returns: error code or 0 on success
2403  **/
soap_release_dependences(Octstr * file_deps,List * lstmaps,Msg * msg,PrivData * privdata)2404 long soap_release_dependences(Octstr* file_deps, List* lstmaps, Msg* msg, PrivData *privdata)
2405 {
2406     List *issues;
2407     long i, j, key_index, key_deps_index, map_index;
2408     int res, k;
2409     List *issue_items, *header_item;
2410     int key_func_index;
2411     ArgumentMap* map;
2412     Octstr *header, *key, *key_deps;
2413     Octstr *func_alias = NULL, *block;
2414 
2415     /* follows  keys and funcs identifiers must be
2416      * the same as in a 'deps' file
2417      **/
2418 
2419 
2420     /*	structure of file_deps;
2421      *
2422      *	<key> <key_deps>
2423      *	<key_deps_value> <function_alias>
2424      *	<key_deps_value> <function_alias>
2425      *	;
2426      */
2427 
2428     /* ADD HERE: */
2429 
2430     char* funcs[][5] = {    /* functions aliasis used in mo.deps file */
2431                            {"text","binary","unicode","default"},	                  /*  msgtype */
2432                            {"set_iso","64_binary","hex_binary","unicode","default"}  /* msgdata */
2433                        };
2434 
2435 
2436     issues = octstr_split(file_deps, octstr_imm(";"));					/* get paragraphs */
2437 
2438     if (gwlist_len(issues) == 0) {
2439         error(0, "SOAP: soap_release_dependences, empty or broken 'deps' file");
2440         return -1;
2441     }
2442 
2443     for (i=0; i<gwlist_len(issues); ++i)						            /* loop paragraphs */
2444     {
2445         block = gwlist_get(issues, i);
2446         octstr_strip_crlfs(block);
2447         octstr_strip_blanks(block);
2448 
2449         issue_items = octstr_split(block, octstr_imm("\n"));
2450         if (gwlist_len(issue_items) < 2) {
2451             error(0, "SOAP: soap_release_dependences, broken file 'deps' can't find any definition for <key>");
2452             gwlist_destroy(issue_items, octstr_destroy_item);
2453             gwlist_destroy(issues, octstr_destroy_item);
2454             return -1;
2455         }
2456 
2457 
2458         header = gwlist_extract_first(issue_items);
2459         header_item = octstr_split_words(header);				/* header content */
2460         O_DESTROY(header);
2461 
2462         if (gwlist_len(header_item) < 2) {
2463             error(0, "SOAP: soap_release_dependences, broken 'deps' file in <key> <key_deps> part");
2464             gwlist_destroy(header_item, octstr_destroy_item);
2465             gwlist_destroy(issue_items, octstr_destroy_item);
2466             gwlist_destroy(issues, octstr_destroy_item);
2467             return -1;
2468         }
2469 
2470         key      = gwlist_get(header_item, 0);
2471         key_deps = gwlist_get(header_item, 1);
2472         key_index      = soap_get_index(lstmaps, key, 0);      /* search key_index */
2473         key_deps_index = soap_get_index(lstmaps, key_deps, 0); /* search key_deps_index, from what depends */
2474 
2475         if (key_index == -1 || key_deps_index == -1) {
2476             gwlist_destroy(header_item, octstr_destroy_item);
2477             gwlist_destroy(issue_items, octstr_destroy_item);
2478             gwlist_destroy(issues, octstr_destroy_item);
2479             return -1;
2480         }
2481 
2482         map_index = soap_get_index(lstmaps, key_deps, 1); /* get index for map->name==key_deps */
2483         map = gwlist_get(lstmaps, map_index);
2484 
2485         /* search <function_identifier> and if not found try to set default */
2486         for (j=0; j < gwlist_len(issue_items); ++j) {
2487 
2488             Octstr *tmp = gwlist_get(issue_items, j);
2489             List *row = octstr_split_words(tmp);
2490 
2491             if (!octstr_str_compare(gwlist_get(row, 0), map->store)) {
2492                 func_alias = octstr_duplicate(gwlist_get(row, 1));
2493                 gwlist_destroy(row, octstr_destroy_item);
2494                 break;
2495             }
2496 
2497             if (j==gwlist_len(issue_items)-1) {
2498                 error(0, "SOAP: soap_release_dependences, \
2499                       can't find function_alias for <%s> in 'deps' file, set default", (char*)map->store);
2500                 func_alias = octstr_create(SPEC_DEFAULT);
2501             }
2502             gwlist_destroy(row, octstr_destroy_item);
2503         }
2504 
2505         key_func_index = -1;
2506         /* searching index of function by its alias */
2507         for (k=0; k < sizeof(funcs[key_index])/sizeof(funcs[key_index][0]); ++k)
2508         {
2509             if (!octstr_str_compare(func_alias, funcs[key_index][k])) {
2510                 key_func_index = k;
2511                 break;
2512             }
2513         }
2514         if (key_func_index==-1)
2515             error(0, "SOAP: soap_release_dependences, can't find function for alias <%s>", octstr_get_cstr(func_alias));
2516 
2517         O_DESTROY(func_alias);
2518 
2519         gwlist_destroy(header_item, octstr_destroy_item);
2520         gwlist_destroy(issue_items, octstr_destroy_item);
2521 
2522         /* which field has deps, which func need be called, msg need be changed */
2523         if ((res=soap_process_deps(key_index, key_func_index, msg, privdata)) < 0)
2524             error(0, "SOAP: soap_release_dependences, error processing dependent value");
2525     }
2526     gwlist_destroy(issues, octstr_destroy_item);
2527 
2528 
2529     return 0; /* OK */
2530 }
2531 
2532 /*
2533  * function soap_process_deps
2534  * select function for selected <key>
2535  **/
soap_process_deps(int key_index,int key_func_ind,Msg * msg,PrivData * privdata)2536 int soap_process_deps(int key_index, int key_func_ind, Msg* msg, PrivData *privdata)
2537 {
2538     /* ADD HERE: MO 'key' functions */
2539     switch (key_index)
2540     {
2541         case 0:
2542             return soap_msgtype_deps(key_func_ind, msg);
2543         case 1:
2544             return soap_msgdata_deps(key_func_ind, msg, privdata);
2545         default:
2546             return -1;
2547     }
2548     return -1;
2549 }
2550 
2551 /*
2552 * function soap_msgtype_deps
2553 * release dependences fot all types of specific coding
2554 **/
soap_msgtype_deps(int key_func_index,Msg * msg)2555 int soap_msgtype_deps(int key_func_index, Msg* msg)
2556 {
2557     /* {"text","binary","unicode","default"}	msgtype  */
2558 
2559     /* ADD HERE: see order in funcs[][] */
2560     switch (key_func_index)
2561     {
2562         case 0:									/* "text" */
2563             msg->sms.coding = DC_7BIT;
2564             break;
2565         case 1:									/* "binary" */
2566             msg->sms.coding = DC_8BIT;
2567             break;
2568 
2569         case 2:									/* "unicode" */
2570         case 3:									/* "default" == unicode */
2571             msg->sms.coding = DC_UCS2;
2572             break;
2573         default:
2574             /* out of range */
2575             error(0, "SOAP: soap_msgtype_deps, unknown index %d", key_func_index);
2576             return -1;
2577     }
2578     return 0;
2579 }
2580 
soap_msgdata_deps(int key_func_index,Msg * msg,PrivData * privdata)2581 int soap_msgdata_deps(int key_func_index, Msg* msg, PrivData *privdata)
2582 {
2583     int ret = 0;
2584     /* {"set_iso","64_binary","hex_binary","unicode","default"}  msgdata */
2585 
2586     /* ADD HERE: */
2587     switch (key_func_index)
2588     {
2589         case 0:       /* "set_iso" */
2590             msg->sms.charset = octstr_create("ISO-8859-1");
2591             break;
2592         case 1:      /* "64_binary" */
2593             octstr_base64_to_binary(msg->sms.msgdata);
2594             break;
2595         case 2:			 /* "hex_binary" */
2596             octstr_hex_to_binary(msg->sms.msgdata);
2597             break;
2598 
2599         case 3:			/* "unicode" */
2600         case 4:			/* "default" */
2601 
2602             if (!octstr_case_compare(privdata->alt_charset, octstr_imm("UCS-2"))) {
2603                 debug("bb.soap.msgdata_deps",0,"SOAP[%s]: converting from %s to UCS-2BE",
2604                       octstr_get_cstr(privdata->name), octstr_get_cstr(privdata->alt_charset));
2605                 ret = charset_convert(msg->sms.msgdata, octstr_get_cstr(privdata->alt_charset), "UCS-2BE");
2606 
2607                 if (ret == -1) {
2608 
2609                     error(2,"SOAP[%s]: Error converting MO data from %s to unicode",
2610                           octstr_get_cstr(privdata->name), octstr_get_cstr(privdata->alt_charset));
2611                 }
2612             }
2613             else if (ret != 0) {
2614                 debug("bb.soap.parse_mo",1,"SOAP[%s]: charset_convert made %d irreversable transformations",
2615                       octstr_get_cstr(privdata->name), ret);
2616             }
2617             msg->sms.charset = octstr_create("UCS-2");
2618 
2619             debug("bb.soap.parse_mo",0,"SOAP[%s]: message decoded -", octstr_get_cstr(privdata->name));
2620             octstr_dump(msg->sms.msgdata, 0);
2621             break;
2622 
2623         default:
2624             /* out of range */
2625 
2626             error(0, "SOAP: soap_msgdata_deps, unknown index %d", key_func_index);
2627             return -1;
2628     }
2629     return 0;
2630 }
2631 
2632 
2633 /* MT spec processing */
2634 /* return index of function alias in array of aliasis for functions
2635 *  used in MT XML file to fill up specific parameters in XML doc
2636 */
soap_lookup_function(Octstr * funcname)2637 int soap_lookup_function(Octstr* funcname)
2638 {
2639     int i;
2640 
2641     /* ADD HERE: XML functions aliasis */
2642     char *aliasis[] = {
2643                           "bouyg_content", "mobitai_content",
2644                           "o2o_msgdata", "msgdata",
2645                           "o2o_validity30", "mobitai_validity_date", "bouyg_validity",
2646                           "o2o_date", "mobitai_date", "rand",
2647                           "o2o_dlrmask_smsc_yn", "o2o_dlrmask_success_01"
2648                       };
2649 
2650     for (i=0; i<sizeof(aliasis)/sizeof(aliasis[0]); ++i)
2651     {
2652         if (!octstr_str_compare(funcname, aliasis[i]))
2653             return i;
2654     }
2655     return -1;
2656 }
2657 
2658 /*
2659 * select function by index
2660 * follow the order defined in array of aliasis in look_up_function()
2661 * return Octstr value
2662 */
soap_select_function(int index,Msg * msg,PrivData * privdata)2663 Octstr* soap_select_function(int index, Msg* msg, PrivData* privdata)
2664 {
2665     /* ADD HERE: XML functions  */
2666     switch (index)
2667     {
2668         case 0:
2669             return soap_bouyg_content_attribute(msg);
2670         case 1:
2671             return soap_mobitai_content_attribute(msg);
2672         case 2:
2673             return soap_o2o_msgdata_attribute(msg, privdata);
2674         case 3:
2675             return soap_msgdata_attribute(msg, privdata);			/* mobitai/bouyg */
2676         case 4:
2677             return soap_o2o_validity30_attribute(msg);
2678         case 5:
2679             return soap_mobitai_validity_date_attribute(msg);
2680         case 6:
2681             return soap_bouyg_validity_attribute(msg);
2682         case 7:
2683             return soap_o2o_date_attribute(msg);
2684         case 8:
2685             return soap_mobitai_date_attribute(msg);
2686         case 9:
2687             return soap_rand_attribute(msg);
2688         case 10:
2689             return soap_o2o_dlrmask_smsc_yn_attribute(msg);
2690         case 11:
2691             return soap_o2o_dlrmask_success_01_attribute(msg);
2692         default:
2693             error(0,"SOAP: soap_select_function can't find function");
2694             return NULL;
2695     }
2696 }
2697 
2698 /* set of MT msg structure to XML specific converting functions */
2699 
soap_bouyg_content_attribute(Msg * msg)2700 Octstr* soap_bouyg_content_attribute(Msg* msg)
2701 {
2702 
2703     if (msg->sms.coding == DC_8BIT)
2704         return octstr_create("D");
2705     else
2706         return octstr_create("A");
2707 }
2708 
soap_mobitai_content_attribute(Msg * msg)2709 Octstr* soap_mobitai_content_attribute(Msg* msg)
2710 {
2711     if (msg->sms.coding == DC_8BIT)
2712         return octstr_create("binary");
2713     else
2714         return octstr_create("text");
2715 
2716 }
2717 
soap_o2o_msgdata_attribute(Msg * msg,PrivData * privdata)2718 Octstr* soap_o2o_msgdata_attribute(Msg* msg, PrivData *privdata)
2719 {
2720     Octstr *data, *res, *udhres;
2721     int ret;
2722 
2723     data = octstr_duplicate(msg->sms.msgdata);
2724 
2725     if (msg->sms.coding == DC_8BIT) {
2726         debug("bb.soap.o2o_msgdata_attribute",0,"SOAP: base 64 encoding");
2727         octstr_binary_to_base64(data);
2728         res = octstr_format("<Control_Data>%S</Control_Data>", data);
2729 
2730         if (octstr_len(msg->sms.udhdata) > 0) { /* add UDH */
2731             O_DESTROY(data);
2732             data = octstr_duplicate(msg->sms.udhdata);
2733             debug("bb.soap.o2o_msgdata_attribute",0,"SOAP: UDH base 64 encoding");
2734             octstr_binary_to_base64(data);
2735             udhres = octstr_format("<UDH>%S</UDH>", data);
2736             octstr_append(res, udhres);
2737             O_DESTROY(udhres);
2738         }
2739         else {
2740             error(0, "SOAP: o2o_msgdata_attribute, UDH not defined");
2741             udhres = octstr_create("<UDH></UDH>");
2742             octstr_append(res, udhres);
2743             O_DESTROY(udhres);
2744         }
2745         O_DESTROY(data);
2746         return res;
2747     }
2748     else if (msg->sms.coding == DC_7BIT || msg->sms.coding == DC_UNDEF) {
2749         /* convert message data to target encoding */
2750         debug("bb.soap.o2o_msgdata_attribute", 0, "SOAP: converting from UTF-8 to %s", octstr_get_cstr(privdata->alt_charset));
2751         ret = charset_convert(data, "UTF-8", octstr_get_cstr(privdata->alt_charset));
2752         if (ret == -1) {
2753             error(0,"SOAP: soap_o2o_msgdata_attribute, charset_convert failed");
2754             octstr_dump(msg->sms.msgdata, 0);
2755             O_DESTROY(data);
2756             return NULL;
2757         }
2758         debug("bb.soap.o2o_msgdata_attribute",0,"SOAP: converting to HTML entities");
2759         octstr_convert_to_html_entities(data);
2760         res = octstr_format("<Message_Text>%S</Message_Text>", data);
2761         O_DESTROY(data);
2762         return res;
2763     }
2764     else if (msg->sms.coding == DC_UCS2) {
2765         /* convert message data to target encoding */
2766         debug("bb.soap.o2o_msgdata_attribute", 0, "converting from USC-2 to %s", octstr_get_cstr(privdata->alt_charset));
2767         ret = charset_convert(msg->sms.msgdata, "UCS-2BE", octstr_get_cstr(privdata->alt_charset));
2768         if (ret == -1) {
2769             error(0,"SOAP: soap_o2o_msgdata_attribute, charset_convert failed");
2770 
2771             octstr_dump(msg->sms.msgdata, 0);
2772             O_DESTROY(data);
2773             return NULL;
2774         }
2775         res = octstr_format("<Message_Text>%s</Message_Text>", data);
2776         O_DESTROY(data);
2777         return res;
2778     }
2779 
2780     else {
2781         error(0,"SOAP: soap_o2o_msgdata_attribute, unknown coding: %ld", msg->sms.coding);
2782         O_DESTROY(data);
2783         return NULL;
2784 
2785     }
2786 }
2787 
2788 /* mobitai/bouyg */
soap_msgdata_attribute(Msg * msg,PrivData * privdata)2789 Octstr* soap_msgdata_attribute(Msg* msg, PrivData* privdata)
2790 {
2791     Octstr *data, *udhdata;
2792     int ret;
2793 
2794 
2795     data = octstr_duplicate(msg->sms.msgdata);
2796 
2797     if (msg->sms.coding == DC_8BIT) {
2798         udhdata = octstr_duplicate(msg->sms.udhdata);
2799         octstr_append(udhdata, data);
2800         octstr_binary_to_hex(udhdata, 1);
2801         O_DESTROY(data);
2802         return udhdata;
2803     }
2804     else if (msg->sms.coding == DC_7BIT || msg->sms.coding == DC_UNDEF) {
2805         /* convert message data to target encoding */
2806         debug("bb.soap.msgdata_attribute", 0, "SOAP: converting from UTF-8 to %s", octstr_get_cstr(privdata->alt_charset));
2807         ret = charset_convert(data, "UTF-8", octstr_get_cstr(privdata->alt_charset));
2808         if (ret == -1) {
2809             error(0,"SOAP: soap_msgdata_attribute, charset_convert failed");
2810             octstr_dump(msg->sms.msgdata, 0);
2811             O_DESTROY(data);
2812             return NULL;
2813         }
2814         debug("bb.soap.msgdata_attribute",0,"SOAP: converting to HTML entities");
2815         octstr_convert_to_html_entities(data);
2816         return data;
2817     }
2818     else if (msg->sms.coding == DC_UCS2) {
2819         /* convert message data to target encoding */
2820         debug("bb.soap.msgdata_attribute", 0, "converting from USC-2 to %s", octstr_get_cstr(privdata->alt_charset));
2821         ret = charset_convert(data, "UCS-2BE", octstr_get_cstr(privdata->alt_charset));
2822         if (ret == -1) {
2823             error(0,"SOAP: soap_msgdata_attribute, charset_convert failed");
2824 
2825             octstr_dump(data, 0);
2826             O_DESTROY(data);
2827             return NULL;
2828         }
2829         return data;
2830     }
2831     else {
2832         error(0,"SOAP: soap_msgdata_attribute, unknown coding: %ld", msg->sms.coding);
2833         O_DESTROY(data);
2834         return NULL;
2835     }
2836 }
2837 
2838 /* validity in 30 minutes increment */
soap_o2o_validity30_attribute(Msg * msg)2839 Octstr* soap_o2o_validity30_attribute(Msg* msg)
2840 {
2841     return octstr_format("%ld",(msg->sms.validity != SMS_PARAM_UNDEFINED ? (msg->sms.validity - time(NULL))/60 : SOAP_DEFAULT_VALIDITY) / 30);
2842 }
2843 
2844 /* date on which the message's validity expires */
soap_mobitai_validity_date_attribute(Msg * msg)2845 Octstr* soap_mobitai_validity_date_attribute(Msg* msg)
2846 {
2847     return date_create_iso(msg->sms.validity);
2848 }
2849 
2850 /* validity in seconds */
soap_bouyg_validity_attribute(Msg * msg)2851 Octstr* soap_bouyg_validity_attribute(Msg* msg)
2852 {
2853     return octstr_format("%d", msg->sms.validity - time(NULL));
2854 }
2855 
soap_o2o_date_attribute(Msg * msg)2856 Octstr* soap_o2o_date_attribute(Msg* msg)
2857 {
2858     return soap_write_date(msg->sms.time);
2859 }
2860 
2861 
2862 /* TIMESTAMP */
soap_mobitai_date_attribute(Msg * msg)2863 Octstr* soap_mobitai_date_attribute(Msg* msg)
2864 {
2865     return date_create_iso(msg->sms.time);
2866 }
2867 
soap_rand_attribute(Msg * msg)2868 Octstr* soap_rand_attribute(Msg* msg)
2869 {
2870     return octstr_format("%d",gw_rand());
2871 }
2872 
2873 /* "Y" for any of the SMSC generated DLRs, "N" otherwise */
soap_o2o_dlrmask_smsc_yn_attribute(Msg * msg)2874 Octstr* soap_o2o_dlrmask_smsc_yn_attribute(Msg* msg)
2875 {
2876     return octstr_create(DLR_IS_ENABLED_SMSC(msg->sms.dlr_mask) ? "Y" : "N");
2877 }
2878 
2879 /* "1" for any of the SMSC generated DLRs, "0" otherwise */
soap_o2o_dlrmask_success_01_attribute(Msg * msg)2880 Octstr* soap_o2o_dlrmask_success_01_attribute(Msg* msg)
2881 {
2882     return octstr_create( DLR_IS_SUCCESS(msg->sms.dlr_mask) ? "0" : "1");
2883 }
2884 
2885 /*
2886  * looking for key in the map names collection to find key_index
2887  * if map_index==1 i like to know index in the map 'where' for name 'key'
2888  */
soap_get_index(List * where,Octstr * key,int map_index)2889 int soap_get_index(List* where, Octstr* key, int map_index)
2890 {
2891     int i, j;
2892     ArgumentMap* map;
2893 
2894     /* ADD HERE: */
2895     /* key and key_deps values as defined in mo.deps */
2896     char* funcs_deps[] = {
2897                              "msgtype", "msgdata"
2898                          };
2899 
2900     for (i=0; i < gwlist_len(where); ++i) {
2901         map = gwlist_get(where, i);
2902         if (!octstr_compare(map->name, key)) {
2903             if (map_index==1) /* return index from the list where found name */
2904                 return i;
2905 
2906             for (j=0; j < sizeof(funcs_deps)/sizeof(funcs_deps[0]); ++j) {
2907                 if (!octstr_str_compare(map->name, funcs_deps[j]))
2908                     return j;
2909             }
2910         }
2911     }
2912     error(0, "SOAP: soap_get_index, broken 'deps' file, can't find key <%s> ", octstr_get_cstr(key));
2913     return -1;
2914 }
2915 
2916 
2917