[PATCH] AT2 DLR: new revision

"Oded Arbel" <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
Hi list.

Here's a new revision of the AT2 DLR support patch :
- changed to patch cleanly against current CVS
- changed TP-ST handling to be more correct (still not 100%) and better documented (in the code comments).

sorry about the previous email - sent to early..

--
Oded Arbel
m-Wise mobile solutions
[email protected]

+972-9-9581711 (116)
+972-67-340014

::..
It is difficult to legislate morality in the absence of moral legislators. 
 <<smsc_at2.patch>>
smsc_at2.patch (application/octet-stream, 12.7 KB)
--- gateway/gw/smsc/smsc_at2.c	2002-08-08 20:44:38.000000000 +0300
+++ gateway/gw/smsc/smsc_at2.c	2002-08-11 14:18:19.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,119 @@
     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;
+    }
+
+	/* 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.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 +1616,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 +1637,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 +1732,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) */
--- gateway/gw/smsc/smsc_at2.h	2002-08-08 20:44:38.000000000 +0300
+++ gateway/gw/smsc/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);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.