Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

smsc_oisd.c

Go to the documentation of this file.
00001 /* ==================================================================== 
00002  * The Kannel Software License, Version 1.0 
00003  * 
00004  * Copyright (c) 2001-2008 Kannel Group  
00005  * Copyright (c) 1998-2001 WapIT Ltd.   
00006  * All rights reserved. 
00007  * 
00008  * Redistribution and use in source and binary forms, with or without 
00009  * modification, are permitted provided that the following conditions 
00010  * are met: 
00011  * 
00012  * 1. Redistributions of source code must retain the above copyright 
00013  *    notice, this list of conditions and the following disclaimer. 
00014  * 
00015  * 2. Redistributions in binary form must reproduce the above copyright 
00016  *    notice, this list of conditions and the following disclaimer in 
00017  *    the documentation and/or other materials provided with the 
00018  *    distribution. 
00019  * 
00020  * 3. The end-user documentation included with the redistribution, 
00021  *    if any, must include the following acknowledgment: 
00022  *       "This product includes software developed by the 
00023  *        Kannel Group (http://www.kannel.org/)." 
00024  *    Alternately, this acknowledgment may appear in the software itself, 
00025  *    if and wherever such third-party acknowledgments normally appear. 
00026  * 
00027  * 4. The names "Kannel" and "Kannel Group" must not be used to 
00028  *    endorse or promote products derived from this software without 
00029  *    prior written permission. For written permission, please  
00030  *    contact org@kannel.org. 
00031  * 
00032  * 5. Products derived from this software may not be called "Kannel", 
00033  *    nor may "Kannel" appear in their name, without prior written 
00034  *    permission of the Kannel Group. 
00035  * 
00036  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 
00037  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
00038  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
00039  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS 
00040  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,  
00041  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  
00042  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR  
00043  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,  
00044  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE  
00045  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,  
00046  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
00047  * ==================================================================== 
00048  * 
00049  * This software consists of voluntary contributions made by many 
00050  * individuals on behalf of the Kannel Group.  For more information on  
00051  * the Kannel Group, please see <http://www.kannel.org/>. 
00052  * 
00053  * Portions of this software are based upon software originally written at  
00054  * WapIT Ltd., Helsinki, Finland for the Kannel project.  
00055  */ 
00056 
00057 /*
00058  * smsc.oisd.c - Driver for Sema Group SMS Center G8.1 (OIS 5.8)
00059  * using direct TCP/IP access interface
00060  *
00061  * Dariusz Markowicz <dm@tenbit.pl> 2002-2004
00062  *
00063  * This code is based on the CIMD2 module design.
00064  *
00065  * References:
00066  *
00067  *   [1] Sema SMSC Version G8.1 Open Interface Specification
00068  *       document version 5.8, 18 January 2001, Sema Telecoms.
00069  */
00070 
00071 #include <ctype.h>
00072 #include <time.h>
00073 #include <errno.h>
00074 #include <limits.h>
00075 #include <string.h>
00076 
00077 #include <unistd.h>
00078 
00079 #include "gwlib/gwlib.h"
00080 #include "smscconn.h"
00081 #include "smscconn_p.h"
00082 #include "bb_smscconn_cb.h"
00083 
00084 #include "shared.h"
00085 #include "sms.h"
00086 #include "dlr.h"
00087 
00088 
00089 typedef struct privdata {
00090     Octstr  *host;
00091     long    port;
00092     long    keepalive;
00093     Octstr  *my_number;
00094     long    validityperiod;
00095     int     no_dlr;
00096 
00097     int     socket;
00098     unsigned long send_seq;
00099 
00100     Octstr  *inbuffer;
00101     List    *received;
00102 
00103     time_t  next_ping;
00104 
00105     List *outgoing_queue;
00106     SMSCConn *conn;
00107     int io_thread;
00108     int quitting;
00109     List *stopped; /* list-trick for suspend/isolate */
00110 
00111 } PrivData;
00112 
00113 
00114 
00115 /* Microseconds before giving up on a request */
00116 #define RESPONSE_TIMEOUT (10 * 1000000)
00117 #define RESULT_SUCCESS 0
00118 
00119 enum {
00120     INVOKE = 0,
00121     RESULT = 1
00122 };
00123 
00124 /* Textual names for the operation codes defined by the OISD spec. */
00125 /* If you make changes here, also change the operation table. */
00126 enum {
00127     SUBMIT_SM = 0,
00128     STATUS_REPORT = 4,
00129     DELIVER_SM = 9,
00130     RETRIEVE_REQUEST = 11,
00131 
00132     /* Not a request; add to any request to make it a response */
00133     RESPONSE = 50
00134 };
00135 
00136 static int isphonedigit(int c)
00137 {
00138     return isdigit(c) || c == '+' || c == '-';
00139 }
00140 
00141 static int parm_valid_address(Octstr *value)
00142 {
00143     return octstr_check_range(value, 0, octstr_len(value), isphonedigit);
00144 }
00145 
00146 /***************************************************************************/
00147 /* Some functions to look up information about operation codes             */
00148 /***************************************************************************/
00149 
00150 static int operation_find(int operation);
00151 static Octstr *operation_name(int operation);
00152 static int operation_can_send(int operation);
00153 static int operation_can_receive(int operation);
00154 
00155 static const struct
00156 {
00157     char *name;
00158     int code;
00159     int can_send;
00160     int can_receive;
00161 }
00162 operations[] = {
00163     { "Submit SM", SUBMIT_SM, 1, 0 },
00164     { "Status Report", STATUS_REPORT, 0, 1 },
00165     { "Deliver SM", DELIVER_SM, 0, 1 },
00166     { "Retrieve Request", RETRIEVE_REQUEST, 1, 0 },
00167 
00168     { NULL, 0, 0, 0 }
00169 };
00170 
00171 static int operation_find(int operation)
00172 {
00173     int i;
00174 
00175     for (i = 0; operations[i].name != NULL; i++) {
00176         if (operations[i].code == operation)
00177             return i;
00178     }
00179 
00180     return -1;
00181 }
00182 
00183 /* Return a human-readable representation of this operation code */
00184 static Octstr *operation_name(int operation)
00185 {
00186     int i;
00187 
00188     i = operation_find(operation);
00189     if (i >= 0)
00190         return octstr_create(operations[i].name);
00191 
00192     if (operation >= RESPONSE) {
00193         i = operation_find(operation - RESPONSE);
00194         if (i >= 0) {
00195             Octstr *name = octstr_create(operations[i].name);
00196             octstr_append_cstr(name, " response");
00197             return name;
00198         }
00199     }
00200 
00201     /* Put the operation number here when we have octstr_format */
00202     return octstr_create("(unknown)");
00203 }
00204 
00205 /* Return true if a OISD client may send this operation */
00206 static int operation_can_send(int operation)
00207 {
00208     int i = operation_find(operation);
00209 
00210     if (i >= 0)
00211         return operations[i].can_send;
00212 
00213     /* If we can receive the request, then we can send the response. */
00214     if (operation >= RESPONSE)
00215         return operation_can_receive(operation - RESPONSE);
00216 
00217     return 0;
00218 }
00219 
00220 
00221 /* Return true if a OISD server may send this operation */
00222 static int operation_can_receive(int operation)
00223 {
00224     int i = operation_find(operation);
00225 
00226     if (i >= 0)
00227         return operations[i].can_receive;
00228 
00229     /* If we can send the request, then we can receive the response. */
00230     if (operation >= RESPONSE)
00231         return operation_can_send(operation - RESPONSE);
00232 
00233     return 0;
00234 }
00235 
00236 /***************************************************************************
00237  * Packet encoding/decoding functions.  They handle packets at the octet   *
00238  * level, and know nothing of the network.                                 *
00239  ***************************************************************************/
00240 
00241 struct packet
00242 {
00243     unsigned long opref; /* operation reference */
00244     int operation;
00245     Octstr *data;        /* Encoded packet */
00246 };
00247 
00248 /* A reminder that packets are created without a valid sequence number */
00249 #define BOGUS_SEQUENCE 0
00250 
00251 static Msg *oisd_accept_delivery_report_message(struct packet *request,
00252                                                 SMSCConn *conn);
00253 
00254 static void packet_parse_header(struct packet *packet)
00255 {
00256     packet->opref = (octstr_get_char(packet->data, 3) << 24)
00257                   | (octstr_get_char(packet->data, 2) << 16)
00258                   | (octstr_get_char(packet->data, 1) << 8)
00259                   | (octstr_get_char(packet->data, 0));
00260 
00261     packet->operation = octstr_get_char(packet->data, 5);
00262     if (octstr_get_char(packet->data, 4) == 1)
00263         packet->operation += RESPONSE;
00264 }
00265 
00266 
00267 /*
00268  * Accept an Octstr containing one packet, build a struct packet around
00269  * it, and return that struct.  The Octstr is stored in the struct.
00270  * No error checking is done here yet.
00271  */
00272 static struct packet *packet_parse(Octstr *packet_data)
00273 {
00274     struct packet *packet;
00275 
00276     packet = gw_malloc(sizeof(*packet));
00277     packet->data = packet_data;
00278 
00279     /* Fill in packet->operation and packet->opref */
00280     packet_parse_header(packet);
00281 
00282     return packet;
00283 }
00284 
00285 /* Deallocate this packet */
00286 static void packet_destroy(struct packet *packet)
00287 {
00288     if (packet != NULL) {
00289         octstr_destroy(packet->data);
00290         gw_free(packet);
00291     }
00292 }
00293 
00294 /*
00295  * Find the first packet in "in", delete it from "in", and return it as
00296  * a struct.  Return NULL if "in" contains no packet.  Always delete
00297  * leading non-packet data from "in".
00298  */
00299 static struct packet *packet_extract(Octstr *in, SMSCConn *conn)
00300 {
00301     Octstr *packet;
00302     int size, i;
00303     static char s[4][4] = {
00304         { 0x01, 0x0b, 0x00, 0x00 },
00305         { 0x01, 0x00, 0x00, 0x00 },
00306         { 0x00, 0x04, 0x00, 0x00 },
00307         { 0x00, 0x09, 0x00, 0x00 }
00308     }; /* msgtype, oper, 0, 0 */
00309     char known_bytes[4];
00310 
00311     if (octstr_len(in) < 10)
00312         return NULL;
00313     octstr_get_many_chars(known_bytes, in, 4, 4);
00314     /* Find s, and delete everything up to it. */
00315     /* If packet starts with one of s, it should be good packet */
00316     for (i = 0; i < 4; i++) {
00317         if (memcmp(s[i], known_bytes, 4) == 0)
00318             break;
00319     }
00320 
00321     if (i >= 4) {
00322         error(0, "OISD[%s]: wrong packet",
00323               octstr_get_cstr(conn->id));
00324         octstr_dump(in, 0);
00325         return NULL;
00326     }
00327 
00328     /* Find end of packet */
00329     size = (octstr_get_char(in, 9) << 8) | octstr_get_char(in, 8);
00330 
00331     if (size + 10 > octstr_len(in))
00332         return NULL;
00333 
00334     packet = octstr_copy(in, 0, size + 10);
00335     octstr_delete(in, 0, size + 10);
00336 
00337     return packet_parse(packet);
00338 }
00339 
00340 static void packet_check_can_receive(struct packet *packet, SMSCConn *conn)
00341 {
00342     gw_assert(packet != NULL);
00343 
00344     if (!operation_can_receive(packet->operation)) {
00345         Octstr *name = operation_name(packet->operation);
00346         warning(0, "OISD[%s]: SMSC sent us %s request",
00347                 octstr_get_cstr(conn->id),
00348                 octstr_get_cstr(name));
00349         octstr_destroy(name);
00350     }
00351 }
00352 
00353 static int oisd_expand_gsm7_to_bits(char *bits, Octstr *raw7)
00354 {
00355     int i, j, k;
00356     int len;
00357     char ch;
00358 
00359     len = octstr_len(raw7) * 7; /* number of bits in the gsm 7-bit msg */
00360 
00361     for (j = i = 0; j < len; ++i) {
00362         ch = octstr_get_char(raw7, i);
00363         for (k = 0; k < 8; ++k) {
00364             bits[j++] = (char) (ch & 0x01);
00365             ch >>= 1;
00366         }
00367     }
00368 
00369     return j;
00370 }
00371 
00372 static char oisd_expand_gsm7_from_bits(const char *bits, int pos)
00373 {
00374     int i;
00375     char ch;
00376 
00377     pos *= 7; /* septet position in bits */
00378     ch = '\0';
00379     for (i = 6; i >= 0; --i) {
00380         ch <<= 1;
00381         ch |= bits[pos + i];
00382     }
00383 
00384     return ch;
00385 }
00386 
00387 static Octstr *oisd_expand_gsm7(Octstr *raw7)
00388 {
00389     Octstr *raw8;
00390     int i, len;
00391     char *bits;
00392 
00393     raw8 = octstr_create("");
00394     bits = gw_malloc(8 * octstr_len(raw7) + 1);
00395 
00396     oisd_expand_gsm7_to_bits(bits, raw7);
00397     len = octstr_len(raw7);
00398 
00399     for (i = 0; i < len; ++i) {
00400         octstr_append_char(raw8, oisd_expand_gsm7_from_bits(bits, i));
00401     }
00402 
00403     gw_free(bits);
00404 
00405     return raw8;
00406 }
00407 
00408 static void oisd_shrink_gsm7(Octstr *str)
00409 {
00410     Octstr *result;
00411     int len, i;
00412     int numbits, value;
00413 
00414     result = octstr_create("");
00415     len = octstr_len(str);
00416     value = 0;
00417     numbits = 0;
00418     for (i = 0; i < len; i++) {
00419         value += octstr_get_char(str, i) << numbits;
00420         numbits += 7;
00421         if (numbits >= 8) {
00422             octstr_append_char(result, value & 0xff);
00423             value >>= 8;
00424             numbits -= 8;
00425         }
00426     }
00427     if (numbits > 0)
00428         octstr_append_char(result, value);
00429     octstr_delete(str, 0, LONG_MAX);
00430     octstr_append(str, result);
00431     octstr_destroy(result);
00432 }
00433 
00434 /****************************************************************************
00435  * Packet encoding functions.  They do not allow the creation of invalid
00436  * OISD packets.
00437  ***************************************************************************/
00438 
00439 /* Build a new packet struct with this operation code and sequence number. */
00440 static struct packet *packet_create(int operation, unsigned long opref)
00441 {
00442     struct packet *packet;
00443     unsigned char header[10];
00444 
00445     packet = gw_malloc(sizeof(*packet));
00446     packet->operation = operation;
00447     packet->opref = opref;
00448 
00449     /* Opref */
00450     header[0] = opref & 0xff;
00451     header[1] = (opref >> 8) & 0xff;
00452     header[2] = (opref >> 16) & 0xff;
00453     header[3] = (opref >> 24) & 0xff;
00454 
00455     /* Message Type & Operation */
00456     if (operation > RESPONSE) {
00457         header[4] = RESULT;
00458         header[5] = operation - RESPONSE;
00459     } else {
00460         header[4] = INVOKE;
00461         header[5] = operation;
00462     }
00463 
00464     /* Unused */
00465     header[6] = 0;
00466     header[7] = 0;
00467 
00468     /* Data Size */
00469     header[8] = 0;
00470     header[9] = 0;
00471 
00472     packet->data = octstr_create_from_data((char *)header, 10);
00473 
00474     return packet;
00475 }
00476 
00477 static void packet_set_data_size(struct packet *packet)
00478 {
00479     int len;
00480 
00481     gw_assert(packet != NULL);
00482 
00483     len = octstr_len(packet->data) - 10;
00484 
00485     octstr_set_char(packet->data, 8, len & 0xff); /* Data Size */
00486     octstr_set_char(packet->data, 9, (len >> 8) & 0xff);
00487 }
00488 
00489 static void packet_set_sequence(struct packet *packet, unsigned long opref)
00490 {
00491     gw_assert(packet != NULL);
00492 
00493     octstr_set_char(packet->data, 0, opref & 0xff);
00494     octstr_set_char(packet->data, 1, (opref >> 8) & 0xff);
00495     octstr_set_char(packet->data, 2, (opref >> 16) & 0xff);
00496     octstr_set_char(packet->data, 3, (opref >> 24) & 0xff);
00497     packet->opref = opref;
00498 }
00499 
00500 static struct packet *packet_encode_message(Msg *msg, SMSCConn *conn)
00501 {
00502     struct packet *packet;
00503     PrivData *pdata = conn->data;
00504     int DCS;
00505     int setvalidity = 0;
00506     int so = 0;
00507     int udhlen7, udhlen8;
00508     int msglen7, msglen8;
00509     Octstr *udhdata = NULL;
00510     Octstr *msgdata = NULL;
00511 
00512     gw_assert(msg != NULL);
00513     gw_assert(msg->type == sms);
00514     gw_assert(msg->sms.receiver != NULL);
00515 
00516     DCS = fields_to_dcs(msg, 0);
00517     if (msg->sms.sender == NULL)
00518         msg->sms.sender = octstr_create("");
00519 
00520     if (!parm_valid_address(msg->sms.receiver)) {
00521         warning(0, "OISD[%s]: non-digits in destination phone number '%s', discarded",
00522                 octstr_get_cstr(conn->id),
00523                 octstr_get_cstr(msg->sms.receiver));
00524         return NULL;
00525     }
00526 
00527     if (!parm_valid_address(msg->sms.sender)) {
00528         warning(0, "OISD[%s]: non-digits in originating phone number '%s', discarded",
00529                 octstr_get_cstr(conn->id),
00530                 octstr_get_cstr(msg->sms.sender));
00531         return NULL;
00532     }
00533 
00534     packet = packet_create(SUBMIT_SM, BOGUS_SEQUENCE);
00535 
00536     gw_assert(octstr_check_range(msg->sms.receiver, 0,
00537                                  octstr_len(msg->sms.receiver), isphonedigit));
00538     /* MSISDN length */
00539     octstr_append_char(packet->data,
00540                        (unsigned char) octstr_len(msg->sms.receiver));
00541 
00542     /* MSISDN */
00543     octstr_append(packet->data, msg->sms.receiver);
00544 
00545     /* Duplicate msg. behaviour */
00546     /* 1=reject duplicates, 2=allow duplicates */
00547     octstr_append_char(packet->data, 2);
00548 
00549     /* SME ref. no. unused in this protocol implementation, but set */
00550     octstr_append_char(packet->data, 0);
00551     octstr_append_char(packet->data, 0);
00552     octstr_append_char(packet->data, 0);
00553     octstr_append_char(packet->data, 0);
00554 
00555     /* Priority 0=high, 1=normal */
00556     octstr_append_char(packet->data, 1);
00557     gw_assert(octstr_check_range(msg->sms.sender, 0,
00558                                  octstr_len(msg->sms.sender), isphonedigit));
00559 
00560     /* Originating address length */
00561     octstr_append_char(packet->data,
00562                        (unsigned char) (octstr_len(msg->sms.sender) + 2));
00563 
00564     /* XXX: GSM operator dependent ? */
00565     /* TON */
00566     octstr_append_char(packet->data, 0x42);
00567 
00568     /* NPI */
00569     octstr_append_char(packet->data, 0x44);
00570 
00571     /* Originating address */
00572     octstr_append(packet->data, msg->sms.sender);
00573 
00574     /* Validity period type 0=none, 1=absolute, 2=relative */
00575 
00576     /*
00577      * Validity-Period (TP-VP)
00578      * see GSM 03.40 section 9.2.3.12
00579      */
00580     if ((setvalidity = msg->sms.validity) != SMS_PARAM_UNDEFINED ||
00581         (setvalidity = pdata->validityperiod) != SMS_PARAM_UNDEFINED) {
00582         /* Validity period type 0=none, 1=absolute, 2=relative */
00583         octstr_append_char(packet->data, 2);
00584 
00585         if (setvalidity > 635040)
00586             setvalidity = 255;
00587         else if (setvalidity >= 50400 && setvalidity <= 635040)
00588             setvalidity = (setvalidity - 1) / 7 / 24 / 60 + 192 + 1;
00589         else if (setvalidity > 43200 && setvalidity < 50400)
00590             setvalidity = 197;
00591         else if (setvalidity >= 2880 && setvalidity <= 43200)
00592             setvalidity = (setvalidity - 1) / 24 / 60 + 166 + 1;
00593         else if (setvalidity > 1440 && setvalidity < 2880)
00594             setvalidity = 168;
00595         else if (setvalidity >= 750 && setvalidity <= 1440)
00596             setvalidity = (setvalidity - 720 - 1) / 30 + 143 + 1;
00597         else if (setvalidity > 720 && setvalidity < 750)
00598             setvalidity = 144;
00599         else if (setvalidity >= 5 && setvalidity <= 720)
00600             setvalidity = (setvalidity - 1) / 5 - 1 + 1;
00601         else if (setvalidity < 5)
00602             setvalidity = 0;
00603 
00604         octstr_append_char(packet->data, setvalidity);
00605     } else {
00606         /* Validity period type 0=none, 1=absolute, 2=relative */
00607         octstr_append_char(packet->data, 0);
00608         setvalidity = 0; /* reset */
00609     }
00610 
00611     if (setvalidity >= 0 && setvalidity <= 143)
00612         debug("bb.smsc.oisd", 0, "OISD[%s]: Validity-Period: %d minutes",
00613               octstr_get_cstr(conn->id), (setvalidity + 1)*5);
00614     else if (setvalidity >= 144 && setvalidity <= 167)
00615         debug("bb.smsc.oisd", 0, "OISD[%s]: Validity-Period: %3.1f hours",
00616               octstr_get_cstr(conn->id), ((float)(setvalidity - 143) / 2) + 12);
00617     else if (setvalidity >= 168 && setvalidity <= 196)
00618         debug("bb.smsc.oisd", 0, "OISD[%s]: Validity-Period: %d days",
00619               octstr_get_cstr(conn->id), (setvalidity - 166));
00620     else
00621         debug("bb.smsc.oisd", 0, "OISD[%s]: Validity-Period: %d weeks",
00622               octstr_get_cstr(conn->id), (setvalidity - 192));
00623 
00624     /* Data coding scheme */
00625     octstr_append_char(packet->data, DCS);
00626 
00627     /* Explicitly ask not to get status reports.
00628      * If we do not do this, the server's default might be to
00629      * send status reports in some cases, and we don't do anything
00630      * with those reports anyway. */
00631     /* ask for the delivery reports if needed*/
00632 
00633     if (!pdata->no_dlr)
00634         if (DLR_IS_SUCCESS_OR_FAIL(msg->sms.dlr_mask))
00635             octstr_append_char(packet->data, 7);
00636         else
00637             octstr_append_char(packet->data, 0);
00638     else if (pdata->no_dlr && DLR_IS_SUCCESS_OR_FAIL(msg->sms.dlr_mask))
00639         warning(0, "OISD[%s]: dlr request make no sense while no-dlr set to true",
00640              octstr_get_cstr(conn->id));
00641 
00642     /* Protocol id 0=default */
00643     octstr_append_char(packet->data, 0);
00644 
00645     if (octstr_len(msg->sms.udhdata))
00646         so |= 0x02;
00647     if (msg->sms.coding == DC_8BIT)
00648         so |= 0x10;
00649 
00650     /* Submission options */
00651     octstr_append_char(packet->data, so);
00652 
00653     udhlen8 = octstr_len(msg->sms.udhdata);
00654     msglen8 = octstr_len(msg->sms.msgdata);
00655 
00656     udhdata = octstr_duplicate(msg->sms.udhdata);
00657     msgdata = octstr_duplicate(msg->sms.msgdata);
00658 
00659     if (msg->sms.coding == DC_7BIT || msg->sms.coding == DC_UNDEF) {
00660         debug("bb.sms.oisd", 0, "OISD[%s]: sending UTF-8=%s",
00661               octstr_get_cstr(conn->id),
00662               octstr_get_cstr(msg->sms.msgdata));
00663         charset_utf8_to_gsm(msgdata);
00664         oisd_shrink_gsm7(msgdata);
00665     }
00666 
00667     /* calculate lengths */
00668     udhlen7 = octstr_len(udhdata);
00669     msglen7 = octstr_len(msgdata);
00670 
00671     octstr_append_char(packet->data, (unsigned char) (udhlen8 + msglen8));
00672     octstr_append_char(packet->data, (unsigned char) (udhlen7 + msglen7));
00673 
00674     /*
00675      * debug("bb.sms.oisd", 0, "OISD[%s]: packet_encode_message udhlen8=%d, msglen8=%d",
00676      *       octstr_get_cstr(conn->id), udhlen8, msglen8);
00677      * debug("bb.sms.oisd", 0, "OISD[%s]: packet_encode_message udhlen7=%d, msglen7=%d",
00678      *       octstr_get_cstr(conn->id), udhlen7, msglen7);
00679      */
00680 
00681     /* copy text */
00682     octstr_append(packet->data, udhdata);
00683     octstr_append(packet->data, msgdata);
00684 
00685     /* Sub-logical SME number */
00686     octstr_append_char(packet->data, 0);
00687     octstr_append_char(packet->data, 0);
00688 
00689     octstr_destroy(udhdata);
00690     octstr_destroy(msgdata);
00691 
00692     return packet;
00693 }
00694 
00695 /***************************************************************************
00696  * Protocol functions.  These implement various transactions.              *
00697  ***************************************************************************/
00698 
00699 /* Give this packet a proper sequence number for sending. */
00700 static void packet_set_send_sequence(struct packet *packet, PrivData *pdata)
00701 {
00702     gw_assert(pdata != NULL);
00703 
00704     packet_set_sequence(packet, pdata->send_seq);
00705     pdata->send_seq++;
00706 }
00707 
00708 static struct packet *oisd_get_packet(PrivData *pdata, Octstr **ts)
00709 {
00710     struct packet *packet = NULL;
00711 
00712     gw_assert(pdata != NULL);
00713 
00714     /* If packet is already available, don't try to read anything */
00715     packet = packet_extract(pdata->inbuffer, pdata->conn);
00716 
00717     while (packet == NULL) {
00718         if (read_available(pdata->socket, RESPONSE_TIMEOUT) != 1) {
00719             warning(0, "OISD[%s]: SMSC is not responding",
00720                     octstr_get_cstr(pdata->conn->id));
00721             return NULL;
00722         }
00723 
00724         if (octstr_append_from_socket(pdata->inbuffer, pdata->socket) <= 0) {
00725             error(0, "OISD[%s]: oisd_get_packet: read failed",
00726                   octstr_get_cstr(pdata->conn->id));
00727             return NULL;
00728         }
00729 
00730         packet = packet_extract(pdata->inbuffer, pdata->conn);
00731     }
00732 
00733     packet_check_can_receive(packet, pdata->conn);
00734     debug("bb.sms.oisd", 0, "OISD[%s]: received",
00735           octstr_get_cstr(pdata->conn->id));
00736     if (packet->operation != RETRIEVE_REQUEST + RESPONSE)
00737         octstr_dump(packet->data, 0);
00738     if (ts)
00739         *ts = octstr_copy(packet->data, 15, 14);
00740 
00741     if (pdata->keepalive > 0)
00742         pdata->next_ping = time(NULL) + pdata->keepalive;
00743 
00744     return packet;
00745 }
00746 
00747 /*
00748  * Acknowledge a request.
00749  */
00750 static void oisd_send_response(struct packet *request, PrivData *pdata)
00751 {
00752     struct packet *response;
00753 
00754     gw_assert(request != NULL);
00755     gw_assert(request->operation < RESPONSE);
00756 
00757     response = packet_create(request->operation + RESPONSE, request->opref);
00758 
00759     octstr_append_char(response->data, (char) RESULT_SUCCESS);
00760 
00761     packet_set_data_size(response);
00762 
00763     debug("bb.sms.oisd", 0, "OISD[%s]: sending response",
00764           octstr_get_cstr(pdata->conn->id));
00765     octstr_dump(response->data, 0);
00766 
00767     /* Don't check errors here because if there is something
00768      * wrong with the socket, the main loop will detect it. */
00769     octstr_write_to_socket(pdata->socket, response->data);
00770 
00771     packet_destroy(response);
00772 }
00773 
00774 static Msg *oisd_accept_message(struct packet *request, SMSCConn *conn)
00775 {
00776     Msg *msg = NULL;
00777     int DCS;
00778     int dest_len;
00779     int origin_len;
00780     int add_info;
00781     int msglen7, msglen8;
00782     int udh_len;
00783 
00784     msg = msg_create(sms);
00785 
00786     /* See GSM 03.38.  The bit patterns we can handle are:
00787      *   000xyyxx  Uncompressed text, yy indicates alphabet.
00788      *                   yy = 00, default alphabet
00789      *                   yy = 01, 8-bit data
00790      *                   yy = 10, UCS-2
00791      *                   yy = 11, reserved
00792      *   1111xyxx  Data, y indicates alphabet.
00793      *                   y = 0, default alphabet
00794      *                   y = 1, 8-bit data
00795      */
00796 
00797     /* Additional information
00798      *   xxxxxxyz  This field conveys additional information to assist
00799      *             the recipient in interpreting the SM.
00800      *                   z = reply path
00801      *                   y = user data header indicator
00802      *                   x = reserved
00803      */
00804 
00805     /*
00806      * Destination addr. and Originating addr. w/o TOA
00807      */
00808 
00809     /* Destination addr. length */
00810     dest_len = octstr_get_char(request->data, 10);
00811 
00812     /* Destination addr. */
00813     msg->sms.receiver = octstr_copy(request->data, 11+2, dest_len-2);
00814 
00815     /* Originating addr. length */
00816     origin_len = octstr_get_char(request->data, 11+dest_len+4);
00817 
00818     /* Originating addr. */
00819     msg->sms.sender = octstr_copy(request->data, 11+dest_len+5+2, origin_len-2);
00820 
00821     DCS = octstr_get_char(request->data, 11+dest_len+5+origin_len);
00822     if (!dcs_to_fields(&msg, DCS)) {
00823         /* XXX: Should reject this message ? */
00824         debug("bb.sms.oisd", 0, "OISD[%s]: Invalid DCS",
00825               octstr_get_cstr(conn->id));
00826         dcs_to_fields(&msg, 0);
00827     }
00828 
00829     add_info = octstr_get_char(request->data,11+dest_len+5+origin_len+2);
00830 
00831     msglen7 = octstr_get_char(request->data, 11+dest_len+5+origin_len+3);
00832     msglen8 = octstr_get_char(request->data, 11+dest_len+5+origin_len+4);
00833 
00834     msg->sms.rpi = add_info & 0x01;
00835 
00836     debug("bb.sms.oisd", 0,
00837           "OISD[%s]: received DCS=%02X, add_info=%d, msglen7=%d, msglen8=%d, rpi=%ld",
00838           octstr_get_cstr(conn->id),
00839           DCS, add_info, msglen7, msglen8, msg->sms.rpi);
00840 
00841     if (msg->sms.coding == DC_7BIT) {
00842         msg->sms.msgdata =
00843             oisd_expand_gsm7(octstr_copy(request->data,
00844                                          11+dest_len+5+origin_len+5,
00845                                          msglen7));
00846             debug("bb.sms.oisd", 0, "OISD[%s]: received raw8=%s ",
00847                   octstr_get_cstr(conn->id),
00848                   octstr_get_cstr(msg->sms.msgdata));
00849         if (add_info & 0x02) {
00850             warning(0, "OISD[%s]: 7-bit UDH ?",
00851                     octstr_get_cstr(conn->id));
00852         } else {
00853             charset_gsm_to_utf8(msg->sms.msgdata);
00854             debug("bb.sms.oisd", 0, "OISD[%s]: received UTF-8=%s",
00855                   octstr_get_cstr(conn->id),
00856                   octstr_get_cstr(msg->sms.msgdata));
00857         }
00858     } else {
00859         /* 0xf4, 0xf5, 0xf6, 0xf7; 8bit to disp, mem, sim or term */
00860         if (add_info & 0x02) {
00861             udh_len = octstr_get_char(request->data,
00862                                       11+dest_len+5+origin_len+5)+1;
00863             msg->sms.msgdata =
00864                 octstr_copy(request->data,
00865                             11+dest_len+5+origin_len+5+udh_len,
00866                             msglen8);
00867             msg->sms.udhdata =
00868                 octstr_copy(request->data,
00869                             11+dest_len+5+origin_len+5,
00870                             udh_len);
00871         } else {
00872             msg->sms.msgdata =
00873                 octstr_copy(request->data,
00874                             11+dest_len+5+origin_len+5,
00875                             msglen8);
00876         }
00877     }
00878 
00879     /* Code elsewhere in the gateway always expects the sender and
00880      * receiver fields to be filled, so we discard messages that
00881      * lack them.  If they should not be discarded, then the code
00882      * handling sms messages should be reviewed.  -- RB */
00883     if (!(msg->sms.receiver) || octstr_len(msg->sms.receiver) == 0) {
00884         info(0, "OISD[%s]: Got SMS without receiver, discarding.",
00885              octstr_get_cstr(conn->id));
00886         goto error;
00887     }
00888 
00889     if (!(msg->sms.sender) || octstr_len(msg->sms.sender) == 0) {
00890         info(0, "OISD[%s]: Got SMS without sender, discarding.",
00891               octstr_get_cstr(conn->id));
00892         goto error;
00893     }
00894 
00895     if ((!(msg->sms.msgdata) || octstr_len(msg->sms.msgdata) == 0)
00896         && (!(msg->sms.udhdata) || octstr_len(msg->sms.udhdata) == 0)) {
00897         info(0, "OISD[%s]: Got empty SMS, ignoring.",
00898               octstr_get_cstr(conn->id));
00899         goto error;
00900     }
00901 
00902     return msg;
00903 
00904 error:
00905     msg_destroy(msg);
00906     return NULL;
00907 }
00908 
00909 /* Deal with a request from the OISD server, and acknowledge it. */
00910 static void oisd_handle_request(struct packet *request, SMSCConn *conn)
00911 {
00912     PrivData *pdata = conn->data;
00913     Msg *msg = NULL;
00914 
00915     if (request->operation == STATUS_REPORT) {
00916         msg = oisd_accept_delivery_report_message(request, conn);
00917         if (msg)
00918             gwlist_append(pdata->received, msg);
00919     } else if (request->operation == DELIVER_SM) {
00920         msg = oisd_accept_message(request, conn);
00921         if (msg)
00922             gwlist_append(pdata->received, msg);
00923     }
00924 
00925     oisd_send_response(request, pdata);
00926 }
00927 
00928 /* Send a request and wait for the ack.  If the other side responds with
00929  * an error code, attempt to correct and retry.
00930  * If other packets arrive while we wait for the ack, handle them.
00931  *
00932  * Return -1 if the SMSC refused the request.  Return -2 for other
00933  * errors, such as being unable to send the request at all.  If the
00934  * function returns -2, the caller would do well to try to reopen the
00935  * connection.
00936  *
00937  * The SMSCenter must be already open.
00938  */
00939 static int oisd_request(struct packet *request, SMSCConn *conn, Octstr **ts)
00940 {
00941     PrivData *pdata = conn->data;
00942     int ret;
00943     struct packet *reply = NULL;
00944     int errorcode;
00945     int tries = 0;
00946     Octstr *request_name;
00947 
00948     gw_assert(pdata != NULL);
00949     gw_assert(request != NULL);
00950     gw_assert(operation_can_send(request->operation));
00951 
00952     if (pdata->socket < 0) {
00953         warning(0, "OISD[%s]: oisd_request: socket not open.",
00954                 octstr_get_cstr(conn->id));
00955         return -2;
00956     }
00957 
00958     packet_set_data_size(request);
00959 
00960 retransmit:
00961     packet_set_send_sequence(request, pdata);
00962 
00963     request_name = operation_name(request->operation);
00964     debug("bb.sms.oisd", 0, "OISD[%s]: sending %s request",
00965           octstr_get_cstr(conn->id),
00966           octstr_get_cstr(request_name));
00967     octstr_destroy(request_name);
00968     if (request->operation != RETRIEVE_REQUEST)
00969         octstr_dump(request->data, 0);
00970 
00971     ret = octstr_write_to_socket(pdata->socket, request->data);
00972     if (ret < 0)
00973         goto io_error;
00974 
00975 next_reply:
00976     packet_destroy(reply);  /* destroy old, if any */
00977     reply = oisd_get_packet(pdata, ts);
00978     if (!reply)
00979         goto io_error;
00980 
00981     /* The server sent us a request.  Handle it, then wait for
00982      * a new reply. */
00983     if (reply->operation < RESPONSE) {
00984         oisd_handle_request(reply, conn);
00985         goto next_reply;
00986     }
00987 
00988     if (reply->opref != request->opref) {
00989         /* We got a response to a different request number than
00990          * what we send.  Strange. */
00991         warning(0, "OISD[%s]: response had unexpected sequence number; ignoring.",
00992                 octstr_get_cstr(conn->id));
00993         goto next_reply;
00994     }
00995 
00996     if (reply->operation != request->operation + RESPONSE) {
00997         /* We got a response that didn't match our request */
00998         Octstr *request_name = operation_name(request->operation);
00999         Octstr *reply_name = operation_name(reply->operation);
01000         warning(0, "OISD[%s]: %s request got a %s",
01001                 octstr_get_cstr(conn->id),
01002                 octstr_get_cstr(request_name),
01003                 octstr_get_cstr(reply_name));
01004 
01005         octstr_destroy(request_name);
01006         octstr_destroy(reply_name);
01007         octstr_dump(reply->data, 0);
01008         goto retry;
01009     }
01010 
01011     errorcode = octstr_get_char(reply->data, 10); /* Result */
01012 
01013     if (errorcode > 0)
01014         goto error;
01015 
01016     /* The reply passed all the checks... looks like the SMSC accepted
01017      * our request! */
01018     packet_destroy(reply);
01019     return 0;
01020 
01021 io_error:
01022     packet_destroy(reply);
01023     return -2;
01024 
01025 error:
01026     packet_destroy(reply);
01027     return -1;
01028 
01029 retry:
01030     if (++tries < 3) {
01031         warning(0, "OISD[%s]: Retransmitting (take %d)",
01032                 octstr_get_cstr(conn->id),
01033                 tries);
01034         goto retransmit;
01035     }
01036     warning(0, "OISD[%s]: Giving up.",
01037             octstr_get_cstr(conn->id));
01038     goto io_error;
01039 }
01040 
01041 /* Close the SMSC socket without fanfare. */
01042 static void oisd_close_socket(PrivData *pdata)
01043 {
01044     gw_assert(pdata != NULL);
01045 
01046     if (pdata->socket < 0)
01047         return;
01048 
01049     if (close(pdata->socket) < 0)
01050         warning(errno, "OISD[%s]: error closing socket",
01051                 octstr_get_cstr(pdata->conn->id));
01052     pdata->socket = -1;
01053 }
01054 
01055 /*
01056  * Open a socket to the SMSC, send a login packet, and wait for ack.
01057  * This may block.  Return 0 for success, or -1 for failure.
01058  * Make sure the socket is closed before calling this function, otherwise
01059  * we will leak fd's.
01060  */
01061 static int oisd_login(SMSCConn *conn)
01062 {
01063     PrivData *pdata = conn->data;
01064     struct packet *packet = NULL;
01065 
01066     gw_assert(pdata != NULL);
01067 
01068     if (pdata->socket >= 0) {
01069         warning(0, "OISD[%s]: login: socket was already open; closing",
01070                 octstr_get_cstr(conn->id));
01071         oisd_close_socket(pdata);
01072     }
01073 
01074     pdata->socket = tcpip_connect_to_server(
01075                         octstr_get_cstr(pdata->host),
01076                         pdata->port,
01077                         (conn->our_host ? octstr_get_cstr(conn->our_host) : NULL));
01078     if (pdata->socket != -1) {
01079         info(0, "OISD[%s] logged in.",
01080              octstr_get_cstr(conn->id));
01081         return 0;
01082     }
01083     error(0, "OISD[%s] login failed.",
01084           octstr_get_cstr(conn->id));
01085     oisd_close_socket(pdata);
01086     packet_destroy(packet);
01087     return -1;
01088 }
01089 
01090 static int oisd_send_delivery_request(SMSCConn *conn)
01091 {
01092     PrivData *pdata = conn->data;
01093     struct packet *packet = NULL;
01094     int ret;
01095 
01096     gw_assert(conn != NULL);
01097 
01098     packet = packet_create(RETRIEVE_REQUEST, BOGUS_SEQUENCE);
01099 
01100     gw_assert(octstr_check_range(pdata->my_number, 0,
01101                                  octstr_len(pdata->my_number),
01102                                  isphonedigit));
01103     /* Originating address length */
01104     octstr_append_char(packet->data,
01105                        (char) (octstr_len(pdata->my_number) + 2));
01106     /* TON */
01107     octstr_append_char(packet->data, 0x42);
01108     /* NPI */
01109     octstr_append_char(packet->data, 0x44);
01110     /* Originating address */
01111     octstr_append(packet->data, pdata->my_number);
01112     /* Receive ready flag */
01113     octstr_append_char(packet->data, 1);
01114     /* Retrieve order */
01115     octstr_append_char(packet->data, 0);
01116 
01117     ret = oisd_request(packet, conn, NULL);
01118     packet_destroy(packet);
01119 
01120     if (ret < 0)
01121         warning(0, "OISD[%s]: Sending delivery request failed.\n",
01122                 octstr_get_cstr(conn->id));
01123 
01124     return ret;
01125 }
01126 
01127 static void oisd_destroy(PrivData *pdata)
01128 {
01129     int discarded;
01130 
01131     if (pdata == NULL)
01132         return;
01133 
01134     octstr_destroy(pdata->host);
01135     octstr_destroy(pdata->inbuffer);
01136     octstr_destroy(pdata->my_number);
01137 
01138     discarded = gwlist_len(pdata->received);
01139     if (discarded > 0)
01140         warning(0, "OISD[%s]: discarded %d received messages",
01141                 octstr_get_cstr(pdata->conn->id),
01142                 discarded);
01143 
01144     gwlist_destroy(pdata->received, msg_destroy_item);
01145     gwlist_destroy(pdata->outgoing_queue, NULL);
01146     gwlist_destroy(pdata->stopped, NULL);
01147 
01148     gw_free(pdata);
01149 }
01150 
01151 static int oisd_submit_msg(SMSCConn *conn, Msg *msg)
01152 {
01153     PrivData *pdata = conn->data;
01154     struct packet *packet;
01155     Octstr *ts = NULL;
01156     int ret;
01157 
01158     gw_assert(pdata != NULL);
01159     debug("bb.sms.oisd", 0, "OISD[%s]: sending message",
01160           octstr_get_cstr(conn->id));
01161 
01162     packet = packet_encode_message(msg, conn);
01163     if (!packet) {
01164         /* This is a protocol error. Does this help? I doubt..
01165          * But nevermind that.
01166          */
01167         bb_smscconn_send_failed(conn, msg,
01168                 SMSCCONN_FAILED_MALFORMED, octstr_create("MALFORMED"));
01169         return -1;
01170     }
01171 
01172     ret = oisd_request(packet, conn, &ts);
01173     if((ret == 0) && (ts) && DLR_IS_SUCCESS_OR_FAIL(msg->sms.dlr_mask) && !pdata->no_dlr) {
01174         dlr_add(conn->name, ts, msg);
01175     }
01176     octstr_destroy(ts);
01177     packet_destroy(packet);
01178 
01179     if (ret == -1) {
01180         bb_smscconn_send_failed(conn, msg,
01181                 SMSCCONN_FAILED_REJECTED, octstr_create("REJECTED"));
01182     }
01183     else if (ret == -2) {
01184         oisd_close_socket(pdata);
01185         bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_TEMPORARILY, NULL);
01186         mutex_lock(conn->flow_mutex);
01187         conn->status = SMSCCONN_DISCONNECTED;
01188         mutex_unlock(conn->flow_mutex);
01189     }
01190     else {
01191         bb_smscconn_sent(conn, msg, NULL);
01192     }
01193 
01194     return ret;
01195 }
01196 
01197 static int oisd_receive_msg(SMSCConn *conn, Msg **msg)
01198 {
01199     PrivData *pdata = conn->data;
01200     long ret;
01201     struct packet *packet;
01202 
01203     gw_assert(pdata != NULL);
01204 
01205     if (gwlist_len(pdata->received) > 0) {
01206         *msg = gwlist_consume(pdata->received);
01207         return 1;
01208     }
01209 
01210     if (pdata->socket < 0) {
01211         /* XXX We have to assume that smsc_send_message is
01212          * currently trying to reopen, so we have to make
01213          * this thread wait.  It should be done in a nicer
01214          * way. */
01215         return 0;
01216     }
01217 
01218     ret = read_available(pdata->socket, 0);
01219     if (ret == 0) {
01220         if (pdata->keepalive > 0 && pdata->next_ping < time(NULL)) {
01221             if (oisd_send_delivery_request(conn) < 0)
01222                 return -1;
01223         }
01224         return 0;
01225     }
01226 
01227     if (ret < 0) {
01228         warning(errno, "OISD[%s]: oisd_receive_msg: read_available failed",
01229                 octstr_get_cstr(conn->id));
01230         return -1;
01231     }
01232 
01233     /* We have some data waiting... see if it is an sms delivery. */
01234     ret = octstr_append_from_socket(pdata->inbuffer, pdata->socket);
01235 
01236     if (ret == 0) {
01237         warning(0, "OISD[%s]: oisd_receive_msg: service center closed connection.",
01238                 octstr_get_cstr(conn->id));
01239         return -1;
01240     }
01241     if (ret < 0) {
01242         warning(0, "OISD[%s]: oisd_receive_msg: read failed",
01243                 octstr_get_cstr(conn->id));
01244         return -1;
01245     }
01246 
01247 
01248     for (;;) {
01249         packet = packet_extract(pdata->inbuffer, conn);
01250         if (!packet)
01251             break;
01252 
01253         packet_check_can_receive(packet, conn);
01254         debug("bb.sms.oisd", 0, "OISD[%s]: received",
01255               octstr_get_cstr(pdata->conn->id));
01256         octstr_dump(packet->data, 0);
01257 
01258         if (packet->operation < RESPONSE)
01259             oisd_handle_request(packet, conn);
01260         else {
01261             error(0, "OISD[%s]: oisd_receive_msg: unexpected response packet",
01262                   octstr_get_cstr(conn->id));
01263             octstr_dump(packet->data, 0);
01264         }
01265 
01266         packet_destroy(packet);
01267     }
01268 
01269     if (gwlist_len(pdata->received) > 0) {
01270         *msg = gwlist_consume(pdata->received);
01271         return 1;
01272     }
01273     return 0;
01274 }
01275 
01276 static Msg *oisd_accept_delivery_report_message(struct packet *request,
01277                                                 SMSCConn *conn)
01278 {
01279     Msg *msg = NULL;
01280     Octstr *destination = NULL;
01281     Octstr *timestamp = NULL;
01282     int st_code;
01283     int code;
01284     int dest_len;
01285 
01286     /* MSISDN length */
01287     dest_len = octstr_get_char(request->data, 10);
01288     /* MSISDN */
01289     destination = octstr_copy(request->data, 10+1, dest_len);
01290     /* Accept time */
01291     timestamp = octstr_copy(request->data, 10+1+dest_len+1+4+4, 14);
01292     /* SM status */
01293     st_code = octstr_get_char(request->data, 10+1+dest_len+1+4+4+14);
01294 
01295     switch (st_code) {
01296     case 1:
01297     case 2:
01298         code = DLR_FAIL;
01299         break;
01300     case 3:   /* success */
01301         code = DLR_SUCCESS;
01302         break;
01303     case 4:
01304     case 5:
01305     case 6:
01306     default:
01307         code = 0;
01308     }
01309 
01310     if (code)
01311         msg = dlr_find(conn->name, timestamp, destination, code);
01312 
01313     octstr_destroy(destination);
01314     octstr_destroy(timestamp);
01315 
01316     return msg;
01317 }
01318 
01319 static Msg *sms_receive(SMSCConn *conn)
01320 {
01321     PrivData *pdata = conn->data;
01322     int ret;
01323     Msg *newmsg = NULL;
01324 
01325     ret = oisd_receive_msg(conn, &newmsg);
01326     if (ret == 1) {
01327         /* if any smsc_id available, use it */
01328         newmsg->sms.smsc_id = octstr_duplicate(conn->id);
01329         return newmsg;
01330     }
01331     else if (ret == 0) { /* no message, just retry... */
01332         return NULL;
01333     }
01334     /* error. reconnect. */
01335     msg_destroy(newmsg);
01336     mutex_lock(conn->flow_mutex);
01337     oisd_close_socket(pdata);
01338     conn->status = SMSCCONN_DISCONNECTED;
01339     mutex_unlock(conn->flow_mutex);
01340     return NULL;
01341 }
01342 
01343 static void io_thread (void *arg)
01344 {
01345     Msg       *msg;
01346     SMSCConn  *conn = arg;
01347     PrivData *pdata = conn->data;
01348     double    sleep = 0.0001;
01349 
01350     /* Make sure we log into our own log-file if defined */
01351     log_thread_to(conn->log_idx);
01352 
01353     /* remove messages from SMSC until we are killed */
01354     while (!pdata->quitting) {
01355 
01356         gwlist_consume(pdata->stopped); /* block here if suspended/isolated */
01357 
01358         /* check that connection is active */
01359         if (conn->status != SMSCCONN_ACTIVE) {
01360             if (oisd_login(conn) != 0) {
01361                 error(0, "OISD[%s]: Couldn't connect to SMSC (retrying in %ld seconds).",
01362                       octstr_get_cstr(conn->id),
01363                       conn->reconnect_delay);
01364                 gwthread_sleep(conn->reconnect_delay);
01365                 mutex_lock(conn->flow_mutex);
01366                 conn->status = SMSCCONN_RECONNECTING;
01367                 mutex_unlock(conn->flow_mutex);
01368                 continue;
01369             }
01370             mutex_lock(conn->flow_mutex);
01371             conn->status = SMSCCONN_ACTIVE;
01372             conn->connect_time = time(NULL);
01373             bb_smscconn_connected(conn);
01374             mutex_unlock(conn->flow_mutex);
01375         }
01376 
01377         /* receive messages */
01378         do {
01379             msg = sms_receive(conn);
01380             if (msg) {
01381                 sleep = 0;
01382                 debug("bb.sms.oisd", 0, "OISD[%s]: new message received",
01383                       octstr_get_cstr(conn->id));
01384                 bb_smscconn_receive(conn, msg);
01385             }
01386         } while (msg);
01387 
01388         /* send messages */
01389         do {
01390             msg = gwlist_extract_first(pdata->outgoing_queue);
01391             if (msg) {
01392                 sleep = 0;
01393                 if (oisd_submit_msg(conn, msg) != 0) break;
01394             }
01395         } while (msg);
01396 
01397         if (sleep > 0) {
01398 
01399             /* note that this implementations means that we sleep even
01400              * when we fail connection.. but time is very short, anyway
01401              */
01402             gwthread_sleep(sleep);
01403             /* gradually sleep longer and longer times until something starts to
01404              * happen - this of course reduces response time, but that's better than
01405              * extensive CPU usage when it is not used
01406              */
01407             sleep *= 2;
01408             if (sleep >= 2.0)
01409                 sleep = 1.999999;
01410         }
01411         else {
01412             sleep = 0.0001;
01413         }
01414     }
01415 }
01416 
01417 static int oisd_add_msg_cb (SMSCConn *conn, Msg *sms)
01418 {
01419     PrivData *pdata = conn->data;
01420     Msg *copy;
01421 
01422     copy = msg_duplicate(sms);
01423     gwlist_produce(pdata->outgoing_queue, copy);
01424     gwthread_wakeup(pdata->io_thread);
01425 
01426     return 0;
01427 }
01428 
01429 static int oisd_shutdown_cb (SMSCConn *conn, int finish_sending)
01430 {
01431     PrivData *pdata = conn->data;
01432 
01433     debug("bb.sms", 0, "Shutting down SMSCConn OISD %s (%s)",
01434           octstr_get_cstr(conn->id),
01435           finish_sending ? "slow" : "instant");
01436 
01437     /* Documentation claims this would have been done by smscconn.c,
01438        but isn't when this code is being written. */
01439     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
01440     pdata->quitting = 1;     /* Separate from why_killed to avoid locking, as
01441                               * why_killed may be changed from outside? */
01442 
01443     if (finish_sending == 0) {
01444         Msg *msg;
01445         while ((msg = gwlist_extract_first(pdata->outgoing_queue)) != NULL) {
01446             bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN, NULL);
01447         }
01448     }
01449 
01450     if (conn->is_stopped) {
01451         gwlist_remove_producer(pdata->stopped);
01452         conn->is_stopped = 0;
01453     }
01454 
01455     if (pdata->io_thread != -1) {
01456         gwthread_wakeup(pdata->io_thread);
01457         gwthread_join(pdata->io_thread);
01458     }
01459 
01460     oisd_close_socket(pdata);
01461     oisd_destroy(pdata);
01462 
01463     debug("bb.sms", 0, "SMSCConn OISD %s shut down.",
01464           octstr_get_cstr(conn->id));
01465     conn->status = SMSCCONN_DEAD;
01466     bb_smscconn_killed();
01467     return 0;
01468 }
01469 
01470 static void oisd_start_cb (SMSCConn *conn)
01471 {
01472     PrivData *pdata = conn->data;
01473 
01474     gwlist_remove_producer(pdata->stopped);
01475     /* in case there are messages in the buffer already */
01476     gwthread_wakeup(pdata->io_thread);
01477     debug("bb.sms", 0, "SMSCConn OISD %s, start called",
01478           octstr_get_cstr(conn->id));
01479 }
01480 
01481 static void oisd_stop_cb (SMSCConn *conn)
01482 {
01483     PrivData *pdata = conn->data;
01484     gwlist_add_producer(pdata->stopped);
01485     debug("bb.sms", 0, "SMSCConn OISD %s, stop called",
01486           octstr_get_cstr(conn->id));
01487 }
01488 
01489 static long oisd_queued_cb (SMSCConn *conn)
01490 {
01491     PrivData *pdata = conn->data;
01492     conn->load = (pdata ? (conn->status != SMSCCONN_DEAD ?
01493                   gwlist_len(pdata->outgoing_queue) : 0) : 0);
01494     return conn->load;
01495 }
01496 
01497 int smsc_oisd_create(SMSCConn *conn, CfgGroup *grp)
01498 {
01499     PrivData *pdata;
01500     int ok;
01501 
01502     pdata = gw_malloc(sizeof(PrivData));
01503     conn->data = pdata;
01504     pdata->conn = conn;
01505 
01506     pdata->no_dlr = 0;
01507     pdata->quitting = 0;
01508     pdata->socket = -1;
01509     pdata->received = gwlist_create();
01510     pdata->inbuffer = octstr_create("");
01511     pdata->send_seq = 1;
01512     pdata->outgoing_queue = gwlist_create();
01513     pdata->stopped = gwlist_create();
01514     gwlist_add_producer(pdata->outgoing_queue);
01515 
01516     if (conn->is_stopped)
01517         gwlist_add_producer(pdata->stopped);
01518 
01519     pdata->host = cfg_get(grp, octstr_imm("host"));
01520     if (cfg_get_integer(&(pdata->port), grp, octstr_imm("port")) == -1)
01521         pdata->port = 0;
01522     pdata->my_number = cfg_get(grp, octstr_imm("my-number"));
01523     if (cfg_get_integer(&(pdata->keepalive), grp, octstr_imm("keepalive")) == -1)
01524         pdata->keepalive = 0;
01525     if (cfg_get_integer(&(pdata->validityperiod), grp, octstr_imm("validityperiod")) == -1)
01526         pdata->validityperiod = 0;
01527 
01528     cfg_get_bool(&pdata->no_dlr, grp, octstr_imm("no-dlr"));
01529 
01530     /* Check that config is OK */
01531     ok = 1;
01532     if (pdata->host == NULL) {
01533         error(0, "OISD[%s]: Configuration file doesn't specify host",
01534               octstr_get_cstr(conn->id));
01535         ok = 0;
01536     }
01537     if (pdata->port == 0) {
01538         error(0, "OISD[%s]: Configuration file doesn't specify port",
01539               octstr_get_cstr(conn->id));
01540         ok = 0;
01541     }
01542     if (pdata->my_number == NULL && pdata->keepalive > 0) {
01543         error(0, "OISD[%s]: Configuration file doesn't specify my-number.",
01544               octstr_get_cstr(conn->id));
01545         ok = 0;
01546     }
01547 
01548     if (!ok) {
01549         oisd_destroy(pdata);
01550         return -1;
01551     }
01552 
01553     conn->name = octstr_format("OISD:%s:%d",
01554                      octstr_get_cstr(pdata->host),
01555                      pdata->port);
01556 
01557 
01558     if (pdata->keepalive > 0) {
01559         debug("bb.sms.oisd", 0, "OISD[%s]: Keepalive set to %ld seconds",
01560               octstr_get_cstr(conn->id),
01561               pdata->keepalive);
01562         pdata->next_ping = time(NULL) + pdata->keepalive;
01563     }
01564 
01565     if (pdata->validityperiod > 0) {
01566         debug("bb.sms.oisd", 0, "OISD[%s]: Validity-Period set to %ld",
01567               octstr_get_cstr(conn->id),
01568               pdata->validityperiod);
01569     }
01570 
01571     pdata->io_thread = gwthread_create(io_thread, conn);
01572 
01573     if (pdata->io_thread == -1) {
01574 
01575         error(0, "OISD[%s]: Couldn't start I/O thread.",
01576               octstr_get_cstr(conn->id));
01577         pdata->quitting = 1;
01578         gwthread_wakeup(pdata->io_thread);
01579         gwthread_join(pdata->io_thread);
01580         oisd_destroy(pdata);
01581         return -1;
01582     }
01583 
01584     conn->send_msg = oisd_add_msg_cb;
01585     conn->shutdown = oisd_shutdown_cb;
01586     conn->queued = oisd_queued_cb;
01587     conn->start_conn = oisd_start_cb;
01588     conn->stop_conn = oisd_stop_cb;
01589 
01590     return 0;
01591 }
01592 
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.