Index: gw/bb_boxc.c
===================================================================
RCS file: /home/cvs/gateway/gw/bb_boxc.c,v
retrieving revision 1.85
diff -u -r1.85 bb_boxc.c
--- gw/bb_boxc.c	23 Mar 2006 18:36:29 -0000	1.85
+++ gw/bb_boxc.c	5 Sep 2006 08:16:41 -0000
@@ -154,6 +154,8 @@
 static void boxc_sent_push(Boxc*, Msg*);
 static void boxc_sent_pop(Boxc*, Msg*);
 
+static void init_concat_handler(void);
+static void shutdown_concat_handler(void);
 
 /*-------------------------------------------------
  *  receiver thingies
@@ -968,6 +970,8 @@
     smsbox_by_receiver = NULL;
     
     gwlist_remove_producer(flow_threads);
+    /* Stop concat handling */
+    shutdown_concat_handler();
 }
 
 
@@ -1131,6 +1135,7 @@
 
     gwlist_add_producer(outgoing_sms);
     gwlist_add_producer(smsbox_list);
+    init_concat_handler();
 
     smsbox_running = 1;
     
@@ -1351,6 +1356,7 @@
     boxid = NULL;
 }
 
+static Msg *check_concatenation(Msg *msg);
 
 /*
  * Route the incoming message to one of the following input queues:
@@ -1371,6 +1377,11 @@
     s = r = NULL;
     gw_assert(msg_type(msg) == sms);
 
+    if (msg->sms.sms_type == mo) {
+	 if ((msg = check_concatenation(msg)) == NULL)  
+	      return 1; /* i.e. message has been handled. */
+    }
+    
     /* msg_dump(msg, 0); */
 
     /* 
@@ -1531,8 +1542,8 @@
 
         /* debug("bb.sms", 0, "sms_boxc_router: handling message (%p vs %p)",
 	          msg, startmsg); */
+	 ret = route_incoming_to_boxc(msg);
 
-        ret = route_incoming_to_boxc(msg);
         if (ret == 1)
             startmsg = newmsg = NULL;
         else if (ret == -1) {
@@ -1550,3 +1561,183 @@
 
     gwlist_remove_producer(flow_threads);
 }
+
+
+/*--------------------------------
+ * 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. */
+     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 int concat_inited = 0;
+
+static void destroy_concatMsg(void *msg);
+static void init_concat_handler(void)
+{
+     if (concat_inited)
+	  return;
+     incoming_concat_msgs = dict_create(1024, destroy_concatMsg);
+     incoming_concat_queue = gwlist_create();
+     concat_rwlock = gw_rwlock_create();
+     concat_inited = 1;
+}
+
+static void shutdown_concat_handler(void)
+{
+     if (!concat_inited)
+	  return;
+     dict_destroy(incoming_concat_msgs);
+     gwlist_destroy(incoming_concat_queue,NULL);
+     gw_rwlock_destroy(concat_rwlock);
+     concat_inited = 0;
+}
+
+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])
+	       msg_destroy(msg->parts[i]);
+     octstr_destroy(msg->key);
+     gw_free(msg);     
+}
+
+/* Checks if message is concatenated. Returns:
+ * -  NULL if message is part of concatenated set and we need to wait for other parts
+ * - non-NULL if it is not, or if we have found all parts and are returning the message.
+ */
+static Msg *check_concatenation(Msg *msg)
+{
+     int l, iel, refnum, pos, c, part, totalparts, i, sixteenbit;
+     Octstr *udh = msg->sms.udhdata, *key;
+     ConcatMsg *cmsg, *x;
+     char xudh[1024];
+     
+     if ((l = octstr_len(udh)) == 0)
+	  return msg;
+     
+     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 msg;
+     
+     /* 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 msg;
+     }
+
+     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 %d", msg->sms.sender, 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);
+	  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) {	  
+	  msg_destroy(msg); /* if we have, destroy this one.. */
+	  warning(0, "Duplicate message part %d, ref %d, from %s. Discarding!",
+		  part, refnum, octstr_get_cstr(msg->sms.sender));
+     } else {
+	  cmsg->num_parts++;
+	  cmsg->parts[part-1] = msg;
+     }
+     
+     if (cmsg->num_parts < cmsg->total_parts) {  /* wait for more parts. */
+	  msg = NULL;
+	  gw_rwlock_unlock(concat_rwlock);     
+	  goto done;
+     }
+
+     /* we have all the parts: Put them together, mod UDH, return message. */
+     msg = cmsg->parts[0];
+     cmsg->parts[0] = NULL; /* remove from array. */
+     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);
+
+     /* 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);
+
+	  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 msg;
+}
Index: gwlib/mime.c
===================================================================
RCS file: /home/cvs/gateway/gwlib/mime.c,v
retrieving revision 1.14
diff -u -r1.14 mime.c
--- gwlib/mime.c	14 Mar 2006 19:57:16 -0000	1.14
+++ gwlib/mime.c	5 Sep 2006 08:16:51 -0000
@@ -74,10 +74,11 @@
 #include "gwlib/mime.h"
 
 struct MIMEEntity {
-    List *headers;
-    List *multiparts;
-    Octstr *body;
-    struct MIMEEntity *start;   /* in case multipart/related */
+     List *headers;
+     List *multiparts;
+     Octstr *body;
+     struct MIMEEntity *start;   /* in case multipart/related */
+     struct MIMEEntity *parent; /* Pointer to the MIME container or NULL */
 };
 
 /********************************************************************
@@ -93,19 +94,27 @@
     e->multiparts = gwlist_create();
     e->body = NULL;
     e->start = NULL;
+    e->parent = NULL;
 
     return e;
 }
 
 void static mime_entity_destroy_item(void *e)
 {
-    mime_entity_destroy(e);
+     ((MIMEEntity *)e)->parent = NULL; /* So that func below will destroy it. */
+     mime_entity_destroy(e);
 }
 
+ /* Added: Never actually destroy a mime entity that has a parent,
+  * only its parent may be destroyed (which will recursively destroy children
+  */
 void mime_entity_destroy(MIMEEntity *e) 
 {
     gw_assert(e != NULL);
 
+    if (e->parent) 
+	 return;
+
     if (e->headers != NULL)
         gwlist_destroy(e->headers, octstr_destroy_item);
     if (e->multiparts != NULL)
@@ -508,9 +517,11 @@
      mime_replace_headers(copy, e->headers);
      copy->body = e->body ? octstr_duplicate(e->body) : NULL;
      
-     for (i = 0, n = gwlist_len(e->multiparts); i < n; i++)
-	  gwlist_append(copy->multiparts, 
-			mime_entity_duplicate(gwlist_get(e->multiparts, i)));
+     for (i = 0, n = gwlist_len(e->multiparts); i < n; i++) {
+	  MIMEEntity *ce = mime_entity_duplicate(gwlist_get(e->multiparts, i));   
+	  gwlist_append(copy->multiparts, ce);
+	  ce->parent = copy;
+     }
      return copy;
 }
 
@@ -536,19 +547,20 @@
 }
 
 
-/* Append  a new part to list of body parts. Copy is made
+/* Append  a new part to list of body parts. Copy is NOT made
  * Note that if it was not multipart, this action makes it so!
  */ 
 void mime_entity_add_part(MIMEEntity *e, MIMEEntity *part)
 {
      gw_assert(e != NULL);
      gw_assert(part != NULL);
-     
-     gwlist_append(e->multiparts, mime_entity_duplicate(part));
+
+     gwlist_append(e->multiparts, part);
+     part->parent = e;
 }
 
 
-/* Get part i in list of body parts. Copy is made*/ 
+/* Get part i in list of body parts. Copy is NOT made*/ 
 MIMEEntity *mime_entity_get_part(MIMEEntity *e, int i)
 {
      MIMEEntity *m;
@@ -558,7 +570,7 @@
 
      m = gwlist_get(e->multiparts, i);
      gw_assert(m);
-     return mime_entity_duplicate(m);
+     return m;
 }
 
 
@@ -575,11 +587,11 @@
      m = gwlist_get(e->multiparts, i);
      gwlist_delete(e->multiparts, i, 1);
      if (m == e->start) e->start = NULL;
-
+     m->parent = NULL;
      mime_entity_destroy(m);
 }
 
-/* Replace part i in list of body parts.  Old one will be deleted */ 
+/* Replace part i in list of body parts.  Old one will be deleted. No copy is made */ 
 void mime_entity_replace_part(MIMEEntity *e, int i, MIMEEntity *newpart)
 {
 
@@ -591,9 +603,12 @@
      
      m = gwlist_get(e->multiparts, i);
      gwlist_delete(e->multiparts, i, 1);
-     gwlist_insert(e->multiparts, i, mime_entity_duplicate(newpart));
+     gwlist_insert(e->multiparts, i, newpart);
+     newpart->parent = e;
+
      if (m == e->start) e->start = NULL;
 
+     m->parent = NULL;
      mime_entity_destroy(m);
 }
 
@@ -611,7 +626,7 @@
      e->body = octstr_duplicate(body);
 }
 
-/* Returns (copy of) the 'start' element of a multi-part entity. */
+/* Returns  the 'start' element of a multi-part entity. */
 MIMEEntity *mime_multipart_start_elem(MIMEEntity *e)
 {
      gw_assert(e != NULL);
@@ -651,7 +666,7 @@
 	       octstr_destroy(start);
      }
      
-     return (e->start) ? mime_entity_duplicate(e->start) : NULL;
+     return e->start;
 }
 
 /********************************************************************
Index: gwlib/mime.h
===================================================================
RCS file: /home/cvs/gateway/gwlib/mime.h,v
retrieving revision 1.10
diff -u -r1.10 mime.h
--- gwlib/mime.h	14 Mar 2006 19:57:16 -0000	1.10
+++ gwlib/mime.h	5 Sep 2006 08:16:51 -0000
@@ -104,6 +104,10 @@
 MIMEEntity *mime_entity_duplicate(MIMEEntity *e);
 
 
+/* NOTE: Most of the functions below returning or taking MIMEEntities
+ * as arguments do not make copies of their arguments
+ */
+
 /* Replace top-level MIME headers: Old ones removed completetly */
 void mime_replace_headers(MIMEEntity *e, List *headers);
 
@@ -113,22 +117,22 @@
  */
 int mime_entity_num_parts(MIMEEntity *e);
 
-/* Append  a new part to list of body parts. Copy is made*/ 
+/* Append  a new part to list of body parts. Copy is NOT made*/ 
 void mime_entity_add_part(MIMEEntity *e, MIMEEntity *part);
 
-/* Get part i in list of body parts. Copy is made*/ 
+/* Get part i in list of body parts. Copy is NOT made*/ 
 MIMEEntity *mime_entity_get_part(MIMEEntity *e, int i);
 
 /* Replace part i in list of body parts.  Old one will be deleted */ 
 void mime_entity_replace_part(MIMEEntity *e, int i, MIMEEntity *newpart);
 
-/* Remove part i in list of body parts. */ 
+/* Remove and destroy part i in list of body parts. */ 
 void mime_entity_remove_part(MIMEEntity *e, int i);
 
 /* Change body element of non-multipart entity. */
 void mime_entity_set_body(MIMEEntity *e, Octstr *body);
 
-/* Returns (copy of) the 'start' element of a multi-part entity. */
+/* Returns the 'start' element of a multi-part entity. */
 MIMEEntity *mime_multipart_start_elem(MIMEEntity *e);
 
 /*
