Kannel: Open Source WAP and SMS gateway  svn-r5335
smsc_smasi.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  * Implementation of a SM/ASI SMSC module.
59  *
60  * Stipe Tolj <stolj@wapme.de>
61  *
62  * This module connects to a CriticalPath InVoke SMS Center which
63  * uses the SM/ASI protocol.
64  * The module is heavily based on the SMPP module design.
65  *
66  * TODO:
67  * 1. alt_dcs is not used. Instead, msg->sms.mclass is used as the SMASI
68  * Class.
69  * 2. Numbers are not handled correctly, I guess. SMASI allows only(?)
70  * international numbers without leading double zero. How to ensure
71  * this?
72  * 3. Handling of npi and ton correct?
73  * 4. SubmitMulti PDUs not supported.
74  * 5. Replace PDUs not supported.
75  * 6. Status PDUs not supported.
76  * 7. Cancel PDUs not supported.
77  * 8. UserRes PDUs not supported.
78  * 9. Smsc PDUs not supported.
79  * 10. EnquireLink PDUs not supported.
80  */
81 
82 #include "gwlib/gwlib.h"
83 #include "msg.h"
84 #include "smsc_p.h"
85 #include "smasi_pdu.h"
86 #include "smscconn_p.h"
87 #include "bb_smscconn_cb.h"
88 #include "sms.h"
89 #include "dlr.h"
90 
91 #define DEBUG 1
92 
93 #ifndef DEBUG
94 static void dump_pdu(const char *msg, Octstr *id, SMASI_PDU *pdu) { }
95 #else
96 static void dump_pdu(const char *msg, Octstr *id, SMASI_PDU *pdu)
97 {
98  debug("bb.sms.smasi", 0, "SMASI[%s]: %s", octstr_get_cstr(id), msg);
99  smasi_pdu_dump(pdu);
100 }
101 #endif
102 
103 
104 /************************************************************************/
105 /* DEFAULT SETTINGS */
106 /************************************************************************/
107 
108 #define SMASI_DEFAULT_PORT 21500
109 #define SMASI_DEFAULT_PRIORITY 0
110 #define MAX_PENDING_SUBMITS 10
111 #define SMASI_THROTTLING_SLEEP_TIME 15
112 #define SMASI_ENQUIRE_LINK_INTERVAL 30.0
113 
114 
115 /************************************************************************/
116 /* OVERRIDE SETTINGS */
117 /************************************************************************/
118 
119 /* Set these to -1 if no override desired. Values carried in message will
120  * be used then. Or the defaults - if message has no values.
121  *
122  * Otherwise these values will be forced!
123  */
124 
125 #define SMASI_OVERRIDE_SOURCE_TON 1
126 #define SMASI_OVERRIDE_SOURCE_NPI -1
127 #define SMASI_OVERRIDE_DEST_TON -1
128 #define SMASI_OVERRIDE_DEST_NPI -1
129 
130 
131 /************************************************************************/
132 /* SMASI STRUCTURE AND RELATED FUNCTIONS */
133 /************************************************************************/
134 
135 typedef struct {
136  SMSCConn * conn; /* connection to the bearerbox */
137  int thread_handle; /* handle for the SMASI thread */
139  Dict *sent_msgs; /* hash table for send, but yet not confirmed */
140  List *received_msgs; /* list of received, but yet not processed */
141  Counter *message_id_counter; /* sequence number */
142  Octstr *host; /* host or IP of the SMASI server */
143  long port; /* port to connect to */
151  long priority;
153  int quitting;
156 } SMASI;
157 
158 
159 static SMASI *smasi_create(SMSCConn *conn)
160 {
161 
162  SMASI *smasi = gw_malloc(sizeof(SMASI));
163 
164  smasi->conn = conn;
165 
166  smasi->thread_handle = -1;
167  smasi->msgs_to_send = gwlist_create();
168  smasi->sent_msgs = dict_create(16, NULL);
169  smasi->received_msgs = gwlist_create();
171  smasi->host = NULL;
172  smasi->username = NULL;
173  smasi->password = NULL;
174  smasi->source_addr_ton = -1;
175  smasi->source_addr_npi = -1;
176  smasi->dest_addr_ton = -1;
177  smasi->dest_addr_npi = -1;
178  smasi->my_number = NULL;
179  smasi->port = 21500;
180  smasi->quitting = 0;
181  smasi->logged_off = 0;
182  smasi->priority = 0;
183  smasi->throttling_err_time = 0;
184  smasi->enquire_link_interval = 30;
185 
187 
188  return smasi;
189 }
190 
191 
192 static void smasi_destroy(SMASI *smasi)
193 {
194  if (smasi == NULL) return;
195 
197  dict_destroy(smasi->sent_msgs);
200  octstr_destroy(smasi->host);
201  octstr_destroy(smasi->username);
202  octstr_destroy(smasi->password);
203  gw_free(smasi);
204 }
205 
206 
207 
208 /************************************************************************/
209 /* DATA ENCODING */
210 /************************************************************************/
211 
212 /* These values will be initialized on module startup. They contain the
213  * ASCII representation of the chars that need to be escaped in the message
214  * body before transmission. Example: "," (comma) will be represented by
215  * the octet string ":2c".
216  */
217 
218 static Octstr *colon = NULL;
219 static Octstr *assign = NULL;
220 static Octstr *comma = NULL;
221 static Octstr *cr = NULL;
222 static Octstr *lf = NULL;
223 
224 
225 /*
226  * Escapes outgoing message body data by replacing occurrences of "special"
227  * chars inside the octet string.
228  */
229 static void escape_data(Octstr *data)
230 {
231  long pos = 0;
232 
233  /* This one uses a different approach than the encode and decode
234  * functions. Because it is assumed, that only a fraction of the
235  * contained chars have to be escaped.
236  */
237  while (pos < octstr_len(data)) {
238  Octstr * escaped = NULL;
239  int check = octstr_get_char(data, pos);
240 
241  if (check == ':') escaped = colon;
242  else if (check == '=') escaped = assign;
243  else if (check == ',') escaped = comma;
244  else if (check == '\n') escaped = cr;
245  else if (check == '\r') escaped = lf;
246 
247  if (escaped != NULL) {
248  /* If the current char has to be escaped, delete the char from
249  * the source string, replace it with the escape sequence, and
250  * advance position until after the inserted sequence.
251  */
252  octstr_delete(data, pos, 1);
253  octstr_insert(data, escaped, pos);
254  pos += octstr_len(escaped);
255  } else {
256  /* If not escaped, simply skip the current char. */
257  pos++;
258  }
259  }
260 }
261 
262 
263 /*
264  * Unescapes incoming message body data by replacing occurrences of escaped
265  * chars with their original character representation.
266  */
267 static void unescape_data(Octstr *data)
268 {
269  long pos = 0;
270 
271  /* Again, an inplace transformation is used. Because, again, it is
272  * assumed that only a fraction of chars has to be unescaped.
273  */
274  while (pos < octstr_len(data)) {
275  int check = octstr_get_char(data, pos);
276 
277  if (check == ':') {
278  char byte = 0;
279  int msb = octstr_get_char(data, pos + 1);
280  int lsb = octstr_get_char(data, pos + 2);
281 
282  if (msb == '0') msb = 0;
283  else if (msb >= '1' && msb <= '9') msb -= '1' + 1;
284  else msb -= 'a' + 10;
285 
286  if (lsb == '0') lsb = 0;
287  else if (lsb >= '1' && lsb <= '9') lsb -= '1' + 1;
288  else lsb -= 'a' + 10;
289 
290  byte = msb << 4 | lsb;
291 
292  /* Do inplace unescaping. */
293  octstr_delete(data, pos, 3);
294  octstr_insert_data(data, pos, &byte, 1);
295  }
296  pos++;
297  }
298 }
299 
300 
301 /*
302  * Will replace a binary data octet string (inplace) with a SMASI conform
303  * ASCII representation of the data.
304  */
305 static void encode_binary_data(Octstr *data)
306 {
307  Octstr *result = octstr_create("");
308  long pos = 0;
309 
310  while (pos < octstr_len(data)) {
311  int encode = octstr_get_char(data, pos);
312  int msb = (encode & 0xf0) >> 4;
313  int lsb = (encode & 0x0f) >> 0;
314 
315  if (msb == 0) msb = '0';
316  else if (msb < 10) msb = '1' + msb - 1;
317  else msb = 'a' + msb - 10;
318 
319  if (lsb == 0) lsb = '0';
320  else if (lsb < 10) lsb = '1' + lsb - 1;
321  else lsb = 'a' + lsb - 10;
322 
323  octstr_append_char(result, ':');
324  octstr_append_char(result, msb);
325  octstr_append_char(result, lsb);
326 
327  pos++;
328  }
329  /* Replace binary data octet string with ASCII representation. */
330  octstr_delete(data, 0, octstr_len(data));
331  octstr_append(data, result);
332  octstr_destroy(result);
333 }
334 
335 
336 /*
337  * Re-escape SMASI ASCII representation of binary data with the
338  * original binary data octet string.
339  * XXX this may be done by the internal parser routines too.
340  */
341 static void decode_binary_data(Octstr *data)
342 {
343  long pos = 0;
344 
345  while (pos < octstr_len(data)) {
346  int check = octstr_get_char(data, pos);
347 
348  if (check == ':') {
349  Octstr *byte;
350  int msb = octstr_get_char(data, pos + 1);
351  int lsb = octstr_get_char(data, pos + 2);
352 
353  if (msb != -1 && lsb != -1) {
354  byte = octstr_create("");
355  octstr_append_char(byte, msb);
356  octstr_append_char(byte, lsb);
357 
358  if (octstr_hex_to_binary(byte) != -1) {
359  /* Do inplace unescaping. */
360  octstr_delete(data, pos, 3);
361  octstr_insert(data, byte, pos);
362  } else {
363  error(0, "Malformed binary encoded data.");
364  }
365 
366  octstr_destroy(byte);
367  }
368  }
369  pos++;
370  }
371 }
372 
373 
374 /************************************************************************/
375 /* MESSAGE PROCESSING */
376 /************************************************************************/
377 
378 static Octstr *get_ton_npi_value(int override, int message)
379 {
380  if (override != -1) {
381  debug("bb.sms.smasi", 0, "SMASI: Manually forced ton or npi to `%d'",
382  override);
383  return (octstr_format("%ld", override));
384  } else {
385  return (octstr_format("%ld", message));
386  }
387 }
388 
389 
390 /*
391  * Gets the value to be used as source_addr_ton. Will use override values
392  * if configured. Will use values from message otherwise. Or fall back to
393  * defaults if nothing given.
394  */
396 {
397  return get_ton_npi_value(smasi->source_addr_ton,
399 }
400 
401 
402 /*
403  * Gets the value to be used as source_addr_npi. Will use override values
404  * if configured. Will use values from message otherwise. Or fall back to
405  * defaults if nothing given.
406  */
408 {
409  return get_ton_npi_value(smasi->source_addr_npi,
411 }
412 
413 
414 /*
415  * Gets the value to be used as dest_addr_ton. Will use override values
416  * if configured. Will use values from message otherwise. Or fall back to
417  * defaults if nothing given.
418  */
420 {
421  return get_ton_npi_value(smasi->dest_addr_ton,
423 }
424 
425 
426 /*
427  * Gets the value to be used as dest_addr_npi. Will use override values
428  * if configured. Will use values from message otherwise. Or fall back to
429  * defaults if nothing given.
430  */
432 {
433  return get_ton_npi_value(smasi->dest_addr_npi,
435 }
436 
437 
438 /*
439  * Determine the originator (sender number) type based on the number. Will
440  * change the originator number if necessary.
441  */
442 static Octstr *get_originator_type(SMASI *smasi, Octstr *originator)
443 {
444  /* International or alphanumeric sender? */
445  if (octstr_get_char(originator, 0) == '+') {
446  if (!octstr_check_range(originator, 1, 256, gw_isdigit)) {
448  } else {
449  /* Numeric sender address with + in front: The + has to be
450  * removed from this international number.
451  */
452  octstr_delete(originator, 0, 1);
454  }
455  } else if (!octstr_check_range(originator, 0, 256, gw_isdigit)) {
457  }
458 
459  /* Return the default value. */
461 }
462 
463 
464 /*
465  * Creates a SubmitReq PDU from an outgoing message.
466  */
467 static SMASI_PDU *msg_to_pdu(SMASI *smasi, Msg *msg)
468 {
469  SMASI_PDU *pdu = smasi_pdu_create(SubmitReq);
470 
471  pdu->u.SubmitReq.Destination = octstr_duplicate(msg->sms.receiver);
472  pdu->u.SubmitReq.Body = octstr_duplicate(msg->sms.msgdata);
473  pdu->u.SubmitReq.Originator = octstr_duplicate(msg->sms.sender);
474 
475  pdu->u.SubmitReq.OriginatorType =
476  get_originator_type(smasi, pdu->u.SubmitReq.Originator);
477 
478  pdu->u.SubmitReq.Sequence =
480 
481 
482  /* If its a international number starting with +, lets remove the +. */
483  if (octstr_get_char(pdu->u.SubmitReq.Destination, 0) == '+')
484  octstr_delete(pdu->u.SubmitReq.Destination, 0, 1);
485 
486  /* Do ton and npi override - if configured. Use values from message
487  * otherwise.
488  */
489  pdu->u.SubmitReq.OriginatorType = get_source_addr_ton(smasi, msg);
490  pdu->u.SubmitReq.OriginatorPlan = get_source_addr_npi(smasi, msg);
491  pdu->u.SubmitReq.DestinationType = get_dest_addr_ton(smasi, msg);
492  pdu->u.SubmitReq.DestinationPlan = get_dest_addr_npi(smasi, msg);
493 
494  /* Set priority. */
495  if (smasi->priority >= 0 && smasi->priority <= 3) {
496  pdu->u.SubmitReq.MqPriority = octstr_format("%ld", smasi->priority);
497  } else {
498  pdu->u.SubmitReq.MqPriority = octstr_format("%ld", 0);
499  }
500 
501  /* Set encoding. */
502  if (msg->sms.coding != DC_UNDEF) {
503  if (msg->sms.coding == DC_7BIT)
504  pdu->u.SubmitReq.MsEncoding = octstr_create("7bit");
505  else if (msg->sms.coding == DC_8BIT)
506  pdu->u.SubmitReq.MsEncoding = octstr_create("8bit");
507  else if (msg->sms.coding == DC_UCS2)
508  pdu->u.SubmitReq.MsEncoding = octstr_create("16bit");
509 
510  /* Everything else will default to 7bit. */
511  }
512 
513  /* Set messaging class - if within defined parameter range. */
514  if (msg->sms.mclass != MC_UNDEF)
515  pdu->u.SubmitReq.Class = octstr_format("%ld", msg->sms.mclass);
516 
517  /* Set Protocol ID. */
518  pdu->u.SubmitReq.ProtocolID = octstr_format("%ld",
519  (msg->sms.pid == SMS_PARAM_UNDEFINED ? 0 : msg->sms.pid));
520 
521  /* Check if SMS is binary. */
522  if (msg->sms.udhdata && octstr_len(msg->sms.udhdata) > 0) {
523 
524  pdu->u.SubmitReq.UserDataHeader =
525  octstr_duplicate(msg->sms.udhdata);
526 
527  pdu->u.SubmitReq.BodyEncoding =
528  octstr_create("Data");
529 
530  if (pdu->u.SubmitReq.MsEncoding)
531  octstr_destroy(pdu->u.SubmitReq.MsEncoding);
532 
533  pdu->u.SubmitReq.MsEncoding =
534  octstr_create("transparent");
535 
536  /* Encode data. */
537  encode_binary_data(pdu->u.SubmitReq.UserDataHeader);
538  encode_binary_data(pdu->u.SubmitReq.Body);
539  } else {
540 
541  /* Otherwise do data escaping. */
542  escape_data(pdu->u.SubmitReq.Body);
543  }
544 
545  return pdu;
546 }
547 
548 
549 /*
550  * Create a message structure from an incoming DeliverReq PDU.
551  */
552 static Msg *pdu_to_msg(SMASI_PDU *pdu)
553 {
554  Msg *msg = NULL;
555 
556  gw_assert(pdu->type == DeliverReq);
557  gw_assert(pdu->u.DeliverReq.Originator);
558  gw_assert(pdu->u.DeliverReq.Destination);
559  gw_assert(pdu->u.DeliverReq.Body);
560 
561  msg = msg_create(sms);
562 
563  msg->sms.sender = octstr_duplicate(pdu->u.DeliverReq.Originator);
564  msg->sms.receiver = octstr_duplicate(pdu->u.DeliverReq.Destination);
565  msg->sms.msgdata = octstr_duplicate(pdu->u.DeliverReq.Body);
566 
567  /* Read priority. */
568  if (pdu->u.DeliverReq.ProtocolId)
569  if (octstr_parse_long(&msg->sms.pid,
570  pdu->u.DeliverReq.ProtocolId, 0, 10) == -1)
571  msg->sms.pid = SMS_PARAM_UNDEFINED;
572 
573  /* Read Coding. */
574  if (pdu->u.DeliverReq.MsEncoding) {
575 
576  /* Use specified coding. */
577  if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "7bit") == 0)
578  msg->sms.coding = DC_7BIT;
579  else if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "8bit") == 0)
580  msg->sms.coding = DC_8BIT;
581  else if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "UCS2") == 0)
582  msg->sms.coding = DC_UCS2;
583  else if (octstr_str_compare(pdu->u.DeliverReq.MsEncoding, "transparent") == 0)
584  msg->sms.coding = DC_8BIT;
585  } else {
586 
587  /* Determine specified coding according to udhdata presence. */
588  if (pdu->u.DeliverReq.UserDataHeader)
589  msg->sms.coding = DC_8BIT;
590  else
591  msg->sms.coding = DC_7BIT;
592  }
593 
594  /* Unescape (non-binary) or decode (binary) data. */
595  if (msg->sms.coding == DC_8BIT) {
596 
597  decode_binary_data(msg->sms.msgdata);
598  if (pdu->u.DeliverReq.UserDataHeader &&
599  octstr_len(pdu->u.DeliverReq.UserDataHeader) > 0) {
600  msg->sms.udhdata = octstr_duplicate(pdu->u.DeliverReq.UserDataHeader);
601  decode_binary_data(msg->sms.udhdata);
602  }
603 
604  } else {
605  unescape_data(msg->sms.msgdata);
606  }
607 
608  /* Read message class. */
609  if (pdu->u.DeliverReq.Class &&
610  octstr_parse_long(&msg->sms.mclass,
611  pdu->u.DeliverReq.Class, 0, 10) == -1)
612  msg->sms.mclass = MC_UNDEF; /* Set to unspecified. */
613 
614  /* Read protocol ID. */
615  if (pdu->u.DeliverReq.ProtocolId &&
616  octstr_parse_long(&msg->sms.pid,
617  pdu->u.DeliverReq.ProtocolId, 0, 10) == -1)
618  msg->sms.pid = SMS_PARAM_UNDEFINED;
619 
620  return msg;
621 }
622 
623 
624 /************************************************************************/
625 /* PDU HANDLING */
626 /************************************************************************/
627 
628 static void send_logoff(SMASI *smasi, Connection *conn)
629 {
630  SMASI_PDU *pdu = NULL;
631  Octstr *os = NULL;
632 
634 
635  pdu = smasi_pdu_create(LogoffReq);
636  pdu->u.LogoffReq.Reason = octstr_create("Client shutting down");
637  dump_pdu("Sending !LogoffReq:", smasi->conn->id, pdu);
638 
639  os = smasi_pdu_pack(pdu);
640  conn_write(conn, os);
641  octstr_destroy(os);
642  smasi_pdu_destroy(pdu);
643 }
644 
645 
646 static void send_enquire_link(SMASI *smasi, Connection *conn, long *last_sent)
647 {
648  SMASI_PDU *pdu = NULL;
649  Octstr *os = NULL;
650 
651  if (date_universal_now() - *last_sent < smasi->enquire_link_interval)
652  return;
653  *last_sent = date_universal_now();
654 
655  pdu = smasi_pdu_create(EnquireLinkReq);
656  dump_pdu("Sending EnquireLinkReq:", smasi->conn->id, pdu);
657  os = smasi_pdu_pack(pdu);
658  if (os)
659  conn_write(conn, os); /* Write errors checked by caller. */
660  octstr_destroy(os);
661  smasi_pdu_destroy(pdu);
662 }
663 
664 
665 static int send_pdu(Connection *conn, Octstr *id, SMASI_PDU *pdu)
666 {
667  Octstr * os = NULL;
668  int ret = 0;
669 
670  dump_pdu("Sending PDU:", id, pdu);
671  os = smasi_pdu_pack(pdu);
672  if (os) ret = conn_write(conn, os);
673  else ret = -1;
674 
675  octstr_destroy(os);
676  return ret;
677 }
678 
679 
680 /*
681  * Try to read a SMASI PDU from a connection. Return -1 for error (caller
682  * should close the connection), 0 for no PDU ready yet, or 1 for PDU read
683  * and unpacked. Return a pointer to the PDU in `*pdu'.
684  */
685 static int read_pdu(SMASI *smasi, Connection *conn, SMASI_PDU **pdu)
686 {
687  Octstr *os;
688 
689  os = smasi_pdu_read(conn);
690  if (os == NULL) {
691  if (conn_eof(conn) || conn_error(conn))
692  return -1;
693  return 0;
694  }
695 
696  *pdu = smasi_pdu_unpack(os);
697  if (*pdu == NULL) {
698  error(0, "SMASI[%s]: PDU unpacking failed.",
699  octstr_get_cstr(smasi->conn->id));
700  debug("bb.sms.smasi", 0, "SMASI[%s]: Failed PDU follows.",
701  octstr_get_cstr(smasi->conn->id));
702  octstr_dump(os, 0);
703  octstr_destroy(os);
704  return -1;
705  }
706  octstr_destroy(os);
707  return 1;
708 }
709 
710 
711 static void handle_pdu(SMASI *smasi, Connection *conn,
712  SMASI_PDU *pdu, long *pending_submits)
713 {
714  SMASI_PDU *resp = NULL;
715  Msg *msg = NULL;
716  long reason;
717 
718  switch (pdu->type) {
719 
720  case DeliverReq:
721  msg = pdu_to_msg(pdu);
722 
723  msg_dump(msg, 0);
724 
725  if (smasi->my_number && octstr_len(smasi->my_number)) {
726  octstr_destroy(msg->sms.receiver);
727  msg->sms.receiver = octstr_duplicate(smasi->my_number);
728  }
729 
730  time(&msg->sms.time);
731  msg->sms.smsc_id = octstr_duplicate(smasi->conn->id);
732  bb_smscconn_receive(smasi->conn, msg);
733  resp = smasi_pdu_create(DeliverConf);
734 
735  if (pdu->u.DeliverReq.Sequence)
736  resp->u.DeliverConf.Sequence =
737  octstr_duplicate(pdu->u.DeliverReq.Sequence);
738 
739  if (pdu->u.DeliverReq.MsgReference)
740  resp->u.DeliverConf.MsgReference =
741  octstr_duplicate(pdu->u.DeliverReq.MsgReference);
742  break;
743 
744  case SubmitConf:
745  if (pdu->u.SubmitConf.Sequence) {
746  msg = dict_remove(smasi->sent_msgs,
747  pdu->u.SubmitConf.Sequence);
748  } else {
749  msg = NULL;
750  }
751 
752  if (msg == NULL) {
753  warning(0, "SMASI[%s]: SMSC sent SubmitConf for unknown message.",
754  octstr_get_cstr(smasi->conn->id));
755  } else {
756  debug("bb.sms.smasi",0,
757  "SMSC[%s]: SMSC confirmed msg seq <%s> ref <%s>",
758  octstr_get_cstr(smasi->conn->id),
759  octstr_get_cstr(pdu->u.SubmitConf.Sequence),
760  octstr_get_cstr(pdu->u.SubmitConf.MsgReference));
761 
762  bb_smscconn_sent(smasi->conn, msg, NULL);
763 
764  --(*pending_submits);
765  }
766  break;
767 
768  case SubmitRej:
769  if (pdu->u.SubmitRej.Sequence) {
770  msg = dict_remove(smasi->sent_msgs,
771  pdu->u.SubmitRej.Sequence);
772  } else {
773  msg = NULL;
774  }
775 
776  error(0, "SMASI[%s]: SMSC returned error code %s for "
777  "message ref <%s>", octstr_get_cstr(smasi->conn->id),
778  octstr_get_cstr(pdu->u.SubmitRej.RejectCode),
779  octstr_get_cstr(pdu->u.SubmitRej.MsgReference));
780 
781  if (msg == NULL) {
782  warning(0, "SMASI[%s]: SMSC sent SubmitRej for unknown message.",
783  octstr_get_cstr(smasi->conn->id));
784  } else {
785  reason = SMSCCONN_FAILED_REJECTED;
786  bb_smscconn_send_failed(smasi->conn, msg, reason,
787  octstr_create("REJECTED"));
788  --(*pending_submits);
789  }
790  break;
791 
792  case LogonConf:
793  *pending_submits = 0;
794 
795  smasi->conn->status = SMSCCONN_ACTIVE;
796  smasi->conn->connect_time = time(NULL);
797 
798  bb_smscconn_connected(smasi->conn);
799 
800  info(0, "SMASI[%s]: connection to SMSC established.",
801  octstr_get_cstr(smasi->conn->id));
802  break;
803 
804  case LogonRej:
805  if (octstr_len(pdu->u.LogonRej.Reason) > 0) {
806  error(0, "SMASI[%s]: SMSC rejected login with reason <%s>",
807  octstr_get_cstr(smasi->conn->id),
808  octstr_get_cstr(pdu->u.LogonRej.Reason));
809  } else {
810  error(0, "SMASI[%s]: SMSC rejected login without reason",
811  octstr_get_cstr(smasi->conn->id));
812  }
813  break;
814 
815  case LogoffConf:
816  info(0, "SMASI[%s]: SMSC confirmed logoff.",
817  octstr_get_cstr(smasi->conn->id));
818  smasi->logged_off = 1;
819  break;
820 
821  default:
822  warning(0, "SMASI[%s]: Unknown PDU type <%s>, ignored.",
823  octstr_get_cstr(smasi->conn->id), pdu->type_name);
824  break;
825  }
826 
827  if (resp != NULL) {
828  send_pdu(conn, smasi->conn->id, resp);
829  smasi_pdu_destroy(resp);
830  }
831 }
832 
833 
834 /************************************************************************/
835 /* SMASI CONNECTION HANDLING */
836 /************************************************************************/
837 
838 /*
839  * Open transmission connection to SMS center. Return NULL for error,
840  * open connection for OK. Caller must set smasi->conn->status correctly
841  * before calling this.
842  */
844 {
845  Connection *conn = conn_open_tcp_with_port(smasi->host, smasi->port, 0, smasi->conn->our_host);
846 
847  if (conn == NULL) {
848  error(0, "SMASI[%s]: Couldn't connect to server.",
849  octstr_get_cstr(smasi->conn->id));
850  return NULL;
851  } else {
852  SMASI_PDU *logon = smasi_pdu_create(LogonReq);
853 
854  logon->u.LogonReq.Name = octstr_duplicate(smasi->username);
855  logon->u.LogonReq.Password = octstr_duplicate(smasi->password);
856 
858 
859  send_pdu(conn, smasi->conn->id, logon);
860 
861  smasi_pdu_destroy(logon);
862  }
863 
864  return conn;
865 }
866 
867 
868 static void send_messages(SMASI *smasi, Connection *conn,
869  long *pending_submits)
870 {
871  double delay = 0;
872 
873  if (*pending_submits == -1) return;
874 
875  if (smasi->conn->throughput > 0) {
876  delay = 1.0 / smasi->conn->throughput;
877  }
878 
879  while (*pending_submits < MAX_PENDING_SUBMITS) {
880  SMASI_PDU *pdu = NULL;
881  /* Get next message, quit if none to be sent. */
883 
884  if (msg == NULL) break;
885 
886  /* Send PDU, record it as waiting for ack from SMSC. */
887  pdu = msg_to_pdu(smasi, msg);
888 
889  if (pdu->u.SubmitReq.Sequence)
890  dict_put(smasi->sent_msgs, pdu->u.SubmitReq.Sequence, msg);
891 
892  send_pdu(conn, smasi->conn->id, pdu);
893 
894  smasi_pdu_destroy(pdu);
895 
896  /* obey throughput speed limit, if any */
897  if (smasi->conn->throughput > 0)
899 
900  ++(*pending_submits);
901  }
902 }
903 
904 
905 /*
906  * This is the main function for the background thread for doing I/O on
907  * one SMASI connection (the one for transmitting or receiving messages).
908  * It makes the initial connection to the SMASI server and re-connects
909  * if there are I/O errors or other errors that require it.
910  */
911 static void smasi_thread(void *arg)
912 {
913  long pending_submits;
914  SMASI_PDU *pdu;
915  SMASI *smasi;
916  int logoff_already_sent = 0;
917  int ret;
918  Connection *conn;
919  long last_enquire_sent;
920  double timeout;
921 
922  smasi = arg;
923 
924  /* Make sure we log into our own log-file if defined */
925  log_thread_to(smasi->conn->log_idx);
926 
927  while (!smasi->quitting) {
928 
929  conn = open_connection(smasi);
930  if (conn == NULL) {
931  error(0, "SMASI[%s]: Could not connect to SMSC center " \
932  "(retrying in %ld seconds).",
933  octstr_get_cstr(smasi->conn->id), smasi->conn->reconnect_delay);
934 
937  continue;
938  }
939 
940  last_enquire_sent = date_universal_now();
941  pending_submits = -1;
942 
943  for (;;) {
944  timeout = last_enquire_sent + smasi->enquire_link_interval
945  - date_universal_now();
946 
947  /* wait for activity */
948  if (conn_wait(conn, timeout) == -1) {
949  error(0, "SMASI[%s]: I/O error or other error. Re-connecting.",
950  octstr_get_cstr(smasi->conn->id));
951  break;
952  }
953 
954  /* Send logoff request if module is shutting down. */
955  if (smasi->quitting && !logoff_already_sent) {
956  send_logoff(smasi, conn);
957  logoff_already_sent = 1;
958  }
959 
960  /* send an enquire link */
961  send_enquire_link(smasi, conn, &last_enquire_sent);
962 
963  /* Receive incoming PDUs. */
964  while ((ret = read_pdu(smasi, conn, &pdu)) == 1) {
965  /* Deal with the PDU we just got */
966  dump_pdu("Got PDU:", smasi->conn->id, pdu);
967 
968  /* Process the received PDU. */
969  handle_pdu(smasi, conn, pdu, &pending_submits);
970 
971  smasi_pdu_destroy(pdu);
972 
973  /* Bail out if logoff confirmed. */
974  if (smasi->logged_off) break;
975 
976  /* Make sure we send even if we read a lot. */
977  if ((!smasi->throttling_err_time ||
978  ((time(NULL) - smasi->throttling_err_time) >
980  && !(smasi->throttling_err_time = 0))))
981  send_messages(smasi, conn, &pending_submits);
982  }
983 
984  /* Check if connection broken. */
985  if (ret == -1) {
986  error(0, "SMASI[%s]: I/O error or other error. Re-connecting.",
987  octstr_get_cstr(smasi->conn->id));
988  break;
989  }
990 
991  /* Bail out if logoff confirmed. */
992  if (smasi->logged_off) break;
993 
994  if ((!smasi->throttling_err_time ||
995  ((time(NULL) - smasi->throttling_err_time) >
997  && !(smasi->throttling_err_time = 0))))
998  send_messages(smasi, conn, &pending_submits);
999 
1000  }
1001 
1002  conn_destroy(conn);
1003  conn = NULL;
1004  }
1005 }
1006 
1007 
1008 /************************************************************************/
1009 /* SMSCCONN INTERFACE */
1010 /************************************************************************/
1011 
1012 static long queued_cb(SMSCConn *conn)
1013 {
1014  SMASI *smasi = conn->data;
1015 
1016  conn->load = (smasi ? (conn->status != SMSCCONN_DEAD ?
1017  gwlist_len(smasi->msgs_to_send) : 0) : 0);
1018 
1019  return conn->load;
1020 }
1021 
1022 
1023 static int send_msg_cb(SMSCConn *conn, Msg *msg)
1024 {
1025  SMASI *smasi = conn->data;
1026 
1029 
1030  return 0;
1031 }
1032 
1033 
1034 static int shutdown_cb(SMSCConn *conn, int finish_sending)
1035 {
1036  SMASI *smasi = NULL;
1037 
1038  debug("bb.sms.smasi", 0, "Shutting down SMSCConn %s (%s)",
1039  octstr_get_cstr(conn->name), finish_sending ? "slow" : "instant");
1040 
1042 
1043  smasi = conn->data;
1044  smasi->quitting = 1;
1046  gwthread_join(smasi->thread_handle);
1047  smasi_destroy(smasi);
1048 
1049  debug("bb.sms.smasi", 0, "SMSCConn %s shut down.",
1050  octstr_get_cstr(conn->name));
1051  conn->status = SMSCCONN_DEAD;
1053 
1054  /* Clean up. */
1058  octstr_destroy(cr);
1059  octstr_destroy(lf);
1060 
1061  return 0;
1062 }
1063 
1064 
1065 /*
1066  * Configures the SMASI structure according to the configuration.
1067  *
1068  * @return 0 on complete success. -1 if failed due to missing or invalid
1069  * configuration entry.
1070  */
1071 static int init_configuration(SMASI *smasi, CfgGroup *config)
1072 {
1073  /* Read mandatory entries. */
1074  smasi->host = cfg_get(config, octstr_imm("host"));
1075  smasi->username = cfg_get(config, octstr_imm("smsc-username"));
1076  smasi->password = cfg_get(config, octstr_imm("smsc-password"));
1077 
1078  /* Check configuration. */
1079  if (smasi->host == NULL) {
1080  error(0,"SMASI: Configuration file doesn't specify host");
1081  return -1;
1082  }
1083  if (smasi->username == NULL) {
1084  error(0, "SMASI: Configuration file doesn't specify username.");
1085  return -1;
1086  }
1087  if (smasi->password == NULL) {
1088  error(0, "SMASI: Configuration file doesn't specify password.");
1089  return -1;
1090  }
1091 
1092  /* Read optional entries. Set default values if not set. */
1093  smasi->my_number = cfg_get(config, octstr_imm("my-number"));
1094  if (cfg_get_integer(&smasi->port, config, octstr_imm("port")) == -1)
1095  smasi->port = SMASI_DEFAULT_PORT;
1096  if (cfg_get_integer(&smasi->source_addr_ton, config,
1097  octstr_imm("source-addr-ton")) == -1)
1099  if (cfg_get_integer(&smasi->source_addr_npi, config,
1100  octstr_imm("source-addr-npi")) == -1)
1102  if (cfg_get_integer(&smasi->dest_addr_ton, config,
1103  octstr_imm("dest-addr-ton")) == -1)
1105  if (cfg_get_integer(&smasi->dest_addr_npi, config,
1106  octstr_imm("dest-addr-npi")) == -1)
1108  if (cfg_get_integer(&smasi->priority, config,
1109  octstr_imm("priority")) == -1)
1111  if (cfg_get_integer(&smasi->enquire_link_interval, config,
1112  octstr_imm("enquire-link-interval")) == -1)
1114 
1115  /* Configure SMSC connection. */
1116  smasi->conn->data = smasi;
1117  smasi->conn->name = octstr_format("SMASI:%S:%d:%S",
1118  smasi->host, smasi->port, smasi->username);
1119 
1120  smasi->conn->id = cfg_get(config, octstr_imm("smsc-id"));
1121 
1122  if (smasi->conn->id == NULL)
1123  smasi->conn->id = octstr_duplicate(smasi->conn->name);
1124 
1125  return 0;
1126 }
1127 
1128 
1130 {
1131  SMASI *smasi = NULL;
1132 
1133  /* Initialize data encoding subsystem. */
1134  colon = octstr_create(":3a");
1135  assign = octstr_create(":3d");
1136  comma = octstr_create(":2c");
1137  cr = octstr_create(":0a");
1138  lf = octstr_create(":0d");
1139 
1140  /* Create main SMASI structure and initialize it with configuration
1141  * settings.
1142  */
1143  smasi = smasi_create(conn);
1144 
1145  if (init_configuration(smasi, config) != 0)
1146  panic(0, "SMASI SMSC module configuration invalid.");
1147 
1148  conn->status = SMSCCONN_CONNECTING;
1149 
1150  /* Port is always set to a configured value or defaults to 21500.
1151  * Therefore, threads are always started.
1152  */
1153  smasi->thread_handle = gwthread_create(smasi_thread, smasi);
1154 
1155  if (smasi->thread_handle == -1) {
1156  error(0, "SMASI[%s]: Couldn't start SMASI thread.",
1157  octstr_get_cstr(smasi->conn->id));
1158  smasi_destroy(conn->data);
1159  return -1;
1160  }
1161 
1162  /* Setup control function pointers. */
1163  conn->shutdown = shutdown_cb;
1164  conn->queued = queued_cb;
1165  conn->send_msg = send_msg_cb;
1166 
1167  return 0;
1168 }
1169 
Dict * dict_create(long size_hint, void(*destroy_value)(void *))
Definition: dict.c:192
static int send_pdu(Connection *conn, Octstr *id, SMASI_PDU *pdu)
Definition: smsc_smasi.c:665
void smasi_pdu_dump(SMASI_PDU *pdu)
Definition: smasi_pdu.c:280
Octstr * name
Definition: smscconn_p.h:173
void msg_dump(Msg *msg, int level)
Definition: msg.c:152
void error(int err, const char *fmt,...)
Definition: log.c:648
static Octstr * cr
Definition: smsc_smasi.c:221
const char * type_name
Definition: smasi_pdu.h:86
SMSCConn * conn
Definition: smsc_smasi.c:136
void info(int err, const char *fmt,...)
Definition: log.c:672
Dict * sent_msgs
Definition: smsc_smasi.c:139
Msg * msg_duplicate(Msg *msg)
Definition: msg.c:111
void bb_smscconn_connected(SMSCConn *conn)
Definition: bb_smscconn.c:192
static void decode_binary_data(Octstr *data)
Definition: smsc_smasi.c:341
long source_addr_npi
Definition: smsc_smasi.c:148
#define SMASI_OVERRIDE_SOURCE_TON
Definition: smsc_smasi.c:125
#define SMASI_OVERRIDE_SOURCE_NPI
Definition: smsc_smasi.c:126
gw_assert(wtls_machine->packet_to_send !=NULL)
void dict_put(Dict *dict, Octstr *key, void *value)
Definition: dict.c:240
void counter_destroy(Counter *counter)
Definition: counter.c:110
#define SMASI_DEFAULT_PRIORITY
Definition: smsc_smasi.c:109
void bb_smscconn_killed(void)
Definition: bb_smscconn.c:199
int octstr_check_range(Octstr *ostr, long pos, long len, octstr_func_t filter)
Definition: octstr.c:814
void octstr_append(Octstr *ostr1, const Octstr *ostr2)
Definition: octstr.c:1504
void gwlist_produce(List *list, void *item)
Definition: list.c:411
Octstr * id
Definition: smscconn_p.h:174
void gwthread_join(long thread)
static SMASI_PDU * msg_to_pdu(SMASI *smasi, Msg *msg)
Definition: smsc_smasi.c:467
long gwlist_len(List *list)
Definition: list.c:166
void * data
Definition: smscconn_p.h:250
time_t throttling_err_time
Definition: smsc_smasi.c:152
void octstr_append_char(Octstr *ostr, int ch)
Definition: octstr.c:1517
unsigned long type
Definition: smasi_pdu.h:85
int log_idx
Definition: smscconn_p.h:197
#define SMASI_OVERRIDE_DEST_TON
Definition: smsc_smasi.c:127
static int shutdown_cb(SMSCConn *conn, int finish_sending)
Definition: smsc_smasi.c:1034
#define cfg_get(grp, varname)
Definition: cfg.h:86
List * received_msgs
Definition: smsc_smasi.c:140
#define DC_8BIT
Definition: sms.h:111
static Octstr * comma
Definition: smsc_smasi.c:220
int logged_off
Definition: smsc_smasi.c:155
#define msg_create(type)
Definition: msg.h:136
long enquire_link_interval
Definition: smsc_smasi.c:154
static void check(void *arg)
Definition: check_counter.c:76
Octstr * our_host
Definition: smscconn_p.h:192
void octstr_insert_data(Octstr *ostr, long pos, const char *data, long len)
Definition: octstr.c:1461
int conn_eof(Connection *conn)
Definition: conn.c:705
static void escape_data(Octstr *data)
Definition: smsc_smasi.c:229
SMASI_PDU * smasi_pdu_create(unsigned long type)
Definition: smasi_pdu.c:132
static void encode_binary_data(Octstr *data)
Definition: smsc_smasi.c:305
#define octstr_get_cstr(ostr)
Definition: octstr.h:233
union SMASI_PDU::@12 u
unsigned long counter_increase(Counter *counter)
Definition: counter.c:123
#define GSM_ADDR_TON_INTERNATIONAL
Definition: smasi_pdu.h:103
long reconnect_delay
Definition: smscconn_p.h:199
void log_thread_to(int idx)
Definition: log.c:759
Octstr * smasi_pdu_pack(SMASI_PDU *pdu)
Definition: smasi_pdu.c:178
static void send_enquire_link(SMASI *smasi, Connection *conn, long *last_sent)
Definition: smsc_smasi.c:646
Octstr * my_number
Definition: smsc_smasi.c:146
int smsc_smasi_create(SMSCConn *conn, CfgGroup *config)
Definition: smsc_smasi.c:1129
smscconn_killed_t why_killed
Definition: smscconn_p.h:153
static void unescape_data(Octstr *data)
Definition: smsc_smasi.c:267
void msg_destroy_item(void *msg)
Definition: msg.c:147
Octstr * octstr_imm(const char *cstr)
Definition: octstr.c:283
int conn_write(Connection *conn, Octstr *data)
Definition: conn.c:1051
Definition: msg.h:79
void octstr_insert(Octstr *ostr1, const Octstr *ostr2, long pos)
Definition: octstr.c:1303
void * dict_remove(Dict *dict, Octstr *key)
Definition: dict.c:307
Counter * counter_create(void)
Definition: counter.c:94
Octstr * host
Definition: smsc_smasi.c:142
void * gwlist_extract_first(List *list)
Definition: list.c:305
static Octstr * get_originator_type(SMASI *smasi, Octstr *originator)
Definition: smsc_smasi.c:442
static SMASI * smasi_create(SMSCConn *conn)
Definition: smsc_smasi.c:159
SMASI_PDU * smasi_pdu_unpack(Octstr *data_without_len)
Definition: smasi_pdu.c:223
void octstr_delete(Octstr *ostr1, long pos, long len)
Definition: octstr.c:1527
static Octstr * get_source_addr_npi(SMASI *smasi, Msg *msg)
Definition: smsc_smasi.c:407
long bb_smscconn_receive(SMSCConn *conn, Msg *sms)
Definition: bb_smscconn.c:477
void conn_destroy(Connection *conn)
Definition: conn.c:627
time_t connect_time
Definition: smscconn_p.h:155
static void send_messages(SMASI *smasi, Connection *conn, long *pending_submits)
Definition: smsc_smasi.c:868
Definition: dict.c:116
#define octstr_duplicate(ostr)
Definition: octstr.h:187
#define octstr_dump(ostr, level,...)
Definition: octstr.h:564
Octstr * smasi_pdu_read(Connection *conn)
Definition: smasi_pdu.c:299
double throughput
Definition: smscconn_p.h:203
static void smasi_destroy(SMASI *smasi)
Definition: smsc_smasi.c:192
Connection * conn_open_tcp_with_port(Octstr *host, int port, int our_port, Octstr *our_host)
Definition: conn.c:548
int gw_isdigit(int c)
Definition: utils.c:988
#define SMASI_THROTTLING_SLEEP_TIME
Definition: smsc_smasi.c:111
Octstr * password
Definition: smsc_smasi.c:145
static double delay
Definition: mtbatch.c:99
void warning(int err, const char *fmt,...)
Definition: log.c:660
int quitting
Definition: smsc_smasi.c:153
static Octstr * get_ton_npi_value(int override, int message)
Definition: smsc_smasi.c:378
Octstr * octstr_format(const char *fmt,...)
Definition: octstr.c:2464
void octstr_destroy(Octstr *ostr)
Definition: octstr.c:324
#define gwthread_create(func, arg)
Definition: gwthread.h:90
#define octstr_create(cstr)
Definition: octstr.h:125
void gwthread_sleep(double seconds)
#define SMS_PARAM_UNDEFINED
Definition: sms.h:91
#define MAX_PENDING_SUBMITS
Definition: smsc_smasi.c:110
List * msgs_to_send
Definition: smsc_smasi.c:138
static Octstr * colon
Definition: smsc_smasi.c:218
static int init_configuration(SMASI *smasi, CfgGroup *config)
Definition: smsc_smasi.c:1071
#define SMASI_ENQUIRE_LINK_INTERVAL
Definition: smsc_smasi.c:112
long date_universal_now(void)
Definition: date.c:340
static Octstr * get_source_addr_ton(SMASI *smasi, Msg *msg)
Definition: smsc_smasi.c:395
long octstr_len(const Octstr *ostr)
Definition: octstr.c:342
void dict_destroy(Dict *dict)
Definition: dict.c:215
static void send_logoff(SMASI *smasi, Connection *conn)
Definition: smsc_smasi.c:628
#define MC_UNDEF
Definition: sms.h:93
Definition: octstr.c:118
void bb_smscconn_sent(SMSCConn *conn, Msg *sms, Octstr *reply)
Definition: bb_smscconn.c:281
int conn_wait(Connection *conn, double seconds)
Definition: conn.c:904
#define GSM_ADDR_NPI_E164
Definition: smasi_pdu.h:112
int(* shutdown)(SMSCConn *conn, int finish_sending)
Definition: smscconn_p.h:230
static int send_msg_cb(SMSCConn *conn, Msg *msg)
Definition: smsc_smasi.c:1023
static Msg * pdu_to_msg(SMASI_PDU *pdu)
Definition: smsc_smasi.c:552
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
long dest_addr_npi
Definition: smsc_smasi.c:150
#define panic
Definition: log.h:87
long dest_addr_ton
Definition: smsc_smasi.c:149
#define SMASI_DEFAULT_PORT
Definition: smsc_smasi.c:108
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
Octstr * username
Definition: smsc_smasi.c:144
smscconn_status_t status
Definition: smscconn_p.h:151
static void dump_pdu(const char *msg, Octstr *id, SMASI_PDU *pdu)
Definition: smsc_smasi.c:96
int thread_handle
Definition: smsc_smasi.c:137
long octstr_parse_long(long *nump, Octstr *ostr, long pos, int base)
Definition: octstr.c:749
static Octstr * get_dest_addr_npi(SMASI *smasi, Msg *msg)
Definition: smsc_smasi.c:431
#define gwlist_create()
Definition: list.h:136
long(* queued)(SMSCConn *conn)
Definition: smscconn_p.h:241
int(* send_msg)(SMSCConn *conn, Msg *msg)
Definition: smscconn_p.h:236
static Octstr * get_dest_addr_ton(SMASI *smasi, Msg *msg)
Definition: smsc_smasi.c:419
void bb_smscconn_send_failed(SMSCConn *conn, Msg *sms, int reason, Octstr *reply)
Definition: bb_smscconn.c:329
long priority
Definition: smsc_smasi.c:151
int conn_error(Connection *conn)
Definition: conn.c:716
#define DC_UNDEF
Definition: sms.h:109
#define SMASI_OVERRIDE_DEST_NPI
Definition: smsc_smasi.c:128
static int read_pdu(SMASI *smasi, Connection *conn, SMASI_PDU **pdu)
Definition: smsc_smasi.c:685
#define GSM_ADDR_TON_ALPHANUMERIC
Definition: smasi_pdu.h:107
long source_addr_ton
Definition: smsc_smasi.c:147
static void smasi_thread(void *arg)
Definition: smsc_smasi.c:911
void gwlist_add_producer(List *list)
Definition: list.c:383
int octstr_get_char(const Octstr *ostr, long pos)
Definition: octstr.c:406
Definition: list.c:102
static Octstr * lf
Definition: smsc_smasi.c:222
static XMLRPCDocument * msg
Definition: test_xmlrpc.c:86
long port
Definition: smsc_smasi.c:143
static void handle_pdu(SMASI *smasi, Connection *conn, SMASI_PDU *pdu, long *pending_submits)
Definition: smsc_smasi.c:711
void smasi_pdu_destroy(SMASI_PDU *pdu)
Definition: smasi_pdu.c:160
static long queued_cb(SMSCConn *conn)
Definition: smsc_smasi.c:1012
Counter * message_id_counter
Definition: smsc_smasi.c:141
static Octstr * assign
Definition: smsc_smasi.c:219
#define DC_UCS2
Definition: sms.h:112
#define DC_7BIT
Definition: sms.h:110
int load
Definition: smscconn_p.h:152
static Connection * open_connection(SMASI *smasi)
Definition: smsc_smasi.c:843
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.