smsc_smasi.c
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:34k
源码类别:

手机WAP编程

开发平台:

WINDOWS

  1. /*
  2.  * Implementation of a SM/ASI SMSC module.
  3.  *
  4.  * Stipe Tolj <tolj@wapme-systems.de>
  5.  *
  6.  * This module connects to a CriticalPath InVoke SMS Center which
  7.  * uses the SM/ASI protocoll. 
  8.  * The module is heavily based on the SMPP module design.
  9.  *
  10.  * TODO:
  11.  * 1. alt_dcs is not used. Instead, msg->sms.mclass is used as the SMASI
  12.  *    Class.
  13.  * 2. Numbers are not handled correctly, I guess. SMASI allows only(?)
  14.  *    international numbers without leading double zero. How to ensure
  15.  *    this?
  16.  * 3. Handling of npi and ton correct?
  17.  * 4. SubmitMulti PDUs not supported.
  18.  * 5. Replace PDUs not supported.
  19.  * 6. Status PDUs not supported.
  20.  * 7. Cancel PDUs not supported.
  21.  * 8. UserRes PDUs not supported.
  22.  * 9. Smsc PDUs not supported.
  23.  * 10. EnquireLink PDUs not supported.
  24.  */
  25. #include "gwlib/gwlib.h"
  26. #include "msg.h"
  27. #include "smsc_p.h"
  28. #include "smasi_pdu.h"
  29. #include "smscconn_p.h"
  30. #include "bb_smscconn_cb.h"
  31. #include "sms.h"
  32. #include "dlr.h"
  33. #define DEBUG 1
  34. #ifndef DEBUG
  35. static void dump_pdu(const char *msg, Octstr *id, SMASI_PDU *pdu) { }
  36. #else
  37. static void dump_pdu(const char *msg, Octstr *id, SMASI_PDU *pdu) 
  38. {
  39.     debug("bb.sms.smasi", 0, "SMASI[%s]: %s", octstr_get_cstr(id), msg);
  40.     smasi_pdu_dump(pdu);
  41. }
  42. #endif
  43. /************************************************************************/
  44. /* DEFAULT SETTINGS                                                     */
  45. /************************************************************************/
  46. #define SMASI_DEFAULT_PORT          21500
  47. #define SMASI_RECONNECT_DELAY       10.0
  48. #define SMASI_DEFAULT_PRIORITY      0
  49. #define MAX_PENDING_SUBMITS         10
  50. #define SMASI_THROTTLING_SLEEP_TIME 15
  51. #define SMASI_ENQUIRE_LINK_INTERVAL  30.0 
  52. /************************************************************************/
  53. /* OVERRIDE SETTINGS                                                    */
  54. /************************************************************************/
  55. /* Set these to -1 if no override desired. Values carried in message will
  56.  * be used then. Or the defaults - if message has no values.
  57.  * 
  58.  * Otherwise these values will be forced!
  59.  */
  60. #define SMASI_OVERRIDE_SOURCE_TON    1
  61. #define SMASI_OVERRIDE_SOURCE_NPI    -1
  62. #define SMASI_OVERRIDE_DEST_TON      -1
  63. #define SMASI_OVERRIDE_DEST_NPI      -1
  64. /************************************************************************/
  65. /* SMASI STRUCTURE AND RELATED FUNCTIONS                                */
  66. /************************************************************************/
  67. typedef struct {
  68.     SMSCConn * conn;                 /* connection to the bearerbox */
  69.     int thread_handle;               /* handle for the SMASI thread */
  70.     List *msgs_to_send;
  71.     Dict *sent_msgs;                 /* hash table for send, but yet not confirmed */
  72.     List *received_msgs;             /* list of received, but yet not processed */
  73.     Counter *message_id_counter;     /* sequence number */
  74.     Octstr *host;                    /* host or IP of the SMASI server */
  75.     long port;                       /* port to connect to */
  76.     Octstr *username;     
  77.     Octstr * password;
  78.     Octstr * my_number;
  79.     long source_addr_ton;
  80.     long source_addr_npi;
  81.     long dest_addr_ton;
  82.     long dest_addr_npi;
  83.     long reconnect_delay;
  84.     long priority;
  85.     time_t throttling_err_time;
  86.     int quitting;
  87.     long enquire_link_interval;
  88.     int logged_off;
  89. } SMASI;
  90. static SMASI *smasi_create(SMSCConn *conn) 
  91. {
  92.     SMASI *smasi = gw_malloc(sizeof(SMASI));
  93.     smasi->conn = conn;
  94.     smasi->thread_handle = -1;
  95.     smasi->msgs_to_send = list_create();
  96.     smasi->sent_msgs = dict_create(16, NULL);
  97.     smasi->received_msgs = list_create();
  98.     smasi->message_id_counter = counter_create();
  99.     smasi->host = NULL;
  100.     smasi->username = NULL;
  101.     smasi->password = NULL;
  102.     smasi->source_addr_ton = -1;
  103.     smasi->source_addr_npi = -1;
  104.     smasi->dest_addr_ton = -1;
  105.     smasi->dest_addr_npi = -1;
  106.     smasi->my_number = NULL;
  107.     smasi->port = 21500;
  108.     smasi->reconnect_delay = 10;
  109.     smasi->quitting = 0;
  110.     smasi->logged_off = 0;
  111.     smasi->priority = 0;
  112.     smasi->throttling_err_time = 0;
  113.     smasi->enquire_link_interval = 30;
  114.     list_add_producer(smasi->msgs_to_send);
  115.     return smasi;
  116. static void smasi_destroy(SMASI *smasi) 
  117. {
  118.     if (smasi == NULL) return;
  119.     list_destroy(smasi->msgs_to_send, msg_destroy_item);
  120.     dict_destroy(smasi->sent_msgs);
  121.     list_destroy(smasi->received_msgs, msg_destroy_item);
  122.     counter_destroy(smasi->message_id_counter);
  123.     octstr_destroy(smasi->host);
  124.     octstr_destroy(smasi->username);
  125.     octstr_destroy(smasi->password);
  126.     gw_free(smasi);
  127. /************************************************************************/
  128. /* DATA ENCODING                                                        */
  129. /************************************************************************/
  130. /* These values will be initialized on module startup. They contain the
  131.  * ASCII representation of the chars that need to be escaped in the message
  132.  * body before transmission. Example: "," (comma) will be represented by
  133.  * the octet string ":2c".
  134.  */
  135. static Octstr *colon = NULL;
  136. static Octstr *assign = NULL;
  137. static Octstr *comma = NULL;
  138. static Octstr *cr = NULL;
  139. static Octstr *lf = NULL;
  140. /*
  141.  * Escapes outgoing message body data by replacing occurrences of "special"
  142.  * chars inside the octet string.
  143.  */
  144. static void escape_data(Octstr *data) 
  145. {
  146.     long pos = 0;
  147.     /* This one uses a different approach than the encode and decode
  148.      * functions. Because it is assumed, that only a fraction of the
  149.      * contained chars have to be escaped.
  150.      */
  151.     while (pos < octstr_len(data)) {
  152.         Octstr * escaped = NULL;
  153.         int check = octstr_get_char(data, pos);
  154.         if (check == ':') escaped = colon;
  155.         else if (check == '=') escaped = assign;
  156.         else if (check == ',') escaped = comma;
  157.         else if (check == 'n') escaped = cr;
  158.         else if (check == 'r') escaped = lf;
  159.         if (escaped != NULL) {
  160.             /* If the current char has to be escaped, delete the char from
  161.              * the source string, replace it with the escape sequence, and
  162.              * advance position until after the inserted sequence.
  163.              */
  164.             octstr_delete(data, pos, 1);
  165.             octstr_insert(data, escaped, pos);
  166.             pos += octstr_len(escaped);
  167.         } else {
  168.             /* If not escaped, simply skip the current char. */
  169.             pos++;
  170.         } 
  171.     } 
  172. /*
  173.  * Unescapes incoming message body data by replacing occurrences of escaped
  174.  * chars with their original character representation.
  175.  */
  176. static void unescape_data(Octstr *data) 
  177. {
  178.     long pos = 0;
  179.     /* Again, an inplace transformation is used. Because, again, it is
  180.      * assumed that only a fraction of chars has to be unescaped.
  181.      */
  182.     while (pos < octstr_len(data)) {
  183.         int check = octstr_get_char(data, pos);
  184.         if (check == ':') {
  185.             char byte = 0;
  186.             int msb = octstr_get_char(data, pos + 1);
  187.             int lsb = octstr_get_char(data, pos + 2);
  188.             if (msb == '0') msb = 0;
  189.             else if (msb >= '1' && msb <= '9') msb -= '1' + 1;
  190.             else msb -= 'a' + 10;
  191.             if (lsb == '0') lsb = 0;
  192.             else if (lsb >= '1' && lsb <= '9') lsb -= '1' + 1;
  193.             else lsb -= 'a' + 10;
  194.             byte = msb << 4 | lsb;
  195.             /* Do inplace unescaping. */
  196.             octstr_delete(data, pos, 3);
  197.             octstr_insert_data(data, pos, &byte, 1);
  198.         } 
  199.         pos++;
  200.     } 
  201. }
  202. /*
  203.  * Will replace a binary data octet string (inplace) with a SMASI conform
  204.  * ASCII representation of the data.
  205.  */
  206. static void encode_binary_data(Octstr *data) 
  207. {
  208.     Octstr *result = octstr_create("");
  209.     long pos = 0;
  210.     while (pos < octstr_len(data)) {
  211.         int encode = octstr_get_char(data, pos);
  212.         int msb = (encode & 0xf0) >> 4;
  213.         int lsb = (encode & 0x0f) >> 0;
  214.         if (msb == 0) msb = '0';
  215.         else if (msb < 10) msb = '1' + msb - 1;
  216.         else msb = 'a' + msb - 10;
  217.         if (lsb == 0) lsb = '0';
  218.         else if (lsb < 10) lsb = '1' + lsb - 1;
  219.         else lsb = 'a' + lsb - 10;
  220.         octstr_append_char(result, ':');
  221.         octstr_append_char(result, msb);
  222.         octstr_append_char(result, lsb);
  223.         pos++;
  224.     } 
  225.     /* Replace binary data octet string with ASCII representation. */
  226.     octstr_delete(data, 0, octstr_len(data));
  227.     octstr_append(data, result);
  228.     octstr_destroy(result);
  229. }
  230. /*
  231.  * Replaces a SMASI conform ASCII representation of binary data with the
  232.  * original binary data octet string. Will abort data decoding if the ASCII
  233.  * representation is invalid.
  234.  */
  235. static void decode_binary_data(Octstr *data) 
  236. {
  237.     long pos = 0;
  238.     Octstr * result = octstr_create("");
  239.     for (pos = 0; pos < octstr_len(data); pos += 3) {
  240.         int check = octstr_get_char(data, pos);
  241.         if (check != ':') {
  242.             warning(0, "Malformed binary encoded data.");
  243.             return;
  244.         } else {
  245.             int byte = 0;
  246.             int msb = octstr_get_char(data, pos + 1);
  247.             int lsb = octstr_get_char(data, pos + 2);
  248.             if (msb == '0') msb = 0;
  249.             else if (msb >= '1' && msb <= '9') msb = msb - 48;
  250.             else msb = msb - 'a' + 10;
  251.             if (lsb == '0') lsb = 0;
  252.             else if (lsb >= '1' && lsb <= '9') lsb = lsb - 48;
  253.             else lsb = lsb - 'a' + 10;
  254.             byte = msb << 4 | lsb;
  255.             octstr_append_char(result, byte);
  256.         } 
  257.     } 
  258.     /* Replace ASCII representation with binary data octet string. */
  259.     octstr_delete(data, 0, octstr_len(data));
  260.     octstr_append(data, result);
  261.     octstr_destroy(result);
  262. }
  263. /************************************************************************/
  264. /* MESSAGE PROCESSING                                                   */
  265. /************************************************************************/
  266. static Octstr *get_ton_npi_value(int override, int message) 
  267. {
  268.     if(override != -1) {
  269.         debug("bb.sms.smasi", 0, "SMASI: Manually forced source addr ton = %d", 
  270.               override);
  271.         return(octstr_format("%ld", override));
  272.     } else {
  273.         return(octstr_format("%ld", message));
  274.     }
  275. }
  276. /*
  277.  * Gets the value to be used as source_addr_ton. Will use override values
  278.  * if configured. Will use values from message otherwise. Or fall back to
  279.  * defaults if nothing given.
  280.  */
  281. static Octstr *get_source_addr_ton(SMASI *smasi, Msg *msg) 
  282. {
  283.     return get_ton_npi_value(smasi->source_addr_ton, 
  284.                              GSM_ADDR_TON_INTERNATIONAL);
  285. }
  286. /*
  287.  * Gets the value to be used as source_addr_npi. Will use override values
  288.  * if configured. Will use values from message otherwise. Or fall back to
  289.  * defaults if nothing given.
  290.  */
  291. static Octstr *get_source_addr_npi(SMASI *smasi, Msg *msg) 
  292. {
  293.     return get_ton_npi_value(smasi->source_addr_npi, 
  294.                              GSM_ADDR_NPI_E164);
  295. }
  296. /*
  297.  * Gets the value to be used as dest_addr_ton. Will use override values
  298.  * if configured. Will use values from message otherwise. Or fall back to
  299.  * defaults if nothing given.
  300.  */
  301. static Octstr *get_dest_addr_ton(SMASI *smasi, Msg *msg) 
  302. {
  303.     return get_ton_npi_value(smasi->dest_addr_ton, 
  304.                              GSM_ADDR_TON_INTERNATIONAL);
  305. }
  306. /*
  307.  * Gets the value to be used as dest_addr_npi. Will use override values
  308.  * if configured. Will use values from message otherwise. Or fall back to
  309.  * defaults if nothing given.
  310.  */
  311. static Octstr *get_dest_addr_npi(SMASI *smasi, Msg *msg) 
  312. {
  313.     return get_ton_npi_value(smasi->dest_addr_npi, 
  314.                              GSM_ADDR_NPI_E164);
  315. }
  316. /*
  317.  * Determine the originator (sender number) type based on the number. Will
  318.  * change the originator number if necessary.
  319.  */
  320. static Octstr *get_originator_type(SMASI *smasi, Octstr *originator) 
  321. {
  322.     /* International or alphanumeric sender? */
  323.     if (octstr_get_char(originator, 0) == '+') {
  324.         if (!octstr_check_range(originator, 1, 256, gw_isdigit)) {
  325.             return octstr_format("%ld", GSM_ADDR_TON_ALPHANUMERIC);
  326.         } else {
  327.            /* Numeric sender address with + in front: The + has to be
  328.             * removed from this international number.
  329.             */
  330.            octstr_delete(originator, 0, 1);
  331.            return octstr_format("%ld", GSM_ADDR_TON_INTERNATIONAL);
  332.         }
  333.     } else if (!octstr_check_range(originator, 0, 256, gw_isdigit)) {
  334.        return octstr_format("%ld", GSM_ADDR_TON_ALPHANUMERIC);
  335.     }
  336.     /* Return the default value. */
  337.     return octstr_format("%ld", GSM_ADDR_TON_INTERNATIONAL);
  338. }
  339. /*
  340.  * Creates a SubmitReq PDU from an outgoing message.
  341.  */
  342. static SMASI_PDU *msg_to_pdu(SMASI *smasi, Msg *msg) 
  343. {
  344.     SMASI_PDU *pdu = smasi_pdu_create(SubmitReq);
  345.     pdu->u.SubmitReq.Destination = octstr_duplicate(msg->sms.receiver);
  346.     pdu->u.SubmitReq.Body = octstr_duplicate(msg->sms.msgdata);
  347.     pdu->u.SubmitReq.Originator = octstr_duplicate(msg->sms.sender);
  348.     pdu->u.SubmitReq.OriginatorType = 
  349.         get_originator_type(smasi, pdu->u.SubmitReq.Originator);
  350.     pdu->u.SubmitReq.Sequence = 
  351.         octstr_format("%ld", counter_increase(smasi->message_id_counter));
  352.     /* If its a international number starting with +, lets remove the +. */
  353.     if (octstr_get_char(pdu->u.SubmitReq.Destination, 0) == '+')
  354.         octstr_delete(pdu->u.SubmitReq.Destination, 0,1);
  355.     /* Do ton and npi override - if configured. Use values from message
  356.      * otherwise.
  357.      */
  358.     pdu->u.SubmitReq.OriginatorType = get_source_addr_ton(smasi, msg);
  359.     pdu->u.SubmitReq.OriginatorPlan = get_source_addr_npi(smasi, msg);
  360.     pdu->u.SubmitReq.DestinationType = get_dest_addr_ton(smasi, msg);
  361.     pdu->u.SubmitReq.DestinationPlan = get_dest_addr_npi(smasi, msg);
  362.        
  363.     /* Set priority. */
  364.     if (smasi->priority >= 0 && smasi->priority <= 3) {
  365.         pdu->u.SubmitReq.MqPriority = octstr_format("%ld", smasi->priority);
  366.     } else {
  367.         pdu->u.SubmitReq.MqPriority = octstr_format("%ld", 0);
  368.     }
  369.     /* Set encoding. */
  370.     if (msg->sms.coding != 0) {
  371.         if (msg->sms.coding == 1)
  372.             pdu->u.SubmitReq.MsEncoding = octstr_create("7bit");
  373.         else if (msg->sms.coding == 2)
  374.             pdu->u.SubmitReq.MsEncoding = octstr_create("8bit");
  375.         else if (msg->sms.coding == 2)
  376.             pdu->u.SubmitReq.MsEncoding = octstr_create("16bit");
  377.         /* Everything else will default to 7bit. */
  378.     }
  379.     /* Set messaging class - if within defined parameter range. */
  380.     if (msg->sms.mclass >= 0 && msg->sms.mclass <= 4)
  381.         pdu->u.SubmitReq.Class = octstr_format("%ld", (msg->sms.mclass - 1));
  382.     /* Set Protocol ID. */
  383.     pdu->u.SubmitReq.ProtocolID = octstr_format("%ld", msg->sms.pid);
  384.     /* Check if SMS is binary. */
  385.     if (msg->sms.udhdata && octstr_len(msg->sms.udhdata) > 0) {
  386.         
  387.         pdu->u.SubmitReq.UserDataHeader =
  388.           octstr_duplicate(msg->sms.udhdata);
  389.         pdu->u.SubmitReq.BodyEncoding =
  390.           octstr_create("Data");
  391.         if (pdu->u.SubmitReq.MsEncoding)
  392.           octstr_destroy(pdu->u.SubmitReq.MsEncoding);
  393.         pdu->u.SubmitReq.MsEncoding =
  394.           octstr_create("transparent");
  395.         /* Encode data. */
  396.         encode_binary_data(pdu->u.SubmitReq.UserDataHeader);
  397.         encode_binary_data(pdu->u.SubmitReq.Body);
  398.     } else {
  399.         /* Otherwise do data escaping. */
  400.         escape_data(pdu->u.SubmitReq.Body);
  401.     } 
  402.     return pdu;
  403. /*
  404.  * Create a message structure from an incoming DeliverReq PDU.
  405.  */
  406. static Msg *pdu_to_msg(SMASI_PDU *pdu) 
  407. {
  408.     Msg *msg = NULL;
  409.     gw_assert(pdu->type == DeliverReq);
  410.     gw_assert(pdu->u.DeliverReq.Originator);
  411.     gw_assert(pdu->u.DeliverReq.Destination);
  412.     gw_assert(pdu->u.DeliverReq.Body);
  413.     msg = msg_create(sms);;
  414.     msg->sms.sender = octstr_duplicate(pdu->u.DeliverReq.Originator);
  415.     msg->sms.receiver = octstr_duplicate(pdu->u.DeliverReq.Destination);
  416.     msg->sms.msgdata = octstr_duplicate(pdu->u.DeliverReq.Body);
  417.  
  418.     /* Unescape (non-binary) or decode (binary) data. */
  419.     if (pdu->u.DeliverReq.UserDataHeader &&
  420.         octstr_len(pdu->u.DeliverReq.UserDataHeader) > 0) {
  421.         msg->sms.udhdata = octstr_duplicate(pdu->u.DeliverReq.UserDataHeader);
  422.         decode_binary_data(msg->sms.msgdata);
  423.         decode_binary_data(msg->sms.udhdata);
  424.     } else {
  425.         unescape_data(msg->sms.msgdata);
  426.     } 
  427.     /* Read priority. */
  428.     if (pdu->u.DeliverReq.ProtocolId)
  429.         if (octstr_parse_long(&msg->sms.pid, 
  430.                               pdu->u.DeliverReq.ProtocolId, 0, 10) == -1)
  431.             msg->sms.pid = 0;
  432.     /* Read Coding. */
  433.     if (pdu->u.SubmitReq.MsEncoding) {
  434.         /* Use specified coding. */
  435.         if (octstr_str_compare(pdu->u.SubmitReq.MsEncoding, "7bit") == 0)
  436.             msg->sms.coding = 1;
  437.         else if (octstr_str_compare(pdu->u.SubmitReq.MsEncoding, "8bit") == 0)
  438.             msg->sms.coding = 2;
  439.         else if (octstr_str_compare(pdu->u.SubmitReq.MsEncoding, "UCS2") == 0)
  440.             msg->sms.coding = 3;
  441.         else if (octstr_str_compare(pdu->u.SubmitReq.MsEncoding, "transparent") == 0)
  442.             msg->sms.coding = 2;
  443.     } else {
  444.         /* Determine specified coding according to udhdata presence. */
  445.         if (pdu->u.SubmitReq.UserDataHeader)
  446.             msg->sms.coding = 2;
  447.         else
  448.             msg->sms.coding = 1;
  449.     }
  450.     /* Read message class. */
  451.     if (pdu->u.SubmitReq.Class) {
  452.         if (octstr_parse_long(&msg->sms.mclass, 
  453.                               pdu->u.SubmitReq.Class, 0, 10) == -1)
  454.             msg->sms.mclass = 0;    /* Set to unspecified. */
  455.         else
  456.             msg->sms.mclass++;      /* Correct value mapping. */
  457.     }
  458.     /* Read protocol ID. */
  459.     if (pdu->u.SubmitReq.ProtocolID)
  460.         if (octstr_parse_long(&msg->sms.pid, 
  461.                               pdu->u.SubmitReq.ProtocolID, 0, 10) == -1)
  462.             msg->sms.pid = 0;
  463.     return msg;
  464. }
  465.  
  466. /************************************************************************/
  467. /* PDU HANDLING                                                         */
  468. /************************************************************************/
  469. static void send_logoff(SMASI *smasi, Connection *conn) 
  470. {
  471.     SMASI_PDU *pdu = NULL;
  472.     Octstr *os = NULL;
  473.     counter_increase(smasi->message_id_counter);
  474.     pdu = smasi_pdu_create(LogoffReq);
  475.     pdu->u.LogoffReq.Reason = octstr_create("Client shutting down");
  476.     dump_pdu("Sending !LogoffReq:", smasi->conn->id, pdu); 
  477.     os = smasi_pdu_pack(pdu);
  478.     conn_write(conn, os);
  479.     octstr_destroy(os);
  480.     smasi_pdu_destroy(pdu);
  481. }
  482.  
  483. static void send_enquire_link(SMASI *smasi, Connection *conn, long *last_sent) 
  484.     SMASI_PDU *pdu = NULL; 
  485.     Octstr *os = NULL; 
  486.  
  487.     if (date_universal_now() - *last_sent < smasi->enquire_link_interval)
  488.         return; 
  489.     *last_sent = date_universal_now(); 
  490.  
  491.     pdu = smasi_pdu_create(EnquireLinkReq);
  492.     dump_pdu("Sending EnquireLinkReq:", smasi->conn->id, pdu); 
  493.     os = smasi_pdu_pack(pdu); 
  494.     if (os)
  495.    conn_write(conn, os); /* Write errors checked by caller. */ 
  496.     octstr_destroy(os); 
  497.     smasi_pdu_destroy(pdu); 
  498. static int send_pdu(Connection *conn, Octstr *id, SMASI_PDU *pdu)
  499. {
  500.     Octstr * os = NULL;
  501.     int ret = 0;
  502.     dump_pdu("Sending PDU:", id, pdu); 
  503.     os = smasi_pdu_pack(pdu);
  504.     if (os) ret = conn_write(conn, os);
  505.     else ret = -1;
  506.     octstr_destroy(os);
  507.     return ret;
  508. }
  509.  
  510. /*
  511.  * Try to read a SMASI PDU from a connection. Return -1 for error (caller
  512.  * should close the connection), 0 for no PDU ready yet, or 1 for PDU read
  513.  * and unpacked. Return a pointer to the PDU in `*pdu'.
  514.  */
  515. static int read_pdu(SMASI *smasi, Connection *conn, SMASI_PDU **pdu) 
  516. {
  517.     Octstr *os;
  518.     
  519.     os = smasi_pdu_read(conn);
  520.     if (os == NULL) {
  521.         if (conn_eof(conn) || conn_read_error(conn)) 
  522.             return -1;
  523.         return 0;
  524.     }
  525.     *pdu = smasi_pdu_unpack(os);
  526.     if (*pdu == NULL) {
  527.         error(0, "SMASI[%s]: PDU unpacking failed.",
  528.               octstr_get_cstr(smasi->conn->id));
  529.         debug("bb.sms.smasi", 0, "SMASI[%s]: Failed PDU follows.",
  530.               octstr_get_cstr(smasi->conn->id));
  531.         octstr_dump(os, 0);
  532.         octstr_destroy(os);
  533.         return -1;
  534.     }
  535.     octstr_destroy(os);
  536.     return 1;
  537. }
  538. static void handle_pdu(SMASI *smasi, Connection *conn, 
  539.                        SMASI_PDU *pdu, long *pending_submits) 
  540. {
  541.     SMASI_PDU *resp = NULL;
  542.     Msg *msg = NULL;
  543.     long reason;
  544.     switch (pdu->type) {
  545.         case DeliverReq:
  546.             msg = pdu_to_msg(pdu);
  547.             if (smasi->my_number && octstr_len(smasi->my_number)) {
  548.                 octstr_destroy(msg->sms.receiver);
  549.                 msg->sms.receiver = octstr_duplicate(smasi->my_number);
  550.             }
  551.             time(&msg->sms.time);
  552.             msg->sms.smsc_id = octstr_duplicate(smasi->conn->id);
  553.             bb_smscconn_receive(smasi->conn, msg);
  554.             resp = smasi_pdu_create(DeliverConf);
  555.             if (pdu->u.DeliverReq.Sequence)
  556.                 resp->u.DeliverConf.Sequence =
  557.                   octstr_duplicate(pdu->u.DeliverReq.Sequence);
  558.             if (pdu->u.DeliverReq.MsgReference)
  559.                 resp->u.DeliverConf.MsgReference =
  560.                   octstr_duplicate(pdu->u.DeliverReq.MsgReference);
  561.             break;
  562.         case SubmitConf:
  563.             if (pdu->u.SubmitConf.Sequence) {
  564.                 msg = dict_remove(smasi->sent_msgs, 
  565.                                   pdu->u.SubmitConf.Sequence);
  566.             } else {
  567.                 msg = NULL;
  568.             }
  569.             if (msg == NULL) {
  570.                 warning(0, "SMASI[%s]: SMSC sent SubmitConf for unknown message.",
  571.                         octstr_get_cstr(smasi->conn->id));
  572.             } else {
  573.                 debug("bb.sms.smasi",0,
  574.                       "SMSC[%s]: SMSC confirmed msg seq <%s> ref <%s>",
  575.                        octstr_get_cstr(smasi->conn->id),
  576.                        octstr_get_cstr(pdu->u.SubmitConf.Sequence),
  577.                        octstr_get_cstr(pdu->u.SubmitConf.MsgReference));
  578.                 bb_smscconn_sent(smasi->conn, msg);
  579.                 --(*pending_submits);
  580.             }
  581.             break;
  582.         case SubmitRej:
  583.             if (pdu->u.SubmitRej.Sequence) {
  584.                 msg = dict_remove(smasi->sent_msgs, 
  585.                                   pdu->u.SubmitRej.Sequence);
  586.             } else {
  587.                 msg = NULL;
  588.             }
  589.             error(0, "SMASI[%s]: SMSC returned error code %s for "
  590.                   "message ref <%s>", octstr_get_cstr(smasi->conn->id),
  591.                   octstr_get_cstr(pdu->u.SubmitRej.RejectCode),
  592.                   octstr_get_cstr(pdu->u.SubmitRej.MsgReference));
  593.             if (msg == NULL) {
  594.                warning(0, "SMASI[%s]: SMSC sent SubmitRej for unknown message.",
  595.                        octstr_get_cstr(smasi->conn->id));
  596.             } else {
  597.                 reason = SMSCCONN_FAILED_REJECTED;
  598.                 bb_smscconn_send_failed(smasi->conn, msg, reason);
  599.                 --(*pending_submits);
  600.             }
  601.             break;
  602.         case LogonConf:
  603.             *pending_submits = 0;
  604.             smasi->conn->status = SMSCCONN_ACTIVE;
  605.             smasi->conn->connect_time = time(NULL);
  606.             bb_smscconn_connected(smasi->conn);
  607.             info(0, "SMASI[%s]: connection to SMSC established.",
  608.                  octstr_get_cstr(smasi->conn->id));
  609.             break;
  610.         case LogonRej:
  611.             if (octstr_len(pdu->u.LogonRej.Reason) > 0) {
  612.                 error(0, "SMASI[%s]: SMSC rejected login with reason <%s>",
  613.                       octstr_get_cstr(smasi->conn->id),
  614.                       octstr_get_cstr(pdu->u.LogonRej.Reason));
  615.             } else {
  616.                 error(0, "SMASI[%s]: SMSC rejected login without reason",
  617.                       octstr_get_cstr(smasi->conn->id));
  618.             }
  619.             break;
  620.         case LogoffConf:
  621.             info(0, "SMASI[%s]: SMSC confirmed logoff.",
  622.                  octstr_get_cstr(smasi->conn->id));
  623.             smasi->logged_off = 1;
  624.             break;
  625.         default:
  626.             warning(0, "SMASI[%s]: Unknown PDU type <%s>, ignored.",
  627.                     octstr_get_cstr(smasi->conn->id), pdu->type_name);
  628.             break;
  629.     }
  630.     if (resp != NULL) {
  631.         send_pdu(conn, smasi->conn->id, resp);
  632.         smasi_pdu_destroy(resp);
  633.     }
  634. }
  635. /************************************************************************/
  636. /* SMASI CONNECTION HANDLING                                            */
  637. /************************************************************************/
  638. /*
  639.  * Open transmission connection to SMS center. Return NULL for error,
  640.  * open connection for OK. Caller must set smasi->conn->status correctly
  641.  * before calling this.
  642.  */
  643. static Connection *open_connection(SMASI *smasi) 
  644. {
  645.     Connection *conn = conn_open_tcp(smasi->host, smasi->port, NULL);
  646.     if (conn == NULL) {
  647.         error(0, "SMASI[%s]: Couldn't connect to server.",
  648.               octstr_get_cstr(smasi->conn->id));
  649.         return NULL;
  650.     } else {
  651.         SMASI_PDU *logon = smasi_pdu_create(LogonReq);
  652.         logon->u.LogonReq.Name = octstr_duplicate(smasi->username);
  653.         logon->u.LogonReq.Password = octstr_duplicate(smasi->password);
  654.         counter_increase(smasi->message_id_counter);
  655.         send_pdu(conn, smasi->conn->id, logon);
  656.         smasi_pdu_destroy(logon);
  657.     }
  658.     return conn;
  659. static void send_messages(SMASI *smasi, Connection *conn, 
  660.                           long *pending_submits) 
  661. {
  662.     if (*pending_submits == -1) return;
  663.     while (*pending_submits < MAX_PENDING_SUBMITS) {
  664.         SMASI_PDU *pdu = NULL;
  665.         /* Get next message, quit if none to be sent. */
  666.         Msg * msg = list_extract_first(smasi->msgs_to_send);
  667.         if (msg == NULL) break;
  668.         /* Send PDU, record it as waiting for ack from SMSC. */
  669.         pdu = msg_to_pdu(smasi, msg);
  670.         if (pdu->u.SubmitReq.Sequence)
  671.             dict_put(smasi->sent_msgs, pdu->u.SubmitReq.Sequence, msg);
  672.         send_pdu(conn, smasi->conn->id, pdu);
  673.         smasi_pdu_destroy(pdu);
  674.         ++(*pending_submits);
  675.     }
  676. }
  677. /*
  678.  * This is the main function for the background thread for doing I/O on
  679.  * one SMASI connection (the one for transmitting or receiving messages).
  680.  * It makes the initial connection to the SMASI server and re-connects
  681.  * if there are I/O errors or other errors that require it.
  682.  */
  683. static void smasi_thread(void *arg) 
  684. {
  685.     long pending_submits;
  686.     long len;
  687.     SMASI_PDU *pdu;
  688.     SMASI *smasi;
  689.     int logoff_already_sent = 0;
  690.     int ret;
  691.     Connection *conn;
  692.     long last_enquire_sent; 
  693.     double timeout; 
  694.     smasi = arg;
  695.     while (!smasi->quitting) {
  696.         conn = open_connection(smasi);
  697.         if (conn == NULL) {
  698.             error(0, "SMASI[%s]: Could not connect to SMSC center " 
  699.                   "(retrying in %ld seconds).",
  700.                   octstr_get_cstr(smasi->conn->id), smasi->reconnect_delay);
  701.             gwthread_sleep(smasi->reconnect_delay);
  702.             smasi->conn->status = SMSCCONN_RECONNECTING;
  703.             continue;
  704.         }
  705.         last_enquire_sent = date_universal_now(); 
  706.         pending_submits = -1;
  707.         len = 0;
  708.         for (;;) {
  709.             timeout = last_enquire_sent + smasi->enquire_link_interval
  710.                         - date_universal_now(); 
  711.             /* Send logoff request if module is shutting down. */
  712.             if (smasi->quitting && !logoff_already_sent) {
  713.                 send_logoff(smasi, conn);
  714.                 logoff_already_sent = 1;
  715.             } 
  716.             /* send an enquire link */
  717.             send_enquire_link(smasi, conn, &last_enquire_sent); 
  718.             /* Receive incoming PDUs. */
  719.             while ((ret = read_pdu(smasi, conn, &pdu)) == 1) {
  720.                 /* Deal with the PDU we just got */ 
  721.                 dump_pdu("Got PDU:", smasi->conn->id, pdu);
  722.                 /* Process the received PDU. */
  723.                 handle_pdu(smasi, conn, pdu, &pending_submits);
  724.                 smasi_pdu_destroy(pdu);
  725.                 /* Bail out if logoff confirmed. */
  726.                 if (smasi->logged_off) break;
  727.                 /* Make sure we send even if we read a lot. */
  728.                 if ((!smasi->throttling_err_time ||
  729.                     ((time(NULL) - smasi->throttling_err_time) >
  730.                      SMASI_THROTTLING_SLEEP_TIME
  731.                       && !(smasi->throttling_err_time = 0))))
  732.                     send_messages(smasi, conn, &pending_submits);
  733.             } 
  734.             /* Check if connection broken. */
  735.             if (ret == -1 || conn_wait(conn, -1) == -1) {
  736.                 error(0, "SMASI[%s]: I/O error or other error. Re-connecting.",
  737.                       octstr_get_cstr(smasi->conn->id));
  738.                 break;
  739.             } 
  740.             /* Bail out if logoff confirmed. */
  741.             if (smasi->logged_off) break;
  742.             if ((!smasi->throttling_err_time ||
  743.                 ((time(NULL) - smasi->throttling_err_time) >
  744.                  SMASI_THROTTLING_SLEEP_TIME
  745.                   && !(smasi->throttling_err_time = 0))))
  746.                 send_messages(smasi, conn, &pending_submits);
  747.         } 
  748.         conn_destroy(conn);
  749.         conn = NULL;
  750.     } 
  751. /************************************************************************/
  752. /* SMSCCONN INTERFACE                                                   */
  753. /************************************************************************/
  754. static long queued_cb(SMSCConn *conn) 
  755. {
  756.     SMASI *smasi = conn->data;
  757.     conn->load = (smasi ? (conn->status != SMSCCONN_DEAD ? 
  758.                     list_len(smasi->msgs_to_send) : 0) : 0);
  759.     return conn->load;
  760. static int send_msg_cb(SMSCConn *conn, Msg *msg) 
  761. {
  762.     SMASI *smasi = conn->data;
  763.     list_produce(smasi->msgs_to_send, msg_duplicate(msg));
  764.     gwthread_wakeup(smasi->thread_handle);
  765.     return 0;
  766. }
  767. static int shutdown_cb(SMSCConn *conn, int finish_sending) 
  768. {
  769.     SMASI *smasi = NULL;
  770.     debug("bb.sms.smasi", 0, "Shutting down SMSCConn %s (%s)",
  771.           octstr_get_cstr(conn->name), finish_sending ? "slow" : "instant");
  772.     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
  773.     smasi = conn->data;
  774.     smasi->quitting = 1;
  775.     gwthread_wakeup(smasi->thread_handle);
  776.     gwthread_join(smasi->thread_handle);
  777.     smasi_destroy(smasi);
  778.     debug("bb.sms.smasi", 0, "SMSCConn %s shut down.",
  779.           octstr_get_cstr(conn->name));
  780.     conn->status = SMSCCONN_DEAD;
  781.     bb_smscconn_killed();
  782.     /* Clean up. */
  783.     octstr_destroy(colon);
  784.     octstr_destroy(assign);
  785.     octstr_destroy(comma);
  786.     octstr_destroy(cr);
  787.     octstr_destroy(lf);
  788.     return 0;
  789. }
  790. /*
  791.  * Configures the SMASI structure according to the configuration.
  792.  *
  793.  * @return 0 on complete success. -1 if failed due to missing or invalid
  794.  * configuration entry.
  795.  */
  796. static int init_configuration(SMASI *smasi, CfgGroup *config) 
  797. {
  798.     /* Read mandatory entries. */
  799.     smasi->host = cfg_get(config, octstr_imm("host"));
  800.     smasi->username = cfg_get(config, octstr_imm("smsc-username"));
  801.     smasi->password = cfg_get(config, octstr_imm("smsc-password"));
  802.     /* Check configuration. */
  803.     if (smasi->host == NULL) {
  804.         error(0,"SMASI: Configuration file doesn't specify host");
  805.         return -1;
  806.     }
  807.     if (smasi->username == NULL) {
  808.         error(0, "SMASI: Configuration file doesn't specify username.");
  809.         return -1;
  810.     }
  811.     if (smasi->password == NULL) {
  812.         error(0, "SMASI: Configuration file doesn't specify password.");
  813.         return -1;
  814.     }
  815.     /* Read optional entries. Set default values if not set. */
  816.     smasi->my_number = cfg_get(config, octstr_imm("my-number"));
  817.     if (cfg_get_integer(&smasi->port, config, octstr_imm("port")) == -1)
  818.         smasi->port = SMASI_DEFAULT_PORT;
  819.     if (cfg_get_integer(&smasi->reconnect_delay, config,
  820.       octstr_imm("reconnect-delay")) == -1)
  821.         smasi->reconnect_delay = SMASI_RECONNECT_DELAY;
  822.     if (cfg_get_integer(&smasi->source_addr_ton, config,
  823.       octstr_imm("source-addr-ton")) == -1)
  824.         smasi->source_addr_ton = SMASI_OVERRIDE_SOURCE_TON;
  825.     if (cfg_get_integer(&smasi->source_addr_npi, config,
  826.       octstr_imm("source-addr-npi")) == -1)
  827.         smasi->source_addr_npi = SMASI_OVERRIDE_SOURCE_NPI;
  828.     if (cfg_get_integer(&smasi->dest_addr_ton, config,
  829.       octstr_imm("dest-addr-ton")) == -1)
  830.         smasi->source_addr_ton = SMASI_OVERRIDE_DEST_TON;
  831.     if (cfg_get_integer(&smasi->dest_addr_npi, config,
  832.       octstr_imm("dest-addr-npi")) == -1)
  833.         smasi->source_addr_npi = SMASI_OVERRIDE_DEST_NPI;
  834.     if (cfg_get_integer(&smasi->priority, config,
  835.       octstr_imm("priority")) == -1)
  836.         smasi->priority = SMASI_DEFAULT_PRIORITY;
  837.     if (cfg_get_integer(&smasi->enquire_link_interval, config,
  838.       octstr_imm("enquire-link-interval")) == -1)
  839.         smasi->enquire_link_interval = SMASI_ENQUIRE_LINK_INTERVAL;
  840.    
  841.     /* Configure SMSC connection. */
  842.     smasi->conn->data = smasi;
  843.     smasi->conn->name = octstr_format("SMASI:%S:%d:%S",
  844.         smasi->host, smasi->port, smasi->username);
  845.     smasi->conn->id = cfg_get(config, octstr_imm("smsc-id"));
  846.     if (smasi->conn->id == NULL)
  847.       smasi->conn->id = octstr_duplicate(smasi->conn->name);
  848.     return 0;
  849. int smsc_smasi_create(SMSCConn *conn, CfgGroup *config) 
  850. {
  851.     SMASI *smasi = NULL;
  852.     /* Initialize data encoding subsystem. */
  853.     colon = octstr_create(":3a");
  854.     assign = octstr_create(":3d");
  855.     comma = octstr_create(":2c");
  856.     cr = octstr_create(":0a");
  857.     lf = octstr_create(":0d");
  858.     /* Create main SMASI structure and initialize it with configuration
  859.      * settings.
  860.      */
  861.     smasi = smasi_create(conn);
  862.     if (init_configuration(smasi, config) != 0)
  863.         panic(0, "SMASI SMSC module configuration invalid.");
  864.     conn->status = SMSCCONN_CONNECTING;
  865.     /* Port is always set to a configured value or defaults to 21500.
  866.      * Therefore, threads are always started.
  867.      */
  868.     smasi->thread_handle = gwthread_create(smasi_thread, smasi);
  869.     if (smasi->thread_handle == -1) {
  870.         error(0, "SMASI[%s]: Couldn't start SMASI thread.",
  871.               octstr_get_cstr(smasi->conn->id));
  872.         smasi_destroy(conn->data);
  873.         return -1;
  874.     } 
  875.     /* Setup control function pointers. */
  876.     conn->shutdown = shutdown_cb;
  877.     conn->queued = queued_cb;
  878.     conn->send_msg = send_msg_cb;
  879.     return 0;
  880. }