1 /* ====================================================================
2 * The Kannel Software License, Version 1.0
3 *
4 * Copyright (c) 2001-2014 Kannel Group
5 * Copyright (c) 1998-2001 WapIT Ltd.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * 3. The end-user documentation included with the redistribution,
21 * if any, must include the following acknowledgment:
22 * "This product includes software developed by the
23 * Kannel Group (http://www.kannel.org/)."
24 * Alternately, this acknowledgment may appear in the software itself,
25 * if and wherever such third-party acknowledgments normally appear.
26 *
27 * 4. The names "Kannel" and "Kannel Group" must not be used to
28 * endorse or promote products derived from this software without
29 * prior written permission. For written permission, please
30 * contact org@kannel.org.
31 *
32 * 5. Products derived from this software may not be called "Kannel",
33 * nor may "Kannel" appear in their name, without prior written
34 * permission of the Kannel Group.
35 *
36 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39 * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47 * ====================================================================
48 *
49 * This software consists of voluntary contributions made by many
50 * individuals on behalf of the Kannel Group. For more information on
51 * the Kannel Group, please see <http://www.kannel.org/>.
52 *
53 * Portions of this software are based upon software originally written at
54 * WapIT Ltd., Helsinki, Finland for the Kannel project.
55 */
56
57 /*
58 * smsc_soap_parlayx.c - Kannel SMSC module for ParlayX 2.1
59 *
60 * Stipe Tolj <stolj at kannel.org>
61 */
62
63 #include <sys/types.h>
64 #include <sys/socket.h>
65 #include <unistd.h>
66 #include <errno.h>
67 #include <time.h>
68 #include <limits.h>
69
70 #include "gwlib/gwlib.h"
71 #include "smscconn.h"
72 #include "smscconn_p.h"
73 #include "bb_smscconn_cb.h"
74 #include "msg.h"
75 #include "sms.h"
76 #include "dlr.h"
77 #include "urltrans.h"
78
79 #ifdef HAVE_GSOAP
80
81 #include "soapH.h"
82 #include "SendSmsBinding.nsmap"
83 #include "wsseapi.h"
84
85
86 /*
87 * Define DEBUG macro to also activate the DLR sending
88 * thread, which allows us to self-inject DLRs.
89 */
90 #undef DEBUG
91 #define DEBUG 1
92
93 /* Default character encoding */
94 #define DEFAULT_CHARSET "UTF-8"
95
96
97 typedef struct ConnData {
98 HTTPCaller *http_ref;
99 long receive_thread;
100 long senders; /* number of concurrent sending threads */
101 int shutdown;
102 int port; /* port for receiving SMS'es */
103 Octstr *allow_ip;
104 Octstr *send_url;
105 Octstr *dlr_url;
106 long open_sends;
107 Octstr *username; /* if needed */
108 Octstr *password; /* as said */
109 Octstr *alt_charset; /* alternative charset use */
110 gw_prioqueue_t *msgs_to_send;
111 List *sender_threads;
112 long dlr_thread;
113 List *dlr_queue;
114
115 /* callback functions */
116 void (*send_sms) (SMSCConn *conn, Msg *msg);
117 void (*parse_reply) (SMSCConn *conn, Msg *msg, int status,
118 List *headers, Octstr *body);
119 void (*receive_sms) (SMSCConn *conn, HTTPClient *client,
120 List *headers, Octstr *body, List *cgivars);
121 void (*httpsmsc_sender) (void *arg);
122 } ConnData;
123
124
125 static void octstr_remove_crlfs(Octstr *ostr);
126 static void soap_send_sms(struct soap *soap, SMSCConn *conn, Msg *sms);
127 #ifdef DEBUG
128 static void soap_send_dlr(struct soap *soap, SMSCConn *conn, Msg *sms);
129 #endif
130
131
132 /********************************************************************
133 * DLR state mapping
134 *
135 * See VMP spec v0.9, section 2.4.2, page 21 for the VMP specific
136 * value details.
137 */
138
139 static struct StateTable {
140 unsigned int dlr_mask;
141 enum pxSms__DeliveryStatus state;
142 } state_table[] = {
143 #define ENTRY(mask, state) { mask, state },
144 ENTRY(DLR_SUCCESS, pxSms__DeliveryStatus__DeliveredToTerminal)
145 ENTRY(DLR_FAIL, pxSms__DeliveryStatus__DeliveryImpossible)
146 ENTRY(DLR_BUFFERED, pxSms__DeliveryStatus__DeliveredToNetwork)
147 ENTRY(DLR_SMSC_FAIL, pxSms__DeliveryStatus__DeliveryNotificationNotSupported)
148 ENTRY(DLR_FAIL, pxSms__DeliveryStatus__DeliveryUncertain)
149 ENTRY(DLR_BUFFERED, pxSms__DeliveryStatus__MessageWaiting)
150 #undef ENTRY
151 };
152
153 static int state_table_entries = sizeof(state_table) / sizeof(state_table[0]);
154
155
156 /********************************************************************
157 * Internal threads
158 */
159
160 /*
161 * Each sender thread has it's own gSOAP IO context that is passed along
162 * to the sending function in the consume loop. This ensures we maintain
163 * the TCP connection, along with the HTTP/1.1 keep-alive state for the
164 * HTTP transport layer.
165 */
166
167 /*
168 * The various ParlayX variants use separate thread logic that are
169 * addressed via the corresponding function pointer callback in
170 * conndata.
171 *
172 * The variants 'ericsson-sdp' and 'oneapi-v1' have the same ParlayX
173 * SOAP XML PDUs, but differ in the authentication scheme they use, where
174 * 'ericsson-sdp' uses WS-Security via wsse and 'oneapi-v1' uses plain
175 * HTTP basic authentication.
176 */
177
httpsmsc_sender_ercisson_sdp(void * arg)178 static void httpsmsc_sender_ercisson_sdp(void *arg)
179 {
180 SMSCConn *conn = arg;
181 ConnData *conndata = conn->data;
182 Msg *msg;
183 struct soap *soap;
184
185 /* Make sure we log into our own log-file if defined */
186 log_thread_to(conn->log_idx);
187
188 /* establish soap context */
189 soap = soap_new1(SOAP_XML_INDENT|SOAP_IO_KEEPALIVE);
190
191 /* register wsse plugin */
192 soap_register_plugin(soap, soap_wsse);
193
194 #ifdef WITH_OPENSSL
195 /* setup the SSL context */
196 if (soap_ssl_client_context(soap, SOAP_SSL_NO_AUTHENTICATION,
197 NULL, NULL, NULL, NULL, NULL)) {
198 char buf[1024];
199 Octstr *os;
200
201 soap_sprint_fault(soap, buf, 1024);
202 os = octstr_create(buf);
203 octstr_remove_crlfs(os);
204
205 error(0, "SOAP[%s]: Could not assign gSOAP SSL context:",
206 octstr_get_cstr(conn->id));
207 error(0, "SOAP[%s]: %s",
208 octstr_get_cstr(conn->id), octstr_get_cstr(os));
209
210 octstr_destroy(os);
211 goto done;
212 }
213 #endif
214
215 /* main consume loop for the sender */
216 while ((msg = gw_prioqueue_consume(conndata->msgs_to_send)) != NULL) {
217
218 /* message lifetime of 10 seconds */
219 soap_wsse_add_Timestamp(soap, "Time", 10);
220
221 /* add user name with digest password */
222 soap_wsse_add_UsernameTokenDigest(soap, "User",
223 octstr_get_cstr(conndata->username), octstr_get_cstr(conndata->password));
224
225 soap_send_sms(soap, conn, msg);
226 conndata->open_sends--;
227
228 /* clean up security header */
229 soap_wsse_delete_Security(soap);
230 }
231
232 done:
233 /* destroy soap context */
234 soap_destroy(soap);
235 soap_end(soap);
236 soap_free(soap);
237 }
238
239
httpsmsc_sender_gsma_oneapi(void * arg)240 static void httpsmsc_sender_gsma_oneapi(void *arg)
241 {
242 SMSCConn *conn = arg;
243 ConnData *conndata = conn->data;
244 Msg *msg;
245 struct soap *soap;
246
247 /* Make sure we log into our own log-file if defined */
248 log_thread_to(conn->log_idx);
249
250 /* establish soap context */
251 soap = soap_new1(SOAP_XML_INDENT|SOAP_IO_KEEPALIVE);
252
253 /* assign HTTP basic authentication tokens */
254 soap->userid = octstr_get_cstr(conndata->username);
255 soap->passwd = octstr_get_cstr(conndata->password);
256
257 #ifdef WITH_OPENSSL
258 /* setup the SSL context */
259 if (soap_ssl_client_context(soap, SOAP_SSL_NO_AUTHENTICATION,
260 NULL, NULL, NULL, NULL, NULL)) {
261 char buf[1024];
262 Octstr *os;
263
264 soap_sprint_fault(soap, buf, 1024);
265 os = octstr_create(buf);
266 octstr_remove_crlfs(os);
267
268 error(0, "SOAP[%s]: Could not assign gSOAP SSL context:",
269 octstr_get_cstr(conn->id));
270 error(0, "SOAP[%s]: %s",
271 octstr_get_cstr(conn->id), octstr_get_cstr(os));
272
273 octstr_destroy(os);
274 goto done;
275 }
276 #endif
277
278 /* main consume loop for the sender */
279 while ((msg = gw_prioqueue_consume(conndata->msgs_to_send)) != NULL) {
280 soap_send_sms(soap, conn, msg);
281 conndata->open_sends--;
282 }
283
284 done:
285 /* destroy soap context */
286 soap_destroy(soap);
287 soap_end(soap);
288 soap_free(soap);
289 }
290
291
292 /*
293 * Thread to listen to HTTP requests from SMSC entity
294 */
httpsmsc_receiver(void * arg)295 static void httpsmsc_receiver(void *arg)
296 {
297 SMSCConn *conn = arg;
298 ConnData *conndata = conn->data;
299 HTTPClient *client;
300 Octstr *ip, *url, *body;
301 List *headers, *cgivars;
302
303 ip = url = body = NULL;
304 headers = cgivars = NULL;
305
306 /* Make sure we log into our own log-file if defined */
307 log_thread_to(conn->log_idx);
308
309 while (conndata->shutdown == 0) {
310
311 /* XXX if conn->is_stopped, do not receive new messages.. */
312
313 client = http_accept_request(conndata->port, &ip, &url,
314 &headers, &body, &cgivars);
315 if (client == NULL)
316 break;
317
318 debug("smsc.soap", 0, "SOAP[%s]: Got HTTP request `%s'",
319 octstr_get_cstr(conn->id), octstr_get_cstr(url));
320
321 if (connect_denied(conndata->allow_ip, ip)) {
322 info(0, "SOAP[%s]: Connection `%s' tried from denied "
323 "host %s, ignored", octstr_get_cstr(conn->id),
324 octstr_get_cstr(url), octstr_get_cstr(ip));
325 http_close_client(client);
326 } else
327 conndata->receive_sms(conn, client, headers, body, cgivars);
328
329 debug("smsc.soap", 0, "SOAP[%s]: Destroying client information",
330 octstr_get_cstr(conn->id));
331 octstr_destroy(url);
332 octstr_destroy(ip);
333 octstr_destroy(body);
334 http_destroy_headers(headers);
335 http_destroy_cgiargs(cgivars);
336 }
337 debug("smsc.soap", 0, "SOAP[%s]: httpsmsc_receiver dying",
338 octstr_get_cstr(conn->id));
339
340 conndata->shutdown = 1;
341 http_close_port(conndata->port);
342
343 /* unblock http_receive_result() if there are no open sends */
344 if (conndata->open_sends == 0)
345 http_caller_signal_shutdown(conndata->http_ref);
346 }
347
348
349 #ifdef DEBUG
dlr_sender(void * arg)350 static void dlr_sender(void *arg)
351 {
352 SMSCConn *conn = arg;
353 ConnData *conndata = conn->data;
354 Msg *msg;
355 struct soap *soap;
356
357 /* Make sure we log into our own log-file if defined */
358 log_thread_to(conn->log_idx);
359
360 /* establish soap context */
361 soap = soap_new1(SOAP_XML_INDENT|SOAP_IO_KEEPALIVE);
362
363 #ifdef WITH_OPENSSL
364 /* setup the SSL context */
365 if (soap_ssl_client_context(soap, SOAP_SSL_NO_AUTHENTICATION,
366 NULL, NULL, NULL, NULL, NULL)) {
367 char buf[1024];
368 Octstr *os;
369
370 soap_sprint_fault(soap, buf, 1024);
371 os = octstr_create(buf);
372 octstr_remove_crlfs(os);
373
374 error(0, "SOAP[%s]: Could not assign gSOAP SSL context:",
375 octstr_get_cstr(conn->id));
376 error(0, "SOAP[%s]: %s",
377 octstr_get_cstr(conn->id), octstr_get_cstr(os));
378
379 octstr_destroy(os);
380 goto done;
381 }
382 #endif
383
384 while ((msg = gwlist_consume(conndata->dlr_queue)) != NULL) {
385 /* first we delay a bit to simulate the SMSC latency */
386 gwthread_sleep(1);
387 soap_send_dlr(soap, conn, msg);
388 msg_destroy(msg);
389 }
390
391 done:
392 /* destroy soap context */
393 soap_destroy(soap);
394 soap_end(soap);
395 soap_free(soap);
396 }
397 #endif
398
399
400 /***********************************************************************
401 * Helper functions
402 */
403
404 #define octstr_cstr(os) \
405 (os ? octstr_get_cstr(os) : NULL)
406
407 #define octstr(os) \
408 (os ? octstr_create(os) : NULL)
409
410
iscrlf(unsigned char c)411 static int iscrlf(unsigned char c)
412 {
413 return c == '\n' || c == '\r';
414 }
415
416
octstr_remove_crlfs(Octstr * ostr)417 static void octstr_remove_crlfs(Octstr *ostr)
418 {
419 int i, end;
420
421 end = octstr_len(ostr);
422
423 for (i = 0; i < end; i++) {
424 if (iscrlf(octstr_get_char(ostr, i)))
425 octstr_set_char(ostr, i, ' ');
426 }
427 }
428
429
gw_free_wrapper(void * data)430 static void gw_free_wrapper(void *data)
431 {
432 gw_free(data);
433 }
434
435
436 /***********************************************************************
437 * gSOAP callbacks for internal XML transport
438 */
439
440 typedef struct gBuffer {
441 SMSCConn *conn;
442 char *buffer;
443 size_t size;
444 size_t rlen, slen;
445 } gBuffer;
446
447
gbuffer_create(SMSCConn * conn,size_t size)448 static gBuffer *gbuffer_create(SMSCConn *conn, size_t size)
449 {
450 gBuffer *buf;
451
452 buf = gw_malloc(sizeof(gBuffer));
453 buf->conn = conn;
454 buf->buffer = gw_malloc(size);
455 buf->size = size;
456 buf->rlen = buf->slen = 0;
457
458 return buf;
459 }
460
461
gbuffer_destroy(gBuffer * buf)462 static void gbuffer_destroy(gBuffer *buf)
463 {
464 if (buf == NULL)
465 return;
466
467 gw_free(buf->buffer);
468 gw_free(buf);
469 }
470
471
472 /*
473 * Callback function of gSOAP internals that sends response bytes
474 * via this callback. We use it to intercept the response to the
475 * own handled buffer.
476 */
mysend(struct soap * soap,const char * s,size_t n)477 static int mysend(struct soap *soap, const char *s, size_t n)
478 {
479 gBuffer *buf = (gBuffer*) soap->user;
480
481 if (buf->slen + n > buf->size)
482 return SOAP_EOF;
483
484 strcpy(buf->buffer + buf->slen, s);
485 buf->slen += n;
486
487 return SOAP_OK;
488 }
489
490
491 /*
492 * Callback function of gSOAP internals to read the HTTP POST
493 * body contents into the gSOAP processing. We inject the input
494 * via our own mapped buffer here.
495 */
myrecv(struct soap * soap,char * s,size_t n)496 static size_t myrecv(struct soap *soap, char *s, size_t n)
497 {
498 gBuffer *buf = (gBuffer*) soap->user;
499
500 strncpy(s, buf->buffer + buf->rlen, n);
501 buf->rlen += n;
502
503 return n;
504 }
505
506
507 /*
508 * Callback function of gSOAP's internal HTTP response headers.
509 * We don't use them here and simply ensure that they are skipped.
510 */
myheader(struct soap * soap,const char * key,const char * value)511 static int myheader(struct soap *soap, const char *key, const char *value)
512 {
513 return SOAP_OK;
514 }
515
516
517 /********************************************************************
518 * SOAP specific operations
519 */
520
soap_send_sms(struct soap * soap,SMSCConn * conn,Msg * sms)521 static void soap_send_sms(struct soap *soap, SMSCConn *conn, Msg *sms)
522 {
523 ConnData *conndata = conn->data;
524 struct pxSmsSend__sendSmsResponse resp;
525 int ret;
526 char tid[UUID_STR_LEN + 1];
527 Octstr *mid = NULL;
528 Octstr *receiver;
529 const char *receiv[1];
530
531 /* request parameters */
532 struct pxSmsSend__sendSms req;
533 struct pxCommon__ChargingInformation charge;
534 struct pxCommon__SimpleReference dlr;
535
536 /* unparse our msg ID */
537 if (DLR_IS_ENABLED_DEVICE(sms->sms.dlr_mask) && !uuid_is_null(sms->sms.id)) {
538 uuid_unparse(sms->sms.id, tid);
539 mid = octstr_create(tid);
540 }
541
542 req.__sizeaddresses = 1;
543 receiver = octstr_format("tel:%s", octstr_get_cstr(sms->sms.receiver));
544 receiv[0] = octstr_get_cstr(receiver);
545 req.addresses = (char**)receiv;
546 req.senderName = octstr_cstr(sms->sms.sender);
547 req.message = octstr_get_cstr(sms->sms.msgdata);
548 req.charging = NULL;
549 req.receiptRequest = NULL;
550
551 /*
552 * If billing identifier is set then we will parse for the
553 * ChargingInformation fields using the following notation:
554 *
555 * binfo = <D><description><D><currency><D><amount><D><code>
556 *
557 * where <D> is the delimiter character, defined as the FIRST
558 * character of the binfo field.
559 */
560 if (sms->sms.binfo) {
561 Octstr *delim = octstr_copy(sms->sms.binfo, 0, 1);
562 List *l = octstr_split(sms->sms.binfo, delim);
563 if (gwlist_len(l) != 5) {
564 error(0, "SOAP[%s]: Billing identifier <%s> has wrong format!",
565 octstr_get_cstr(conn->id), octstr_get_cstr(sms->sms.binfo));
566 } else {
567 charge.description = octstr_get_cstr(gwlist_get(l, 1));
568 charge.currency = octstr_get_cstr(gwlist_get(l, 2));
569 charge.amount = octstr_get_cstr(gwlist_get(l, 3));
570 charge.code = octstr_get_cstr(gwlist_get(l, 3));
571 req.charging = &charge;
572 }
573 gwlist_destroy(l, octstr_destroy_item);
574 octstr_destroy(delim);
575 gwlist_destroy(l, octstr_destroy_item);
576 }
577
578 /*
579 * Define callback for DLR notification.
580 */
581 if (DLR_IS_ENABLED_DEVICE(sms->sms.dlr_mask) && conndata->dlr_url) {
582 dlr.endpoint = octstr_get_cstr(conndata->dlr_url);
583 dlr.correlator = octstr_get_cstr(mid);
584 dlr.interfaceName = NULL;
585 req.receiptRequest = &dlr;
586 }
587
588 /* perform the SOAP call itself */
589 ret = soap_call___px1__sendSms(
590 soap, octstr_get_cstr(conndata->send_url), "",
591 &req, &resp);
592
593 if (ret) {
594 /* HTTP request failed, or any other SOAP fault raised. */
595 char buf[1024];
596 Octstr *os;
597
598 soap_sprint_fault(soap, buf, 1024);
599 os = octstr_create(buf);
600 octstr_remove_crlfs(os);
601
602 error(0, "SOAP[%s]: Sending HTTP request failed:",
603 octstr_get_cstr(conn->id));
604 error(0, "SOAP[%s]: %s",
605 octstr_get_cstr(conn->id), octstr_get_cstr(os));
606
607 /*
608 * TODO: we may consider this also as temporary error,
609 * or even better interpret the SOAP fault detail tags.
610 */
611 bb_smscconn_send_failed(conn, sms, SMSCCONN_FAILED_MALFORMED,
612 octstr_duplicate(os));
613
614 octstr_destroy(os);
615 } else {
616 /*
617 * We got a corresponding SOAP/XML PDU response,
618 * so this is considered a successful transaction.
619 */
620
621 /*
622 * The following code parts can be used to verify
623 * for transaction time and authentication.
624 *
625 * They are NOT used in the ParlayX variants we
626 * support, but we keep them here for reference
627 * to any possible new variant.
628 */
629
630 /*
631 if (soap_wsse_verify_Timestamp(soap)) {
632 error(0, "SOAP[%s]: Server response expired.",
633 octstr_get_cstr(conn->id));
634 goto done;
635 }
636
637 username = soap_wsse_get_Username(soap);
638 if (!username || octstr_str_compare(conndata->username, username) ||
639 soap_wsse_verify_Password(soap, octstr_get_cstr(conndata->password))) {
640 error(0, "SOAP[%s]: Server authentication failed.",
641 octstr_get_cstr(conn->id));
642 goto done;
643 }
644 */
645
646 /*
647 * The sendSmsResponse result is defined to be a message identifier
648 * for the transaction we did, so keep that as foreign ID.
649 */
650 if (resp.result) {
651 octstr_destroy(sms->sms.foreign_id);
652 sms->sms.foreign_id = octstr_create(resp.result);
653 }
654
655 /* add to our own DLR storage */
656 if (DLR_IS_ENABLED_DEVICE(sms->sms.dlr_mask) && mid) {
657 dlr_add(conn->id, mid, sms, 0);
658
659 #ifdef DEBUG
660 /*
661 * Pass duplicate into DLR re-inject queue with the
662 * msg ID that the SMSC gave us.
663 * BEWARE: This works only for internal tests where
664 * the msg ID returned is a UUID string.
665 */
666 {
667 Msg *msg = msg_duplicate(sms);
668 uuid_clear(msg->sms.id);
669 uuid_parse(octstr_get_cstr(mid), msg->sms.id);
670 gwlist_produce(conndata->dlr_queue, msg);
671 }
672 #endif
673 }
674 bb_smscconn_sent(conn, sms, NULL);
675 }
676
677 octstr_destroy(mid);
678 octstr_destroy(receiver);
679 }
680
681
soap_parse_reply(SMSCConn * conn,Msg * msg,int status,List * headers,Octstr * body)682 static void soap_parse_reply(SMSCConn *conn, Msg *msg, int status,
683 List *headers, Octstr *body)
684 {
685 if (status == HTTP_OK || status == HTTP_ACCEPTED) {
686
687 /* add to our own DLR storage */
688 if (DLR_IS_ENABLED_DEVICE(msg->sms.dlr_mask)) {
689 char id[UUID_STR_LEN + 1];
690 Octstr *mid;
691
692 uuid_unparse(msg->sms.id, id);
693 mid = octstr_create(id);
694
695 dlr_add(conn->id, mid, msg, 0);
696
697 octstr_destroy(mid);
698 }
699
700 bb_smscconn_sent(conn, msg, NULL);
701 } else {
702 bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_MALFORMED,
703 octstr_duplicate(body));
704 }
705 }
706
707
soap_receive_sms(SMSCConn * conn,HTTPClient * client,List * headers,Octstr * body,List * cgivars)708 static void soap_receive_sms(SMSCConn *conn, HTTPClient *client,
709 List *headers, Octstr *body, List *cgivars)
710 {
711 List *reply_headers;
712 struct soap *soap;
713 gBuffer *buf;
714 Octstr *response;
715
716 /*
717 * We expect a SOAP/XML element as POST body. If there is no
718 * body, then return an error instantly.
719 */
720 if (octstr_len(body) == 0) {
721 http_send_reply(client, HTTP_BAD_METHOD, NULL, NULL);
722 return;
723 }
724
725 /* dump the SOAP/XML we got */
726 octstr_dump(body, 0);
727
728 /* move the XML into the buffer */
729 buf = gbuffer_create(conn, 32000);
730 octstr_get_many_chars(buf->buffer, body, 0, octstr_len(body));
731
732 /* create gSOAP context and assign the buffer */
733 soap = soap_new();
734 soap->fsend = mysend;
735 soap->frecv = myrecv;
736 soap->fposthdr = myheader;
737 soap->user = buf;
738
739 /* perform the server operation */
740 soap_serve(soap);
741
742 /* move response XML from buffer to octstr */
743 response = octstr_create("");
744 octstr_append_data(response, buf->buffer, buf->slen);
745
746 /* destroy buffer */
747 gbuffer_destroy(buf);
748
749 /* destroy context */
750 soap_destroy(soap);
751 soap_end(soap);
752 soap_free(soap);
753
754 /* send the HTTP response */
755 reply_headers = gwlist_create();
756 http_header_add(reply_headers, "SOAPAction" , "\"\"");
757 http_header_add(reply_headers, "Content-Type", "text/xml;charset=\"utf-8\"");
758 debug("smsc.soap", 0, "SOAP[%s]: Sending HTTP response",
759 octstr_get_cstr(conn->id));
760 octstr_dump(response, 0);
761 http_send_reply(client, HTTP_OK, reply_headers, response);
762
763 octstr_destroy(response);
764 http_destroy_headers(reply_headers);
765 }
766
767
soap_send_sms_cb(SMSCConn * conn,Msg * sms)768 static void soap_send_sms_cb(SMSCConn *conn, Msg *sms)
769 {
770 ConnData *conndata = conn->data;
771
772 gw_prioqueue_produce(conndata->msgs_to_send, sms);
773 }
774
775
776 #ifdef DEBUG
soap_send_dlr(struct soap * soap,SMSCConn * conn,Msg * sms)777 static void soap_send_dlr(struct soap *soap, SMSCConn *conn, Msg *sms)
778 {
779 ConnData *conndata = conn->data;
780 struct pxSmsNotification__notifySmsDeliveryReceiptResponse resp;
781 int ret;
782 char tid[UUID_STR_LEN + 1];
783 Octstr *mid = NULL;
784 Octstr *receiver = octstr_format("tel:%s", sms->sms.receiver);
785
786 /* request parameters */
787 struct pxSmsNotification__notifySmsDeliveryReceipt req;
788 struct pxSms__DeliveryInformation deliveryStatus;
789
790 /* get the message id */
791 if (!uuid_is_null(sms->sms.id)) {
792 uuid_unparse(sms->sms.id, tid);
793 mid = octstr_create(tid);
794 }
795
796 /* apply values from Kannel msg struct */
797 req.correlator = octstr_get_cstr(mid);
798 deliveryStatus.address = octstr_get_cstr(receiver);
799 deliveryStatus.deliveryStatus = pxSms__DeliveryStatus__DeliveredToTerminal;
800 req.deliveryStatus = &deliveryStatus;
801
802 /* no SOAP header set */
803
804 /* perform the SOAP call itself */
805 ret = soap_call___px2__notifySmsDeliveryReceipt(
806 soap, octstr_get_cstr(conndata->send_url), "", &req, &resp);
807
808 if (ret) {
809 /* HTTP request failed */
810 char buf[1024];
811 Octstr *os;
812
813 soap_sprint_fault(soap, buf, 1024);
814 os = octstr_create(buf);
815 octstr_remove_crlfs(os);
816
817 error(0, "SOAP[%s]: Sending DLR HTTP request failed:",
818 octstr_get_cstr(conn->id));
819 error(0, "SOAP[%s]: %s",
820 octstr_get_cstr(conn->id), octstr_get_cstr(os));
821
822 octstr_destroy(os);
823 } else {
824 /* we got a corresponding SOAP/XML response */
825
826 debug("smsc.soap",0,"SOAP[%s] Received DLR HTTP response.",
827 octstr_get_cstr(conn->id));
828 }
829
830 octstr_destroy(receiver);
831 octstr_destroy(mid);
832 }
833 #endif
834
835
836 /********************************************************************
837 * Internal smscconn operations
838 */
839
conndata_destroy(ConnData * conndata)840 static void conndata_destroy(ConnData *conndata)
841 {
842 if (conndata == NULL)
843 return;
844
845 if (conndata->http_ref)
846 http_caller_destroy(conndata->http_ref);
847
848 gwlist_destroy(conndata->sender_threads, (void(*)(void *)) gw_free_wrapper);
849 gw_prioqueue_destroy(conndata->msgs_to_send, msg_destroy_item);
850 #ifdef DEBUG
851 gwlist_destroy(conndata->dlr_queue, msg_destroy_item);
852 #endif
853
854 octstr_destroy(conndata->allow_ip);
855 octstr_destroy(conndata->send_url);
856 octstr_destroy(conndata->dlr_url);
857 octstr_destroy(conndata->username);
858 octstr_destroy(conndata->password);
859 octstr_destroy(conndata->alt_charset);
860
861 gw_free(conndata);
862 }
863
864
httpsmsc_send(SMSCConn * conn,Msg * msg)865 static int httpsmsc_send(SMSCConn *conn, Msg *msg)
866 {
867 ConnData *conndata = conn->data;
868 Msg *sms = msg_duplicate(msg);
869 double delay = 0;
870
871 if (conn->throughput > 0) {
872 delay = 1.0 / conn->throughput;
873 }
874
875 /* convert character encoding if required */
876 if (conndata->alt_charset &&
877 charset_convert(sms->sms.msgdata, DEFAULT_CHARSET,
878 octstr_get_cstr(conndata->alt_charset)) != 0)
879 error(0, "Failed to convert msgdata from charset <%s> to <%s>, will send as is.",
880 DEFAULT_CHARSET, octstr_get_cstr(conndata->alt_charset));
881
882 conndata->open_sends++;
883 conndata->send_sms(conn, sms);
884
885 /* obey throughput speed limit, if any */
886 if (conn->throughput > 0)
887 gwthread_sleep(delay);
888
889 return 0;
890 }
891
892
httpsmsc_queued(SMSCConn * conn)893 static long httpsmsc_queued(SMSCConn *conn)
894 {
895 ConnData *conndata = conn->data;
896
897 return (conndata ? (conn->status != SMSCCONN_DEAD ?
898 conndata->open_sends : 0) : 0);
899 }
900
901
httpsmsc_shutdown(SMSCConn * conn,int finish_sending)902 static int httpsmsc_shutdown(SMSCConn *conn, int finish_sending)
903 {
904 ConnData *conndata = conn->data;
905 long *id;
906
907 debug("httpsmsc_shutdown", 0, "SOAP[%s]: Shutting down",
908 octstr_get_cstr(conn->id));
909 conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
910 conndata->shutdown = 1;
911
912 #ifdef DEBUG
913 /* stop DLR re-injection thread */
914 gwlist_remove_producer(conndata->dlr_queue);
915 gwthread_join(conndata->dlr_thread);
916 #endif
917
918 /* stop receiver thread */
919 http_close_port(conndata->port);
920
921 /* stop all sender threads */
922 gw_prioqueue_remove_producer(conndata->msgs_to_send);
923 while ((id = gwlist_consume(conndata->sender_threads)) != NULL) {
924 gwthread_join(*id);
925 gw_free(id);
926 }
927
928 conn->data = NULL;
929 conndata_destroy(conndata);
930
931 conn->status = SMSCCONN_DEAD;
932 bb_smscconn_killed();
933
934 return 0;
935 }
936
937
smsc_soap_parlayx_create(SMSCConn * conn,CfgGroup * cfg)938 int smsc_soap_parlayx_create(SMSCConn *conn, CfgGroup *cfg)
939 {
940 ConnData *conndata = NULL;
941 Octstr *type;
942 long portno; /* has to be long because of cfg_get_integer */
943 int ssl = 0; /* indicate if SSL-enabled server should be used */
944 int i;
945
946 if (cfg_get_integer(&portno, cfg, octstr_imm("port")) == -1) {
947 error(0, "SOAP[%s]: 'port' invalid in 'smsc = parlayx' group.",
948 octstr_get_cstr(conn->id));
949 return -1;
950 }
951 cfg_get_bool(&ssl, cfg, octstr_imm("use-ssl"));
952 if ((type = cfg_get(cfg, octstr_imm("system-type"))) == NULL) {
953 error(0, "SOAP[%s]: 'system-type' missing in 'smsc = parlayx' group.",
954 octstr_get_cstr(conn->id));
955 octstr_destroy(type);
956 return -1;
957 }
958 conndata = gw_malloc(sizeof(ConnData));
959 conndata->http_ref = NULL;
960 conndata->allow_ip = cfg_get(cfg, octstr_imm("connect-allow-ip"));
961 conndata->send_url = cfg_get(cfg, octstr_imm("send-url"));
962 conndata->dlr_url = cfg_get(cfg, octstr_imm("dlr-url"));
963 conndata->username = cfg_get(cfg, octstr_imm("smsc-username"));
964 conndata->password = cfg_get(cfg, octstr_imm("smsc-password"));
965 conndata->alt_charset = cfg_get(cfg, octstr_imm("alt-charset"));
966 if (cfg_get_integer(&(conndata->senders), cfg, octstr_imm("window")) == -1) {
967 conndata->senders = 1;
968 } else {
969 info(0, "SOAP[%s]: Using %ld sender threads.",
970 octstr_get_cstr(conn->id), conndata->senders);
971 }
972
973 if (conndata->send_url == NULL)
974 panic(0, "SOAP[%s]: Sending not allowed. No 'send-url' specified.",
975 octstr_get_cstr(conn->id));
976
977 if (conndata->dlr_url == NULL)
978 warning(0, "SOAP[%s]: DLR requesting not allowed. No 'dlr-url' specified.",
979 octstr_get_cstr(conn->id));
980
981 if (conndata->username == NULL || conndata->password == NULL) {
982 error(0, "SOAP[%s]: 'username' and 'password' required for smsc",
983 octstr_get_cstr(conn->id));
984 goto error;
985 }
986
987 if (octstr_case_compare(type, octstr_imm("ericsson-sdp")) == 0) {
988 conndata->httpsmsc_sender = httpsmsc_sender_ercisson_sdp;
989 }
990 else if (octstr_case_compare(type, octstr_imm("oneapi-v1")) == 0) {
991 conndata->httpsmsc_sender = httpsmsc_sender_gsma_oneapi;
992 }
993 /*
994 * Add new ParlayX variants here
995 */
996 else {
997 error(0, "SOAP[%s]: system-type '%s' unknown in 'smsc = parlayx' group.",
998 octstr_get_cstr(conn->id), octstr_get_cstr(type));
999 goto error;
1000 }
1001
1002 #ifdef WITH_OPENSSL
1003 /* setup gSOAP SSL internals */
1004 soap_ssl_init();
1005 #endif
1006
1007 /* setup MT queue */
1008 conndata->msgs_to_send = gw_prioqueue_create(sms_priority_compare);
1009
1010 /* assign our SOAP operations */
1011 conndata->receive_sms = soap_receive_sms;
1012 conndata->send_sms = soap_send_sms_cb;
1013 conndata->parse_reply = soap_parse_reply;
1014
1015 conndata->open_sends = 0;
1016 conndata->http_ref = http_caller_create();
1017
1018 conn->data = conndata;
1019 conn->name = octstr_create("SOAP");
1020 conn->status = SMSCCONN_ACTIVE;
1021 conn->connect_time = time(NULL);
1022
1023 conn->shutdown = httpsmsc_shutdown;
1024 conn->queued = httpsmsc_queued;
1025 conn->send_msg = httpsmsc_send;
1026
1027 if (http_open_port_if(portno, ssl, conn->our_host) == -1)
1028 goto error;
1029
1030 conndata->port = portno;
1031 conndata->shutdown = 0;
1032
1033 /* receiver thread */
1034 if ((conndata->receive_thread = gwthread_create(httpsmsc_receiver, conn)) == -1)
1035 goto error;
1036
1037 #ifdef DEBUG
1038 /* DLR re-injection thread */
1039 conndata->dlr_queue = gwlist_create();
1040 gwlist_add_producer(conndata->dlr_queue);
1041 if ((conndata->dlr_thread = gwthread_create(dlr_sender, conn)) == -1)
1042 goto error;
1043 #else
1044 conndata->dlr_queue = NULL;
1045 conndata->dlr_thread = -1;
1046 #endif
1047
1048 /* sender thread(s), keep record of the thread IDs in our
1049 * sender_threads list to ensure we join the right threads
1050 * at the shutdown sequence. */
1051 conndata->sender_threads = gwlist_create();
1052 gw_prioqueue_add_producer(conndata->msgs_to_send);
1053 for (i = 0; i < conndata->senders; i++) {
1054 long *id = gw_malloc(sizeof(long));
1055 if ((*id = gwthread_create(conndata->httpsmsc_sender, conn)) == -1) {
1056 gw_free(id);
1057 goto error;
1058 }
1059 gwlist_produce(conndata->sender_threads, id);
1060 }
1061
1062 info(0, "SOAP[%s]: Initiated and ready", octstr_get_cstr(conn->id));
1063
1064 octstr_destroy(type);
1065 return 0;
1066
1067 error:
1068 error(0, "SOAP[%s]: Failed to create smsc connection",
1069 octstr_get_cstr(conn->id));
1070
1071 conn->data = NULL;
1072 conndata_destroy(conndata);
1073 conn->why_killed = SMSCCONN_KILLED_CANNOT_CONNECT;
1074 conn->status = SMSCCONN_DEAD;
1075
1076 octstr_destroy(type);
1077 return -1;
1078 }
1079
1080
1081 /********************************************************************
1082 * SOAP specific methods
1083 */
1084
1085 /*
1086 * This operation is handled by the server side. We just implement it
1087 * here with hard-coded values to be able to test our own gSOAP sending
1088 * routines to our own HTTP end-point.
1089 */
__px1__sendSms(struct soap * soap,struct pxSmsSend__sendSms * req,struct pxSmsSend__sendSmsResponse * resp)1090 int __px1__sendSms (
1091 /* soap structure hook */
1092 struct soap *soap,
1093 /* request struct */
1094 struct pxSmsSend__sendSms *req,
1095 /* response struct */
1096 struct pxSmsSend__sendSmsResponse *resp)
1097 {
1098 /* positive response */
1099 resp->result = "100";
1100
1101 /* negative response */
1102 /*
1103 resp->result = "300";
1104 */
1105
1106 return SOAP_OK;
1107 }
1108
1109
__px2__notifySmsDeliveryReceipt(struct soap * soap,struct pxSmsNotification__notifySmsDeliveryReceipt * req,struct pxSmsNotification__notifySmsDeliveryReceiptResponse * resp)1110 int __px2__notifySmsDeliveryReceipt (
1111 /* soap structure hook */
1112 struct soap *soap,
1113 /* request struct */
1114 struct pxSmsNotification__notifySmsDeliveryReceipt *req,
1115 /* response struct */
1116 struct pxSmsNotification__notifySmsDeliveryReceiptResponse *resp)
1117 {
1118 gBuffer *buf = soap->user;
1119 ConnData *conndata = buf->conn->data;
1120 Msg *dlrmsg;
1121 Octstr *id, *destination;
1122 int sm_status, state, i;
1123
1124 destination = NULL;
1125 sm_status = -1;
1126
1127 /* get corresponding values from the SOAP/XML request */
1128 id = octstr(req->correlator);
1129 if (req->deliveryStatus) {
1130 destination = octstr(req->deliveryStatus->address);
1131
1132 /* strip 'tel:' from address */
1133 if (destination)
1134 octstr_delete_matching(destination, octstr_imm("tel:"));
1135
1136 sm_status = req->deliveryStatus->deliveryStatus;
1137 }
1138
1139 /* map the DLR state */
1140 state = DLR_NOTHING;
1141 for (i = 0; sm_status != -1 && i < state_table_entries; ++i) {
1142 if (sm_status == state_table[i].state) {
1143 state = state_table[i].dlr_mask;
1144 break;
1145 }
1146 }
1147
1148 /* resolve the DLR from the DLR temp storage */
1149 dlrmsg = dlr_find(buf->conn->id,
1150 id, /* smsc message id */
1151 destination, /* destination */
1152 state, 0);
1153
1154 if (dlrmsg != NULL) {
1155
1156 dlrmsg->sms.sms_type = report_mo;
1157 dlrmsg->sms.account = octstr_duplicate(conndata->username);
1158
1159 /*
1160 * There is no response values returned.
1161 */
1162
1163 /* passing DLR to upper layer */
1164 bb_smscconn_receive(buf->conn, dlrmsg);
1165
1166 } else {
1167 error(0,"SOAP[%s]: got DLR but could not find message or was not interested "
1168 "in it id<%s> dst<%s>, type<%d>",
1169 octstr_get_cstr(buf->conn->id), octstr_get_cstr(id),
1170 octstr_get_cstr(destination), state);
1171 }
1172
1173 octstr_destroy(id);
1174 octstr_destroy(destination);
1175
1176 return SOAP_OK;
1177 }
1178
1179
__px2__notifySmsReception(struct soap * soap,struct pxSmsNotification__notifySmsReception * req,struct pxSmsNotification__notifySmsReceptionResponse * resp)1180 int __px2__notifySmsReception (
1181 /* soap structure hook */
1182 struct soap *soap,
1183 /* request struct */
1184 struct pxSmsNotification__notifySmsReception *req,
1185 /* response struct */
1186 struct pxSmsNotification__notifySmsReceptionResponse *resp)
1187 {
1188 gBuffer *buf = soap->user;
1189 ConnData *conndata = buf->conn->data;
1190 Msg *msg;
1191 int ret;
1192
1193 msg = msg_create(sms);
1194 msg->sms.sms_type = mo;
1195 msg->sms.sender = octstr(req->message->senderAddress);
1196 msg->sms.receiver = octstr(req->message->smsServiceActivationNumber);
1197 msg->sms.msgdata = octstr(req->message->message);
1198 msg->sms.foreign_id = octstr(req->correlator);
1199 msg->sms.smsc_id = octstr_duplicate(buf->conn->id);
1200 msg->sms.time = time(NULL);
1201 msg->sms.account = octstr_duplicate(conndata->username);
1202
1203 ret = bb_smscconn_receive(buf->conn, msg);
1204 if (ret == SMSCCONN_SUCCESS) {
1205 /* no response is set */
1206 } else {
1207 /* no response is set */
1208 }
1209
1210 return SOAP_OK;
1211 }
1212
1213
1214 /********************************************************************
1215 * SOAP functions stubs that are not used.
1216 */
1217
__px1__sendSmsLogo(struct soap * soap,struct pxSmsSend__sendSmsLogo * pxSmsSend__sendSmsLogo,struct pxSmsSend__sendSmsLogoResponse * pxSmsSend__sendSmsLogoResponse)1218 int __px1__sendSmsLogo(
1219 struct soap *soap,
1220 // request parameters:
1221 struct pxSmsSend__sendSmsLogo* pxSmsSend__sendSmsLogo,
1222 // response parameters:
1223 struct pxSmsSend__sendSmsLogoResponse* pxSmsSend__sendSmsLogoResponse
1224 )
1225 {
1226 return SOAP_OK;
1227 }
1228
__px1__sendSmsRingtone(struct soap * soap,struct pxSmsSend__sendSmsRingtone * pxSmsSend__sendSmsRingtone,struct pxSmsSend__sendSmsRingtoneResponse * pxSmsSend__sendSmsRingtoneResponse)1229 int __px1__sendSmsRingtone(
1230 struct soap *soap,
1231 // request parameters:
1232 struct pxSmsSend__sendSmsRingtone* pxSmsSend__sendSmsRingtone,
1233 // response parameters:
1234 struct pxSmsSend__sendSmsRingtoneResponse* pxSmsSend__sendSmsRingtoneResponse
1235 )
1236 {
1237 return SOAP_OK;
1238 }
1239
__px1__getSmsDeliveryStatus(struct soap * soap,struct pxSmsSend__getSmsDeliveryStatus * pxSmsSend__getSmsDeliveryStatus,struct pxSmsSend__getSmsDeliveryStatusResponse * pxSmsSend__getSmsDeliveryStatusResponse)1240 int __px1__getSmsDeliveryStatus(
1241 struct soap *soap,
1242 // request parameters:
1243 struct pxSmsSend__getSmsDeliveryStatus* pxSmsSend__getSmsDeliveryStatus,
1244 // response parameters:
1245 struct pxSmsSend__getSmsDeliveryStatusResponse* pxSmsSend__getSmsDeliveryStatusResponse
1246 )
1247 {
1248 return SOAP_OK;
1249 }
1250
__px3__getReceivedSms(struct soap * soap,struct pxSmsReceive__getReceivedSms * pxSmsReceive__getReceivedSms,struct pxSmsReceive__getReceivedSmsResponse * pxSmsReceive__getReceivedSmsResponse)1251 int __px3__getReceivedSms(
1252 struct soap *soap,
1253 // request parameters:
1254 struct pxSmsReceive__getReceivedSms* pxSmsReceive__getReceivedSms,
1255 // response parameters:
1256 struct pxSmsReceive__getReceivedSmsResponse* pxSmsReceive__getReceivedSmsResponse
1257 )
1258 {
1259 return SOAP_OK;
1260 }
1261
1262 #endif /* HAVE_GSOAP */
1263