Re: [PATCH] concatenate incoming SMS prior to hand over to smsbox
Paul Bagyenda <[email protected]>
| Newsgroups | gmane.comp.mobile.kannel.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi All, Responding to Alejandro's comment on the above subject (to quote: "IMHO this should be enabled/disabled from the config files."), attached is the same patch for handling incoming concatenated messages in smsbox. What I've done is add a boolean config variable in the core group: smsbox-combine-concatenated-mo Setting this to "false" will turn off the feature, which is on by default. Would be grateful for your consideration of this patch as it is something that is quite useful for us. Paul.
concat.diff
(application/octet-stream, 14.4 KB)
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 15 Sep 2006 07:02:43 -0000
@@ -128,6 +128,8 @@
/* sms_to_smsboxes thread-id */
static long sms_dequeue_thread;
+/* Flag for handling concatenated incoming messages. */
+static int handle_concatenated_mo;
typedef struct _boxc {
Connection *conn;
@@ -154,6 +156,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 +972,8 @@
smsbox_by_receiver = NULL;
gwlist_remove_producer(flow_threads);
+ /* Stop concat handling */
+ shutdown_concat_handler();
}
@@ -1132,6 +1138,12 @@
gwlist_add_producer(outgoing_sms);
gwlist_add_producer(smsbox_list);
+ 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();
+
smsbox_running = 1;
if ((sms_dequeue_thread = gwthread_create(sms_to_smsboxes, NULL)) == -1)
@@ -1351,6 +1363,7 @@
boxid = NULL;
}
+static Msg *check_concatenation(Msg *msg);
/*
* Route the incoming message to one of the following input queues:
@@ -1371,6 +1384,11 @@
s = r = NULL;
gw_assert(msg_type(msg) == sms);
+ if (handle_concatenated_mo && 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 +1549,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 +1568,188 @@
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 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])
+ 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 (incoming_concat_msgs == NULL
+ || (l = octstr_len(udh)) == 0) /* ... module not initialised or there is no UDH. */
+ 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/cfg.def
===================================================================
RCS file: /home/cvs/gateway/gwlib/cfg.def,v
retrieving revision 1.121
diff -u -r1.121 cfg.def
--- gwlib/cfg.def 28 Aug 2006 12:06:13 -0000 1.121
+++ gwlib/cfg.def 15 Sep 2006 07:02:48 -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)
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 15 Sep 2006 07:02:52 -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 15 Sep 2006 07:02:53 -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);
/*