Re: [PATCH] Re: MO Concatenation

Paul Bagyenda <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
Following your comments, here is a MO concatenation patch that now  
resides in bb_smscconn.c, and uses the key as advised below.  Same  
code, largely, just integrated in a different place. There is also  
better handling of the message stores...

Tests, comments, votes.

P.

Ps. If you are using the smsc_at2 module, be sure to patch with  
additional patch below.


On Jan 08, 2007, at 19:52, Andreas Fink wrote:

>
> On 08.01.2007, at 17:38, Δημήτρης Ευμορφόπουλος  
> wrote:
>
>> The triple key is not valid. From personal experience I was  
>> getting the SMS parts from all SMSC's not the one that transmitted  
>> the first part only. All the operators I worked with do the same  
>> thing. So the key should remain msisdn + refnum only.
>
> msisdn + refnum + smscid.
>
> multiple SMSC's behaving the same are / must be grouped in the same  
> smsc-id for DLR consolidation too.
>
>
>
concat.diff (application/octet-stream, 10 KB)
Index: gw/bb_smscconn.c
===================================================================
RCS file: /home/cvs/gateway/gw/bb_smscconn.c,v
retrieving revision 1.91
diff -u -r1.91 bb_smscconn.c
--- gw/bb_smscconn.c	31 Jan 2007 12:06:00 -0000	1.91
+++ gw/bb_smscconn.c	26 Feb 2007 17:53:20 -0000
@@ -130,11 +130,19 @@
  */
 Counter *split_msg_counter;
 
+/* Flag for handling concatenated incoming messages. */
+static int handle_concatenated_mo;
+/* Flag for return value of check_concat */
+enum {concat_error = -1, concat_complete = 0, concat_pending = 1, concat_none};
+
 /*
  * forward declaration
  */
 static long route_incoming_to_smsc(SMSCConn *conn, Msg *msg);
 
+static void init_concat_handler(void);
+static void shutdown_concat_handler(void);
+static int check_concatenation(Msg **msg, Octstr *smscid);
 
 /*---------------------------------------------------------------------------
  * CALLBACK FUNCTIONS
@@ -396,6 +404,26 @@
      * Scope: internal routing (to smsc-ids)
      */
     if ((rc = route_incoming_to_smsc(conn, copy)) == -1) {
+	 int ret;
+	 /* Before routing to bearerbox, do concat handling
+	  * and replace copy as such.
+	  */
+	 if (handle_concatenated_mo && copy->sms.sms_type == mo &&
+	     (ret = check_concatenation(&copy, conn->id)) != concat_none)
+	      if (ret == concat_pending) {
+		   bb_alog_sms(conn, sms, "CONCATENATED SMS Part Received, stored");	     		   
+		   counter_increase(incoming_sms_counter); /* ?? */
+		   if (conn != NULL) counter_increase(conn->received);		   
+		   msg_destroy(sms);
+		   return SMSCCONN_SUCCESS;
+	      }  else if (ret == concat_complete) { /* Combined sms received! save new one since it is now combined. */ 
+		   msg_destroy(sms);
+		   sms = msg_duplicate(copy); /* Change the sms. */
+	      } else if (ret == concat_error) {
+		   /* failed to save, go away. */
+		   msg_destroy(copy);
+		   return SMSCCONN_FAILED_TEMPORARILY;
+	      } 	 
         /*
          * Now try to route the message to a specific smsbox
          * connection based on the existing msg->sms.boxc_id or
@@ -563,6 +591,12 @@
     else
         info(0, "SMS resend retry set to %ld.", sms_resend_retry);
 
+    if (cfg_get_bool(&handle_concatenated_mo, grp, octstr_imm("smsbox-combine-concatenated-mo")) == -1)
+	 handle_concatenated_mo = 1; /* default is TRUE. */
+
+    if (handle_concatenated_mo)
+	 init_concat_handler();
+
     smsc_groups = cfg_get_multi_group(cfg, octstr_imm("smsc"));
     /*
     while(groups && (grp = gwlist_extract_first(groups)) != NULL) {
@@ -806,6 +840,9 @@
     counter_destroy(split_msg_counter);
     gw_rwlock_destroy(&smsc_list_lock);
 
+    /* Stop concat handling */
+    shutdown_concat_handler();
+
     smsc_running = 0;
 }
 
@@ -1078,3 +1115,207 @@
     return -1; 
 }
 
+
+/*--------------------------------
+ * incoming concatenated messages handling
+ */
+
+#define CONCATENATED_TIMEOUT 1800
+typedef struct ConcatMsg {
+     int refnum;
+     int total_parts;
+     int num_parts;
+     time_t trecv;
+     Octstr *key; /* in dict. */
+     int ack;     /* set to the type of ack to send when deleting. */
+     Msg *parts[1];
+     /* hidden fields! do not put in an array. */
+} ConcatMsg;
+
+static Dict *incoming_concat_msgs;
+static List *incoming_concat_queue;
+static RWLock *concat_rwlock;
+
+static void destroy_concatMsg(void *msg);
+static void init_concat_handler(void)
+{
+     if (incoming_concat_msgs != NULL)
+	  return;
+     incoming_concat_msgs = dict_create(1024, destroy_concatMsg);
+     incoming_concat_queue = gwlist_create();
+     concat_rwlock = gw_rwlock_create();
+     debug("bb",0,"smsbox MO concatenated message handling enabled");
+}
+
+static void shutdown_concat_handler(void)
+{
+     if (incoming_concat_msgs == NULL)
+	  return;
+     dict_destroy(incoming_concat_msgs);
+     gwlist_destroy(incoming_concat_queue,NULL);
+     gw_rwlock_destroy(concat_rwlock);
+
+     incoming_concat_msgs = NULL;
+     incoming_concat_queue = NULL;
+     concat_rwlock = NULL;
+     debug("bb",0,"smsbox MO concatenated message handling cleaned up");
+}
+
+static void destroy_concatMsg(void *x)
+{
+     int i;
+     ConcatMsg *msg = x;
+
+     gw_assert(msg);
+     for (i = 0; i < msg->total_parts; i++)
+	  if (msg->parts[i]) {
+	       store_save_ack(msg->parts[i], msg->ack);
+	       msg_destroy(msg->parts[i]);
+	  }
+     octstr_destroy(msg->key);
+     gw_free(msg);
+}
+
+/* Checks if message is concatenated. Returns:
+ * - returns concat_complete if no concat parts, or message complete
+ * - returns concat_pending (and sets *pmsg to NULL) if parts pending
+ * - returns concat_error if store_save fails
+ */
+static int check_concatenation(Msg **pmsg, Octstr *smscid)
+{
+     Msg *msg = *pmsg;
+     int l, iel, refnum, pos, c, part, totalparts, i, sixteenbit;
+     Octstr *udh = msg->sms.udhdata, *key;
+     ConcatMsg *cmsg, *x;
+     int ret = concat_complete;
+
+     if (incoming_concat_msgs == NULL
+	 || (l = octstr_len(udh)) == 0) /* ... module not initialised or there is no UDH. */
+	  return concat_none;
+     
+     for (pos = 1; pos < l - 1; pos += iel + 2) {
+	  iel = octstr_get_char(udh, pos + 1);
+	  if ((c = octstr_get_char(udh,pos)) == 0 || c == 8)
+	       break;
+     }
+     if (pos >= l)  /* no concat UDH found. */
+	  return concat_none;
+     
+     /* c = 0 means 8 bit, c = 8 means 16 bit concat info */
+     sixteenbit = (c == 8);
+     refnum = (!sixteenbit) ? octstr_get_char(udh, pos + 2) :
+	  (octstr_get_char(udh, pos + 2) << 8) | octstr_get_char(udh, pos + 3);
+     totalparts = octstr_get_char(udh, pos + 3 + sixteenbit);
+     part = octstr_get_char(udh, pos + 4 + sixteenbit);
+     
+     if (part < 1 || part > totalparts) {
+	  warning(0, "Invalid concatenation UDH [ref = %d] in message from %s!",
+		  refnum, octstr_get_cstr(msg->sms.sender));
+	  return concat_none;
+     }
+
+     debug("concat.sms", 0, "Got part %d [ref %d, total parts %d] of message from %s. Dump follows:",
+	   part, refnum,totalparts, octstr_get_cstr(msg->sms.sender));
+     
+     msg_dump(msg,0);
+     
+     key = octstr_format("%S %S %d", msg->sms.sender, smscid, refnum);          
+     gw_rwlock_wrlock(concat_rwlock);
+     if ((cmsg = dict_get(incoming_concat_msgs, key)) == NULL) {
+	  cmsg = gw_malloc(totalparts * sizeof (cmsg->parts[0]) + sizeof *cmsg);
+	  cmsg->refnum = refnum;
+	  cmsg->total_parts = totalparts;
+	  cmsg->num_parts = 0;
+	  cmsg->trecv = time(NULL);
+	  cmsg->key = octstr_duplicate(key);
+	  cmsg->ack = ack_success;
+	  memset(cmsg->parts, 0, (cmsg->total_parts+1)*sizeof cmsg->parts[0]); /* clear it. */	  
+	  
+	  dict_put(incoming_concat_msgs, key, cmsg);
+	  gwlist_append(incoming_concat_queue, cmsg);
+     }
+     octstr_destroy(key); 
+     
+     /* check if we have seen message part before... */
+     if (cmsg->parts[part-1] != NULL) {	  
+	  debug("concat.sms",0, "Duplicate message part %d, ref %d, from %s. Discarded!",
+		  part, refnum, octstr_get_cstr(msg->sms.sender));
+	  store_save_ack(msg, ack_success);
+	  msg_destroy(msg); 
+	  *pmsg = msg = NULL;
+     } else {
+	  cmsg->num_parts++;
+	  cmsg->parts[part-1] = msg;
+     }
+     
+     if (cmsg->num_parts < cmsg->total_parts) {  /* wait for more parts. */
+	  *pmsg = msg = NULL;
+	  gw_rwlock_unlock(concat_rwlock);     
+	  ret = concat_pending;
+	  goto done;
+     }
+
+     /* we have all the parts: Put them together, mod UDH, return message. */
+     msg = msg_duplicate(cmsg->parts[0]);
+     uuid_generate(msg->sms.id); /* give it a new ID. */
+
+     debug("concat.sms",0,"Received all concatenated message parts from %s, refnum %d",
+	   octstr_get_cstr(msg->sms.sender), refnum);
+     for (i = 1; i<cmsg->total_parts;i++)
+	  octstr_append(msg->sms.msgdata, cmsg->parts[i]->sms.msgdata);
+
+     /* Attempt to save the new one, if that fails, then reply with fail. */
+     if (store_save(msg) == -1) {	  
+	  msg_destroy(msg);
+	  *pmsg = msg = NULL;
+	  ret = concat_error;
+	  goto done;
+     } else 
+	  *pmsg = msg; /* return the message part. */
+
+     /* Delete it from the queue and from the Dict. */
+     gwlist_delete_equal(incoming_concat_queue, cmsg);
+     key = octstr_duplicate(cmsg->key); 
+     dict_put(incoming_concat_msgs, key, NULL); /* delete it. */
+     octstr_destroy(key);
+
+     gw_rwlock_unlock(concat_rwlock);     
+     
+     /* fix up UDH */
+     udh = msg->sms.udhdata;
+     l = octstr_len(udh);
+     for (pos = 1; pos < l - 1; pos += iel + 2) {
+	  iel = octstr_get_char(udh,pos+1);
+	  if ((c = octstr_get_char(udh,pos)) == 0 || c == 8) {
+	       octstr_delete(udh, pos, iel + 2);
+
+	       if (octstr_len(udh) <= 1) /* no other UDH elements. */
+		    octstr_delete(udh, 0, octstr_len(udh));
+	       else
+		    octstr_set_char(udh, 0, octstr_len(udh) - 1);
+	       break;
+	  }
+     }
+     debug("concat.sms", 0, "Got full message [ref %d] of message from %s. Dumping: ",
+	   refnum, octstr_get_cstr(msg->sms.sender));
+     msg_dump(msg,0);
+
+done:
+     /* Remove any pending messages that are too old. */
+     gw_rwlock_wrlock(concat_rwlock);
+     while (gwlist_len(incoming_concat_queue) > 0 && 
+	    (x = gwlist_get(incoming_concat_queue, 0)) != NULL &&
+	    time(NULL)  - x->trecv > CONCATENATED_TIMEOUT) {
+	  Octstr *xkey = octstr_duplicate(x->key);
+
+	  x->ack = ack_failed;
+	  gwlist_delete(incoming_concat_queue, 0,1);
+	  dict_put(incoming_concat_msgs, xkey, NULL); /* delete it. */	  
+	  
+	  info(0, "Time-out waiting for concatenated message '%s'. Dropping message parts", 
+	       octstr_get_cstr(xkey));
+	  octstr_destroy(xkey);
+     }
+     gw_rwlock_unlock(concat_rwlock);
+     return ret;
+}
Index: gwlib/cfg.def
===================================================================
RCS file: /home/cvs/gateway/gwlib/cfg.def,v
retrieving revision 1.126
diff -u -r1.126 cfg.def
--- gwlib/cfg.def	22 Jan 2007 15:12:42 -0000	1.126
+++ gwlib/cfg.def	26 Feb 2007 17:53:26 -0000
@@ -85,6 +85,7 @@
     OCTSTR(smsbox-port)
     OCTSTR(smsbox-port-ssl)
     OCTSTR(smsbox-max-pending)
+    OCTSTR(smsbox-combine-concatenated-mo)
     OCTSTR(wapbox-port)
     OCTSTR(wapbox-port-ssl)
     OCTSTR(box-deny-ip)
smscat.diff (application/octet-stream, 611 B)
Index: gw/smsc/smsc_at.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_at.c,v
retrieving revision 1.33
diff -u -r1.33 smsc_at.c
--- gw/smsc/smsc_at.c	7 Jan 2007 23:52:53 -0000	1.33
+++ gw/smsc/smsc_at.c	26 Feb 2007 17:53:26 -0000
@@ -1731,7 +1731,7 @@
         pos++;
         if (udhlen + 1 > len)
             goto msg_error;
-        udh = octstr_copy(pdu, pos, udhlen);
+        udh = octstr_copy(pdu, pos-1, udhlen+1);
         pos += udhlen;
         len -= udhlen + 1;
     } else if (len <= 0) /* len < 0 is impossible, but sure is sure */
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.