Kannel: Open Source WAP and SMS gateway  svn-r5335
smsc_soap.c
Go to the documentation of this file.
1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2018 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 responses 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 
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;
212 } ClientData;
213 
214 /* struct useful for the XML mapping routines */
215 typedef struct argument_map {
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 */
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 */
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 */
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 */
325 
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  */
355 {
357  Octstr* temp = NULL;
358  List* filenames = NULL;
359 
360  /* allocate and init internat data structure */
361  privdata = gw_malloc(sizeof(PrivData));
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  }
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);
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);
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);
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  }
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);
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);
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 
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 
476 
477  debug("bb.soap.create",0,"Connecting to %s",
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 
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
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) {
523  /* gwlist_destroy(privdata->pending_ack_queue, NULL); */
524 
538  }
539  gw_free(privdata);
540  octstr_destroy(temp);
542 
543  /* notify bearerbox */
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  */
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),
579 
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  */
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.*/
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)
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  */
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",
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) &&
647  error(0, "SOAP: soap_start, failed to spawn thread - aborting");
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",
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  */
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",
675 
676  /* make connection thread disconnect */
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  */
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 
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  */
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",
725 
726  while (!privdata->shutdown) {
727 
728  /* check connection status */
729  switch (conn->status) {
731  case SMSCCONN_CONNECTING:
732  if (privdata->soap_server) {
734  }
735 
736  if (soap_server_start(conn)) {
737  privdata->shutdown = 1;
738  error(0, "SOAP[%s]: failed to start HTTP server!",
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 
751  if (privdata->soap_server)
753  break;
754 
755  case SMSCCONN_ACTIVE:
756  if (!privdata->soap_server) {
757  mutex_lock(conn->flow_mutex);
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 */
781  }
782 
783  debug("bb.soap.connection",0,"SOAP[%s]: connection shutting down",
785 
787 
788  /* send all queued messages to bearerbox for recycling */
789  debug("bb.soap.connection",0,"SOAP[%s]: sending messages back to bearerbox",
791 
792  while ((msg = gwlist_extract_first(privdata->outgoing_queue)) != 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",
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",
805 
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",
812  if (privdata->soap_client)
814 
815  /* destroy private data stores */
816  debug("bb.soap.connection",0,"SOAP[%s]: done with privdata",
821 
823 
827 
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.");
842 }
843 
844 
845 /*
846  * function soap_server()
847  * server thread - accepts incoming MOs
848  * Input: SMSCConn connection state data
849  */
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",
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",
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",
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 
946  }
947 
948  debug("bb.soap.server",0,"SOAP[%s]: server going down",
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  */
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",
972 
973  while ((counter < SOAP_MAX_MESSAGE_PER_ROUND) &&
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",
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",
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  */
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",
1055  octstr_destroy(xml);
1056  return NULL;
1057 
1058  }
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  */
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  **/
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) */
1131  return;
1132 
1133 
1134  /* see if we have any responses pending */
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)",
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 */
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  **/
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",
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",
1242  }
1243 
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",
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 response 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  **/
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) {
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 - */
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 
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",
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");
1353  }
1354 
1355  if (strlen(sender) == 0) {
1356  error(0,"SOAP: parse_mo - failed to get sender");
1359  }
1360 
1361  if (strlen(msgdata) == 0) {
1362  error(0,"SOAP: parse_mo - failed to get message content");
1364 
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  }
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 */
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 
1496  if (*response)
1497  debug("bb.soap.response_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 response 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  **/
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) {
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 - */
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 */
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 
1582  }
1583 
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");
1593  }
1594 
1595  if (result == -1) {
1596 
1597  error(0,"SOAP: parse_dlr - failed to get delivery 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);
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 
1657  if (*response)
1658  debug("bb.soap.response_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  **/
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  **/
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  **/
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  **/
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  **/
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  **/
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 */
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));
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  **/
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 */
1885 
1886  if (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  **/
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  **/
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)
1942 
1943  /* I'm going to change the list, so lock it */
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) {
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);
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  **/
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) {
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  **/
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  **/
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  **/
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);
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; */
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  **/
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  **/
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  **/
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",
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  **/
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);
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);
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);
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));
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  }
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  }
2527 
2528 
2529  return 0; /* OK */
2530 }
2531 
2532 /*
2533  * function soap_process_deps
2534  * select function for selected <key>
2535  **/
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 **/
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 
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 
2603  debug("bb.soap.msgdata_deps",0,"SOAP[%s]: converting from %s to UCS-2BE",
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",
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 */
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 */
2664 {
2665  /* ADD HERE: XML functions */
2666  switch (index)
2667  {
2668  case 0:
2670  case 1:
2672  case 2:
2674  case 3:
2675  return soap_msgdata_attribute(msg, privdata); /* mobitai/bouyg */
2676  case 4:
2678  case 5:
2680  case 6:
2682  case 7:
2683  return soap_o2o_date_attribute(msg);
2684  case 8:
2686  case 9:
2687  return soap_rand_attribute(msg);
2688  case 10:
2690  case 11:
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 
2701 {
2702 
2703  if (msg->sms.coding == DC_8BIT)
2704  return octstr_create("D");
2705  else
2706  return octstr_create("A");
2707 }
2708 
2710 {
2711  if (msg->sms.coding == DC_8BIT)
2712  return octstr_create("binary");
2713  else
2714  return octstr_create("text");
2715 
2716 }
2717 
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");
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");
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");
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 */
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");
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 */
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 */
2846 {
2847  return date_create_iso(msg->sms.validity);
2848 }
2849 
2850 /* validity in seconds */
2852 {
2853  return octstr_format("%d", msg->sms.validity - time(NULL));
2854 }
2855 
2857 {
2858  return soap_write_date(msg->sms.time);
2859 }
2860 
2861 
2862 /* TIMESTAMP */
2864 {
2865  return date_create_iso(msg->sms.time);
2866 }
2867 
2869 {
2870  return octstr_format("%d",gw_rand());
2871 }
2872 
2873 /* "Y" for any of the SMSC generated DLRs, "N" otherwise */
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 */
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  */
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 
int port
Definition: smsc_cgw.c:159
Octstr * name
Definition: smscconn_p.h:173
static long soap_parse_mo(SMSCConn *conn, Octstr *request, Octstr **response)
Definition: smsc_soap.c:1275
void error(int err, const char *fmt,...)
Definition: log.c:648
static ClientData * soap_client_have_response(List *client_list)
Definition: smsc_soap.c:2009
long year
Definition: date.h:72
void info(int err, const char *fmt,...)
Definition: log.c:672
static void soap_send_loop(SMSCConn *conn)
Definition: smsc_soap.c:963
Msg * msg_duplicate(Msg *msg)
Definition: msg.c:111
void bb_smscconn_connected(SMSCConn *conn)
Definition: bb_smscconn.c:192
static ClientData * soap_create_client_data()
Definition: smsc_soap.c:1915
static void soap_server_stop(PrivData *privdata)
Definition: smsc_soap.c:1875
#define SOAP_ERROR_NO_DLR_MESSAGE
Definition: smsc_soap.c:146
#define SOAP_ERROR_NO_DATA_CODE
Definition: smsc_soap.c:157
void http_header_add(List *headers, char *name, char *contents)
Definition: http.c:2886
int soap_msgdata_deps(int key_func_index, Msg *msg, PrivData *privdata)
Definition: smsc_soap.c:2581
Octstr * soap_rand_attribute(Msg *msg)
Definition: smsc_soap.c:2868
Octstr * soap_mobitai_date_attribute(Msg *msg)
Definition: smsc_soap.c:2863
static Octstr * soap_convert_token(Msg *msg, Octstr *name, PrivData *privdata)
Definition: smsc_soap.c:2040
void http_caller_signal_shutdown(HTTPCaller *caller)
Definition: http.c:913
#define mutex_unlock(m)
Definition: thread.h:136
void gwlist_append(List *list, void *item)
Definition: list.c:179
void bb_smscconn_killed(void)
Definition: bb_smscconn.c:199
#define O_DESTROY(a)
Definition: smsc_soap.c:225
#define SOAP_MAX_MESSAGE_PER_ROUND
Definition: smsc_soap.c:136
void octstr_append(Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:1504
#define DLR_IS_SUCCESS(dlr)
Definition: dlr.h:86
Octstr * soap_o2o_validity30_attribute(Msg *msg)
Definition: smsc_soap.c:2839
static void soap_server(void *arg)
Definition: smsc_soap.c:850
Octstr * id
Definition: smscconn_p.h:174
void gwthread_join(long thread)
Octstr * date_create_iso(time_t unixtime)
Definition: date.c:328
long gwlist_len(List *list)
Definition: list.c:166
void * data
Definition: smscconn_p.h:250
long soap_release_dependences(Octstr *deps, List *lstmaps, Msg *msg, PrivData *privdata)
Definition: smsc_soap.c:2404
#define SOAP_DEFAULT_MESSAGE
Definition: smsc_soap.c:145
void * gwlist_get(List *list, long pos)
Definition: list.c:292
#define SOAP_QUERY_OK
Definition: smsc_soap.c:159
int form_urlencoded
Definition: smsc_soap.c:195
List * outgoing_queue
Definition: smsc_cgw.c:153
Octstr * mt_spec_file
Definition: smsc_soap.c:199
void(* stop_conn)(SMSCConn *conn)
Definition: smscconn_p.h:247
int octstr_url_decode(Octstr *ostr)
Definition: octstr.c:1746
#define SOAP_SLEEP_TIME
Definition: smsc_soap.c:135
void octstr_append_char(Octstr *ostr, int ch)
Definition: octstr.c:1517
HTTPCaller * caller
Definition: smsc_soap.c:211
time_t soap_read_date(Octstr *dateString)
Definition: smsc_soap.c:1777
#define cfg_get(grp, varname)
Definition: cfg.h:86
void octstr_binary_to_base64(Octstr *ostr)
Definition: octstr.c:542
Octstr * soap_bouyg_validity_attribute(Msg *msg)
Definition: smsc_soap.c:2851
#define DC_8BIT
Definition: sms.h:111
void uuid_generate(uuid_t out)
Definition: gw_uuid.c:393
#define CLIENT_BUSY_TIME
Definition: smsc_soap.c:166
int soap_xmlnode_get_long(xmlNodePtr cur, long *out)
Definition: smsc_soap.c:1679
Octstr * soap_fetch_xml_data(xmlNodePtr xml, Octstr *path)
Definition: smsc_soap.c:2162
static void soap_listener(void *arg)
Definition: smsc_soap.c:718
int soap_xmlnode_get_int64(xmlNodePtr cur, long long *out)
Definition: smsc_soap.c:1712
#define msg_create(type)
Definition: msg.h:136
long minute
Definition: date.h:74
Octstr * mo_spec_file
Definition: smsc_soap.c:201
void octstr_convert_to_html_entities(Octstr *input)
Definition: octstr.c:2738
static Cfg * cfg
Definition: opensmppbox.c:95
void octstr_strip_blanks(Octstr *text)
Definition: octstr.c:1346
Msg * dlr_find(const Octstr *smsc, const Octstr *ts, const Octstr *dst, int typ, int use_dst)
Definition: dlr.c:387
void dlr_add(const Octstr *smsc, const Octstr *ts, Msg *msg, int use_dst)
Definition: dlr.c:330
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
Octstr * mt_xml_file
Definition: smsc_soap.c:198
#define octstr_copy(ostr, from, len)
Definition: octstr.h:178
void octstr_binary_to_hex(Octstr *ostr, int uppercase)
Definition: octstr.c:465
void(* start_conn)(SMSCConn *conn)
Definition: smscconn_p.h:246
long octstr_search_char(const Octstr *ostr, int ch, long pos)
Definition: octstr.c:1012
#define DLR_IS_ENABLED_SMSC(dlr)
Definition: dlr.h:83
static struct pid_list * found
long listener_thread
Definition: smsc_soap.c:176
Octstr * sscan_type
Definition: smsc_soap.c:219
static struct @117 keywords[]
static void soap_stop_cb(SMSCConn *conn)
Definition: smsc_soap.c:665
smscconn_killed_t why_killed
Definition: smscconn_p.h:153
void http_destroy_headers(List *headers)
Definition: http.c:2879
void gwlist_unlock(List *list)
Definition: list.c:354
struct client_data ClientData
#define DLR_SUCCESS
Definition: dlr.h:72
Octstr * soap_mobitai_content_attribute(Msg *msg)
Definition: smsc_soap.c:2709
void http_start_request(HTTPCaller *caller, int method, Octstr *url, List *headers, Octstr *body, int follow, void *id, Octstr *certkeyfile)
Definition: http.c:1760
static void soap_start_cb(SMSCConn *conn)
Definition: smsc_soap.c:634
void http_send_reply(HTTPClient *client, int status, List *headers, Octstr *body)
Definition: http.c:2695
#define SOAP_ERROR_MALFORMED_DATA_MESSAGE
Definition: smsc_soap.c:150
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:283
static long long soap_parse_response(PrivData *privdata, Octstr *xmlResponse)
Definition: smsc_soap.c:1205
Definition: msg.h:79
int soap_xmlnode_get_octstr(xmlNodePtr cur, Octstr **out)
Definition: smsc_soap.c:1746
int token
Definition: wslexer.c:159
void * gwlist_extract_first(List *list)
Definition: list.c:305
#define SOAP_MO_URI
Definition: smsc_soap.c:141
void gwlist_delete(List *list, long pos, long count)
Definition: list.c:232
time_t last_access
Definition: smsc_soap.c:209
HTTPClient * http_accept_request(int port, Octstr **client_ip, Octstr **url, List **headers, Octstr **body, List **cgivars)
Definition: http.c:2571
void http_close_port(int port)
Definition: http.c:2515
void octstr_delete(Octstr *ostr1, long pos, long len)
Definition: octstr.c:1527
Octstr * attribute
Definition: smsc_soap.c:218
long bb_smscconn_receive(SMSCConn *conn, Msg *sms)
Definition: bb_smscconn.c:477
static int soap_shutdown_cb(SMSCConn *conn, int finish_sending)
Definition: smsc_soap.c:594
int uuid_is_null(const uuid_t uu)
Definition: gw_uuid.c:413
List * http_create_empty_headers(void)
Definition: http.c:2872
#define SOAP_ERROR_DLR_MESSAGE
Definition: smsc_soap.c:147
Octstr * soap_o2o_dlrmask_smsc_yn_attribute(Msg *msg)
Definition: smsc_soap.c:2874
time_t connect_time
Definition: smscconn_p.h:155
static void soap_send(PrivData *privdata, Octstr *xmlbuffer, Msg *msgid)
Definition: smsc_soap.c:1080
int soap_server
Definition: smsc_soap.c:180
#define octstr_duplicate(ostr)
Definition: octstr.h:187
#define octstr_dump(ostr, level,...)
Definition: octstr.h:564
long day
Definition: date.h:70
#define CLIENT_BUSY_LOAD
Definition: smsc_soap.c:168
Mutex * flow_mutex
Definition: smscconn_p.h:157
Octstr * allow_ip
Definition: smsc_cgw.c:164
char * name
Definition: smsc_cimd2.c:212
int octstr_case_compare(const Octstr *os1, const Octstr *os2)
Definition: octstr.c:903
#define SOAP_ERROR_MO_MESSAGE
Definition: smsc_soap.c:148
void warning(int err, const char *fmt,...)
Definition: log.c:660
List * octstr_split_words(const Octstr *ostr)
Definition: octstr.c:1602
Octstr * soap_o2o_date_attribute(Msg *msg)
Definition: smsc_soap.c:2856
Octstr * octstr_format(const char *fmt,...)
Definition: octstr.c:2464
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:324
#define SOAP_ERROR_MALFORMED_DATA_CODE
Definition: smsc_soap.c:158
Octstr * alt_charset
Definition: smsc_soap.c:196
#define gwthread_create(func, arg)
Definition: gwthread.h:90
#define octstr_create(cstr)
Definition: octstr.h:125
void octstr_destroy_item(void *os)
Definition: octstr.c:336
Octstr * name
Definition: smsc_emi.c:96
void gwthread_sleep(double seconds)
#define SMS_PARAM_UNDEFINED
Definition: sms.h:91
static void soap_destroy_client_data(void *data)
Definition: smsc_soap.c:1991
Octstr * form_variable
Definition: smsc_soap.c:194
void soap_destroy_map(void *item)
Definition: smsc_soap.c:2143
Octstr * soap_bouyg_content_attribute(Msg *msg)
Definition: smsc_soap.c:2700
volatile sig_atomic_t is_stopped
Definition: smscconn_p.h:169
void octstr_base64_to_binary(Octstr *ostr)
Definition: octstr.c:663
int http_open_port(int port, int ssl)
Definition: http.c:2509
long second
Definition: date.h:75
static void soap_client_init_query(PrivData *privdata, List *headers, Octstr *data, Msg *msg)
Definition: smsc_soap.c:1933
#define SPEC_DEFAULT
Definition: smsc_soap.c:170
Octstr * name
Definition: smsc_soap.c:216
Octstr * dlr_spec_file
Definition: smsc_soap.c:203
int shutdown
Definition: smsc_cgw.c:156
void gwlist_lock(List *list)
Definition: list.c:347
#define http_receive_result(caller, status, final_url, headers, body)
Definition: http.h:394
Octstr * octstr_read_file(const char *filename)
Definition: octstr.c:1548
int soap_lookup_function(Octstr *funcname)
Definition: smsc_soap.c:2637
#define SOAP_ERROR_DLR_CODE
Definition: smsc_soap.c:155
int date_parse_iso(struct universaltime *ut, Octstr *os)
Definition: date.c:230
void octstr_strip_crlfs(Octstr *text)
Definition: octstr.c:1378
Octstr * path
Definition: smsc_soap.c:217
long octstr_len(const Octstr *ostr)
Definition: octstr.c:342
static Octstr * soap_format_xml(Octstr *xml_file, Msg *msg, PrivData *privdata)
Definition: smsc_soap.c:1014
int cfg_get_bool(int *n, CfgGroup *grp, Octstr *varname)
Definition: cfg.c:759
#define SOAP_DEFAULT_CODE
Definition: smsc_soap.c:154
#define MAX_SOAP_CLIENTS
Definition: smsc_soap.c:165
int alt_charset
Definition: smsc_emi.c:134
Octstr * uri
Definition: smsc_soap.c:185
Definition: octstr.c:118
void bb_smscconn_sent(SMSCConn *conn, Msg *sms, Octstr *reply)
Definition: bb_smscconn.c:281
#define SOAP_ERROR_MO_CODE
Definition: smsc_soap.c:156
int(* shutdown)(SMSCConn *conn, int finish_sending)
Definition: smscconn_p.h:230
Octstr * soap_mobitai_validity_date_attribute(Msg *msg)
Definition: smsc_soap.c:2845
long hour
Definition: date.h:73
long month
Definition: date.h:71
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:726
int cfg_get_integer(long *n, CfgGroup *grp, Octstr *varname)
Definition: cfg.c:742
#define SOAP_DEFAULT_VALIDITY
Definition: smsc_soap.c:138
unsigned long requests
Definition: smsc_soap.c:210
void gwthread_wakeup(long thread)
Definition: cfg.c:73
int octstr_hex_to_binary(Octstr *ostr)
Definition: octstr.c:494
int octstr_str_compare(const Octstr *ostr, const char *str)
Definition: octstr.c:973
#define SOAP_ERROR_NO_DLR_CODE
Definition: smsc_soap.c:153
Octstr * mo_xml_file
Definition: smsc_soap.c:200
Octstr * mo_deps_file
Definition: smsc_soap.c:204
Octstr * soap_o2o_msgdata_attribute(Msg *msg, PrivData *privdata)
Definition: smsc_soap.c:2718
struct privdata PrivData
smscconn_status_t status
Definition: smscconn_p.h:151
long octstr_parse_long(long *nump, Octstr *ostr, long pos, int base)
Definition: octstr.c:749
HTTPCaller * http_caller_create(void)
Definition: http.c:897
Octstr * soap_select_function(int index, Msg *msg, PrivData *privdata)
Definition: smsc_soap.c:2663
#define SOAP_ERROR_NO_DATA_MESSAGE
Definition: smsc_soap.c:149
#define gwlist_create()
Definition: list.h:136
long(* queued)(SMSCConn *conn)
Definition: smscconn_p.h:241
Octstr * soap_o2o_dlrmask_success_01_attribute(Msg *msg)
Definition: smsc_soap.c:2880
List * soap_client
Definition: smsc_soap.c:189
int ssl
Definition: smsc_soap.c:183
Octstr * date_format_http(unsigned long unixtime)
Definition: date.c:89
int(* send_msg)(SMSCConn *conn, Msg *msg)
Definition: smscconn_p.h:236
Octstr * soap_msgdata_attribute(Msg *msg, PrivData *privdata)
Definition: smsc_soap.c:2789
void * store
Definition: smsc_soap.c:220
#define DLR_BUFFERED
Definition: dlr.h:74
void bb_smscconn_send_failed(SMSCConn *conn, Msg *sms, int reason, Octstr *reply)
Definition: bb_smscconn.c:329
void http_caller_destroy(HTTPCaller *caller)
Definition: http.c:907
long server_thread
Definition: smsc_soap.c:177
static int date(int hex)
static int soap_add_msg_cb(SMSCConn *conn, Msg *sms)
Definition: smsc_soap.c:563
int soap_map_xml_data(xmlNodePtr xml, List *maps)
Definition: smsc_soap.c:2282
#define DC_UNDEF
Definition: sms.h:109
#define SOAP_DLR_URI
Definition: smsc_soap.c:142
static Octstr * content
Definition: mtbatch.c:87
long date_convert_universal(struct universaltime *t)
Definition: date.c:118
int soap_msgtype_deps(int key_func_index, Msg *msg)
Definition: smsc_soap.c:2555
Octstr * dlr_xml_file
Definition: smsc_soap.c:202
static int response(List *push_headers, Octstr **username, Octstr **password)
static Octstr * soap_write_date(time_t date)
Definition: smsc_soap.c:1814
int octstr_get_char(const Octstr *ostr, long pos)
Definition: octstr.c:406
#define mutex_lock(m)
Definition: thread.h:130
struct argument_map ArgumentMap
static long soap_parse_dlr(SMSCConn *conn, Octstr *request, Octstr **response)
Definition: smsc_soap.c:1512
Octstr * deny_ip
Definition: smsc_cgw.c:164
int gw_rand(void)
Definition: protected.c:174
List * soap_create_map(Octstr *spec, long count, char *keywords[], char *types[], void *storage[])
Definition: smsc_soap.c:2088
List * octstr_split(const Octstr *os, const Octstr *sep)
Definition: octstr.c:1640
Definition: list.c:102
static void soap_read_response(SMSCConn *conn)
Definition: smsc_soap.c:1118
#define SOAP_DEFAULT_SENDER_STRING
Definition: smsc_soap.c:137
static XMLRPCDocument * msg
Definition: test_xmlrpc.c:86
#define DLR_FAIL
Definition: dlr.h:73
static int start
int soap_get_index(List *where, Octstr *key, int map_index)
Definition: smsc_soap.c:2889
int soap_server_start(SMSCConn *conn)
Definition: smsc_soap.c:1845
static Counter * counter
#define DC_UCS2
Definition: sms.h:112
static long soap_queued_cb(SMSCConn *conn)
Definition: smsc_soap.c:688
int charset_convert(Octstr *string, char *charset_from, char *charset_to)
Definition: charset.c:589
#define DC_7BIT
Definition: sms.h:110
int soap_process_deps(int key_index, int key_func_ind, Msg *msg, PrivData *privdata)
Definition: smsc_soap.c:2536
int octstr_compare(const Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:871
int load
Definition: smscconn_p.h:152
int smsc_soap_create(SMSCConn *conn, CfgGroup *cfg)
Definition: smsc_soap.c:354
void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)
Definition: list.c:145
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.