--- gateway/gw/smsc/smsc_at2.c	2002-08-11 14:46:55.000000000 +0300
+++ gateway/gw/smsc/smsc_at2.c	2002-08-18 21:06:47.000000000 +0300
@@ -35,13 +35,6 @@
 #include "dlr.h"
 #include "smsc_at2.h"
 
-
-static void at2_octstr_destroy(Octstr *ostr)
-{
-    octstr_destroy(ostr);
-}
-
-
 static int at2_open_device1(PrivAT2data *privdata)
 {
     info(0, "AT2[%s]: opening device", octstr_get_cstr(privdata->name));
@@ -241,6 +234,7 @@
 {
     int count;
     int s = 0;
+    int write_count = 0;
     Octstr *linestr = NULL;
 
     linestr = octstr_format("%s\r", line);
@@ -248,7 +242,15 @@
     debug("bb.smsc.at2", 0, "AT2[%s]: --> %s^M", octstr_get_cstr(privdata->name), line);
 
     count = octstr_len(linestr);
-    s = write(privdata->fd, octstr_get_cstr(linestr), count);
+    while (1) {
+	errno = 0;
+	s = write(privdata->fd, octstr_get_cstr(linestr), count);
+	if (s < 0 && errno == EAGAIN && write_count < RETRY_SEND) {
+	    gwthread_sleep(1);
+	    ++write_count;
+	} else
+	    break;
+    };
     O_DESTROY(linestr);
     if (s < 0) {
         debug("bb.smsc.at2", 0, "AT2[%s]: write failed with errno %d", 
@@ -266,9 +268,18 @@
 {
     int s;
     char *ctrlz = "\032" ;
-
+    int write_count = 0;
+    
     debug("bb.smsc.at2", 0, "AT2[%s]: --> ^Z", octstr_get_cstr(privdata->name));
-    s = write(privdata->fd, ctrlz, 1);
+    while (1) {
+	errno = 0;
+	s = write(privdata->fd, ctrlz, 1);
+	if (s < 0 && errno == EAGAIN && write_count < RETRY_SEND) {
+	    gwthread_sleep(1);
+	    ++write_count;
+	} else
+	    break;
+    };
     if (s < 0) {
         debug("bb.smsc.at2", 0, "AT2[%s]: write failed with errno %d", 
               octstr_get_cstr(privdata->name), errno);
@@ -430,7 +441,7 @@
         ts = list_search(vals, octstr_imm("1"), (void*) octstr_item_match);
         if (ts)
             privdata->phase2plus = 1;
-        list_destroy(vals, (void*) at2_octstr_destroy);
+        list_destroy(vals, octstr_destroy_item);
     }
     if (privdata->phase2plus) {
         info(0, "AT2[%s]: Phase 2+ is supported", octstr_get_cstr(privdata->name));
@@ -470,7 +481,7 @@
 
 
 int at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, int gt_flag, 
-                           int *messages_collected)
+                           int *output)
 {
     Octstr *line = NULL;
     Octstr *line2 = NULL;
@@ -523,7 +534,8 @@
                 ret = 1;
                 goto end;
             }
-            if (octstr_search(line, octstr_imm("+CMT:"), 0) != -1 || 
+            if (octstr_search(line, octstr_imm("+CMT:"), 0) != -1 ||
+		octstr_search(line, octstr_imm("+CDS:"), 0) != -1 ||
                 ((octstr_search(line, octstr_imm("+CMGR:"), 0) != -1) && (cmgr_flag = 1)) ) {
                 line2 = at2_wait_line(privdata, 1, 0);
 
@@ -541,8 +553,8 @@
                               octstr_get_cstr(privdata->name));
                     } else {
                         /* count message even if I can't decode it */
-                        if (messages_collected)
-                            ++(*messages_collected);
+                        if (output)
+                            ++(*output);
                         msg = at2_pdu_decode(pdu, privdata);
                         if (msg != NULL) {
                             msg->sms.smsc_id = octstr_duplicate(privdata->conn->id);
@@ -559,6 +571,14 @@
                 }
                 continue;
             }
+            if ((octstr_search(line, octstr_imm("+CMGS:"),0) != -1) && (output)) {
+		/* found response to a +CMGS command, read the message id and return it in output */
+		long temp;
+		if (octstr_parse_long(&temp, line, octstr_search(line, octstr_imm("+CMGS:"),0)+6,10) == -1)
+		    error(0,"AT2[%s]: got +CMGS but failed to read message id", octstr_get_cstr(privdata->name));
+		else
+		    *output = temp;
+            }
 
             if ( -1 != octstr_search(line, octstr_imm("ERROR"), 0)) {
                 ret = -1;
@@ -914,6 +934,15 @@
     conn->status = SMSCCONN_DISCONNECTED;
     /* maybe some cleanup here? */
     at2_destroy_modem(privdata->modem);
+    octstr_destroy(privdata->device);
+    octstr_destroy(privdata->ilb);
+    octstr_destroy(privdata->lines);
+    octstr_destroy(privdata->pin);
+    octstr_destroy(privdata->validityperiod);
+    octstr_destroy(privdata->my_number);
+    octstr_destroy(privdata->sms_center);
+    octstr_destroy(privdata->name);
+    octstr_destroy(privdata->configfile);
     gw_free(conn->data);
     conn->data = NULL;
     conn->why_killed = SMSCCONN_KILLED_SHUTDOWN;
@@ -1107,25 +1149,30 @@
 
     buffer = octstr_duplicate(line);
     /* find the beginning of a message from the modem*/
-    if ((pos = octstr_search(buffer, octstr_imm("+CMT:"), 0)) != -1)
-        pos += 5;
-    else if ((pos = octstr_search(buffer, octstr_imm("+CMGR:"), 0)) != -1) {
-        pos += 6;
-        if ((pos = octstr_search(buffer, octstr_imm(","), pos)) != -1) 
-            /* skip status field in +CMGR response */
-            pos++;
-        else
-            goto nomsg;
-    } else
-        goto nomsg;
 
-    tmp = octstr_search(buffer, octstr_imm(","), pos);
-    if (! privdata->modem->broken && tmp == -1)
-        goto nomsg;
-    if (tmp != -1)
-        pos = tmp + 1;
+    if ((pos = octstr_search(buffer, octstr_imm("+CDS:"), 0)) != -1) 
+	pos += 5;
+    else {
+	if ((pos = octstr_search(buffer, octstr_imm("+CMT:"), 0)) != -1)
+	    pos += 5;
+	else if ((pos = octstr_search(buffer, octstr_imm("+CMGR:"), 0)) != -1) {
+	    /* skip status field in +CMGR response */
+	    if ((pos = octstr_search(buffer, octstr_imm(","), pos + 6)) != -1) 
+		pos++;
+	    else
+		goto nomsg;
+	} else
+	    goto nomsg;
+
+	/* skip the next comma in CMGR and CMT responses */
+	tmp = octstr_search(buffer, octstr_imm(","), pos);
+	if (! privdata->modem->broken && tmp == -1)
+	    goto nomsg;
+	if (tmp != -1)
+	    pos = tmp + 1;
+    }
 
-    /* The message length is after the comma */
+    /* read the message length */
     pos = octstr_parse_long(&len, buffer, pos, 10);
     if (pos == -1)
         goto nomsg;
@@ -1183,6 +1230,9 @@
         case AT_DELIVER_SM:
             msg = at2_pdu_decode_deliver_sm(data, privdata);
             break;
+        case AT_STATUS_REPORT_SM:
+	    msg = at2_pdu_decode_report_sm(data, privdata);
+	    break;
 
             /* Add other message types here: */
 
@@ -1354,6 +1404,112 @@
     return message;
 }
 
+Msg *at2_pdu_decode_report_sm(Octstr *data, PrivAT2data *privdata)
+{
+   Msg* dlrmsg = NULL;
+   Octstr *pdu, *msg_id, *tmpstr = NULL, *receiver = NULL;
+   int type, tp_mr, len, ntype, pos;
+
+    /*
+     * parse the PDU.
+     */
+
+    /* convert the pdu to binary format for ease of processing */
+    pdu = at2_convertpdu(data);
+
+    /* Message reference */
+    tp_mr = octstr_get_char(pdu,1);
+    msg_id = octstr_format("%d",tp_mr);
+    debug("bb.smsc.at2",0,"AT2[%s]: got STATUS-REPORT for message <%d>:", octstr_get_cstr(privdata->name), tp_mr);
+    
+    /* reciver address */
+    len = octstr_get_char(pdu, 2);
+    ntype = octstr_get_char(pdu, 3);
+
+    pos = 4;
+    if ((ntype & 0xD0) == 0xD0) {
+        /* Alphanumeric sender */
+        receiver = octstr_create("");
+        tmpstr = octstr_copy(pdu, pos, (len+1)/2);
+        at2_decode7bituncompressed(tmpstr, (((len - 1) * 4 - 3) / 7) + 1, receiver, 0);
+        octstr_destroy(tmpstr);
+        debug("bb.smsc.at2", 0, "AT2[%s]: Alphanumeric receiver <%s>",
+              octstr_get_cstr(privdata->name), octstr_get_cstr(receiver));
+        pos += (len + 1) / 2;
+    } else {
+	int i;
+        receiver = octstr_create("");
+        if ((ntype & 0x90) == 0x90) {
+            /* International number */
+            octstr_append_char(receiver, '+');
+        }
+        for (i = 0; i < len; i += 2, pos++) {
+            octstr_append_char(receiver, (octstr_get_char(pdu, pos) & 15) + 48);
+            if (i + 1 < len)
+                octstr_append_char(receiver, (octstr_get_char(pdu, pos) >> 4) + 48);
+        }
+        debug("bb.smsc.at2", 0, "AT2[%s]: Numeric receiver %s <%s>",
+              octstr_get_cstr(privdata->name), ((ntype & 0x90) == 0x90 ? "(international)" : ""),
+              octstr_get_cstr(receiver));
+    }
+
+    pos += 14; /* skip time stamps for now */
+
+    if ((type = octstr_get_char(pdu, pos)) == -1 ) {
+	error(1,"AT2[%s]: STATUS-REPORT pdu too short to have TP-Status field !",
+	    octstr_get_cstr(privdata->name));
+	goto error;
+    }
+
+	/* check DLR type:
+	 * 3GPP TS 23.040 defines this a bit mapped field with lots of options
+	 * most of which are not really intersting to us, as we are only interested
+	 * in one of three conditions : failed, held in SC for delivery later, or delivered successfuly
+	 * and here's how I suggest to test it (read the 3GPP reference for further detailes) -
+	 * we'll test the 6th and 5th bits (7th bit when set making all other values 'reseved' so I want to test it).
+	 */
+    type = type & 0xE0; /* filter out everything but the 7th, 6th and 5th bits */
+    switch (type) {
+	case 0x00:
+	    /* 0 0 : success class */
+	    type = DLR_SUCCESS;
+	    tmpstr = octstr_create("Success/");
+	    break;
+	case 0x20:
+	    /* 0 1 : buffered class (temporary error) */
+	    type = DLR_BUFFERED;
+	    tmpstr = octstr_create("Buffered/");
+	    break;
+	case 0x40:
+	case 0x60:
+	default:
+	    /* 1 0 : failed class */
+	    /* 1 1 : failed class (actually, temporary error but timed out) */
+	    /* and any other value (can't think of any) is considered failure */
+	    type = DLR_FAIL;
+	    tmpstr = octstr_create("Failed/");
+	    break;
+    }
+    /* Actually, the above implementation is not correct, as the reference says that implementations should consider
+     * any "reserved" values to be "failure", but most reserved values fall into one of the three categories. it will catch
+     * "reserved" values where the first 3 MSBits are not set as "Success" which may not be correct. */
+
+    if ((dlrmsg = dlr_find(octstr_get_cstr(privdata->conn->id),
+	    octstr_get_cstr(msg_id), octstr_get_cstr(receiver), type)) == NULL) {
+	debug("bb.smsc.at2",1,"AT2[%s]: Received delivery notification but can't find that ID in the DLR storage",
+	    octstr_get_cstr(privdata->name));
+	    goto error;
+    }
+
+    octstr_insert(dlrmsg->sms.msgdata, tmpstr, 0);
+	
+error:
+    O_DESTROY(tmpstr);
+    O_DESTROY(pdu);
+    O_DESTROY(receiver);
+    O_DESTROY(msg_id);
+    return dlrmsg;
+}
 
 Octstr *at2_convertpdu(Octstr *pdutext)
 {
@@ -1434,7 +1590,7 @@
 
 void at2_send_one_message(PrivAT2data *privdata, Msg *msg)
 {
-    unsigned char command[500], pdu[500];
+    unsigned char command[500];
     int ret = -1;
     char sc[3];
     int retries = RETRY_SEND;
@@ -1456,18 +1612,24 @@
         strcpy(sc, "00");
 
     if (msg_type(msg) == sms) {
-        at2_pdu_encode(msg, &pdu[0], privdata);
+	Octstr* pdu;
+
+	if ((pdu = at2_pdu_encode(msg, privdata)) == NULL) {
+	    error(2, "AT2[%s]: Error encoding PDU!",octstr_get_cstr(privdata->name));
+	    return;
+	}	
 
         ret = -99;
         retries = RETRY_SEND;
         while ((ret != 0) && (retries-- > 0)) {
+	    int msg_id = -1;
             /* 
              * send the initial command and then wait for > 
              */
-            sprintf(command, "AT+CMGS=%d", strlen(pdu) / 2);
+            sprintf(command, "AT+CMGS=%ld", octstr_len(pdu) / 2);
             
             ret = at2_send_modem_command(privdata, command, 5, 1);
-            debug("bb.at", 0, "AT2[%s]: send command status: %d",
+            debug("bb.smsc.at2", 0, "AT2[%s]: send command status: %d",
                   octstr_get_cstr(privdata->name), ret);
 
             if (ret != 1) /* > only! */
@@ -1476,22 +1638,83 @@
              * ok the > has been see now so we can send the PDU now and a 
              * control Z but no CR or LF 
              */
-            sprintf(command, "%s%s", sc, pdu);
+            sprintf(command, "%s%s", sc, octstr_get_cstr(pdu));
             at2_write(privdata, command);
             at2_write_ctrlz(privdata);
             
             /* wait 20 secs for modem command */
-            ret = at2_wait_modem_command(privdata, 20, 0, NULL);
-            debug("bb.at", 0, "AT2[%s]: send command status: %d",
+            ret = at2_wait_modem_command(privdata, 20, 0, &msg_id);
+            debug("bb.smsc.at2", 0, "AT2[%s]: send command status: %d",
                   octstr_get_cstr(privdata->name), ret);
             
             if (ret != 0) /* OK only */
                 continue;
+
+	    /* gen DLR_SMSC_SUCCESS */
+	    if (msg->sms.dlr_mask & DLR_SMSC_SUCCESS)
+	    {
+		Msg* dlrmsg;
+
+		dlrmsg = msg_create(sms);
+		dlrmsg->sms.id = msg->sms.id;
+                dlrmsg->sms.service = octstr_duplicate(msg->sms.service);
+                dlrmsg->sms.dlr_mask = DLR_SMSC_SUCCESS;
+                dlrmsg->sms.sms_type = report;
+                dlrmsg->sms.smsc_id = octstr_duplicate(privdata->conn->id);
+                dlrmsg->sms.sender = octstr_duplicate(msg->sms.receiver);
+                dlrmsg->sms.receiver = octstr_duplicate(msg->sms.sender);
+                dlrmsg->sms.msgdata = octstr_create("ACK/");
+                octstr_append(dlrmsg->sms.msgdata,msg->sms.dlr_url);
+                time(&dlrmsg->sms.time);
+
+		debug("bb.smsc.at2",0,"AT2[%s]: sending DLR type ACK", octstr_get_cstr(privdata->name));
+		bb_smscconn_receive(privdata->conn, dlrmsg);
+
+	    }
+
+	    /* store DLR message if needed for SMSC generated delivery reports */
+	    if (msg->sms.dlr_mask & (DLR_SUCCESS | DLR_FAIL | DLR_BUFFERED)) {
+		if (msg_id == -1)
+		    error(0,"AT2[%s]: delivery notification requested, but I have no message ID!",
+			octstr_get_cstr(privdata->name));
+		 else {
+		     Octstr* dlrmsgid = octstr_format("%d", msg_id);
+
+		     dlr_add(octstr_get_cstr(privdata->conn->id),
+			octstr_get_cstr(dlrmsgid),
+			octstr_get_cstr(msg->sms.receiver),
+			octstr_get_cstr(msg->sms.service),
+			octstr_get_cstr(msg->sms.dlr_url),
+			msg->sms.dlr_mask);
+				
+		    O_DESTROY(dlrmsgid);
+		}
+	    }
+
             counter_increase(privdata->conn->sent);
             bb_smscconn_sent(privdata->conn, msg);
         }
 
         if (ret != 0) {
+	    /* gen DLR_SMSC_FAIL */
+	    if (msg->sms.dlr_mask & DLR_SMSC_FAIL) {
+		Msg* dlrmsg;
+
+		dlrmsg = msg_create(sms);
+                dlrmsg->sms.service = octstr_duplicate(msg->sms.service);
+                dlrmsg->sms.dlr_mask = DLR_SMSC_FAIL;
+                dlrmsg->sms.sms_type = report;
+                dlrmsg->sms.smsc_id = octstr_duplicate(privdata->conn->id);
+                dlrmsg->sms.sender = octstr_duplicate(msg->sms.receiver);
+                dlrmsg->sms.receiver = octstr_duplicate(msg->sms.sender);
+                dlrmsg->sms.msgdata = octstr_create("NACK/");
+                octstr_append(dlrmsg->sms.msgdata,msg->sms.dlr_url);
+                time(&dlrmsg->sms.time);
+
+		debug("bb.smsc.at2",0,"AT2[%s]: sending DLR type NACK", octstr_get_cstr(privdata->name));
+		bb_smscconn_receive(privdata->conn, dlrmsg);
+	    }
+
             /*
              * no need to do counter_increase(privdata->conn->failed) here, 
              * since bb_smscconn_send_failed() will inc the counter on 
@@ -1499,107 +1722,47 @@
              */
             bb_smscconn_send_failed(privdata->conn, msg, SMSCCONN_FAILED_MALFORMED);
         }
+
+        O_DESTROY(pdu);
     }
 }
 
 
-int at2_pdu_encode(Msg *msg, unsigned char *pdu, PrivAT2data *privdata)
+Octstr* at2_pdu_encode(Msg *msg, PrivAT2data *privdata)
 {
-    int pos = 0, i, len, setvalidity = 0;
-    int ntype = PNT_UNKNOWN; /* number type default */
-    int nstartpos = 0; /* offset for the phone number */
-    int dcs; /* data coding scheme (GSM 03.38) */
-
-    /* 
-     * The message is encoded directly in the text representation of
-     * the hex values that will be sent to the modem.
-     * Each octet is coded with two characters. 
+    /*
+     * Message coding is done as a binary octet string,
+     * as per 3GPP TS 23.040 specification (GSM 03.40),
      */
+    Octstr *pdu = NULL, *temp = NULL, *buffer = octstr_create("");
+     
+    int len, setvalidity = 0;
 
     /* 
-     * message type SUBMIT
-     *    01010001 = 0x51 indicating add. UDH, TP-VP(Rel) & MSG_SUBMIT
-     * or 00010001 = 0x11 for just TP-VP(Rel) & MSG_SUBMIT 
+     * message type SUBMIT , bit mapped :
+     * bit7                            ..                                    bit0
+     * TP-RP , TP-UDHI, TP-SRR, TP-VPF(4), TP-VPF(3), TP-RD, TP-MTI(1), TP-MTI(0)
      */
-
-    pdu[pos] = octstr_len(msg->sms.udhdata) ? at2_numtext(5) : at2_numtext(1);
-    pos++;
-    pdu[pos] = at2_numtext(AT_SUBMIT_SM);
-    pos++;
+    octstr_append_char(buffer,
+	((msg->sms.rpi ? 1 : 0) << 7) /* TP-RP */
+	| ((octstr_len(msg->sms.udhdata)  ? 1 : 0) << 6) /* TP-UDHI */
+	| (((msg->sms.dlr_mask & (DLR_SUCCESS | DLR_FAIL | DLR_BUFFERED)) ? 1 : 0) << 5) /* TP-SRR */
+	| 16 /* TP-VP(Rel)*/
+	| 1 /* TP-MTI: SUBMIT_SM */
+	);
 
     /* message reference (0 for now) */
-    pdu[pos] = at2_numtext(0);
-    pos++;
-    pdu[pos] = at2_numtext(0);
-    pos++;
+    octstr_append_char(buffer, 0);
 
     /* destination address */
-    octstr_strip_blanks(msg->sms.receiver); /* strip blanks before length calculation */
-    len = octstr_len(msg->sms.receiver);
-
-    /* 
-     * Check for international numbers
-     * number starting with '+' or '00' are international,
-     * others are national. 
-     */
-    if (strncmp(octstr_get_cstr(msg->sms.receiver), "+", 1) == 0) {
-        debug("bb.smsc.at2", 0, "AT2[%s]: international starting with + (%s)",
-              octstr_get_cstr(privdata->name), octstr_get_cstr(msg->sms.receiver));
-        nstartpos++;
-        ntype = PNT_INTER; /* international */
-    } else if (strncmp(octstr_get_cstr(msg->sms.receiver), "00", 2) == 0) {
-        debug("bb.smsc.at2", 0, "AT2[%s]: international starting with 00 (%s)",
-              octstr_get_cstr(privdata->name), octstr_get_cstr(msg->sms.receiver));
-        nstartpos += 2;
-        ntype = PNT_INTER; /* international */
-    }
-
-    /* address length */
-    pdu[pos] = at2_numtext(((len - nstartpos) & 240) >> 4);
-    pos++;
-    pdu[pos] = at2_numtext((len - nstartpos) & 15);
-    pos++;
-
-    /* Type of number */
-    pdu[pos] = at2_numtext(8 + ntype);
-    pos++;
-    /* numbering plan: ISDN/Telephone numbering plan */
-    pdu[pos] = at2_numtext(1);
-    pos++;
-
-    /* 
-     * make sure there is no blank in the phone number and encode
-     * an even number of digits 
-     */
-    octstr_strip_blanks(msg->sms.receiver);
-    for (i = nstartpos; i < len; i += 2) {
-        if (i + 1 < len) {
-            pdu[pos] = octstr_get_char(msg->sms.receiver, i + 1);
-        } else {
-            pdu[pos] = at2_numtext (15);
-        }
-        pos++;
-        pdu[pos] = octstr_get_char(msg->sms.receiver, i);
-        pos++;
-    }
-
-    /* 
-     * protocol identifier 
-     * 0x00 implicit 
-     */
-    pdu[pos] = at2_numtext((msg->sms.pid & 240) >> 4);
-    pos++;
-    pdu[pos] = at2_numtext(msg->sms.pid & 15);
-    pos++;
-
-    /* data coding scheme */
-    dcs = fields_to_dcs(msg,
-                        (msg->sms.alt_dcs ? 2 - msg->sms.alt_dcs : privdata->alt_dcs));
-
-    pdu[pos] = at2_numtext(dcs >> 4);
-    pos++;
-    pdu[pos] = at2_numtext(dcs % 16);
-    pos++;
+    if ((temp = at2_format_address_field(msg->sms.receiver)) == NULL)
+	goto error;
+    octstr_append(buffer, temp);
+    O_DESTROY(temp);
+
+    octstr_append_char(buffer, msg->sms.pid); /* protocol identifier */
+    octstr_append_char(buffer, fields_to_dcs(msg, /* data coding scheme */
+	(msg->sms.alt_dcs ? 2 - msg->sms.alt_dcs : privdata->alt_dcs)));
 
     /* 
      * Validity-Period (TP-VP)
@@ -1641,10 +1804,7 @@
     else
         debug("bb.smsc.at2", 0, "AT2[%s]: TP-Validity-Period: %d weeks",
               octstr_get_cstr(privdata->name), (setvalidity - 192));
-    pdu[pos] = at2_numtext((setvalidity & 240) >> 4);
-    pos++;
-    pdu[pos] = at2_numtext(setvalidity & 15);
-    pos++;
+    octstr_append_char(buffer, setvalidity);
 
     /* user data length - include length of UDH if it exists */
     len = sms_msgdata_len(msg);
@@ -1652,6 +1812,10 @@
     if (octstr_len(msg->sms.udhdata)) {
         if (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) {
             len += octstr_len(msg->sms.udhdata);
+            if (len > SMS_8BIT_MAX_LEN) { /* truncate user data to allow UDH to fit */
+                octstr_delete(msg->sms.msgdata, SMS_8BIT_MAX_LEN - octstr_len(msg->sms.udhdata), 9999);
+                len = SMS_8BIT_MAX_LEN;
+            }
         } else {
             /*
              * The reason we branch here is because UDH data length is determined
@@ -1659,140 +1823,102 @@
              * will ensure that for an octet length of 0, we get septet length 0,
              * and for octet length 1 we get septet length 2. 
              */
-            len += (((8 * octstr_len(msg->sms.udhdata)) + 6) / 7);
+            int temp_len;
+            len += (temp_len = (((8 * octstr_len(msg->sms.udhdata)) + 6) / 7));
+            if (len > SMS_7BIT_MAX_LEN) { /* truncate user data to allow UDH to fit */
+                octstr_delete(msg->sms.msgdata, SMS_7BIT_MAX_LEN - temp_len, 9999);
+                len = SMS_7BIT_MAX_LEN;
+            }
         }
     }
 
-    pdu[pos] = at2_numtext((len & 240) >> 4);
-    pos++;
-    pdu[pos] = at2_numtext(len & 15);
-    pos++;
+    octstr_append_char(buffer,len);
 
-    /* udh */
-    if (octstr_len(msg->sms.udhdata)) {
-        pos += at2_encode8bituncompressed(msg->sms.udhdata, &pdu[pos]);
-    }
+    if (octstr_len(msg->sms.udhdata)) /* udh */
+	octstr_append(buffer, msg->sms.udhdata);
 
-    /* 
-     * user data 
-     * if the data is too long, it is cut 
-     */
+    /* user data */
     if (msg->sms.coding == DC_8BIT || msg->sms.coding == DC_UCS2) {
-        pos += at2_encode8bituncompressed(msg->sms.msgdata, &pdu[pos]);
+        octstr_append(buffer, msg->sms.msgdata);
     } else {
         int offset = 0;
 
+        /*
+         * calculate the number of fill bits needed to align
+         * the 7bit encoded user data on septet boundry
+         */
         if (octstr_len(msg->sms.udhdata)) { /* Have UDH */
             int nbits = octstr_len(msg->sms.udhdata) * 8; /* Includes UDH length byte */
             offset = (((nbits / 7) + 1) * 7 - nbits) % 7; /* Fill bits */
         }
-        pos += at2_encode7bituncompressed(msg->sms.msgdata, &pdu[pos], offset);
+
+        charset_latin1_to_gsm(msg->sms.msgdata);
+        
+        if ((temp = at2_encode7bituncompressed(msg->sms.msgdata, offset)) != NULL)
+	    octstr_append(buffer, temp);
+        O_DESTROY(temp);
     }
-    pdu[pos] = 0;
 
-    return 0;
+    /* convert PDU to HEX representation suitable for the AT2 command set */
+    pdu = at2_encode8bituncompressed(buffer);
+    O_DESTROY(buffer);
+
+    return pdu;
+error:
+    O_DESTROY(temp);
+    O_DESTROY(buffer);
+    O_DESTROY(pdu);
+    return NULL;
 }
 
 
-int at2_encode7bituncompressed(Octstr *input, unsigned char *encoded, int offset)
+Octstr* at2_encode7bituncompressed(Octstr *source, int offset)
 {
-
-    unsigned char prevoctet, tmpenc;
-    int i;
-    int c = 1;
-    int r = 7;
-    int pos = 0;
-    int len;
-    unsigned char enc7bit[256];
-    int j, encpos = 0;
-    int ermask[8] = { 0, 1, 3, 7, 15, 31, 63, 127 };
-    int elmask[8] = { 0, 64, 96, 112, 120, 124, 126, 127 };
-
-    charset_latin1_to_gsm(input);
-    len = octstr_len(input);
-
-    /* 
-     * prevoctet is set to the first character and we'll start the loop
-     * at the following char. 
-     */
-    prevoctet = octstr_get_char(input , 0);
-    for (i = 1; i < octstr_len(input); i++) {
-        /* 
-         * a byte is encoded with what is left of the previous character
-         * and filled with as much as possible of the current one. 
-         */
-        tmpenc = prevoctet + ((octstr_get_char(input, i) & ermask[c]) << r);
-        enc7bit[encpos] = tmpenc;
-        encpos++;
-        c = (c > 6) ? 1 : c + 1;
-        r = (r < 2) ? 7 : r - 1;
-
-        /* 
-         * prevoctet becomes the part of the current octet that hasn't
-         * been copied to 'encoded' or the next char if the current has
-         * been completely copied already. 
-         */
-        prevoctet = (octstr_get_char(input, i) & elmask[r]) >> (c - 1);
-        if (r == 7) {
-            i++;
-            prevoctet = octstr_get_char(input, i);
-        }
-    }
-
-    /* 
-     * if the length of the message is a multiple of 8 then we
-     * are finished. Otherwise prevoctet still contains part of a 
-     * character so we add it. 
-     */
-    if ((len / 8)*8 != len) {
-        enc7bit[encpos] = prevoctet;
-        encpos++;
+    int LSBmask[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F };
+    int MSBmask[8] = { 0x00, 0x40, 0x60, 0x70, 0x78, 0x7C, 0x7E, 0x7F };
+    int i = (offset?8-offset:7), iStore = offset;
+    int posT, posS;
+    Octstr *target = octstr_create("");
+    int target_chr = 0, source_chr;
+
+    /* start packing the septet stream into an octet stream */
+    for (posS = 0, posT = 0; (source_chr = octstr_get_char(source, posS++)) != -1;) {
+	/* grab least significant bits from current septet and store them packed to the right */
+	target_chr |= (source_chr & LSBmask[i]) << iStore;
+	/* store current byte if last command filled it */
+	if (iStore != 0) {
+	    octstr_append_char(target, target_chr);
+	    target_chr = 0;
+	}
+	/* grab most significant bits from current septet and store them packed to the left */
+	target_chr |= (source_chr & MSBmask[7 - i]) >> (8 - iStore) % 8;
+	/* advance target bit index by 7 ( modulo 8 addition ) */
+	iStore = (--iStore < 0 ? 7 : iStore);
+	if (iStore != 0) /* if just finished packing 8 septets (into 7 octets) don't advance mask index */
+	    i = (++i > 7 ? 1 : i); 
     }
 
-    /* Now shift the buffer by the offset */
-    if (offset > 0) {
-        unsigned char nextdrop, lastdrop;
-
-        nextdrop = lastdrop = 0;
-        for (i = 0; i < encpos; i++) {
-            nextdrop = enc7bit[i] >> (8 - offset); /* This drops off by shifting */
-            if (i == 0)
-                enc7bit[i] = enc7bit[i] << offset; /* This drops off by shifting */
-            else
-                enc7bit[i] = (enc7bit[i] << offset) | lastdrop;
-            lastdrop = nextdrop;
-        }
-
-        if (offset > ((len*7) % 8)) {
-            enc7bit [i] = nextdrop;
-            i++;
-        }
-    } else
-        i = encpos;
-
-    for (j = 0; j < i; j++) {
-        encoded[pos] = at2_numtext((enc7bit [j] & 240) >> 4);
-        pos++;
-        encoded[pos] = at2_numtext(enc7bit [j] & 15);
-        pos++;
-    }
-    return pos;
+    /* don't forget to pack the leftovers ;-) */
+    if (target_chr)
+	octstr_append_char(target, target_chr);
 
+    return target;
 }
 
 
-int at2_encode8bituncompressed(Octstr *input, unsigned char *encoded)
+Octstr* at2_encode8bituncompressed(Octstr *input)
 {
     int len, i;
+    Octstr* out = octstr_create("");
 
     len = octstr_len(input);
 
     for (i = 0; i < len; i++) {
         /* each character is encoded in its hex representation (2 chars) */
-        encoded[i*2] = at2_numtext((octstr_get_char(input, i) & 240) >> 4);
-        encoded[i*2 + 1] = at2_numtext(octstr_get_char(input, i) & 15);
+        octstr_append_char(out, at2_numtext( (octstr_get_char(input, i) & 0xF0) >> 4));
+        octstr_append_char(out, at2_numtext( (octstr_get_char(input, i) & 0x0F)));
     }
-    return len*2;
+    return out;
 }
 
 
@@ -1949,7 +2075,7 @@
         ts = list_search(vals, octstr_imm("1"), (void*) octstr_item_match);
         if (ts)
             privdata->phase2plus = 1;
-        list_destroy(vals, (void*) at2_octstr_destroy);
+        list_destroy(vals, octstr_destroy_item);
     }
     if (privdata->phase2plus)
         info(0, "AT2[%s]: Phase 2+ is supported", octstr_get_cstr(privdata->name));
@@ -2028,7 +2154,7 @@
 
         modem->init_string = cfg_get(grp, octstr_imm("init-string"));
         if (modem->init_string == NULL)
-            modem->init_string = octstr_create("AT+CNMI=1,2,0,0,0");
+            modem->init_string = octstr_create("AT+CNMI=1,2,0,1,0");
 
         modem->speed = 9600;
         cfg_get_integer(&modem->speed, grp, octstr_imm("speed"));
@@ -2092,3 +2218,46 @@
     return ( ( byte & 15 ) * 10 ) + ( byte >> 4 );
 }
 
+Octstr* at2_format_address_field(Octstr* msisdn)
+{
+    int ntype;
+    Octstr* out = octstr_create("");
+    Octstr* temp = octstr_duplicate(msisdn);
+
+    octstr_strip_blanks(temp);
+    /*
+     * Check for international numbers
+     * number starting with '+' or '00' are international,
+     * others are national.
+     */
+    if (strncmp(octstr_get_cstr(msisdn), "+", 1) == 0) {
+	octstr_delete(temp, 0, 1);
+        ntype = PNT_INTER; /* international */
+    } else if (strncmp(octstr_get_cstr(msisdn), "00", 2) == 0) {
+        octstr_delete(temp, 0, 2);
+        ntype = PNT_INTER; /* international */
+    }
+
+    /* address length */
+    octstr_append_char(out, octstr_len(temp));
+
+    /* Type of address : bit mapped values */
+    octstr_append_char(out, 0x80 /* Type-of-address prefix */ |
+			    0x01 /* Numbering-plan: MSISDN */ |
+			    (ntype == PNT_INTER ? 0x10 : 0x00) /* Type-of-number: International or National */
+			    );
+
+    /* grab the digits from the MSISDN and encode as swapped semi-octets */
+    while (octstr_len(temp)) {
+	int digit1, digit2;
+	/* get the first two digit */
+	digit1 = octstr_get_char(temp,0) - 48;
+	if ((digit2 = octstr_get_char(temp,1) - 48) < 0)
+	    digit2 = 0x0F;
+	octstr_append_char(out, (digit2 << 4) | digit1);
+	octstr_delete(temp, 0, 2);
+    }
+
+    O_DESTROY(temp);
+    return out;	
+}
--- gateway/gw/smsc/smsc_at2.h	2002-08-11 14:46:56.000000000 +0300
+++ gateway/gw/smsc/smsc_at2.h	2002-08-13 14:55:49.000000000 +0300
@@ -19,6 +19,7 @@
 /* Message types defines */
 #define AT_DELIVER_SM   0
 #define AT_SUBMIT_SM    1
+#define AT_STATUS_REPORT_SM 2
 
 /* type of phone number defines */
 #define PNT_UNKNOWN     0
@@ -164,7 +165,7 @@
  * Waits for the modem to send us something.
  */
 int at2_wait_modem_command(PrivAT2data *privdata, time_t timeout, 
-                           int greaterflag, int* messages_collected);
+                           int greaterflag, int* output);
 
 /*
  * Sets the serial port speed on the device
@@ -209,6 +211,11 @@
 Msg	*at2_pdu_decode_deliver_sm(Octstr *data, PrivAT2data *privdata);
 
 /*
+ * Decode a SUBMIT-REPORT PDU
+ */
+Msg	*at2_pdu_decode_report_sm(Octstr *data, PrivAT2data *privdata);
+
+/*
  * Converts the text representation of hexa to binary
  */
 Octstr *at2_convertpdu(Octstr *pdutext);
@@ -233,18 +240,17 @@
 /*
  * Encode a Msg into a PDU
  */
-int	at2_pdu_encode(Msg *msg, unsigned char *pdu, PrivAT2data *privdata);
+Octstr* at2_pdu_encode(Msg *msg, PrivAT2data *privdata);
 
 /*
- * Encode 7bit uncompressed user data
+ * Encode 7bit uncompressed user data into an Octstr, prefixing with <offset> 0 bits
  */
-int at2_encode7bituncompressed(Octstr *input, unsigned char *encoded, 
-                               int offset);
+Octstr* at2_encode7bituncompressed(Octstr *input, int offset);
 
 /*
- * Encode 8bit uncompressed user data
+ * Encode 8bit uncompressed user data into an Octstr
  */
-int	at2_encode8bituncompressed(Octstr *input, unsigned char *encoded);
+Octstr*	at2_encode8bituncompressed(Octstr *input);
 
 /*
  * Code a half-byte to its text hexa representation
@@ -295,6 +301,11 @@
  */
 int	swap_nibbles(char byte);
 
+/*
+ * creates a buffer with a valid PDU address field as per [GSM 03.40]
+ * from an MSISDN number
+ */
+Octstr* at2_format_address_field(Octstr* msisdn);
 
 #endif /* SMSC_AT2_H */
 
--- gateway/gw/sms.h	2002-01-27 02:16:10.000000000 +0200
+++ gateway/gw/sms.h	2002-08-13 14:53:03.000000000 +0300
@@ -53,6 +53,10 @@
 #define DC_8BIT 2
 #define DC_UCS2 3
 
+#define SMS_7BIT_MAX_LEN 160
+#define SMS_8BIT_MAX_LEN 140
+#define SMS_UCS2_MAX_LEN 70
+
 /* Encode DCS using sms fields
  * mode = 0= encode using 00xxxxxx, 1= encode using 1111xxxx mode
  */
--- gateway/doc/userguide/userguide.xml	2002-08-18 13:02:38.000000000 +0300
+++ gateway/doc/userguide/userguide.xml	2002-08-18 13:04:53.000000000 +0300
@@ -2871,8 +2871,7 @@
                 Polling the memory is done at the same interval as keepalive (if set) or 60 seconds
                 (if not set).
                 NOTE: This behaviour is known to cause minor or major hicups for a few buggy modems.
-                A modem known not to like this setting is Siemens M20.
-                A modem known to work with this setting is Wavecom.
+                Modems known to work with this setting are Wavecom WM02/M1200 and the Siemens M20.
      </entry></row>
 
    </tbody></tgroup></informaltable>
@@ -2928,8 +2927,8 @@
    <row><entry><literal>init-string</literal></entry>
      <entry><literal>string</literal></entry>
      <entry valign=bottom>
-     	Optional initialization string. Defaults to 
-	"AT+CNMI=1,2,0,0,0"
+     	Optional initialization string. Defaults to
+	"AT+CNMI=1,2,0,1,0"
      </entry></row>
 
    <row><entry><literal>speed</literal></entry>
@@ -3011,6 +3010,17 @@
      </entry></row>
 
    </tbody></tgroup></informaltable>
+
+   <para><emphasis>A note about delivery reports and GSM modems:</emphasis>
+   while it is possible (and supported) to receive delivery reports on GSM 
+   modems, it may not work for you. if you encounter problems, check that your 
+   modem's init string (if not the default) is set to correctly allow the modem 
+   to send delivery reports using unsolicted notification (check your modem's 
+   manual). If the init-string is not set as si, some modems will store delivery 
+   reports to SIM memory, to get at which you will need to enable sim-buffering. 
+   finally your GSM network provider may not support delivery reports to mobile 
+   units.</para>
+
 </sect3>
 
 
