Index: gw/smscconn.c
===================================================================
RCS file: /home/cvs/gateway/gw/smscconn.c,v
retrieving revision 1.35
diff -u -r1.35 smscconn.c
--- gw/smscconn.c	19 Mar 2003 17:40:36 -0000	1.35
+++ gw/smscconn.c	21 Mar 2003 15:23:46 -0000
@@ -91,6 +91,8 @@
 
     if (octstr_compare(smsc_type, octstr_imm("fake")) == 0)
 	ret = smsc_fake_create(conn, grp);
+    else if (octstr_compare(smsc_type, octstr_imm("cimd2")) == 0)
+	ret = smsc_cimd2_create(conn, grp);
     else if (octstr_compare(smsc_type, octstr_imm("emi2")) == 0)
 	ret = smsc_emi2_create(conn, grp);
     else if (octstr_compare(smsc_type, octstr_imm("http")) == 0)
Index: gw/smscconn_p.h
===================================================================
RCS file: /home/cvs/gateway/gw/smscconn_p.h,v
retrieving revision 1.35
diff -u -r1.35 smscconn_p.h
--- gw/smscconn_p.h	19 Mar 2003 17:40:36 -0000	1.35
+++ gw/smscconn_p.h	21 Mar 2003 15:23:46 -0000
@@ -185,6 +185,9 @@
 /* Responsible file: smsc/smsc_fake.c */
 int smsc_fake_create(SMSCConn *conn, CfgGroup *cfg);
 
+/* Responsible file: smsc/smsc_cimd2.c */
+int smsc_cimd2_create(SMSCConn *conn, CfgGroup *cfg);
+
 /* Responsible file: smsc/smsc_emi2.c */
 int smsc_emi2_create(SMSCConn *conn, CfgGroup *cfg);
 
Index: gw/smsc/smsc.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc.c,v
retrieving revision 1.2
diff -u -r1.2 smsc.c
--- gw/smsc/smsc.c	28 Jan 2003 00:10:04 -0000	1.2
+++ gw/smsc/smsc.c	21 Mar 2003 15:23:46 -0000
@@ -73,18 +73,6 @@
     smsc->cimd_username = NULL;
     smsc->cimd_password = NULL;
 
-    /* CIMD 2 */
-    smsc->cimd2_hostname = NULL;
-    smsc->cimd2_port = -1;
-    smsc->cimd2_username = NULL;
-    smsc->cimd2_password = NULL;
-    smsc->cimd2_send_seq = 1;
-    smsc->cimd2_receive_seq = 0;
-    smsc->cimd2_inbuffer = NULL;
-    smsc->cimd2_received = NULL;
-    smsc->cimd2_error = 0;
-    smsc->cimd2_next_ping = 0;
-
     /* EMI */
     smsc->emi_phonenum = NULL;
     smsc->emi_serialdevice = NULL;
@@ -164,14 +152,6 @@
     gw_free(smsc->cimd_username);
     gw_free(smsc->cimd_password);
 
-    /* CIMD 2 */
-    octstr_destroy(smsc->cimd2_hostname);
-    octstr_destroy(smsc->cimd2_username);
-    octstr_destroy(smsc->cimd2_password);
-    octstr_destroy(smsc->cimd2_inbuffer);
-    octstr_destroy(smsc->sender_prefix);
-    list_destroy(smsc->cimd2_received, NULL);
-
     /* EMI */
     gw_free(smsc->emi_phonenum);
     gw_free(smsc->emi_serialdevice);
@@ -228,11 +208,6 @@
             goto error;
         break;
 
-    case SMSC_TYPE_CIMD2:
-        if (cimd2_submit_msg(smsc, msg) == -1)
-            goto error;
-        break;
-
     case SMSC_TYPE_EMI:
     case SMSC_TYPE_EMI_IP:
         if (emi_submit_msg(smsc, msg) == -1)
@@ -289,12 +264,6 @@
             goto error;
         break;
 
-    case SMSC_TYPE_CIMD2:
-        ret = cimd2_receive_msg(smsc, msg);
-        if (ret == -1)
-            goto error;
-        break;
-
     case SMSC_TYPE_EMI:
     case SMSC_TYPE_EMI_IP:
         ret = emi_receive_msg(smsc, msg);
@@ -359,12 +328,6 @@
             goto error;
         break;
 
-    case SMSC_TYPE_CIMD2:
-        ret = cimd2_pending_smsmessage(smsc);
-        if (ret == -1)
-            goto error;
-        break;
-
     case SMSC_TYPE_EMI:
     case SMSC_TYPE_EMI_IP:
         ret = emi_pending_smsmessage(smsc);
@@ -532,8 +495,6 @@
     }
     if (octstr_compare(type, octstr_imm("cimd")) == 0)
     	typeno = SMSC_TYPE_CIMD;
-    else if (octstr_compare(type, octstr_imm("cimd2")) == 0)
-    	typeno = SMSC_TYPE_CIMD2;
     else if (octstr_compare(type, octstr_imm("emi")) == 0)
     	typeno = SMSC_TYPE_EMI;
     else if (octstr_compare(type, octstr_imm("emi_ip")) == 0)
@@ -610,18 +571,6 @@
 			     octstr_get_cstr(password));
         break;
 
-    case SMSC_TYPE_CIMD2:
-        if (host == NULL || port == 0 || username == NULL || password == NULL)
-            error(0, "Required field missing for CIMD 2 center.");
-        else
-            smsc = cimd2_open(host,
-	    	    	      port, 
-			      username, 
-			      password, 
-			      keepalive,
-                              sender_prefix);
-        break;
-
     case SMSC_TYPE_EMI:
         if (phone == NULL || device == NULL || username == NULL ||
             password == NULL)
@@ -746,9 +695,6 @@
     case SMSC_TYPE_CIMD:
         ret = cimd_reopen(smsc);
 	break;
-    case SMSC_TYPE_CIMD2:
-        ret = cimd2_reopen(smsc);
-	break;
     case SMSC_TYPE_EMI_IP:
         ret = emi_reopen_ip(smsc);
 	break;
@@ -795,11 +741,6 @@
     switch (smsc->type) {
     case SMSC_TYPE_CIMD:
         if (cimd_close(smsc) == -1)
-            errors = 1;
-        break;
-
-    case SMSC_TYPE_CIMD2:
-        if (cimd2_close(smsc) == -1)
             errors = 1;
         break;
 
Index: gw/smsc/smsc_cimd2.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_cimd2.c,v
retrieving revision 1.6
diff -u -r1.6 smsc_cimd2.c
--- gw/smsc/smsc_cimd2.c	6 Feb 2003 13:50:44 -0000	1.6
+++ gw/smsc/smsc_cimd2.c	21 Mar 2003 15:23:46 -0000
@@ -1,6 +1,8 @@
 /* Driver for CIMD 2 SMS centres.
  * Copyright 2000  WapIT Oy Ltd.
  * Author: Richard Braakman
+ *
+ * Upgrade to SMSCConn API: 2002 Kalle Marjola / 2003 Angel Fradejas
  */
 
 /* TODO: Check checksums on incoming packets */
@@ -28,13 +30,42 @@
 #include <unistd.h>
 
 #include "gwlib/gwlib.h"
-#include "smsc_p.h"
+#include "smscconn.h"
+#include "smscconn_p.h"
+#include "bb_smscconn_cb.h"
+
+#include "shared.h"
 #include "sms.h"
 #include "dlr.h"
 
-#ifndef CIMD2_TRACE
-#define CIMD2_TRACE 0
-#endif
+
+typedef struct privdata {
+    Octstr  *username;
+    Octstr  *password;
+    Octstr  *host;
+    long    port;
+    long    our_port;
+    long    keepalive;
+    Octstr  *my_number;
+
+    int     socket;
+    int     send_seq;
+    int     receive_seq;
+
+    Octstr  *inbuffer;
+    List    *received;
+
+    time_t  next_ping;
+
+    List *outgoing_queue;
+    SMSCConn *conn;
+    int receiver_thread;
+    int sender_thread;
+    int quitting;
+    List *stopped; /* list-trick for suspend/isolate */
+
+} PrivData;
+
 
 
 /* Microseconds before giving up on a request */
@@ -101,6 +132,7 @@
     P_MESSAGE_COUNT = 66,
     P_PRIORITY = 67,
     P_DELIVERY_REQUEST_MODE = 68,
+    P_SERVICE_CENTER_ADDRESS = 69,
     P_GET_PARAMETER = 500,
     P_MC_TIME = 501,
     P_ERROR_CODE = 900,
@@ -159,6 +191,7 @@
     { "message count", P_MESSAGE_COUNT, 3, P_INT, 0, 999 },
     { "priority", P_PRIORITY, 1, P_INT, 1, 9 },
     { "delivery request mode", P_DELIVERY_REQUEST_MODE, 1, P_INT, 0, 2 },
+    { "service center address", P_SERVICE_CENTER_ADDRESS, 20, P_ADDRESS },
     { "get parameter", P_GET_PARAMETER, 3, P_INT, 501, 999 },
     { "MC time", P_MC_TIME, 12, P_TIME },
     { "error code", P_ERROR_CODE, 3, P_INT, 0, 999 },
@@ -356,7 +389,7 @@
     int seq;   /* Sequence number */
     Octstr *data;   /* Encoded packet */
     /* CIMD 2 packet structure is so simple that packet information is
-     * stored as a valid encoded packet, and decoded as necessary. 
+     * stored as a valid encoded packet, and decoded as necessary.
      * Exceptions: operation code and sequence number are also stored
      * as ints for speed, and the checksum is not added until the packet
      * is about to be sent.  Since checksums are optional, the packet
@@ -381,7 +414,8 @@
 /* A reminder that packets are created without a valid sequence number */
 #define BOGUS_SEQUENCE 0
 
-static Msg *cimd2_accept_delivery_report_message(struct packet *request, SMSCenter *smsc);
+static Msg *cimd2_accept_delivery_report_message(struct packet *request,
+						 SMSCConn *conn);
 /* Look for the STX OO:SSS TAB header defined by CIMD 2, where OO is the
  * operation code in two decimals and SSS is the sequence number in three
  * decimals.  Leave the results in the proper fields of the packet.
@@ -440,7 +474,7 @@
  * a struct.  Return NULL if "in" contains no packet.  Always delete
  * leading non-packet data from "in".  (The CIMD 2 spec says we should
  * ignore any data between the packet markers). */
-static struct packet *packet_extract(Octstr *in)
+static struct packet *packet_extract(Octstr *in, SMSCConn *conn)
 {
     int stx, etx;
     Octstr *packet;
@@ -465,7 +499,8 @@
      * will at least allow good logging of the error. */
     stx = octstr_search_char(in, STX, 1);
     if (stx >= 0 && stx < etx) {
-        warning(0, "CIMD2: packet without end marker");
+        warning(0, "CIMD2[%s]: packet without end marker",
+                octstr_get_cstr(conn->id));
         packet = octstr_copy(in, 0, stx);
         octstr_delete(in, 0, stx);
         octstr_append_cstr(packet, ETX_str);
@@ -611,7 +646,7 @@
 
 /* Check if the header is according to CIMD 2 spec, generating log
  * entries as necessary.  Return -1 if anything was wrong, otherwise 0. */
-static int packet_check_header(struct packet *packet)
+static int packet_check_header(struct packet *packet, SMSCConn *conn)
 {
     Octstr *data;
 
@@ -626,14 +661,15 @@
         octstr_get_char(data, 3) != ':' ||
         !octstr_check_range(data, 4, 3, gw_isdigit) ||
         octstr_get_char(data, 7) != TAB) {
-        warning(0, "CIMD2 packet header in wrong format");
+        warning(0, "CIMD2[%s]: packet header in wrong format",
+                octstr_get_cstr(conn->id));
         return -1;
     }
 
     return 0;
 }
 
-static int packet_check_parameter(struct packet *packet, long pos, long len)
+static int packet_check_parameter(struct packet *packet, long pos, long len, SMSCConn *conn)
 {
     Octstr *data;
     long parm;
@@ -653,7 +689,8 @@
     if (len < 5 ||
         !octstr_check_range(data, pos + 1, 3, gw_isdigit) ||
         octstr_get_char(data, pos + 4) != ':') {
-        warning(0, "CIMD2 parameter at offset %ld in wrong format",
+        warning(0, "CIMD2[%s]: parameter at offset %ld in wrong format",
+                octstr_get_cstr(conn->id),
                 pos);
         errors++;
     }
@@ -673,12 +710,15 @@
     i = parm_index(parm);
 
     if (i < 0) {
-        warning(0, "CIMD2 packet contains unknown parameter %ld", parm);
+        warning(0, "CIMD2[%s]: packet contains unknown parameter %ld", 
+                octstr_get_cstr(conn->id),
+                parm);
         return -1;
     }
 
     if (dlen > parameters[i].maxlen) {
-        warning(0, "CIMD2 packet has '%s' parameter with length %ld, spec says max %d",
+        warning(0, "CIMD2[%s]: packet has '%s' parameter with length %ld, spec says max %d",
+                octstr_get_cstr(conn->id),
                 parameters[i].name, len, parameters[i].maxlen);
         errors++;
     }
@@ -689,12 +729,15 @@
         negative = (octstr_get_char(data, dpos) == '-');
         if (!octstr_check_range(data, dpos + negative,
                                 dlen - negative, gw_isdigit)) {
-            warning(0, "CIMD2 packet has '%s' parameter with non-integer contents", parameters[i].name);
+            warning(0, "CIMD2[%s]: packet has '%s' parameter with non-integer contents", 
+                    octstr_get_cstr(conn->id),
+                    parameters[i].name);
             errors++;
         }
         if (octstr_parse_long(&value, data, dpos, 10) >= 0 &&
             (value < parameters[i].minval || value > parameters[i].maxval)) {
-            warning(0, "CIMD2 packet has '%s' parameter out of range (value %ld, min %d, max %d)",
+            warning(0, "CIMD2[%s]: packet has '%s' parameter out of range (value %ld, min %d, max %d)",
+                    octstr_get_cstr(conn->id),
                     parameters[i].name, value,
                     parameters[i].minval, parameters[i].maxval);
             errors++;
@@ -702,23 +745,31 @@
         break;
     case P_TIME:
         if (!octstr_check_range(data, dpos, dlen, gw_isdigit)) {
-            warning(0, "CIMD2 packet has '%s' parameter with non-digit contents", parameters[i].name);
+            warning(0, "CIMD2[%s]: packet has '%s' parameter with non-digit contents", 
+                    octstr_get_cstr(conn->id),
+                    parameters[i].name);
             errors++;
         }
         break;
     case P_ADDRESS:
         if (!octstr_check_range(data, dpos, dlen, isphonedigit)) {
-            warning(0, "CIMD2 packet has '%s' parameter with non phone number contents", parameters[i].name);
+            warning(0, "CIMD2[%s]: packet has '%s' parameter with non phone number contents", 
+                    octstr_get_cstr(conn->id),
+                    parameters[i].name);
             errors++;
         }
         break;
     case P_HEX:
         if (!octstr_check_range(data, dpos, dlen, gw_isxdigit)) {
-            warning(0, "CIMD2 packet has '%s' parameter with non-hex contents", parameters[i].name);
+            warning(0, "CIMD2[%s]: packet has '%s' parameter with non-hex contents", 
+                    octstr_get_cstr(conn->id),
+                    parameters[i].name);
             errors++;
         }
         if (dlen % 2 != 0) {
-            warning(0, "CIMD2 packet has odd-length '%s' parameter", parameters[i].name);
+            warning(0, "CIMD2[%s]: packet has odd-length '%s' parameter", 
+                    octstr_get_cstr(conn->id),
+                    parameters[i].name);
             errors++;
         }
         break;
@@ -736,7 +787,7 @@
 /* Check the packet against the CIMD 2 spec, generating log entries as
  * necessary. Return -1 if anything was wrong, otherwise 0. */
 /* TODO: Check if parameters found actually belong in the packet type */
-static int packet_check(struct packet *packet)
+static int packet_check(struct packet *packet, SMSCConn *conn)
 {
     int errors = 0;
     long pos, len, next;
@@ -747,14 +798,15 @@
 
     if (octstr_search_char(data, 0, 0) >= 0) {
         /* CIMD2 spec does not allow NUL bytes in a packet */
-        warning(0, "CIMD2 packet contains NULs");
+        warning(0, "CIMD2[%s]: packet contains NULs",
+                octstr_get_cstr(conn->id));
         errors++;
     }
 
     /* Assume the packet starts with STX and ends with ETX,
      * because we parsed it that way in the first place. */
 
-    errors += (packet_check_header(packet) < 0);
+    errors += (packet_check_header(packet,conn) < 0);
 
     /* Parameters are separated by tabs.  After the last parameter
      * there is a tab, an optional two-digit checksum, and the ETX.
@@ -767,7 +819,7 @@
     for ( ; pos >= 0; pos = next) {
         next = octstr_search_char(data, TAB, pos + 1);
         if (next >= 0) {
-            errors += (packet_check_parameter(packet, pos, next - pos) < 0);
+            errors += (packet_check_parameter(packet, pos, next - pos, conn) < 0);
         } else {
             /* Check if the checksum has the right format.  Don't
              * check the sum itself here, that will be done in a
@@ -778,7 +830,8 @@
             if (!(octstr_len(data) - pos == 2 ||
                   (octstr_len(data) - pos == 4 &&
                    octstr_check_range(data, pos + 1, 2, gw_isxdigit)))) {
-                warning(0, "CIMD2 packet checksum in wrong format");
+                warning(0, "CIMD2[%s]: packet checksum in wrong format",
+                        octstr_get_cstr(conn->id));
                 errors++;
             }
         }
@@ -793,13 +846,14 @@
     return 0;
 }
 
-static void packet_check_can_receive(struct packet *packet)
+static void packet_check_can_receive(struct packet *packet, SMSCConn *conn)
 {
     gw_assert(packet != NULL);
 
     if (!operation_can_receive(packet->operation)) {
         Octstr *name = operation_name(packet->operation);
-        warning(0, "CIMD2 SMSC sent us %s request",
+        warning(0, "CIMD2[%s]: SMSC sent us %s request",
+                octstr_get_cstr(conn->id),
                 octstr_get_cstr(name));
         octstr_destroy(name);
     }
@@ -867,7 +921,7 @@
     { -1, NULL }
 };
 
-static int packet_display_error(struct packet *packet)
+static int packet_display_error(struct packet *packet, SMSCConn *conn)
 {
     int code;
     Octstr *text = NULL;
@@ -898,7 +952,8 @@
     }
 
     opname = operation_name(packet->operation);
-    error(0, "CIMD2 %s contained error message:",
+    error(0, "CIMD2[%s]: %s contained error message:",
+          octstr_get_cstr(conn->id),
           octstr_get_cstr(opname));
     error(0, "code %03d: %s", code, octstr_get_cstr(text));
     octstr_destroy(opname);
@@ -968,7 +1023,7 @@
  * to the corresponding ISO-8859-1 characters.  Since we are heading
  * toward that character set in the end, we don't bother converting
  * those to GSM. */
-static void convert_cimd2_to_gsm(Octstr *text)
+static void convert_cimd2_to_gsm(Octstr *text, SMSCConn *conn)
 {
     long pos, len;
     int cimd1, cimd2;
@@ -1003,8 +1058,9 @@
                     break;
             }
             if (cimd_combinations[i].cimd1 == 0)
-                warning(0, "CIMD2: Encountered unknown "
+                warning(0, "CIMD2[%s]: Encountered unknown "
                         "escape code _%c%c, ignoring.",
+                        octstr_get_cstr(conn->id),
                         cimd1, cimd2);
             else {
                 octstr_delete(text, pos, 2);
@@ -1078,7 +1134,7 @@
 
 /* Add a parameter to the end of packet */
 static void packet_add_parm(struct packet *packet, int parmtype,
-                            int parmno, Octstr *value)
+                            int parmno, Octstr *value, SMSCConn *conn)
 {
     unsigned char parmh[sizeof("tPPP:")];
     long position;
@@ -1091,9 +1147,12 @@
     gw_assert(parm_type(parmno) == parmtype);
 
     if (len > parm_maxlen(parmno)) {
-        warning(0, "CIMD2: %s parameter too long, truncating from "
-                "%ld to %ld characters", parm_name(parmno),
-                len, (long) parm_maxlen(parmno));
+        warning(0, "CIMD2[%s]: %s parameter too long, truncating from "
+                "%ld to %ld characters",
+                octstr_get_cstr(conn->id), 
+                parm_name(parmno),
+                len, 
+                (long) parm_maxlen(parmno));
         value = octstr_copy(value, 0, parm_maxlen(parmno));
         copied = 1;
     }
@@ -1110,23 +1169,23 @@
 }
 
 /* Add a String parameter to the packet */
-static void packet_add_string_parm(struct packet *packet, int parmno, Octstr *value)
+static void packet_add_string_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
 {
-    packet_add_parm(packet, P_STRING, parmno, value);
+    packet_add_parm(packet, P_STRING, parmno, value, conn);
 }
 
 /* Add an Address parameter to the packet */
-static void packet_add_address_parm(struct packet *packet, int parmno, Octstr *value)
+static void packet_add_address_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
 {
     gw_assert(octstr_check_range(value, 0, octstr_len(value), isphonedigit));
-    packet_add_parm(packet, P_ADDRESS, parmno, value);
+    packet_add_parm(packet, P_ADDRESS, parmno, value, conn);
 }
 
 /* Add an SMS parameter to the packet.  The caller is expected to have done
  * the translation to the GSM character set already.  */
-static void packet_add_sms_parm(struct packet *packet, int parmno, Octstr *value)
+static void packet_add_sms_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
 {
-    packet_add_parm(packet, P_SMS, parmno, value);
+    packet_add_parm(packet, P_SMS, parmno, value, conn);
 }
 
 /* There is no function for adding a Time parameter to the packet, because
@@ -1135,16 +1194,16 @@
  * and using that for synchronization.  And beware of DST changes. */
 
 /* Add a Hexadecimal parameter to the packet */
-static void packet_add_hex_parm(struct packet *packet, int parmno, Octstr *value)
+static void packet_add_hex_parm(struct packet *packet, int parmno, Octstr *value, SMSCConn *conn)
 {
     value = octstr_duplicate(value);
     octstr_binary_to_hex(value, 1);   /* 1 for uppercase hex, i.e. A .. F */
-    packet_add_parm(packet, P_HEX, parmno, value);
+    packet_add_parm(packet, P_HEX, parmno, value, conn);
     octstr_destroy(value);
 }
 
 /* Add an Integer parameter to the packet */
-static void packet_add_int_parm(struct packet *packet, int parmno, long value)
+static void packet_add_int_parm(struct packet *packet, int parmno, long value, SMSCConn *conn)
 {
     unsigned char buf[128];
     Octstr *valuestr;
@@ -1153,7 +1212,7 @@
 
     sprintf(buf, "%ld", value);
     valuestr = octstr_create(buf);
-    packet_add_parm(packet, P_INT, parmno, valuestr);
+    packet_add_parm(packet, P_INT, parmno, valuestr, conn);
     octstr_destroy(valuestr);
 }
 
@@ -1202,7 +1261,7 @@
     packet->seq = seq;
 }
 
-static struct packet *packet_encode_message(Msg *msg, Octstr *sender_prefix)
+static struct packet *packet_encode_message(Msg *msg, Octstr *sender_prefix, SMSCConn *conn)
 {
     struct packet *packet;
     Octstr *text;
@@ -1220,28 +1279,28 @@
         msg->sms.sender = octstr_create("");
 
     if (!parm_valid_address(msg->sms.receiver)) {
-        warning(0, "cimd2_submit_msg: non-digits in "
-                "destination phone number '%s', discarded",
+        warning(0, "CIMD2[%s]: non-digits in destination phone number '%s', discarded",
+                octstr_get_cstr(conn->id),
                 octstr_get_cstr(msg->sms.receiver));
         return NULL;
     }
 
     packet = packet_create(SUBMIT_MESSAGE, BOGUS_SEQUENCE);
 
-    packet_add_address_parm(packet, P_DESTINATION_ADDRESS, msg->sms.receiver);
+    packet_add_address_parm(packet, P_DESTINATION_ADDRESS, msg->sms.receiver, conn);
 
     /* CIMD2 interprets the originating address as a sub-address to
      * our connection number (so if the connection is "400" and we
      * fill in "600" as the sender number, the user sees "400600").
      * Since we have no way to ask what this number is, it has to
      * be configured. */
-    
+
     /* Quick and dirty check to see if we are using alphanumeric sender */
     if (parm_valid_address(msg->sms.sender)) {
         /* We are not, so send in the usual way */
         /* Speed up the default case */
         if (octstr_len(sender_prefix) == 0) {
-            packet_add_address_parm(packet, P_ORIGINATING_ADDRESS,msg->sms.sender);
+            packet_add_address_parm(packet, P_ORIGINATING_ADDRESS,msg->sms.sender, conn);
         }
         else if (octstr_compare(sender_prefix, octstr_imm("never")) != 0) {
             if (octstr_ncompare(sender_prefix, msg->sms.sender,
@@ -1249,18 +1308,19 @@
                 Octstr *sender;
                 sender = octstr_copy(msg->sms.sender,
                                      octstr_len(sender_prefix), octstr_len(msg->sms.sender));
-                packet_add_address_parm(packet, P_ORIGINATING_ADDRESS, sender);
+                packet_add_address_parm(packet, P_ORIGINATING_ADDRESS, sender, conn);
                 octstr_destroy(sender);
             } else {
-                warning(0, "CIMD2: Sending message with originating address <%s>, "
+                warning(0, "CIMD2[%s]: Sending message with originating address <%s>, "
                         "which does not start with the sender-prefix.",
+                        octstr_get_cstr(conn->id),
                         octstr_get_cstr(msg->sms.sender));
             }
         }
     }
     else {
         /* The test above to check if sender was all digits failed, so assume we want alphanumeric sender */
-        packet_add_string_parm(packet, P_ALPHANUMERIC_ORIGINATING_ADDRESS,msg->sms.sender);
+        packet_add_string_parm(packet, P_ALPHANUMERIC_ORIGINATING_ADDRESS,msg->sms.sender, conn);
     }
 
     /* Add the validity period if necessary.  This sets the relative validity
@@ -1273,7 +1333,7 @@
      * 168-196 = 2days to 30days in 1 day increments.
      * 197-255 = 5weeks to 63weeks in 1 week increments.
      *
-     * This code was copied from smsc_at2.c. 
+     * This code was copied from smsc_at2.c.
      */
     if (msg->sms.validity) {
       if (msg->sms.validity > 635040)
@@ -1295,7 +1355,7 @@
       if (msg->sms.validity < 5)
 	setvalidity = 0;
 
-      packet_add_int_parm(packet, P_VALIDITY_PERIOD_RELATIVE, setvalidity);
+      packet_add_int_parm(packet, P_VALIDITY_PERIOD_RELATIVE, setvalidity, conn);
     }
 
     /* Explicitly ask not to get status reports.
@@ -1306,16 +1366,16 @@
 
     if (msg->sms.dlr_mask & 0x03)
     {
-        packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 14);
+        packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 14, conn);
     }
     else
-        packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 0);
+        packet_add_int_parm(packet, P_STATUS_REPORT_REQUEST, 0, conn);
 
     /* Turn off reply path as default.
      * This avoids phones automatically asking for a reply
      * However, it is a nice feature and could be enabled through a parameter
      */
-    packet_add_int_parm(packet, P_REPLY_PATH, 0);
+    packet_add_int_parm(packet, P_REPLY_PATH, 0, conn);
 
     truncated = 0;
 
@@ -1323,8 +1383,7 @@
     if (octstr_len(msg->sms.udhdata)) {
         /* udhdata will be truncated and warned about if
          * it does not fit. */
-        packet_add_hex_parm(packet, P_USER_DATA_HEADER, msg->sms.udhdata);
-        spaceleft -= octstr_len(msg->sms.udhdata);
+        packet_add_hex_parm(packet, P_USER_DATA_HEADER, msg->sms.udhdata, conn);
     }
     if (msg->sms.coding == DC_7BIT)
         spaceleft = spaceleft * 8 / 7;
@@ -1332,39 +1391,33 @@
         spaceleft = 0;
 
     text = octstr_duplicate(msg->sms.msgdata);
+
     if (octstr_len(text) > 0 && spaceleft == 0) {
-        warning(0, "CIMD2: message filled up with "
-                "UDH, no room for message text");
+        warning(0, "CIMD2[%s]: message filled up with UDH, no room for message text",
+                octstr_get_cstr(conn->id));
     } else if (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) {
         if (octstr_len(text) > spaceleft) {
             truncated = octstr_len(text) - spaceleft;
             octstr_truncate(text, spaceleft);
         }
-        packet_add_hex_parm(packet, P_USER_DATA_BINARY, text);
+        packet_add_hex_parm(packet, P_USER_DATA_BINARY, text, conn);
     } else {
-#if CIMD2_TRACE
-        debug("bb.sms.cimd2", 0, "CIMD2 sending message.  Text:");
-        octstr_dump(text, 0);
-#endif
         /* Going from latin1 to GSM to CIMD2 may seem like a
          * detour, but it's the only way to get all the escape
          * codes right. */
         charset_latin1_to_gsm(text);
         truncated = charset_gsm_truncate(text, spaceleft);
         convert_gsm_to_cimd2(text);
-#if CIMD2_TRACE
-        debug("bb.sms.cimd2", 0, "After CIMD2 encoding:");
-        octstr_dump(text, 0);
-#endif
-        packet_add_sms_parm(packet, P_USER_DATA, text);
+        packet_add_sms_parm(packet, P_USER_DATA, text, conn);
     }
 
     if (dcs != 0)
-        packet_add_int_parm(packet, P_DATA_CODING_SCHEME, dcs);
+        packet_add_int_parm(packet, P_DATA_CODING_SCHEME, dcs, conn);
 
     if (truncated > 0) {
-        warning(0, "CIMD2: truncating message text to fit "
-                "in %d characters.", spaceleft);
+        warning(0, "CIMD2[%s]: truncating message text to fit in %d characters.", 
+                octstr_get_cstr(conn->id),
+                spaceleft);
     }
 
     octstr_destroy(text);
@@ -1376,55 +1429,60 @@
 /***************************************************************************/
 
 /* Give this packet a proper sequence number for sending. */
-static void packet_set_send_sequence(struct packet *packet, SMSCenter *smsc)
+static void packet_set_send_sequence(struct packet *packet, PrivData *pdata)
 {
-    gw_assert(smsc != NULL);
+    gw_assert(pdata != NULL);
     /* Send sequence numbers are always odd, receiving are always even */
-    gw_assert(smsc->cimd2_send_seq % 2 == 1);
+    gw_assert(pdata->send_seq % 2 == 1);
 
-    packet_set_sequence(packet, smsc->cimd2_send_seq);
-    smsc->cimd2_send_seq += 2;
-    if (smsc->cimd2_send_seq > 256)
-        smsc->cimd2_send_seq = 1;
+    packet_set_sequence(packet, pdata->send_seq);
+    pdata->send_seq += 2;
+    if (pdata->send_seq > 256)
+        pdata->send_seq = 1;
 }
 
-static struct packet *cimd2_get_packet(SMSCenter *smsc, Octstr **ts)
+static struct packet *cimd2_get_packet(PrivData *pdata, Octstr **ts)
 {
     struct packet *packet = NULL;
 
-    gw_assert(smsc != NULL);
+    gw_assert(pdata != NULL);
 
     /* If packet is already available, don't try to read anything */
-    packet = packet_extract(smsc->cimd2_inbuffer);
+    packet = packet_extract(pdata->inbuffer, pdata->conn);
 
     while (packet == NULL) {
-        if (read_available(smsc->socket, RESPONSE_TIMEOUT) != 1) {
-            warning(0, "CIMD2 SMSCenter is not responding");
+        if (read_available(pdata->socket, RESPONSE_TIMEOUT) != 1) {
+            warning(0, "CIMD2[%s]: SMSC is not responding",
+                    octstr_get_cstr(pdata->conn->id));
             return NULL;
         }
 
-        if (octstr_append_from_socket(smsc->cimd2_inbuffer, smsc->socket) <= 0) {
-            error(0, "cimd2_get_packet: read failed");
+        if (octstr_append_from_socket(pdata->inbuffer, pdata->socket) <= 0) {
+            error(0, "CIMD2[%s]: cimd2_get_packet: read failed",
+                  octstr_get_cstr(pdata->conn->id));
             return NULL;
         }
 
-        packet = packet_extract(smsc->cimd2_inbuffer);
+        packet = packet_extract(pdata->inbuffer, pdata->conn);
     }
 
-    packet_check(packet);
-    packet_check_can_receive(packet);
-	if(ts)
-	    *ts = packet_get_parm(packet,P_MC_TIMESTAMP);
+    packet_check(packet,pdata->conn);
+    packet_check_can_receive(packet,pdata->conn);
+    debug("bb.sms.cimd2", 0, "CIMD2[%s]: received: <%s>",
+          octstr_get_cstr(pdata->conn->id), 
+          octstr_get_cstr(packet->data));
+    if (ts)
+        *ts = packet_get_parm(packet,P_MC_TIMESTAMP);
 
-    if (smsc->keepalive > 0)
-        smsc->cimd2_next_ping = time(NULL) + 60 * smsc->keepalive;
+    if (pdata->keepalive > 0)
+        pdata->next_ping = time(NULL) + pdata->keepalive;
 
     return packet;
 }
 
 /* Acknowledge a request.  The CIMD 2 spec only defines positive responses
  * to the server, because the server is perfect. */
-static void cimd2_send_response(struct packet *request, SMSCenter *smsc)
+static void cimd2_send_response(struct packet *request, PrivData *pdata)
 {
     struct packet *response;
 
@@ -1436,12 +1494,12 @@
 
     /* 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);
+    octstr_write_to_socket(pdata->socket, response->data);
 
     packet_destroy(response);
 }
 
-static Msg *cimd2_accept_message(struct packet *request)
+static Msg *cimd2_accept_message(struct packet *request, SMSCConn *conn)
 {
     Msg *message = NULL;
     Octstr *destination = NULL;
@@ -1452,9 +1510,9 @@
 
     /* See GSM 03.38.  The bit patterns we can handle are:
      *   000xyyxx  Uncompressed text, yy indicates alphabet.
-            *                   yy = 00, default alphabet
+     *                   yy = 00, default alphabet
      *                   yy = 01, 8-bit data
-     *                   yy = 10, UCS2 (can't handle yet)
+     *                   yy = 10, UCS2
      *                   yy = 11, reserved
      *   1111xyxx  Data, y indicates alphabet.
      *                   y = 0, default alphabet
@@ -1468,27 +1526,21 @@
     /* Text is either in User Data or User Data Binary field. */
     text = packet_get_sms_parm(request, P_USER_DATA);
     if (text != NULL) {
-#if CIMD2_TRACE
-        debug("bb.sms.cimd2", 0, "CIMD2 received message.  Text:");
-        octstr_dump(text, 0);
-#endif
-        convert_cimd2_to_gsm(text);
-        charset_gsm_to_latin1(text);
-#if CIMD2_TRACE
-        debug("bb.sms.cimd", 0, "Text in latin1:");
-        octstr_dump(text, 0);
-#endif
+        convert_cimd2_to_gsm(text,conn);
+        /*charset = charset_gsm;  This overrides what we got from the DCS */
     } else {
+        /*
+         * FIXME: If DCS indicates GSM charset, and we get it in binary,
+         * then it's probably bit-packed.  We'll have to undo it because
+         * our "charset_gsm" means one gsm character per octet.  This is
+         * not currently supported. -- RB
+         */
         text = packet_get_hex_parm(request, P_USER_DATA_BINARY);
-#if CIMD2_TRACE
-        debug("bb.sms.cimd2", 0, "CIMD2 received message.  Text:");
-        octstr_dump(text, 0);
-#endif
     }
 
     /* 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
+     * 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, "CIMD2: Got SMS without receiver, discarding.");
@@ -1528,29 +1580,30 @@
 }
 
 /* Deal with a request from the CIMD2 server, and acknowledge it. */
-static void cimd2_handle_request(struct packet *request, SMSCenter *smsc)
+static void cimd2_handle_request(struct packet *request, SMSCConn *conn)
 {
+    PrivData *pdata = conn->data;
     Msg *message = NULL;
 
     /* TODO: Check if the sequence number of this request is what we
      * expected. */
 
     if (request->operation == DELIVER_STATUS_REPORT) {
-        message = cimd2_accept_delivery_report_message(request,smsc);
+        message = cimd2_accept_delivery_report_message(request, conn);
         if (message)
-            list_append(smsc->cimd2_received, message);
+            list_append(pdata->received, message);
      }
      else if (request->operation == DELIVER_MESSAGE) {
-         message = cimd2_accept_message(request);
+         message = cimd2_accept_message(request,conn);
          if (message)
-             list_append(smsc->cimd2_received, message);
+             list_append(pdata->received, message);
     }
 
-    cimd2_send_response(request, smsc);
+    cimd2_send_response(request, pdata);
 }
 
 /* Send a request and wait for the ack.  If the other side responds with
- * an error code, attempt to correct and retry. 
+ * 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
@@ -1563,67 +1616,78 @@
  * TODO: This function has grown large and complex.  Break it up
  * into smaller pieces.
  */
-static int cimd2_request(struct packet *request, SMSCenter *smsc, Octstr **ts)
+static int cimd2_request(struct packet *request, SMSCConn *conn, Octstr **ts)
 {
+    PrivData *pdata = conn->data;
     int ret;
     struct packet *reply = NULL;
     int errorcode;
     int tries = 0;
 
-    gw_assert(smsc != NULL);
+    gw_assert(pdata != NULL);
     gw_assert(request != NULL);
     gw_assert(operation_can_send(request->operation));
 
-    if (smsc->socket < 0) {
-        warning(0, "cimd2_request: socket not open.");
+    debug("bb.sms.cimd2", 0, "CIMD2[%s]: sending <%s>",
+          octstr_get_cstr(conn->id),
+          octstr_get_cstr(request->data));
+
+    if (pdata->socket < 0) {
+        warning(0, "CIMD2[%s]: cimd2_request: socket not open.",
+                octstr_get_cstr(conn->id));
         goto io_error;
     }
 
 retransmit:
-    packet_set_send_sequence(request, smsc);
+    packet_set_send_sequence(request, pdata);
     packet_set_checksum(request);
 
-    ret = octstr_write_to_socket(smsc->socket, request->data);
+    ret = octstr_write_to_socket(pdata->socket, request->data);
     if (ret < 0)
         goto io_error;
 
 next_reply:
-    reply = cimd2_get_packet(smsc, ts);
+    packet_destroy(reply);  // destroy old, if any
+    reply = cimd2_get_packet(pdata, ts);
     if (!reply)
         goto io_error;
 
-    errorcode = packet_display_error(reply);
+    errorcode = packet_display_error(reply,conn);
 
     if (reply->operation == NACK) {
-        warning(0, "CIMD2 received NACK");
+        warning(0, "CIMD2[%s]: received NACK",
+                octstr_get_cstr(conn->id));
         octstr_dump(reply->data, 0);
         /* Correct sequence number if server says it was wrong,
          * but only if server's number is sane. */
         if (reply->seq != request->seq && (reply->seq % 1) == 1) {
-            warning(0, "correcting sequence number from %ld to %ld.",
-                    (long) smsc->cimd2_send_seq, (long) reply->seq);
-            smsc->cimd2_send_seq = reply->seq;
+            warning(0, "CIMD2[%s]: correcting sequence number from %ld to %ld.",
+                    octstr_get_cstr(conn->id),
+                    (long) pdata->send_seq, 
+                    (long) reply->seq);
+            pdata->send_seq = reply->seq;
         }
         goto retry;
     }
 
     if (reply->operation == GENERAL_ERROR_RESPONSE) {
-        error(0, "CIMD2 received general error response");
+        error(0, "CIMD2[%s]: received general error response",
+              octstr_get_cstr(conn->id));
         goto io_error;
     }
 
     /* The server sent us a request.  Handle it, then wait for
      * a new reply. */
     if (reply->operation < RESPONSE) {
-        cimd2_handle_request(reply, smsc);
-        packet_destroy(reply);
+        cimd2_handle_request(reply, conn);
         goto next_reply;
     }
 
     if (reply->seq != request->seq) {
         /* We got a response to a different request number than
          * what we send.  Strange. */
-        warning(0, "CIMD2: response had unexpected sequence number; ignoring.\n");
+        warning(0, "CIMD2[%s]: response had unexpected sequence number; ignoring.",
+                octstr_get_cstr(conn->id));
         goto next_reply;
     }
 
@@ -1631,9 +1695,11 @@
         /* 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, "CIMD2: %s request got a %s",
+        warning(0, "CIMD2[%s]: %s request got a %s",
+                octstr_get_cstr(conn->id),
                 octstr_get_cstr(request_name),
                 octstr_get_cstr(reply_name));
+
         octstr_destroy(request_name);
         octstr_destroy(reply_name);
         octstr_dump(reply->data, 0);
@@ -1658,239 +1724,185 @@
 
 retry:
     if (++tries < 3) {
-        warning(0, "Retransmitting (take %d)", tries);
+        warning(0, "CIMD2[%s]: Retransmitting (take %d)", 
+                octstr_get_cstr(conn->id),
+                tries);
         goto retransmit;
     }
-    warning(0, "Giving up.");
+    warning(0, "CIMD2[%s]: Giving up.",
+            octstr_get_cstr(conn->id));
     goto io_error;
 }
 
 /* Close the SMSC socket without fanfare. */
-static void cimd2_close_socket(SMSCenter *smsc)
+static void cimd2_close_socket(PrivData *pdata)
 {
-    gw_assert(smsc != NULL);
+    gw_assert(pdata != NULL);
 
-    if (smsc->socket < 0)
+    if (pdata->socket < 0)
         return;
 
-    if (close(smsc->socket) < 0)
-        warning(errno, "error closing CIMD2 socket");
-    smsc->socket = -1;
+    if (close(pdata->socket) < 0)
+        warning(errno, "CIMD2[%s]: error closing socket",
+                octstr_get_cstr(pdata->conn->id));
+    pdata->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 cimd2_login(SMSCenter *smsc)
+static int cimd2_login(SMSCConn *conn)
 {
+    PrivData *pdata = conn->data;
     int ret;
     struct packet *packet = NULL;
 
-    gw_assert(smsc != NULL);
+    gw_assert(pdata != NULL);
 
-    if (smsc->socket >= 0) {
-        warning(0, "cimd2_login: socket was already open; closing");
-        cimd2_close_socket(smsc);
+    if (pdata->socket >= 0) {
+        warning(0, "CIMD2[%s]: login: socket was already open; closing",
+                octstr_get_cstr(conn->id));
+        cimd2_close_socket(pdata);
     }
 
-    smsc->socket = tcpip_connect_to_server(octstr_get_cstr(smsc->cimd2_hostname), 
-		    smsc->cimd2_port, NULL);
-    /* XXX add interface_name if required */
-    if (smsc->socket == -1)
-        goto error;
-
-    packet = packet_create(LOGIN, BOGUS_SEQUENCE);
-    packet_add_string_parm(packet, P_USER_IDENTITY, smsc->cimd2_username);
-    packet_add_string_parm(packet, P_PASSWORD, smsc->cimd2_password);
-
-    ret = cimd2_request(packet, smsc, NULL);
-    if (ret < 0)
-        goto error;
-
-    packet_destroy(packet);
-    info(0, "%s logged in.", smsc_name(smsc));
-
-    return 0;
-
-error:
-    error(0, "cimd2_login failed");
-    cimd2_close_socket(smsc);
+    pdata->socket = tcpip_connect_to_server_with_port(
+                                            octstr_get_cstr(pdata->host),
+                                            pdata->port,
+                                            pdata->our_port,
+                                            (conn->our_host ? octstr_get_cstr(conn->our_host) : NULL)); 
+    if (pdata->socket != -1) {
+    
+        packet = packet_create(LOGIN, BOGUS_SEQUENCE);
+        packet_add_string_parm(packet, P_USER_IDENTITY, pdata->username, conn);
+        packet_add_string_parm(packet, P_PASSWORD, pdata->password, conn);
+        
+        ret = cimd2_request(packet, conn, NULL);
+        
+        if (ret >= 0) {
+            packet_destroy(packet);
+            info(0, "CIMD2[%s] logged in.",
+                 octstr_get_cstr(conn->id));
+            return 0;
+        }
+    }
+    error(0, "CIMD2[%s] login failed.",
+          octstr_get_cstr(conn->id));
+    cimd2_close_socket(pdata);
     packet_destroy(packet);
     return -1;
 }
 
-static void cimd2_logout(SMSCenter *smsc)
+static void cimd2_logout(SMSCConn *conn)
 {
     struct packet *packet = NULL;
-
-    gw_assert(smsc != NULL);
+    int ret;
 
     packet = packet_create(LOGOUT, BOGUS_SEQUENCE);
+
     /* TODO: Don't wait very long for a response in this case. */
-    cimd2_request(packet, smsc,NULL);
+    ret = cimd2_request(packet, conn, NULL);
+
+    if (ret == 0) {
+        info(0, "CIMD2[%s] logged out.",
+             octstr_get_cstr(conn->id));
+    }
     packet_destroy(packet);
 }
 
-static int cimd2_send_alive(SMSCenter *smsc)
+static int cimd2_send_alive(SMSCConn *conn)
 {
     struct packet *packet = NULL;
     int ret;
 
-    gw_assert(smsc != NULL);
-
     packet = packet_create(ALIVE, BOGUS_SEQUENCE);
-    ret = cimd2_request(packet, smsc,NULL);
+    ret = cimd2_request(packet, conn, NULL);
     packet_destroy(packet);
 
     if (ret < 0)
-        warning(0, "CIMD2: SMSC not alive.\n");
+        warning(0, "CIMD2[%s]: SMSC not alive.",
+                octstr_get_cstr(conn->id));
 
     return ret;
 }
 
-/***************************************************************************/
-/* SMSC Interface, as defined in smsc_interface.def                        */
-/***************************************************************************/
-
-SMSCenter *cimd2_open(Octstr *hostname, int port, Octstr *username,
-Octstr *password, int keepalive, Octstr *sender_prefix)
-{
-    SMSCenter *smsc = NULL;
-    int maxlen;
-
-    smsc = smscenter_construct();
-    gw_assert(smsc != NULL);
-
-    smsc->type = SMSC_TYPE_CIMD2;
-    smsc->keepalive = keepalive;
-    smsc->cimd2_hostname = octstr_duplicate(hostname);
-    smsc->cimd2_port = port;
-    smsc->cimd2_username = octstr_duplicate(username);
-    smsc->cimd2_password = octstr_duplicate(password);
-    smsc->sender_prefix = octstr_duplicate(sender_prefix);
-    sprintf(smsc->name, "CIMD2:%s:%d:%s", octstr_get_cstr(hostname), port, octstr_get_cstr(username));
-    smsc->cimd2_received = list_create();
-    smsc->cimd2_inbuffer = octstr_create("");
-    smsc->cimd2_error = 0;
-    if (keepalive > 0)
-        smsc->cimd2_next_ping = time(NULL) + keepalive * 60;
-
-    maxlen = parm_maxlen(P_USER_IDENTITY);
-    if (octstr_len(smsc->cimd2_username) > maxlen) {
-        octstr_truncate(smsc->cimd2_username, maxlen);
-        warning(0, "Truncating CIMD2 username to %d chars", maxlen);
-    }
-
-    maxlen = parm_maxlen(P_PASSWORD);
-    if (octstr_len(smsc->cimd2_password) > maxlen) {
-        octstr_truncate(smsc->cimd2_password, maxlen);
-        warning(0, "Truncating CIMD2 password to %d chars", maxlen);
-    }
-
-    if (cimd2_login(smsc) < 0)
-        goto error;
-
-    return smsc;
-
-error:
-    error(0, "cimd2_open failed");
-    smscenter_destruct(smsc);
-    return NULL;
-}
-
-int cimd2_reopen(SMSCenter *smsc)
-{
-    gw_assert(smsc != NULL);
-
-    warning(0, "Attempting to re-open CIMD2 connection");
-
-    cimd2_close_socket(smsc);
-
-    /* Restore message counters to their default values */
-    smsc->cimd2_send_seq = 1;
-    smsc->cimd2_receive_seq = 0;
-
-    /* Clear leftover input */
-    octstr_destroy(smsc->cimd2_inbuffer);
-    smsc->cimd2_inbuffer = octstr_create("");
-
-    return cimd2_login(smsc);
-}
 
-int cimd2_close(SMSCenter *smsc)
+static void cimd2_destroy(PrivData *pdata)
 {
-    int ret;
     int discarded;
 
-    gw_assert(smsc != NULL);
-
-    debug("bb.sms.cimd2", 0, "Closing CIMD2 SMSC");
-
-    if (smsc->socket < 0) {
-        warning(0, "cimd2_close: already closed.\n");
-        return 0;
-    }
-
-    cimd2_logout(smsc);
-
-    ret = close(smsc->socket);
-    smsc->socket = -1;
-
-    smsc->cimd2_send_seq = 0;
-    smsc->cimd2_receive_seq = 1;
-    octstr_destroy(smsc->cimd2_hostname);
-    octstr_destroy(smsc->cimd2_username);
-    octstr_destroy(smsc->cimd2_password);
-    octstr_destroy(smsc->cimd2_inbuffer);
-
-    discarded = list_len(smsc->cimd2_received);
-    list_destroy(smsc->cimd2_received, msg_destroy_item);
+    if (pdata == NULL) 
+        return;
+        
+    octstr_destroy(pdata->host);
+    octstr_destroy(pdata->username);
+    octstr_destroy(pdata->password);
+    octstr_destroy(pdata->inbuffer);
+    octstr_destroy(pdata->my_number);
 
+    discarded = list_len(pdata->received);
     if (discarded > 0)
-        warning(0, "CIMD2: discarded %d received messages", discarded);
+        warning(0, "CIMD2[%s]: discarded %d received messages",
+                octstr_get_cstr(pdata->conn->id), 
+                discarded);
 
-    return ret;
+    list_destroy(pdata->received, msg_destroy_item);
+    list_destroy(pdata->outgoing_queue, NULL);
+    list_destroy(pdata->stopped, NULL);
+
+    gw_free(pdata);
 }
 
-int cimd2_submit_msg(SMSCenter *smsc, Msg *msg)
+
+static int cimd2_submit_msg(SMSCConn *conn, Msg *msg)
 {
+    PrivData *pdata = conn->data;
     struct packet *packet;
-    int ret = 0;
-    int tries;
     Octstr *ts;
-    ts = NULL;
+    int ret;
 
-    gw_assert(smsc != NULL);
+    gw_assert(pdata != NULL);
+    debug("bb.sms.cimd2", 0, "CIMD2[%s]: sending message",
+        octstr_get_cstr(conn->id));
+
+    packet = packet_encode_message(msg, pdata->my_number,conn);
+    if (!packet) {
+      /* This is a protocol error. Does this help? I doubt..
+       * But nevermind that.
+       */
+      bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_MALFORMED);
+        return -1;
+    }
 
-    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 = cimd2_request(packet, smsc,&ts);
-        if((ret == 0) && (ts) && (msg->sms.dlr_mask & 0x03))
-        {
-            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));
-	}
+    ret = cimd2_request(packet, conn, &ts);
+    if((ret == 0) && (ts) && (msg->sms.dlr_mask & 0x03)) {
+    
+        dlr_add(octstr_get_cstr(conn->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 || ret == -1)
-            break;
-        if (cimd2_reopen(smsc) < 0) {
-            ret = -1;
-            break;
-        }
+    packet_destroy(packet);
+    
+    if (ret == -1) {
+        bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_REJECTED);
+    }
+    else if (ret == -2) {
+        cimd2_close_socket(pdata);
+        bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_TEMPORARILY);
+        mutex_lock(conn->flow_mutex);
+        conn->status = SMSCCONN_DISCONNECTED;
+        mutex_unlock(conn->flow_mutex);
+    }
+    else {
+        bb_smscconn_sent(conn,msg);
     }
 
-    packet_destroy(packet);
     return ret;
 }
 
@@ -1899,112 +1911,103 @@
  * Record the error here, and return it in cimd2_receive_msg.  Return
  * "message available" if there is an error so that cimd2_receive_msg
  * is called. */
-int cimd2_pending_smsmessage(SMSCenter *smsc)
+static int cimd2_receive_msg(SMSCConn *conn, Msg **msg)
 {
+    PrivData *pdata = conn->data;
     long ret;
     struct packet *packet;
 
-    gw_assert(smsc != NULL);
-    gw_assert(smsc->type == SMSC_TYPE_CIMD2);
+    gw_assert(pdata != NULL);
 
-    if (list_len(smsc->cimd2_received) > 0)
+    if (list_len(pdata->received) > 0) {
+        *msg = list_consume(pdata->received);
         return 1;
+    }
 
-    if (smsc->socket < 0) {
+    if (pdata->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. */ 
+         * way. */
         return 0;
     }
 
-    ret = read_available(smsc->socket, 0);
+    ret = read_available(pdata->socket, 0);
     if (ret == 0) {
-        if (smsc->keepalive > 0 && smsc->cimd2_next_ping < time(NULL)) {
-            if (cimd2_send_alive(smsc) < 0) {
-                smsc->cimd2_error = 1;
-                return 1;
-            }
+        if (pdata->keepalive > 0 && pdata->next_ping < time(NULL)) {
+            if (cimd2_send_alive(conn) < 0)
+		return -1;
         }
         return 0;
     }
 
     if (ret < 0) {
-        warning(errno, "cimd2_pending_smsmessage: read_available failed");
-        smsc->cimd2_error = 1;
-        return 1;
+        warning(errno, "CIMD2[%s]: cimd2_receive_msg: read_available failed",
+                octstr_get_cstr(conn->id));
+        return -1;
     }
 
     /* We have some data waiting... see if it is an sms delivery. */
-    ret = octstr_append_from_socket(smsc->cimd2_inbuffer, smsc->socket);
+    ret = octstr_append_from_socket(pdata->inbuffer, pdata->socket);
 
     if (ret == 0) {
-        warning(0, "cimd2_pending_smsmessage: service center closed connection.");
-        smsc->cimd2_error = 1;
-        return 1;
+        warning(0, "CIMD2[%s]: cimd2_receive_msg: service center closed connection.",
+                octstr_get_cstr(conn->id));
+        return -1;
     }
     if (ret < 0) {
-        warning(0, "cimd2_pending_smsmessage: read failed");
-        smsc->cimd2_error = 1;
-        return 1;
+        warning(0, "CIMD2[%s]: cimd2_receive_msg: read failed",
+                octstr_get_cstr(conn->id));
+        return -1;
     }
 
     for (;;) {
-        packet = packet_extract(smsc->cimd2_inbuffer);
+        packet = packet_extract(pdata->inbuffer,conn);
         if (!packet)
             break;
 
-        packet_check(packet);
-        packet_check_can_receive(packet);
+        packet_check(packet,conn);
+        packet_check_can_receive(packet,conn);
+        debug("bb.sms.cimd2", 0, "CIMD2[%s]: received: <%s>",
+              octstr_get_cstr(pdata->conn->id), 
+              octstr_get_cstr(packet->data));
 
         if (packet->operation < RESPONSE)
-            cimd2_handle_request(packet, smsc);
+            cimd2_handle_request(packet, conn);
         else {
-            error(0, "cimd2_pending_smsmessage: unexpected response packet");
+            error(0, "CIMD2[%s]: cimd2_receive_msg: unexpected response packet",
+                octstr_get_cstr(conn->id));
             octstr_dump(packet->data, 0);
         }
 
         packet_destroy(packet);
     }
 
-    if (list_len(smsc->cimd2_received) > 0)
+    if (list_len(pdata->received) > 0) {
+	*msg = list_consume(pdata->received);
         return 1;
-
-    return 0;
-}
-
-int cimd2_receive_msg(SMSCenter *smsc, Msg **msg)
-{
-    gw_assert(smsc != NULL);
-    gw_assert(msg != NULL);
-
-    if (smsc->cimd2_error) {
-        smsc->cimd2_error = 0;
-        return -1;
     }
-
-    *msg = list_consume(smsc->cimd2_received);
-
-    return 1;
+    return 0;
 }
 
 
 
-static Msg *cimd2_accept_delivery_report_message(struct packet *request, SMSCenter *smsc)
+static Msg *cimd2_accept_delivery_report_message(struct packet *request,
+						 SMSCConn *conn)
 {
     Msg *msg = NULL;
     Octstr *destination = NULL;
     Octstr *timestamp = NULL;
     Octstr *statuscode = NULL;
-    int st_code; 
+    int st_code;
     int code;
-	
+
     destination = packet_get_parm(request, P_DESTINATION_ADDRESS);
     timestamp = packet_get_parm(request, P_MC_TIMESTAMP);
     statuscode = packet_get_parm(request, P_STATUS_CODE);
 
     st_code = atoi(octstr_get_cstr(statuscode));
-    
+
     switch(st_code)
     {
     case 2:  /* validity period expired */
@@ -2018,9 +2021,9 @@
         code = 0;
     }
     if(code)
-    	msg = dlr_find(smsc->name,
-            octstr_get_cstr(timestamp), 
-            octstr_get_cstr(destination),
+    	msg = dlr_find(octstr_get_cstr(conn->name),
+		       octstr_get_cstr(timestamp),
+		       octstr_get_cstr(destination),
             code);
     else
         msg = NULL;
@@ -2035,3 +2038,333 @@
 
     return msg;
  }
+
+static Msg *sms_receive(SMSCConn *conn)
+{
+    PrivData *pdata = conn->data;
+    int ret;
+    Msg *newmsg = NULL;
+
+    ret = cimd2_receive_msg(conn, &newmsg);
+    if (ret == 1) {
+        /* if any smsc_id available, use it */
+        newmsg->sms.smsc_id = octstr_duplicate(conn->id);
+        return newmsg;
+    } 
+    else if (ret == 0) { /* no message, just retry... */
+        return NULL;
+    } 
+    /* error. reconnect. */
+    msg_destroy(newmsg);
+    mutex_lock(conn->flow_mutex);
+    cimd2_close_socket(pdata);
+    conn->status = SMSCCONN_DISCONNECTED;
+    mutex_unlock(conn->flow_mutex);
+    return NULL;
+}
+
+
+static void sms_receiver(void *arg)
+{
+    Msg       *msg;
+    SMSCConn  *conn = arg;
+    PrivData *pdata = conn->data;
+    double    sleep = 0.0001;
+
+    /* Make sure we log into our own log-file if defined */
+    log_thread_to(conn->log_idx);
+
+    /* remove messages from SMSC until we are killed */
+    while (!pdata->quitting) {
+    
+        list_consume(pdata->stopped); /* block here if suspended/isolated */
+      
+        if (conn->status == SMSCCONN_ACTIVE) {
+            msg = sms_receive(conn);
+            if (msg) {
+                debug("bb.sms.cimd2", 0, "CIMD2[%s]: new message received",
+                      octstr_get_cstr(conn->id));
+                sleep = 0.0001;
+                bb_smscconn_receive(conn, msg);
+                continue;
+            }
+        }
+        /* note that this implementations means that we sleep even
+         * when we fail connection.. but time is very short, anyway
+         */
+        gwthread_sleep(sleep);
+        /* gradually sleep longer and longer times until something starts to
+         * happen - this of course reduces response time, but that's better than
+         * extensive CPU usage when it is not used
+         */
+        sleep *= 2;
+        if (sleep >= 2.0)
+            sleep = 1.999999;
+    }
+}
+
+
+static void sms_sender(void *arg)
+{
+    Msg       *msg;
+    SMSCConn  *conn = arg;
+    PrivData *pdata = conn->data;
+    double sleep = 0.00001;
+    
+    mutex_lock(conn->flow_mutex);
+    conn->status = SMSCCONN_CONNECTING;
+    mutex_unlock(conn->flow_mutex);
+
+    /* Make sure we log into our own log-file if defined */
+    log_thread_to(conn->log_idx);
+
+    while (!pdata->quitting) {
+    
+        list_consume(pdata->stopped); /* block here if suspended/isolated */
+
+        /* check that connection is active */
+        if (conn->status != SMSCCONN_ACTIVE) {
+            if (cimd2_login(conn) != 0) { 
+                error(0, "CIMD2[%s]: Couldn't connect to SMSC (retrying in %ld seconds).",
+                      octstr_get_cstr(conn->id), 
+                      conn->reconnect_delay);
+                gwthread_sleep(conn->reconnect_delay);
+                mutex_lock(conn->flow_mutex);
+                conn->status = SMSCCONN_RECONNECTING; 
+                mutex_unlock(conn->flow_mutex);
+                continue; 
+            } 
+            mutex_lock(conn->flow_mutex);
+            conn->status = SMSCCONN_ACTIVE;
+            conn->connect_time = time(NULL);
+            bb_smscconn_connected(conn);
+            mutex_unlock(conn->flow_mutex);
+        }
+        
+        if (pdata->quitting) 
+            break;
+
+        /* send messages */
+        do {
+            msg = list_extract_first(pdata->outgoing_queue);
+            if (msg) {
+                if (cimd2_submit_msg(conn,msg) != 0) break;
+            }
+            else {
+                gwthread_sleep(sleep);
+            }
+        } while (msg);
+    }
+}
+
+
+
+static int cimd2_add_msg_cb (SMSCConn *conn, Msg *sms)
+{
+    PrivData *pdata = conn->data;
+    Msg *copy;
+
+    copy = msg_duplicate(sms);
+    list_produce(pdata->outgoing_queue, copy);
+    gwthread_wakeup(pdata->sender_thread);
+
+    return 0;
+}
+
+
+static int cimd2_shutdown_cb (SMSCConn *conn, int finish_sending)
+{
+    PrivData *pdata = conn->data;
+    
+    debug("bb.sms", 0, "Shutting down SMSCConn CIMD2 %s (%s)",
+          octstr_get_cstr(conn->id), 
+          finish_sending ? "slow" : "instant");
+
+    /* Documentation claims this would have been done by smscconn.c,
+       but isn't when this code is being written. */
+    conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
+    pdata->quitting = 1;     /* Separate from why_killed to avoid locking, as
+                              * why_killed may be changed from outside? */
+
+    if (finish_sending == 0) {
+        Msg *msg;
+        while ((msg = list_extract_first(pdata->outgoing_queue)) != NULL) {
+            bb_smscconn_send_failed(conn, msg, SMSCCONN_FAILED_SHUTDOWN);
+        }
+    }
+
+    cimd2_logout(conn);
+    if (conn->is_stopped) {
+        list_remove_producer(pdata->stopped);
+        conn->is_stopped = 0;
+    }
+    
+    if (pdata->sender_thread != -1) {
+        gwthread_wakeup(pdata->sender_thread);
+        gwthread_join(pdata->sender_thread); 
+    }
+    
+    if (pdata->receiver_thread != -1) {
+        gwthread_wakeup(pdata->receiver_thread);
+        gwthread_join(pdata->receiver_thread); 
+    }
+
+    cimd2_close_socket(pdata);
+    cimd2_destroy(pdata); 
+     
+    debug("bb.sms", 0, "SMSCConn CIMD2 %s shut down.",  
+          octstr_get_cstr(conn->id)); 
+    conn->status = SMSCCONN_DEAD; 
+    bb_smscconn_killed(); 
+    return 0;
+}    
+
+static void cimd2_start_cb (SMSCConn *conn)
+{
+    PrivData *pdata = conn->data;
+
+    list_remove_producer(pdata->stopped);
+    /* in case there are messages in the buffer already */
+    gwthread_wakeup(pdata->receiver_thread);
+    debug("bb.sms", 0, "SMSCConn CIMD2 %s, start called",
+          octstr_get_cstr(conn->id));
+}
+
+static void cimd2_stop_cb (SMSCConn *conn)
+{
+    PrivData *pdata = conn->data;
+    list_add_producer(pdata->stopped);
+    debug("bb.sms", 0, "SMSCConn CIMD2 %s, stop called",
+          octstr_get_cstr(conn->id));
+}
+
+static long cimd2_queued_cb (SMSCConn *conn)
+{
+    PrivData *pdata = conn->data;
+    conn->load = (pdata ? (conn->status != SMSCCONN_DEAD ? 
+                  list_len(pdata->outgoing_queue) : 0) : 0);
+    return conn->load; 
+}
+
+int smsc_cimd2_create(SMSCConn *conn, CfgGroup *grp)
+{
+    PrivData *pdata;
+    int ok;
+    int maxlen;
+
+    pdata = gw_malloc(sizeof(PrivData));
+    conn->data = pdata;
+    pdata->conn = conn;
+
+    pdata->quitting = 0;
+    pdata->socket = -1;
+    pdata->received = list_create();
+    pdata->inbuffer = octstr_create("");
+    pdata->send_seq = 1;
+    pdata->receive_seq = 0;
+    pdata->outgoing_queue = list_create();
+    pdata->stopped = list_create();
+    list_add_producer(pdata->outgoing_queue);
+
+    if (conn->is_stopped)
+      list_add_producer(pdata->stopped);
+
+    pdata->host = cfg_get(grp, octstr_imm("host"));
+    if (cfg_get_integer(&(pdata->port), grp, octstr_imm("port")) == -1)
+      pdata->port = 0;
+    if (cfg_get_integer(&(pdata->our_port), grp, octstr_imm("our-port")) == -1)
+      pdata->our_port = 0;
+    pdata->username = cfg_get(grp, octstr_imm("smsc-username"));
+    pdata->password = cfg_get(grp, octstr_imm("smsc-password"));
+    pdata->my_number = cfg_get(grp, octstr_imm("my-number"));
+    if (cfg_get_integer(&(pdata->keepalive), grp,octstr_imm("keepalive")) == -1)
+        pdata->keepalive = 0;
+
+    /* Check that config is OK */
+    ok = 1;
+    if (pdata->host == NULL) {
+        error(0,"CIMD2[%s]: Configuration file doesn't specify host",
+              octstr_get_cstr(conn->id));
+        ok = 0;
+    }
+    if (pdata->port == 0) {
+        error(0,"CIMD2[%s]: Configuration file doesn't specify port",
+              octstr_get_cstr(conn->id));
+        ok = 0;
+    }
+    if (pdata->username == NULL) {
+        error(0, "CIMD2[%s]: Configuration file doesn't specify username.",
+              octstr_get_cstr(conn->id));
+        ok = 0;
+    }
+    if (pdata->password == NULL) {
+        error(0, "CIMD2[%s]: Configuration file doesn't specify password.",
+              octstr_get_cstr(conn->id));
+        ok = 0;
+    }
+
+    if (!ok) {
+        cimd2_destroy(pdata);
+        return -1;
+    }
+
+    conn->name = octstr_format("CIMD2:%s:%d:%s",
+                     octstr_get_cstr(pdata->host),
+                     pdata->port,
+                     octstr_get_cstr(pdata->username));
+
+
+    if (pdata->keepalive > 0) {
+      debug("bb.sms.cimd2", 0, "CIMD2[%s]: Keepalive set to %ld seconds", 
+            octstr_get_cstr(conn->id),
+            pdata->keepalive);
+        pdata->next_ping = time(NULL) + pdata->keepalive;
+    }
+
+    maxlen = parm_maxlen(P_USER_IDENTITY);
+    if (octstr_len(pdata->username) > maxlen) {
+        octstr_truncate(pdata->username, maxlen);
+        warning(0, "CIMD2[%s]: Truncating username to %d chars", 
+                octstr_get_cstr(conn->id),
+                maxlen);
+    }
+
+    maxlen = parm_maxlen(P_PASSWORD);
+    if (octstr_len(pdata->password) > maxlen) {
+        octstr_truncate(pdata->password, maxlen);
+        warning(0, "CIMD2[%s]: Truncating password to %d chars", 
+                octstr_get_cstr(conn->id),
+                maxlen);
+    }
+
+    pdata->receiver_thread = gwthread_create(sms_receiver, conn);
+    pdata->sender_thread = gwthread_create(sms_sender, conn);
+
+    if ((pdata->receiver_thread == -1) ||  
+        (pdata->sender_thread == -1)) {  
+
+        error(0,"CIMD2[%s]: Couldn't start I/O threads.",
+              octstr_get_cstr(conn->id));
+        pdata->quitting = 1;
+        if (pdata->sender_thread != -1) {
+            gwthread_wakeup(pdata->sender_thread);
+            gwthread_join(pdata->sender_thread);
+        }  
+        if (pdata->receiver_thread != -1) {
+            gwthread_wakeup(pdata->receiver_thread);
+            gwthread_join(pdata->receiver_thread);
+        }
+        cimd2_destroy(pdata);
+        return -1;  
+
+    } 
+
+    conn->send_msg = cimd2_add_msg_cb;
+    conn->shutdown = cimd2_shutdown_cb;
+    conn->queued = cimd2_queued_cb;
+    conn->start_conn = cimd2_start_cb;
+    conn->stop_conn = cimd2_stop_cb;
+
+    return 0;
+}
+
Index: gw/smsc/smsc_p.h
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_p.h,v
retrieving revision 1.2
diff -u -r1.2 smsc_p.h
--- gw/smsc/smsc_p.h	28 Jan 2003 00:10:04 -0000	1.2
+++ gw/smsc/smsc_p.h	21 Mar 2003 15:23:47 -0000
@@ -23,7 +23,6 @@
 enum {
 	SMSC_TYPE_DELETED,
 	SMSC_TYPE_CIMD,
-	SMSC_TYPE_CIMD2,
 	SMSC_TYPE_EMI,
 	SMSC_TYPE_EMI_X31,
 	SMSC_TYPE_EMI_IP,
@@ -90,19 +89,6 @@
 	time_t cimd_last_spoke;
 	int cimd_config_bits;
 
-	/* CIMD 2 */
-	Octstr *cimd2_hostname;
-	int cimd2_port;
-	Octstr *cimd2_username;
-	Octstr *cimd2_password;
-	int cimd2_send_seq;
-	int cimd2_receive_seq;
-	Octstr *cimd2_inbuffer;
-	List *cimd2_received;
-	int cimd2_error;
-	time_t cimd2_next_ping;
-        Octstr *sender_prefix;
-	
 	/* EMI */
 	int emi_fd;
 	FILE *emi_fp;
@@ -141,6 +127,7 @@
         char *ois_buffer;
         size_t ois_bufsize;
         size_t ois_buflen;
+        Octstr *sender_prefix;
     
 	/* AT Commands (wireless modems...) */
 	char *at_serialdevice;
@@ -207,16 +194,6 @@
 int cimd_pending_smsmessage(SMSCenter *smsc);
 int cimd_submit_msg(SMSCenter *smsc, Msg *msg);
 int cimd_receive_msg(SMSCenter *smsc, Msg **msg);
-
-/*
- * Interface to Nokia SMS centers using CIMD 2.
- */
-SMSCenter *cimd2_open(Octstr *hostname, int port, Octstr *username, Octstr *password, int keepalive, Octstr *sender_prefix);
-int cimd2_reopen(SMSCenter *smsc);
-int cimd2_close(SMSCenter *smsc);
-int cimd2_pending_smsmessage(SMSCenter *smsc);
-int cimd2_submit_msg(SMSCenter *smsc, Msg *msg);
-int cimd2_receive_msg(SMSCenter *smsc, Msg **msg);
 
 /*
  * Interface to CMG SMS centers using EMI.
