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