[PATCH] New SMSC driver - OIS Direct

"Dariusz Markowicz" <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <02a301c2c56d$fa354f20$d4e34cd5@oem>
Hi list,

I'd like to introduce new smsc driver for Sema Group SMSC Center G8.1 (OIS 5.8).
This implementation use direct TCP/IP access interface.

This code is strongly architecture based on CIMD2 driver, and have all
its features (dlr support etc.) and maybe bugs :). It works stable on 
the production server (oisd + cimd2 + emi2) since April 2002.

There are 3 files:
smsc_oisd.diff - new smsc_oisd.c module
gateway.diff - patch against current cvs (doc/examples/kannel.conf,
               doc/userguide/userguide.xml, gw/smsc/smsc.c, gw/smsc/smsc_p.h)

and urltrans.diff - this patch was found with Robert Galach.
It solves problem with smsbox panic when request is DLR not a message
(in smsbox using CIMD2 or this new OISD module).

Best regards,
Dariusz Markowicz
urltrans.diff (application/octet-stream, 894 B)
--- gateway/gw/urltrans.c	Thu Sep  5 14:00:59 2002
+++ gateway-custom/gw/urltrans.c	Sun Jan 26 18:28:37 2003
@@ -305,16 +305,20 @@
     if (request->sms.sms_type != report && t->type == TRANSTYPE_SENDSMS)
         return octstr_create("");
 
-    word_list = octstr_split_words(request->sms.msgdata);
-    num_words = list_len(word_list);
-
+    if (request->sms.msgdata) {
+        word_list = octstr_split_words(request->sms.msgdata);
+        num_words = list_len(word_list);
+    } else {
+    	word_list = list_create();
+    	num_words = 0;
+    }
+		
     result = octstr_create("");
 
     /* check if this is a delivery report message or not */
     if (request->sms.sms_type != report) {
         pattern = t->pattern;
     } else {
-
         /* this is a DLR message */
         reply = octstr_duplicate(request->sms.msgdata);
         url = octstr_duplicate(request->sms.dlr_url);
gateway.diff (application/octet-stream, 7.7 KB)
Index: doc/examples/kannel.conf
===================================================================
RCS file: /home/cvs/gateway/doc/examples/kannel.conf,v
retrieving revision 1.1
diff -u -r1.1 kannel.conf
--- doc/examples/kannel.conf	5 Aug 2002 21:39:28 -0000	1.1
+++ doc/examples/kannel.conf	26 Jan 2003 16:35:19 -0000
@@ -135,6 +135,15 @@
 #password = 
 
 
+# SMSC OISD
+#group = smsc
+#smsc = oisd
+#host =
+#port =
+#keepalive =
+#sender-prefix =
+
+
 # SMSBOX SETUP
 
 group = smsbox
Index: doc/userguide/userguide.xml
===================================================================
RCS file: /home/cvs/gateway/doc/userguide/userguide.xml,v
retrieving revision 1.200
diff -u -r1.200 userguide.xml
--- doc/userguide/userguide.xml	15 Jan 2003 08:52:41 -0000	1.200
+++ doc/userguide/userguide.xml	26 Jan 2003 16:35:20 -0000
@@ -2677,6 +2677,71 @@
 
 
 <sect3>
+<title>Sema Group SMS Center G8.1 (OIS 5.8)</title>
+
+<para>This implementation uses direct TCP/IP access interface.</para>
+
+<programlisting>
+group = smsc
+smsc = oisd
+host = 100.101.102.103
+port = 600
+keepalive = 25
+sender-prefix = "12345"
+</programlisting>
+
+ <informaltable frame="none">
+  <tgroup cols="3"><thead><row>
+     <entry>Variable</entry>
+     <entry>Value</entry>
+     <entry>Description</entry>
+   </row></thead><tbody>
+
+   <row><entry><literal>host (m)</literal></entry>
+     <entry><literal>hostname</literal></entry>
+     <entry valign="bottom">
+        Machine that runs the SMSC. As IP (100.100.100.100) 
+        or hostname (their.machine.here)
+     </entry></row>
+
+   <row><entry><literal>port (m)</literal></entry>
+     <entry><literal>port-number</literal></entry>
+     <entry valign="bottom">
+       Port number in the smsc host machine
+     </entry></row>
+
+   <row><entry><literal>keepalive</literal></entry>
+     <entry><literal>number</literal></entry>
+     <entry valign="bottom">
+       SMSC connection will not be left idle for longer than this many
+       seconds.  The right value to use depends on how eager the SMSC
+       is to close idle connections. 25 seconds is a good guess.  If you
+       see many unexplained reconnects, try lowering this value.
+       Set it to 0 to disable this feature.
+     </entry></row>
+
+   <row><entry><literal>sender-prefix</literal></entry>
+      <entry><literal>string</literal></entry>
+      <entry valign="bottom">
+        The number that the SMSC will add in front of the sender number
+        of all messages sent from Kannel.  If Kannel is asked to send
+        a message, it will remove this prefix from the sender number
+        so that the SMSC will add it again.  If the prefix was not present,
+        Kannel will log a warning and will not send the sender number.
+        If <literal>sender-prefix</literal> is not set, or is set
+        to <literal>"never"</literal>, then Kannel will not send the
+        sender number to the SMSC at all.
+        If you want Kannel to pass all sender numbers to the SMSC
+        unchanged, then just set <literal>sender-prefix</literal> to the
+        empty string <literal>""</literal>.
+      </entry></row>
+
+   </tbody></tgroup></informaltable>
+   
+</sect3>
+
+
+<sect3>
 <title>SM/ASI (for CriticalPath InVoke SMS Center 4.x)</title>
 
    <para>This implements Short Message/Advanced Service Interface 
Index: gw/smsc/smsc.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc.c,v
retrieving revision 1.1
diff -u -r1.1 smsc.c
--- gw/smsc/smsc.c	8 Aug 2002 17:44:38 -0000	1.1
+++ gw/smsc/smsc.c	26 Jan 2003 16:35:20 -0000
@@ -130,6 +130,15 @@
     smsc->at_validityperiod = NULL;
     smsc->at_alt_dcs = 0;
 
+    /* SEMA SMSC G8.1 OIS 5.8 (TCP/IP Direct Access) */
+    smsc->oisd_hostname = NULL;
+    smsc->oisd_port = -1;
+    smsc->oisd_send_seq = 0;
+    smsc->oisd_inbuffer = NULL;
+    smsc->oisd_received = NULL;
+    smsc->oisd_error = 0;
+    smsc->oisd_next_ping = 0;
+
     /* add new SMSCes here */
 
     /* Memory */
@@ -189,6 +198,12 @@
     list_destroy(smsc->at_received, NULL);
     gw_free(smsc->at_inbuffer);
 
+    /* SEMA SMSC G8.1 OIS 5.8 (Direct Access) */
+    octstr_destroy(smsc->oisd_hostname);
+    octstr_destroy(smsc->oisd_inbuffer);
+    octstr_destroy(smsc->sender_prefix);
+    list_destroy(smsc->oisd_received, NULL);
+
     /* add new SMSCes here */
 
     /* Other fields */
@@ -239,6 +254,11 @@
             goto error;
         break;
 
+    case SMSC_TYPE_OISD:
+        if (oisd_submit_msg(smsc, msg) == -1)
+            goto error;
+        break;
+
         /* add new SMSCes here */
 
     default:
@@ -301,6 +321,12 @@
             goto error;
         break;
 
+    case SMSC_TYPE_OISD:
+        ret = oisd_receive_msg(smsc, msg);
+        if (ret == -1)
+            goto error;
+        break;
+
     default:
         goto error;
 
@@ -364,6 +390,12 @@
             goto error;
         break;
 
+    case SMSC_TYPE_OISD:
+        ret = oisd_pending_smsmessage(smsc);
+        if (ret == -1)
+            goto error;
+        break;
+
     default:
         goto error;
     }
@@ -512,6 +544,8 @@
     	typeno = SMSC_TYPE_OIS;
     else if (octstr_compare(type, octstr_imm("at")) == 0)
     	typeno = SMSC_TYPE_AT;
+    else if (octstr_compare(type, octstr_imm("oisd")) == 0)
+    	typeno = SMSC_TYPE_OISD;
     else {
 	error(0, "Unknown SMSC type '%s'", octstr_get_cstr(type));
 	octstr_destroy(type);
@@ -644,6 +678,16 @@
 			       alt_dcs);
         break;
 
+    case SMSC_TYPE_OISD:
+        if (host == NULL || port == 0)
+            error(0, "Required field missing for OISD center.");
+        else
+            smsc = oisd_open(host,
+	    	    	      port, 
+			      keepalive,
+                              sender_prefix);
+        break;
+
         /* add new SMSCes here */
 
     default: 		/* Unknown SMSC type */
@@ -723,6 +767,9 @@
     case SMSC_TYPE_AT:
         ret = at_reopen(smsc);
 	break;
+    case SMSC_TYPE_OISD:
+        ret = oisd_reopen(smsc);
+	break;
         /* add new SMSCes here */
     default: 		/* Unknown SMSC type */
         ret = -2; 		/* no use */
@@ -781,6 +828,11 @@
 
     case SMSC_TYPE_AT:
         if (at_close(smsc) == -1)
+            errors = 1;
+        break;
+
+    case SMSC_TYPE_OISD:
+        if (oisd_close(smsc) == -1)
             errors = 1;
         break;
 
Index: gw/smsc/smsc_p.h
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_p.h,v
retrieving revision 1.1
diff -u -r1.1 smsc_p.h
--- gw/smsc/smsc_p.h	11 Aug 2002 14:21:16 -0000	1.1
+++ gw/smsc/smsc_p.h	26 Jan 2003 16:35:20 -0000
@@ -29,7 +29,8 @@
 	SMSC_TYPE_EMI_IP,
 	SMSC_TYPE_SEMA_X28,
 	SMSC_TYPE_OIS,
-	SMSC_TYPE_AT
+	SMSC_TYPE_AT,
+	SMSC_TYPE_OISD
 };
 
 /*
@@ -140,7 +141,7 @@
         char *ois_buffer;
         size_t ois_bufsize;
         size_t ois_buflen;
-    
+
 	/* AT Commands (wireless modems...) */
 	char *at_serialdevice;
 	int at_fd;
@@ -151,6 +152,16 @@
 	char *at_validityperiod;
 	int at_alt_dcs;
 	
+	/* SEMA SMSC G8.1 OIS 5.8 (TCP/IP Direct Access) */
+	Octstr *oisd_hostname;
+	int oisd_port;
+	unsigned long oisd_send_seq;
+	unsigned long oisd_receive_seq;
+	Octstr *oisd_inbuffer;
+	List *oisd_received;
+	int oisd_error;
+	time_t oisd_next_ping;
+
 	/* For buffering input. */
 	char *buffer;
 	size_t bufsize;
@@ -257,5 +268,15 @@
 int at_submit_msg(SMSCenter *smsc, Msg *msg);
 int at_receive_msg(SMSCenter *smsc, Msg **msg);
 
+
+/*
+ * Interface to Sema SMS centers using OIS 5.8 (Direct Access)
+ */
+SMSCenter *oisd_open(Octstr *hostname, int port, int keepalive, Octstr *sender_prefix);
+int oisd_reopen(SMSCenter *smsc);
+int oisd_close(SMSCenter *smsc);
+int oisd_pending_smsmessage(SMSCenter *smsc);
+int oisd_submit_msg(SMSCenter *smsc, Msg *msg);
+int oisd_receive_msg(SMSCenter *smsc, Msg **msg);
 
 #endif
smsc_oisd.diff (application/octet-stream, 35.3 KB)
diff -r -u -N -P --exclude=CVS gateway/gw/smsc/smsc_oisd.c gateway-custom/gw/smsc/smsc_oisd.c
--- gateway/gw/smsc/smsc_oisd.c	Thu Jan  1 01:00:00 1970
+++ gateway-custom/gw/smsc/smsc_oisd.c	Sun Jan 26 15:44:06 2003
@@ -0,0 +1,1244 @@
+/*
+ * smsc.oisd.c - Driver for Sema Group SMS Center G8.1 (OIS 5.8)
+ * using direct TCP/IP access interface
+ *
+ * Dariusz Markowicz <[email protected]>
+ *
+ * This code is based on the CIMD2 module design.
+ */
+
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+
+#include <unistd.h>
+
+#include "gwlib/gwlib.h"
+#include "smsc_p.h"
+#include "sms.h"
+#include "dlr.h"
+
+/* Microseconds before giving up on a request */
+#define RESPONSE_TIMEOUT (10 * 1000000)
+
+enum {
+    INVOKE = 0,
+    RESULT = 1
+};
+
+/* Textual names for the operation codes defined by the OISD spec. */
+/* If you make changes here, also change the operation table. */
+enum {
+    SUBMIT_SM = 0,
+    STATUS_REPORT = 4,
+    DELIVER_SM = 9,
+    RETRIEVE_REQUEST = 11,
+
+    /* Not a request; add to any request to make it a response */
+    RESPONSE = 50
+};
+
+/* Helper function to check P_ADDRESS type */
+static int isphonedigit(int c)
+{
+    return isdigit(c) || c == '+' || c == '-';
+}
+
+static const int parm_valid_address(Octstr *value)
+{
+    return octstr_check_range(value, 0, octstr_len(value), isphonedigit);
+}
+
+/***************************************************************************/
+/* Some functions to look up information about operation codes             */
+/***************************************************************************/
+
+static int operation_find(int operation);
+static Octstr *operation_name(int operation);
+static const int operation_can_send(int operation);
+static const int operation_can_receive(int operation);
+
+static const struct
+{
+    unsigned char *name;
+    int code;
+    int can_send;
+    int can_receive;
+}
+operations[] = {
+    { "Submit SM", SUBMIT_SM, 1, 0 },
+    { "Status Report", STATUS_REPORT, 0, 1 },
+    { "Deliver SM", DELIVER_SM, 0, 1 },
+    { "Retrieve Request", RETRIEVE_REQUEST, 1, 0 },
+
+    { NULL, 0, 0, 0 }
+};
+
+static int operation_find(int operation)
+{
+    int i;
+
+    for (i = 0; operations[i].name != NULL; i++) {
+        if (operations[i].code == operation)
+            return i;
+    }
+
+    return -1;
+}
+
+/* Return a human-readable representation of this operation code */
+static Octstr *operation_name(int operation)
+{
+    int i;
+
+    i = operation_find(operation);
+    if (i >= 0)
+        return octstr_create(operations[i].name);
+
+    if (operation >= RESPONSE) {
+        i = operation_find(operation - RESPONSE);
+        if (i >= 0) {
+            Octstr *name = octstr_create(operations[i].name);
+            octstr_append_cstr(name, " response");
+            return name;
+        }
+    }
+
+    /* Put the operation number here when we have octstr_format */
+    return octstr_create("(unknown)");
+}
+
+/* Return true if a OISD client may send this operation */
+static const int operation_can_send(int operation)
+{
+    int i = operation_find(operation);
+
+    if (i >= 0)
+        return operations[i].can_send;
+
+    /* If we can receive the request, then we can send the response. */
+    if (operation >= RESPONSE)
+        return operation_can_receive(operation - RESPONSE);
+
+    return 0;
+}
+
+
+/* Return true if a OISD server may send this operation */
+static const int operation_can_receive(int operation)
+{
+    int i = operation_find(operation);
+
+    if (i >= 0)
+        return operations[i].can_receive;
+
+    /* If we can send the request, then we can receive the response. */
+    if (operation >= RESPONSE)
+        return operation_can_send(operation - RESPONSE);
+
+    return 0;
+}
+
+/***************************************************************************
+ * Packet encoding/decoding functions.  They handle packets at the octet   *
+ * level, and know nothing of the network.                                 *
+ ***************************************************************************/
+
+struct packet
+{
+    unsigned long opref; /* operation reference */
+    int operation;
+    Octstr *data;        /* Encoded packet */
+};
+
+/* A reminder that packets are created without a valid sequence number */
+#define BOGUS_SEQUENCE 0
+
+static Msg *oisd_accept_delivery_report_message(struct packet *request,
+                                                SMSCenter *smsc);
+static Msg *oisd_submit_failed(SMSCenter *smsc, Msg* msg, Octstr* ts);
+
+static void packet_parse_header(struct packet *packet)
+{
+    packet->opref = (octstr_get_char(packet->data, 3) << 24)
+                  | (octstr_get_char(packet->data, 2) << 16)
+                  | (octstr_get_char(packet->data, 1) << 8)
+                  | (octstr_get_char(packet->data, 0));
+
+    packet->operation = octstr_get_char(packet->data, 5);
+    if (octstr_get_char(packet->data, 4) == 1)
+        packet->operation += RESPONSE;
+}
+
+
+/*
+ * Accept an Octstr containing one packet, build a struct packet around
+ * it, and return that struct.  The Octstr is stored in the struct.
+ * No error checking is done here yet.
+ */
+static struct packet *packet_parse(Octstr *packet_data)
+{
+    struct packet *packet;
+
+    packet = gw_malloc(sizeof(*packet));
+    packet->data = packet_data;
+
+    /* Fill in packet->operation and packet->opref */
+    packet_parse_header(packet);
+
+    return packet;
+}
+
+/* Deallocate this packet */
+static void packet_destroy(struct packet *packet)
+{
+    if (packet != NULL) {
+        octstr_destroy(packet->data);
+        gw_free(packet);
+    }
+}
+
+/*
+ * Find the first packet in "in", delete it from "in", and return it as
+ * a struct.  Return NULL if "in" contains no packet.  Always delete
+ * leading non-packet data from "in".
+ */
+static struct packet *packet_extract(SMSCenter *smsc)
+{
+    Octstr *packet, *in;
+    int size, i;
+    static char s[4][4] = {
+        { 0x01, 0x0b, 0x00, 0x00 },
+        { 0x01, 0x00, 0x00, 0x00 },
+        { 0x00, 0x04, 0x00, 0x00 },
+        { 0x00, 0x09, 0x00, 0x00 }
+    }; /* msgtype, oper, 0, 0 */
+    char known_bytes[4];
+
+    in = smsc->oisd_inbuffer;
+
+    if (octstr_len(in) < 10)
+        return NULL;
+    octstr_get_many_chars(known_bytes, in, 4, 4);
+    /* Find s, and delete everything up to it. */
+    /* If packet starts with one of s, it should be good packet */
+    for (i = 0; i < 4; i++) {
+        if (memcmp(s[i], known_bytes, 4) == 0)
+            break;
+    }
+
+    if (i >= 4) {
+        debug("bb.sms.oisd", 0, "oisd:packet_extract:wrong packet");
+        octstr_dump(in, 0);
+
+        oisd_reopen(smsc);
+        return NULL;
+    }
+
+    /* Find end of packet */
+    size = (octstr_get_char(in, 9) << 8) | octstr_get_char(in, 8);
+
+    if (size + 10 > octstr_len(in))
+        return NULL;
+
+    packet = octstr_copy(in, 0, size + 10);
+    octstr_delete(in, 0, size + 10);
+
+    return packet_parse(packet);
+}
+
+static void packet_check_can_receive(struct packet *packet)
+{
+    gw_assert(packet != NULL);
+
+    if (!operation_can_receive(packet->operation)) {
+        Octstr *name = operation_name(packet->operation);
+        warning(0, "OISD SMSC sent us %s request",
+                octstr_get_cstr(name));
+        octstr_destroy(name);
+    }
+}
+
+static int oisd_expand_gsm7_to_bits(char *bits, Octstr *raw7)
+{
+    int i, j, k;
+    int len;
+    char ch;
+
+    len = octstr_len(raw7) * 7; /* number of bits in the gsm 7-bit msg */
+
+    for (j = i = 0; j < len; ++i) {
+        ch = octstr_get_char(raw7, i);
+        for (k = 0; k < 8; ++k) {
+            bits[j++] = (char) (ch & 0x01);
+            ch >>= 1;
+        }
+    }
+
+    return j;
+}
+
+static char oisd_expand_gsm7_from_bits(const char *bits, int pos)
+{
+    int i;
+    char ch;
+
+    pos *= 7; /* septet position in bits */
+    ch = '\0';
+    for (i = 6; i >= 0; --i) {
+        ch <<= 1;
+        ch |= bits[pos + i];
+    }
+
+    return ch;
+}
+
+static Octstr *oisd_expand_gsm7(Octstr *raw7)
+{
+    Octstr *raw8;
+    int i, len;
+    char *bits;
+
+    raw8 = octstr_create("");
+    bits = gw_malloc(8 * octstr_len(raw7) + 1);
+
+    oisd_expand_gsm7_to_bits(bits, raw7);
+    len = octstr_len(raw7);
+
+    for (i = 0; i < len; ++i) {
+        octstr_append_char(raw8, oisd_expand_gsm7_from_bits(bits, i));
+    }
+
+    debug("bb.sms.oisd", 0, "oisd_expand_gsm7 raw8=%s ", octstr_get_cstr(raw8));
+
+    gw_free(bits);
+
+    return raw8;
+}
+
+static void oisd_shrink_gsm7(Octstr *str)
+{
+    Octstr *result;
+    int len, i;
+    int numbits, value;
+
+    result = octstr_create("");
+    len = octstr_len(str);
+    value = 0;
+    numbits = 0;
+    for (i = 0; i < len; i++) {
+        value += octstr_get_char(str, i) << numbits;
+        numbits += 7;
+        if (numbits >= 8) {
+            octstr_append_char(result, value & 0xff);
+            value >>= 8;
+            numbits -= 8;
+        }
+    }
+    if (numbits > 0)
+        octstr_append_char(result, value);
+    octstr_delete(str, 0, LONG_MAX);
+    octstr_append(str, result);
+    octstr_destroy(result);
+}
+
+/****************************************************************************
+ * Packet encoding functions.  They do not allow the creation of invalid
+ * OISD packets.
+ ***************************************************************************/
+
+/* Build a new packet struct with this operation code and sequence number. */
+static struct packet *packet_create(int operation, unsigned long opref)
+{
+    struct packet *packet;
+    unsigned char header[10];
+
+    packet = gw_malloc(sizeof(*packet));
+    packet->operation = operation;
+    packet->opref = opref;
+
+    /* Opref */
+    header[0] = opref & 0xff;
+    header[1] = (opref >> 8) & 0xff;
+    header[2] = (opref >> 16) & 0xff;
+    header[3] = (opref >> 24) & 0xff;
+
+    /* Message Type & Operation */
+    if (operation > RESPONSE) {
+        header[4] = RESULT;
+        header[5] = operation - RESPONSE;
+    } else {
+        header[4] = INVOKE;
+        header[5] = operation;
+    }
+
+    /* Unused */
+    header[6] = 0;
+    header[7] = 0;
+
+    /* Data Size */
+    header[8] = 0;
+    header[9] = 0;
+
+    packet->data = octstr_create_from_data(header, 10);
+
+    return packet;
+}
+
+static void packet_set_sequence(struct packet *packet, unsigned long opref)
+{
+    gw_assert(packet != NULL);
+
+    octstr_set_char(packet->data, 0, opref & 0xff);
+    octstr_set_char(packet->data, 1, (opref >> 8) & 0xff);
+    octstr_set_char(packet->data, 2, (opref >> 16) & 0xff);
+    octstr_set_char(packet->data, 3, (opref >> 24) & 0xff);
+    packet->opref = opref;
+}
+
+static struct packet *packet_encode_message(Msg *msg, Octstr *sender_prefix)
+{
+    struct packet *packet;
+    int dcs;
+    int so = 0;
+    int udhlen7, udhlen8;
+    int msglen7, msglen8;
+    Octstr *udhdata = NULL;
+    Octstr *msgdata = NULL;
+
+    gw_assert(msg != NULL);
+    gw_assert(msg->type == sms);
+    gw_assert(msg->sms.receiver != NULL);
+
+    dcs = fields_to_dcs(msg, 0);
+    if (msg->sms.sender == NULL)
+        msg->sms.sender = octstr_create("");
+
+    if (!parm_valid_address(msg->sms.receiver)) {
+        warning(0, "oisd_submit_msg: non-digits in "
+                "destination phone number '%s', discarded",
+                octstr_get_cstr(msg->sms.receiver));
+        return NULL;
+    }
+
+    if (!parm_valid_address(msg->sms.sender)) {
+        warning(0, "oisd_submit_msg: non-digits in "
+                "originating phone number '%s', discarded",
+                octstr_get_cstr(msg->sms.sender));
+        return NULL;
+    }
+
+    packet = packet_create(SUBMIT_SM, BOGUS_SEQUENCE);
+
+    gw_assert(octstr_check_range(msg->sms.receiver, 0,
+                                 octstr_len(msg->sms.receiver), isphonedigit));
+    /* MSISDN length */
+    octstr_append_char(packet->data,
+                       (unsigned char) octstr_len(msg->sms.receiver));
+
+    /* MSISDN */
+    octstr_append(packet->data, msg->sms.receiver);
+
+    /* Duplicate msg. behaviour */
+    /* 1=reject duplicates, 2=allow duplicates */
+    octstr_append_char(packet->data, (char) 2);
+
+    /* SME ref. no. unused in this protocol implementation, but set */
+    octstr_append_char(packet->data, (char) 0);
+    octstr_append_char(packet->data, (char) 0);
+    octstr_append_char(packet->data, (char) 0);
+    octstr_append_char(packet->data, (char) 0);   
+
+    /* Priority 0=high, 1=normal */
+    octstr_append_char(packet->data, (char) 1);
+    gw_assert(octstr_check_range(msg->sms.sender, 0,
+                                 octstr_len(msg->sms.sender), isphonedigit));
+
+    /* Originating address length */
+    octstr_append_char(packet->data,
+                       (unsigned char) (octstr_len(msg->sms.sender) + 2));
+
+    /* XXX: GSM operator dependent ? */
+    /* TON */
+    octstr_append_char(packet->data, (char) 0x42);
+
+    /* NPI */
+    octstr_append_char(packet->data, (char) 0x44);
+
+    /* Originating address */
+    octstr_append(packet->data, msg->sms.sender);
+
+    /* Validity period type 0=none, 1=absolute, 2=relative */
+    octstr_append_char(packet->data, (char) 0);
+
+    /* Data coding scheme */
+    octstr_append_char(packet->data, (char) dcs);
+
+    /* Status report request */
+    if (msg->sms.dlr_mask & 0x07)
+        octstr_append_char(packet->data, (char) 7);
+    else
+        octstr_append_char(packet->data, (char) 0);
+
+    /* Protocol id 0=default */
+    octstr_append_char(packet->data, (char) 0);
+
+    if (octstr_len(msg->sms.udhdata))
+        so |= 0x02;
+    if (msg->sms.coding == DC_8BIT)
+        so |= 0x10;
+
+    /* Submission options */
+    octstr_append_char(packet->data, (char) so);
+
+    udhlen8 = octstr_len(msg->sms.udhdata);
+    msglen8 = octstr_len(msg->sms.msgdata);
+
+    udhdata = octstr_duplicate(msg->sms.udhdata);
+    msgdata = octstr_duplicate(msg->sms.msgdata);
+
+    if (msg->sms.coding == DC_7BIT) {
+        charset_latin1_to_gsm(msgdata);
+        oisd_shrink_gsm7(msgdata);
+    }
+
+    /* calculate lengths */
+
+    udhlen7 = octstr_len(udhdata);
+    msglen7 = octstr_len(msgdata);
+
+    /* copy text */
+
+    /*
+     * debug("bb.sms.oisd", 0, "packet_encode_message udhlen8=%d, msglen8=%d",
+     *       udhlen8, msglen8);
+     * debug("bb.sms.oisd", 0, "packet_encode_message udhlen7=%d, msglen7=%d",
+     *       udhlen7, msglen7);
+     */
+
+    octstr_append_char(packet->data, (unsigned char) (udhlen8 + msglen8));
+    octstr_append_char(packet->data, (unsigned char) (udhlen7 + msglen7));
+
+    octstr_append(packet->data, udhdata);
+    octstr_append(packet->data, msgdata);
+
+    /* Sub-logical SME number */
+    octstr_append_char(packet->data, (char) 0);
+    octstr_append_char(packet->data, (char) 0);
+
+    octstr_destroy(udhdata);
+    octstr_destroy(msgdata);
+
+    return packet;
+}
+
+/***************************************************************************
+ * Protocol functions.  These implement various transactions.              *
+ ***************************************************************************/
+
+/* Give this packet a proper sequence number for sending. */
+static void packet_set_send_sequence(struct packet *packet, SMSCenter *smsc)
+{
+    gw_assert(smsc != NULL);
+
+    packet_set_sequence(packet, smsc->oisd_send_seq);
+    smsc->oisd_send_seq++;
+}
+
+static struct packet *oisd_get_packet(SMSCenter *smsc, Octstr **ts)
+{
+    struct packet *packet = NULL;
+
+    gw_assert(smsc != NULL);
+
+    /* If packet is already available, don't try to read anything */
+    packet = packet_extract(smsc);
+
+    while (packet == NULL) {
+        if (read_available(smsc->socket, RESPONSE_TIMEOUT) != 1) {
+            warning(0, "OISD SMSCenter is not responding");
+            return NULL;
+        }
+
+        if (octstr_append_from_socket(smsc->oisd_inbuffer,
+                                      smsc->socket) <= 0) {
+            error(0, "oisd_get_packet: read failed");
+            return NULL;
+        }
+
+        packet = packet_extract(smsc);
+    }
+
+    packet_check_can_receive(packet);
+    if (ts)
+        *ts = octstr_copy(packet->data, 15, 14);
+
+    if (smsc->keepalive > 0)
+        smsc->oisd_next_ping = time(NULL) + smsc->keepalive;
+
+    return packet;
+}
+
+/*
+ * Acknowledge a request. The OISD spec only defines positive responses
+ * to the server, because the server is perfect.
+ */
+static void oisd_send_response(struct packet *request, SMSCenter *smsc)
+{
+    struct packet *response;
+    int len;
+
+    gw_assert(request != NULL);
+    gw_assert(request->operation < RESPONSE);
+
+    response = packet_create(request->operation + RESPONSE, request->opref);
+
+    octstr_append_char(response->data, (char) 0);
+
+    len = octstr_len(response->data) - 10;
+    if (len < 1)
+        goto error;
+
+    octstr_set_char(response->data, 8, len & 0xff); /* Data Size */
+    octstr_set_char(response->data, 9, (len >> 8) & 0xff);
+
+    debug("bb.sms.oisd", 0, "oisd_send_response.");
+
+    /* Don't check errors here because if there is something
+     * wrong with the socket, the main loop will detect it. */
+    octstr_write_to_socket(smsc->socket, response->data);
+
+error:
+    packet_destroy(response);
+}
+
+static Msg *oisd_accept_message(struct packet *request, SMSCenter *smsc)
+{
+    Msg *message = NULL;
+    Octstr *destination = NULL;
+    Octstr *origin = NULL;
+    Octstr *UDH = NULL;
+    Octstr *text = NULL;
+    int DCS;
+    int dest_len;
+    int origin_len;
+    int add_info;
+    int msglen7, msglen8;
+
+    /* See GSM 03.38.  The bit patterns we can handle are:
+     *   000xyyxx  Uncompressed text, yy indicates alphabet.
+     *                   yy = 00, default alphabet
+     *                   yy = 01, 8-bit data
+     *                   yy = 10, UCS2 (can't handle yet)
+     *                   yy = 11, reserved
+     *   1111xyxx  Data, y indicates alphabet.
+     *                   y = 0, default alphabet
+     *                   y = 1, 8-bit data
+     */
+
+    /*
+     * Destination addr. and Originating addr. w/o TOA
+     */
+
+    /* Destination addr. length */
+    dest_len = octstr_get_char(request->data, 10);
+    
+    /* Destination addr. */
+    destination = octstr_copy(request->data, 11+2, dest_len-2);
+    
+    /* Originating addr. length */
+    origin_len = octstr_get_char(request->data, 11+dest_len+4);
+
+    /* Originating addr. */
+    origin = octstr_copy(request->data, 11+dest_len+5+2, origin_len-2);
+
+    DCS = octstr_get_char(request->data, 11+dest_len+5+origin_len);
+
+    add_info = octstr_get_char(request->data, 11+dest_len+5+origin_len+2);
+
+    msglen7 = octstr_get_char(request->data, 11+dest_len+5+origin_len+3);
+    msglen8 = octstr_get_char(request->data, 11+dest_len+5+origin_len+4);
+
+    switch (DCS) {
+    case 0x00:  /* gsm7 */
+        if (add_info & 0x02) {
+            text = oisd_expand_gsm7(octstr_copy(request->data,
+                                    11+dest_len+5+origin_len+5, msglen7));
+            UDH = octstr_create("");
+            warning(0, "oisd_accept_message: 7-bit UDH ?");
+        } else {
+            text = oisd_expand_gsm7(octstr_copy(request->data,
+                                    11+dest_len+5+origin_len+5, msglen7));
+            charset_gsm_to_latin1(text);
+            UDH = octstr_create("");
+        }
+        break;
+    default:  /* 0xf4, 0xf5, 0xf6, 0xf7; 8bit to disp, mem, sim or term */
+        if (add_info & 0x02) {
+            text = octstr_create("");
+            UDH = octstr_copy(request->data,
+                              11+dest_len+5+origin_len+5, msglen8);
+        } else {
+            text = octstr_copy(request->data,
+                               11+dest_len+5+origin_len+5, msglen8);
+            UDH = octstr_create("");
+        }
+        break;
+    }
+
+    /* Code elsewhere in the gateway always expects the sender and
+     * receiver fields to be filled, so we discard messages that
+     * lack them.  If they should not be discarded, then the code
+     * handling sms messages should be reviewed.  -- RB */
+    if (!destination || octstr_len(destination) == 0) {
+        info(0, "OISD: Got SMS without receiver, discarding.");
+        goto error;
+    }
+
+    if (!origin || octstr_len(origin) == 0) {
+        info(0, "OISD: Got SMS without sender, discarding.");
+        goto error;
+    }
+
+    if ((!text || octstr_len(text) == 0) && (!UDH || octstr_len(UDH) == 0)) {
+        info(0, "OISD: Got empty SMS, ignoring.");
+        goto error;
+    }
+
+    message = msg_create(sms);
+    if (!dcs_to_fields(&message, DCS)) {
+        /* XXX Should reject this message ? */
+        debug("OISD", 0, "Invalid DCS");
+        dcs_to_fields(&message, 0);
+    }
+    message->sms.sender = origin;
+    message->sms.receiver = destination;
+    if (UDH) {
+        message->sms.udhdata = UDH;
+    }
+    message->sms.msgdata = text;
+
+    debug("OISD", 0, "oisd_accept_message");
+    return message;
+
+error:
+    msg_destroy(message);
+    octstr_destroy(destination);
+    octstr_destroy(origin);
+    octstr_destroy(UDH);
+    octstr_destroy(text);
+    return NULL;
+}
+
+/* Deal with a request from the OISD server, and acknowledge it. */
+static void oisd_handle_request(struct packet *request, SMSCenter *smsc)
+{
+    Msg *message = NULL;
+
+    /* TODO: Check if the sequence number of this request is what we
+     * expected. */
+
+    debug("OISD", 0, "oisd_handle_request");
+
+    if (request->operation == STATUS_REPORT) {
+        message = oisd_accept_delivery_report_message(request, smsc);
+        if (message) {
+            list_append(smsc->oisd_received, message);
+        }
+    } else if (request->operation == DELIVER_SM) {
+        message = oisd_accept_message(request, smsc);
+        if (message) {
+            list_append(smsc->oisd_received, message);
+        }
+    }
+
+    oisd_send_response(request, smsc);
+}
+
+/* Send a request and wait for the ack.  If the other side responds with
+ * an error code, attempt to correct and retry. 
+ * If other packets arrive while we wait for the ack, handle them.
+ *
+ * Return -1 if the SMSC refused the request.  Return -2 for other
+ * errors, such as being unable to send the request at all.  If the
+ * function returns -2, the caller would do well to try to reopen the
+ * connection.
+ *
+ * The SMSCenter must be already open.
+ */
+static int oisd_request(struct packet *request, SMSCenter *smsc, Octstr **ts)
+{
+    int ret;
+    struct packet *reply = NULL;
+    int errorcode;
+    int tries = 0;
+    int len;
+
+    gw_assert(smsc != NULL);
+    gw_assert(request != NULL);
+    gw_assert(operation_can_send(request->operation));
+
+    if (smsc->socket < 0) {
+        warning(0, "oisd_request: socket not open.");
+        goto io_error;
+    }
+
+    len = octstr_len(request->data) - 10;
+    if (len < 1)
+        goto error;
+
+    octstr_set_char(request->data, 8, len & 0xff); /* Data Size */
+    octstr_set_char(request->data, 9, (len >> 8) & 0xff);
+
+retransmit:
+    packet_set_send_sequence(request, smsc);
+
+    ret = octstr_write_to_socket(smsc->socket, request->data);
+    if (ret < 0)
+        goto io_error;
+
+next_reply:
+    reply = oisd_get_packet(smsc, ts);
+    if (!reply)
+        goto io_error;
+
+    /* The server sent us a request.  Handle it, then wait for
+     * a new reply. */
+    if (reply->operation < RESPONSE) {
+        oisd_handle_request(reply, smsc);
+        packet_destroy(reply);
+        goto next_reply;
+    }
+
+    if (reply->opref != request->opref) {
+        /* We got a response to a different request number than
+         * what we send.  Strange. */
+        warning(0, "OISD: response had unexpected sequence number; ignoring.\n");
+        goto next_reply;
+    }
+
+    if (reply->operation != request->operation + RESPONSE) {
+        /* We got a response that didn't match our request */
+        Octstr *request_name = operation_name(request->operation);
+        Octstr *reply_name = operation_name(reply->operation);
+        warning(0, "OISD: %s request got a %s",
+                octstr_get_cstr(request_name),
+                octstr_get_cstr(reply_name));
+        octstr_destroy(request_name);
+        octstr_destroy(reply_name);
+        octstr_dump(reply->data, 0);
+        goto retry;
+    }
+
+    errorcode = octstr_get_char(reply->data, 10); /* Result */
+
+    if (errorcode > 0)
+        goto error;
+
+    /* The reply passed all the checks... looks like the SMSC accepted
+     * our request! */
+    packet_destroy(reply);
+    return 0;
+
+io_error:
+    packet_destroy(reply);
+    return -2;
+
+error:
+    packet_destroy(reply);
+    return -1;
+
+retry:
+    if (++tries < 3) {
+        warning(0, "Retransmitting (take %d)", tries);
+        goto retransmit;
+    }
+    warning(0, "Giving up.");
+    goto io_error;
+}
+
+/* Close the SMSC socket without fanfare. */
+static void oisd_close_socket(SMSCenter *smsc)
+{
+    gw_assert(smsc != NULL);
+
+    if (smsc->socket < 0)
+        return ;
+
+    if (close(smsc->socket) < 0)
+        warning(errno, "error closing OISD socket");
+    smsc->socket = -1;
+}
+
+/*
+ * Open a socket to the SMSC, send a login packet, and wait for ack.
+ * This may block.  Return 0 for success, or -1 for failure.
+ * Make sure the socket is closed before calling this function, otherwise
+ * we will leak fd's.
+ */
+static int oisd_login(SMSCenter *smsc)
+{
+    gw_assert(smsc != NULL);
+
+    if (smsc->socket >= 0) {
+        warning(0, "oisd_login: socket was already open; closing");
+        oisd_close_socket(smsc);
+    }
+
+    smsc->socket = tcpip_connect_to_server(octstr_get_cstr(smsc->oisd_hostname),
+                                           smsc->oisd_port, NULL);
+    /* XXX add interface_name if required */
+    if (smsc->socket == -1)
+        goto error;
+
+    info(0, "%s logged in.", smsc_name(smsc));
+
+    return 0;
+
+error:
+    error(0, "oisd_login failed");
+    oisd_close_socket(smsc);
+    return -1;
+}
+
+static int oisd_send_delivery_request(SMSCenter *smsc)
+{
+    struct packet *packet = NULL;
+    int ret;
+
+    gw_assert(smsc != NULL);
+
+    /* debug("bb.sms.oisd", 0, "Sending OISD retrieve request."); */
+    packet = packet_create(RETRIEVE_REQUEST, BOGUS_SEQUENCE);
+
+    gw_assert(octstr_check_range(smsc->sender_prefix, 0,
+                                 octstr_len(smsc->sender_prefix),
+                                 isphonedigit));
+    /* Originating address length */
+    octstr_append_char(packet->data,
+                       (char) (octstr_len(smsc->sender_prefix) + 2));
+    /* TON */
+    octstr_append_char(packet->data, (char) 0x42);
+    /* NPI */
+    octstr_append_char(packet->data, (char) 0x44);
+    /* Originating address */
+    octstr_append(packet->data, smsc->sender_prefix);
+    /* Receive ready flag */
+    octstr_append_char(packet->data, (char) 1);
+    /* Retrieve order */
+    octstr_append_char(packet->data, (char) 0);
+
+    ret = oisd_request(packet, smsc, NULL);
+    packet_destroy(packet);
+
+    if (ret < 0)
+        warning(0, "OISD: Sending delivery request failed.\n");
+
+    return ret;
+}
+
+/***************************************************************************/
+/* SMSC Interface, as defined in smsc_interface.def                        */
+/***************************************************************************/
+
+SMSCenter *oisd_open(Octstr *hostname, int port, int keepalive,
+                     Octstr *sender_prefix)
+{
+    SMSCenter *smsc = NULL;
+
+    smsc = smscenter_construct();
+    gw_assert(smsc != NULL);
+
+    smsc->type = SMSC_TYPE_OISD;
+    smsc->keepalive = keepalive;
+    smsc->oisd_hostname = octstr_duplicate(hostname);
+    smsc->oisd_port = port;
+    smsc->sender_prefix = octstr_duplicate(sender_prefix);
+    sprintf(smsc->name, "OISD:%s:%d", octstr_get_cstr(hostname), port);
+    smsc->oisd_received = list_create();
+    smsc->oisd_inbuffer = octstr_create("");
+    smsc->oisd_error = 0;
+    if (keepalive > 0)
+        smsc->oisd_next_ping = time(NULL) + keepalive;
+
+    if (oisd_login(smsc) < 0)
+        goto error;
+
+    return smsc;
+
+error:
+    error(0, "oisd_open failed");
+    smscenter_destruct(smsc);
+    return NULL;
+}
+
+int oisd_reopen(SMSCenter *smsc)
+{
+    gw_assert(smsc != NULL);
+
+    warning(0, "Attempting to re-open OISD connection");
+
+    oisd_close_socket(smsc);
+
+    /* Restore message counters to their default values */
+    smsc->oisd_send_seq = 0;
+
+    /* Clear leftover input */
+    octstr_destroy(smsc->oisd_inbuffer);
+    smsc->oisd_inbuffer = octstr_create("");
+
+    return oisd_login(smsc);
+}
+
+int oisd_close(SMSCenter *smsc)
+{
+    int ret;
+    int discarded;
+
+    gw_assert(smsc != NULL);
+
+    debug("bb.sms.oisd", 0, "Closing OISD SMSC");
+
+    if (smsc->socket < 0) {
+        warning(0, "oisd_close: already closed.\n");
+        return 0;
+    }
+
+    ret = close(smsc->socket);
+    smsc->socket = -1;
+
+    smsc->oisd_send_seq = 0;
+    octstr_destroy(smsc->oisd_hostname);
+    octstr_destroy(smsc->oisd_inbuffer);
+    octstr_destroy(smsc->sender_prefix);
+
+    discarded = list_len(smsc->oisd_received);
+    list_destroy(smsc->oisd_received, msg_destroy_item);
+
+    if (discarded > 0)
+        warning(0, "OISD: discarded %d received messages", discarded);
+
+    return ret;
+}
+
+int oisd_submit_msg(SMSCenter *smsc, Msg *msg)
+{
+    struct packet *packet;
+    int ret = 0;
+    int tries;
+    Octstr *ts;
+    ts = NULL;
+
+    gw_assert(smsc != NULL);
+
+    packet = packet_encode_message(msg, smsc->sender_prefix);
+    if (!packet)
+        return 0;   /* We can't signal protocol errors yet */
+
+    for (tries = 0; tries < 3; tries++) {
+        ret = oisd_request(packet, smsc, &ts);
+        if ((ret == 0) && (ts) && (msg->sms.dlr_mask & 0x03)) {
+            debug("bb.sms.oisd", 0, "oisd_submit_msg dlr_add url=%s ",
+                  octstr_get_cstr(msg->sms.dlr_url));
+            dlr_add(smsc->name,
+                    octstr_get_cstr(ts),
+                    octstr_get_cstr(msg->sms.sender),
+                    octstr_get_cstr(msg->sms.receiver),
+                    octstr_get_cstr(msg->sms.service),
+                    octstr_get_cstr(msg->sms.dlr_url),
+                    msg->sms.dlr_mask,
+                    octstr_get_cstr(msg->sms.boxc_id));
+            octstr_destroy(ts);
+            ts = NULL;
+        }
+        if ((ret < 0) && (msg->sms.dlr_mask & 0x03)) {
+            Msg* fake_dlr_msg;
+            debug("bb.sms.oisd", 0, "oisd_submit_msg request ret=%d ", ret);
+            fake_dlr_msg = oisd_submit_failed(smsc, msg, ts);
+            if (fake_dlr_msg) {
+                list_append(smsc->oisd_received, fake_dlr_msg);
+            }
+        }
+        if (ret == 0 || ret == -1)
+            break;
+        if (oisd_reopen(smsc) < 0) {
+            ret = -1;
+            break;
+        }
+    }
+
+    packet_destroy(packet);
+    return ret;
+}
+
+/*
+ * The bearerbox really doesn't like it if pending_smsmessage returns
+ * an error code.  We work around it until the bearerbox is rewritten.
+ * Record the error here, and return it in oisd_receive_msg.  Return
+ * "message available" if there is an error so that oisd_receive_msg
+ * is called.
+ */
+int oisd_pending_smsmessage(SMSCenter *smsc)
+{
+    long ret;
+    struct packet *packet;
+
+    gw_assert(smsc != NULL);
+    gw_assert(smsc->type == SMSC_TYPE_OISD);
+
+    if (list_len(smsc->oisd_received) > 0)
+        return 1;
+
+    if (smsc->socket < 0) {
+        /* XXX We have to assume that smsc_send_message is
+         * currently trying to reopen, so we have to make
+         * this thread wait.  It should be done in a nicer
+         * way. */ 
+        return 0;
+    }
+
+    ret = read_available(smsc->socket, 0);
+    if (ret == 0) {
+        if (smsc->keepalive > 0 && smsc->oisd_next_ping < time(NULL)) {
+            if (oisd_send_delivery_request(smsc) < 0) {
+                smsc->oisd_error = 1;
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    if (ret < 0) {
+        warning(errno, "oisd_pending_smsmessage: read_available failed");
+        smsc->oisd_error = 1;
+        return 1;
+    }
+
+    /* We have some data waiting... see if it is an sms delivery. */
+    ret = octstr_append_from_socket(smsc->oisd_inbuffer, smsc->socket);
+
+    if (ret == 0) {
+        warning(0, "oisd_pending_smsmessage: service center closed connection.");
+        smsc->oisd_error = 1;
+        return 1;
+    }
+    if (ret < 0) {
+        warning(0, "oisd_pending_smsmessage: read failed");
+        smsc->oisd_error = 1;
+        return 1;
+    }
+
+    for (;;) {
+        packet = packet_extract(smsc);
+        if (!packet)
+            break;
+
+        packet_check_can_receive(packet);
+
+        if (packet->operation < RESPONSE)
+            oisd_handle_request(packet, smsc);
+        else {
+            error(0, "oisd_pending_smsmessage: unexpected response packet");
+            octstr_dump(packet->data, 0);
+        }
+
+        packet_destroy(packet);
+    }
+
+    if (list_len(smsc->oisd_received) > 0)
+        return 1;
+
+    return 0;
+}
+
+int oisd_receive_msg(SMSCenter *smsc, Msg **msg)
+{
+    gw_assert(smsc != NULL);
+    gw_assert(msg != NULL);
+
+    if (smsc->oisd_error) {
+        smsc->oisd_error = 0;
+        return -1;
+    }
+
+    *msg = list_consume(smsc->oisd_received);
+
+    return 1;
+}
+
+static Msg *oisd_accept_delivery_report_message(struct packet *request,
+                                                SMSCenter *smsc)
+{
+    Msg *msg = NULL;
+    Octstr *destination = NULL;
+    Octstr *timestamp = NULL;
+    int st_code;
+    int code;
+    int dest_len;
+
+    /* MSISDN length */
+    dest_len = octstr_get_char(request->data, 10);
+    /* MSISDN */
+    destination = octstr_copy(request->data, 10+1, dest_len);
+    /* Accept time */
+    timestamp = octstr_copy(request->data, 10+1+dest_len+1+4+4, 14);
+    /* SM status */
+    st_code = octstr_get_char(request->data, 10+1+dest_len+1+4+4+14);
+
+    switch (st_code) {
+    case 1:
+    case 2:
+        code = DLR_FAIL;
+        break;
+    case 3:   /* success */
+        code = DLR_SUCCESS;
+        break;
+    case 4:
+    case 5:
+    case 6:
+    default:
+        code = 0;
+    }
+    if (code) {
+        msg = dlr_find(smsc->name,
+                       octstr_get_cstr(timestamp),
+                       octstr_get_cstr(destination),
+                       code);
+        debug("bb.sms.oisd", 0, "oisd_accept_dlr_message val=%d ",
+              st_code);
+        if (msg != NULL && msg->sms.msgdata != NULL) {
+            debug("bb.sms.oisd", 0, "oisd_accept_dlr_message url=%s ",
+                  octstr_get_cstr(msg->sms.msgdata));
+        }
+    } else
+        msg = NULL;
+    octstr_destroy(destination);
+    octstr_destroy(timestamp);
+
+    return msg;
+}
+
+static Msg *oisd_submit_failed(SMSCenter *smsc, Msg* msg, Octstr* ts)
+{
+    Msg *dlr_msg;
+
+    if (! msg->sms.dlr_url || ! octstr_len(msg->sms.dlr_url))
+        return NULL;
+    dlr_msg = msg_create(sms);
+    dlr_msg->sms.service = octstr_duplicate(msg->sms.service);
+    dlr_msg->sms.dlr_mask = DLR_FAIL;
+    dlr_msg->sms.sms_type = report;
+    dlr_msg->sms.smsc_id = octstr_create(smsc->name);
+    dlr_msg->sms.sender = octstr_duplicate(msg->sms.sender);
+    dlr_msg->sms.receiver = octstr_duplicate(msg->sms.receiver);
+    dlr_msg->sms.dlr_url = octstr_len(msg->sms.dlr_url) ?
+                           octstr_duplicate(msg->sms.dlr_url) : NULL;
+    dlr_msg->sms.msgdata = NULL;
+    time(&dlr_msg->sms.time);
+
+    debug("bb.sms.oisd", 0, "oisd_submit_failed url=%s ",
+          octstr_get_cstr(dlr_msg->sms.dlr_url));
+    return dlr_msg;
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.