--- gw/smsc_at2.c	2002-08-07 10:56:44.000000000 +0300
+++ gw/smsc_at2.c	2002-08-08 22:31:31.000000000 +0300
@@ -241,6 +241,7 @@
 {
     int count;
     int s = 0;
+    int write_count = 0;
     Octstr *linestr = NULL;
 
     linestr = octstr_format("%s\r", line);
@@ -248,7 +249,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 +275,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);
@@ -470,7 +488,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;
@@ -541,8 +559,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 +577,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;
@@ -1183,6 +1222,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 +1396,102 @@
     return message;
 }
 
+Msg *at2_pdu_decode_report_sm(Octstr *data, PrivAT2data *privdata)
+{
+/* example : 06 D2 0B 91 79 62 37 04 10 F4 20 80 80 81 52 05 00 20 80 80 81 52 45 00 00
+                MR 0B 91 97267340014-sender 02080818255000 ts 02080818255400 ts 00
+
+  buffered : 06DC0B917962370410F4208080027075002080800270850030
+  received : 06DC0B917962370410F4208080027075002080800211740000
+	     
+*/
+   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;
+    }
+
+    switch (type) {
+	case 0x30:
+	    type = DLR_BUFFERED;
+	    tmpstr = octstr_create("Buffered/");
+	    break;
+	case 0x00:
+	    type = DLR_SUCCESS;
+	    tmpstr = octstr_create("Success/");
+	    break;
+	default:
+	    /* this is prety stupid : I have no idea how a DLR_FAIL looks like, as I couldn't produce it */
+	    type = DLR_FAIL;
+	    tmpstr = octstr_create("Failed/");
+	    break;
+    }
+
+    if ((dlrmsg = dlr_find(octstr_get_cstr(privdata->conn->id),
+	    octstr_get_cstr(msg_id), octstr_get_cstr(receiver), type)) == NULL) {
+	debug("bb.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)
 {
@@ -1461,6 +1599,7 @@
         ret = -99;
         retries = RETRY_SEND;
         while ((ret != 0) && (retries-- > 0)) {
+	    int msg_id = -1;
             /* 
              * send the initial command and then wait for > 
              */
@@ -1481,17 +1620,76 @@
             at2_write_ctrlz(privdata);
             
             /* wait 20 secs for modem command */
-            ret = at2_wait_modem_command(privdata, 20, 0, NULL);
+            ret = at2_wait_modem_command(privdata, 20, 0, &msg_id);
             debug("bb.at", 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.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.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 
@@ -1517,14 +1715,18 @@
      */
 
     /* 
-     * 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);
+    i  = (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 */
+    pdu[pos] = at2_numtext(i >> 4);
     pos++;
-    pdu[pos] = at2_numtext(AT_SUBMIT_SM);
+    pdu[pos] = at2_numtext(i & 15);
     pos++;
 
     /* message reference (0 for now) */
--- gw/smsc_at2.h	2002-04-15 11:39:00.000000000 +0300
+++ gw/smsc_at2.h	2002-08-08 21:18:38.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);
