[PATCH] get catenated sms work right

Alexander Malysh <[email protected]>
Newsgroups gmane.comp.mobile.kannel.devel
Message-ID <[email protected]>
Hi List,

attached you can find a patch that implements catenation of large sms inside
of bearerbox and does care of a sending of all message parts over one smsc
link. Now we have a problem with catenated large sms that bearerbox will
try to load balance those over diff. smsc links and such messages arrive as
junk (_all_ parts of catenated large sms _must_ go through the same smsc).
we avoid such a problems now with a config workarounds
(allowed/denied/etc.-smsc-id), but here is a clean solution attached;)

Please review/comment/ask/vote!

P.S. idea was stolen from netikos gateway, so I must say: thank you guys!

-- 
Thanks,
Alex
sms-catenation.diff (text/x-diff, 15.8 KB)
Index: gw/bb_smscconn.c
===================================================================
RCS file: /home/cvs/gateway/gw/bb_smscconn.c,v
retrieving revision 1.78
diff -a -u -p -r1.78 bb_smscconn.c
--- gw/bb_smscconn.c	8 Aug 2004 19:50:18 -0000	1.78
+++ gw/bb_smscconn.c	24 Aug 2004 21:56:11 -0000
@@ -117,6 +117,15 @@ static regex_t *black_list_regex;
 
 static long router_thread = -1;
 
+/*
+ * Counter for catenated SMS messages. The counter that can be put into
+ * the catenated SMS message's UDH headers is actually the lowest 8 bits.
+ */
+Counter *split_msg_counter;
+
+/*
+ * forward declaration
+ */
 static long route_incoming_to_smsc(SMSCConn *conn, Msg *msg);
 
 
@@ -150,22 +159,70 @@ void bb_smscconn_killed(void)
 }
 
 
-void bb_smscconn_sent(SMSCConn *conn, Msg *sms, Octstr *reply)
+static void handle_split(SMSCConn *conn, Msg *msg, long reason)
 {
-    Msg *mack;
+    struct split_parts *split = msg->sms.split_parts;
+    
+    /* if temporarely failed, try again immediately */
+    if (reason == SMSCCONN_FAILED_TEMPORARILY && smscconn_send(conn, msg) == 0)
+        return;
+    
+    /*
+     * if the reason is not a success and status is still success
+     * then set status of a split to the reason.
+     * Note: reason 'malformed','discarded' or 'rejected' has higher priority!
+     */
+    switch(reason) {
+    case SMSCCONN_FAILED_DISCARDED:
+    case SMSCCONN_FAILED_REJECTED:
+    case SMSCCONN_FAILED_MALFORMED:
+        debug("bb.sms.splits", 0, "Set split msg status to %ld", reason);
+        split->status = reason;
+        break;
+    case SMSCCONN_SUCCESS:
+        break; /* nothing todo */
+    default:
+        if (split->status == SMSCCONN_SUCCESS) {
+            debug("bb.sms.splits", 0, "Set split msg status to %ld", reason);
+            split->status = reason;
+        }
+        break;
+    }
+
+    /*
+     * now destroy this message, because we don't need it anymore.
+     * we will split it again in smscconn_send(...).
+     */
+    msg_destroy(msg);
+        
+    if (counter_decrease(split->parts_left) <= 1) {
+        /* all splited parts were processed */
+        counter_destroy(split->parts_left);
+        msg = split->orig;
+        msg->sms.split_parts = NULL;
+        if (split->status == SMSCCONN_SUCCESS)
+            bb_smscconn_sent(conn, msg, NULL);
+        else {
+            debug("bb.sms.splits", 0, "Parts of concatenated message failed.");
+            bb_smscconn_send_failed(conn, msg, split->status, NULL);
+        }
+        gw_free(split);
+    }
+}
 
+
+void bb_smscconn_sent(SMSCConn *conn, Msg *sms, Octstr *reply)
+{
+    if (sms->sms.split_parts != NULL) {
+        handle_split(conn, sms, SMSCCONN_SUCCESS);
+        return;
+    }
+    
     counter_increase(outgoing_sms_counter);
     if (conn) counter_increase(conn->sent);
 
     /* write ACK to store file */
-
-    mack = msg_create(ack);
-    mack->ack.nack = ack_success;
-    mack->ack.time = sms->sms.time;
-    uuid_copy(mack->ack.id, sms->sms.id);
-
-    (void) store_save(mack);
-    msg_destroy(mack);
+    store_save_ack(sms, ack_success);
 
     bb_alog_sms(conn, sms, "Sent SMS");
 
@@ -191,6 +248,11 @@ void bb_smscconn_sent(SMSCConn *conn, Ms
 
 void bb_smscconn_send_failed(SMSCConn *conn, Msg *sms, int reason, Octstr *reply)
 {
+    if (sms->sms.split_parts != NULL) {
+        handle_split(conn, sms, reason);
+        return;
+    }
+    
     switch (reason) {
 
     case SMSCCONN_FAILED_SHUTDOWN:
@@ -198,7 +260,6 @@ void bb_smscconn_send_failed(SMSCConn *c
 	list_produce(outgoing_sms, sms);
 	break;
     default:
-
 	/* write NACK to store file */
         store_save_ack(sms, ack_failed);
 
@@ -409,6 +470,9 @@ int smsc2_start(Cfg *cfg)
 
     if (smsc_running) return -1;
 
+    /* create split sms counter */
+    split_msg_counter = counter_create();
+    
     smsc_list = list_create();
     gw_rwlock_init_static(&smsc_list_lock);
 
@@ -662,6 +726,8 @@ void smsc2_cleanup(void)
         gw_regex_destroy(white_list_regex);
     if (black_list_regex != NULL)
         gw_regex_destroy(black_list_regex);
+    /* destroy msg split counter */
+    counter_destroy(split_msg_counter);
     gw_rwlock_destroy(&smsc_list_lock);
 }
 
@@ -849,7 +915,6 @@ int smsc2_rout(Msg *msg)
 	    bo_load = info.load;
 	}
     }
-    gw_rwlock_unlock(&smsc_list_lock);
 
     if (best_preferred)
 	ret = smscconn_send(best_preferred, msg);
@@ -858,15 +923,18 @@ int smsc2_rout(Msg *msg)
     else if (bad_found) {
 	if (bb_status != BB_SHUTDOWN)
 	    list_produce(outgoing_sms, msg);
+        gw_rwlock_unlock(&smsc_list_lock);
 	return 0;
     }
     else {
+        gw_rwlock_unlock(&smsc_list_lock);
 	if (bb_status == BB_SHUTDOWN)
 	    return 0;
 	warning(0, "Cannot find SMSCConn for message to <%s>, rejected.",
 		octstr_get_cstr(msg->sms.receiver));
 	return -1;
     }
+    gw_rwlock_unlock(&smsc_list_lock);
     /* check the status of sending operation */
     if (ret == -1)
 	return (smsc2_rout(msg));	/* re-try */
Index: gw/msg-decl.h
===================================================================
RCS file: /home/cvs/gateway/gw/msg-decl.h,v
retrieving revision 1.27
diff -a -u -p -r1.27 msg-decl.h
--- gw/msg-decl.h	21 Jun 2004 21:16:05 -0000	1.27
+++ gw/msg-decl.h	24 Aug 2004 21:56:11 -0000
@@ -104,6 +104,7 @@ MSG(sms,
 		OCTSTR(boxc_id);
 		OCTSTR(binfo);
 		INTEGER(msg_left);
+		VOID(split_parts);
 	})
 
 MSG(ack,
@@ -126,3 +127,4 @@ MSG(wdp_datagram,
 #undef INTEGER
 #undef OCTSTR
 #undef UUID
+#undef VOID
Index: gw/msg.c
===================================================================
RCS file: /home/cvs/gateway/gw/msg.c,v
retrieving revision 1.33
diff -a -u -p -r1.33 msg.c
--- gw/msg.c	22 Jan 2004 14:08:24 -0000	1.33
+++ gw/msg.c	24 Aug 2004 21:56:11 -0000
@@ -101,6 +101,7 @@ Msg *msg_create_real(enum msg_type type,
 #define INTEGER(name) p->name = MSG_PARAM_UNDEFINED
 #define OCTSTR(name) p->name = NULL
 #define UUID(name) uuid_generate(p->name)
+#define VOID(name) p->name = NULL
 #define MSG(type, stmt) { struct type *p = &msg->type; stmt }
 #include "msg-decl.h"
 
@@ -118,6 +119,7 @@ Msg *msg_duplicate(Msg *msg)
     if (q->name == NULL) p->name = NULL; \
     else p->name = octstr_duplicate(q->name);
 #define UUID(name) uuid_copy(p->name, q->name)
+#define VOID(name) p->name = q->name
 #define MSG(type, stmt) { \
     struct type *p = &new->type; \
     struct type *q = &msg->type; \
@@ -135,6 +137,7 @@ void msg_destroy(Msg *msg)
 #define INTEGER(name) p->name = 0
 #define OCTSTR(name) octstr_destroy(p->name)
 #define UUID(name) uuid_clear(p->name)
+#define VOID(name)
 #define MSG(type, stmt) { struct type *p = &msg->type; stmt }
 #include "msg-decl.h"
 
@@ -160,6 +163,8 @@ void msg_dump(Msg *msg, int level)
 #define UUID(name) \
     uuid_unparse(p->name, buf); \
     debug("gw.msg", 0 , "%*s %s.%s: %s", level, "", t, #name, buf)
+#define VOID(name) \
+     debug("gw.msg", 0, "%*s %s.%s: %p", level, "", t, #name, p->name)
 #define MSG(tt, stmt) \
     if (tt == msg->type) \
         { char *t = #tt; struct tt *p = &msg->tt; stmt }
@@ -183,6 +188,7 @@ Octstr *msg_pack(Msg *msg)
 #define INTEGER(name) append_integer(os, p->name)
 #define OCTSTR(name) append_string(os, p->name)
 #define UUID(name) append_uuid(os, p->name)
+#define VOID(name)
 #define MSG(type, stmt) \
     case type: { struct type *p = &msg->type; stmt } break;
 
@@ -219,6 +225,7 @@ Msg *msg_unpack_real(Octstr *os, const c
     if (parse_string(&(p->name), os, &off) == -1) goto error
 #define UUID(name) \
     if (parse_uuid(p->name, os, &off) == -1) goto error
+#define VOID(name)
 #define MSG(type, stmt) \
     case type: { struct type *p = &(msg->type); stmt } break;
 
Index: gw/msg.h
===================================================================
RCS file: /home/cvs/gateway/gw/msg.h,v
retrieving revision 1.22
diff -a -u -p -r1.22 msg.h
--- gw/msg.h	28 Jun 2004 15:48:29 -0000	1.22
+++ gw/msg.h	24 Aug 2004 21:56:11 -0000
@@ -82,10 +82,16 @@ typedef struct {
 	#define INTEGER(name) long name
 	#define OCTSTR(name) Octstr *name
 	#define UUID(name) uuid_t name
+	#define VOID(name) void *name
 	#define MSG(type, stmt) struct type stmt type;
 	#include "msg-decl.h"
 } Msg;
 
+struct split_parts {
+    Msg *orig;
+    Counter *parts_left;
+    long status;
+};
 
 /* enums for Msg fields */
 
Index: gw/sms.h
===================================================================
RCS file: /home/cvs/gateway/gw/sms.h,v
retrieving revision 1.11
diff -a -u -p -r1.11 sms.h
--- gw/sms.h	28 Jun 2004 15:48:29 -0000	1.11
+++ gw/sms.h	24 Aug 2004 21:56:12 -0000
@@ -122,6 +122,11 @@
 #define SMS_7BIT_MAX_LEN 160
 #define SMS_8BIT_MAX_LEN 140
 #define SMS_UCS2_MAX_LEN 70
+/*
+ * Maximum number of octets in an SMS message. Note that this is 8 bit
+ * characters, not 7 bit characters.
+ */
+#define MAX_SMS_OCTETS 140
 
 /* Encode DCS using sms fields
  * mode = 0= encode using 00xxxxxx, 1= encode using 1111xxxx mode
Index: gw/smsbox.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsbox.c,v
retrieving revision 1.250
diff -a -u -p -r1.250 smsbox.c
--- gw/smsbox.c	11 Aug 2004 16:41:29 -0000	1.250
+++ gw/smsbox.c	24 Aug 2004 21:56:13 -0000
@@ -84,13 +84,6 @@
 #endif
 
 
-/*
- * Maximum number of octets in an SMS message. Note that this is 8 bit
- * characters, not 7 bit characters.
- */
-enum { MAX_SMS_OCTETS = 140 };
-
-
 #define SENDSMS_DEFAULT_CHARS "0123456789 +-"
 
 #define O_DESTROY(a) { if(a) octstr_destroy(a); a = NULL; }
@@ -298,12 +291,30 @@ static int send_message(URLTranslation *
     list = sms_split(msg, header, footer, suffix, split_chars, catenate,
     	    	     msg_sequence, max_msgs, sms_max_length);
     msg_count = list_len(list);
-
+    
     debug("sms", 0, "message length %ld, sending %ld messages",
           octstr_len(msg->sms.msgdata), msg_count);
-
-    while ((part = list_extract_first(list)) != NULL)
-	write_to_bearerbox(part);
+    
+    /*
+     * In order to get catenated msgs work properly, we
+     * have moved catenation to bearerbox.
+     * So here we just need to put splitted msgs into one again and send
+     * to bearerbox that will care about catenation.
+     */
+    if (catenate) {
+        Msg *new_msg = msg_duplicate(msg);
+        octstr_delete(new_msg->sms.msgdata, 0, octstr_len(new_msg->sms.msgdata));
+        while((part = list_extract_first(list)) != NULL) {
+            octstr_append(new_msg->sms.msgdata, part->sms.msgdata);
+            msg_destroy(part);
+        }
+        write_to_bearerbox(new_msg);
+    } else {
+        /* msgs are the independed parts so sent those as is */
+        while ((part = list_extract_first(list)) != NULL)
+            write_to_bearerbox(part);
+    }
+    
     list_destroy(list, NULL);
 
     return msg_count;
Index: gw/smscconn.c
===================================================================
RCS file: /home/cvs/gateway/gw/smscconn.c,v
retrieving revision 1.48
diff -a -u -p -r1.48 smscconn.c
--- gw/smscconn.c	28 Jun 2004 15:18:35 -0000	1.48
+++ gw/smscconn.c	24 Aug 2004 21:56:13 -0000
@@ -70,6 +70,9 @@
 #include "smscconn.h"
 #include "smscconn_p.h"
 #include "bb_smscconn_cb.h"
+#include "sms.h"
+
+extern Counter *split_msg_counter;
 
 /*
  * Some defaults
@@ -489,8 +492,8 @@ int smscconn_usable(SMSCConn *conn, Msg 
 
 int smscconn_send(SMSCConn *conn, Msg *msg)
 {
-    int ret;
-    char *uf;
+    int ret = -1;
+    List *parts = NULL;
     
     gw_assert(conn != NULL);
     mutex_lock(conn->flow_mutex);
@@ -499,11 +502,55 @@ int smscconn_send(SMSCConn *conn, Msg *m
         return -1;
     }
 
-    /* normalize the destination number for this smsc */
-    uf = conn->unified_prefix ? octstr_get_cstr(conn->unified_prefix) : NULL;
-    normalize_number(uf, &(msg->sms.receiver));
-
-    ret = conn->send_msg(conn, msg);
+    /* if this a retry of splitted message, don't unify prefix and don't try to split */
+    if (msg->sms.split_parts == NULL) {    
+        /* normalize the destination number for this smsc */
+        char *uf = conn->unified_prefix ? octstr_get_cstr(conn->unified_prefix) : NULL;
+        normalize_number(uf, &(msg->sms.receiver));
+
+        /* split msg */
+        parts = sms_split(msg, NULL, NULL, NULL, NULL, 1, 
+            counter_increase(split_msg_counter) & 0xff, 0xff, MAX_SMS_OCTETS);
+    }
+    
+    if (parts == NULL)
+        ret = conn->send_msg(conn, msg);
+    else {
+        long i, parts_len = list_len(parts);
+        struct split_parts *split = gw_malloc(sizeof(*split));
+         /* must duplicate, because smsc2_route will destroy this msg */
+        split->orig = msg_duplicate(msg);
+        split->parts_left = counter_create();
+        split->status = SMSCCONN_SUCCESS;
+        counter_set(split->parts_left, parts_len);
+        debug("bb.sms.splits", 0, "new split_parts created %p", split);
+        for (i = 0; i < parts_len; i++) {
+            msg = list_get(parts, i);
+            msg->sms.split_parts = split;
+            ret = conn->send_msg(conn, msg);
+            if (ret < 0) {
+                if (i == 0) {
+                    counter_destroy(split->parts_left);
+                    list_destroy(parts, msg_destroy_item);
+                    gw_free(split);
+                    mutex_unlock(conn->flow_mutex);
+                    return ret;
+                }
+                /*
+                 * Some parts were sent. So handle this within
+                 * bb_smscconn_XXX().
+                 */
+                split->status = SMSCCONN_FAILED_REJECTED;
+                while (++i < parts_len) {
+                    msg_destroy(list_get(parts, i));
+                    counter_decrease(split->parts_left);
+                }
+                warning(0, "Could not send all parts of a split message");
+                break;
+            }
+        }
+        list_destroy(parts, NULL);
+    }
     mutex_unlock(conn->flow_mutex);
     return ret;
 }
Index: gw/wapbox.c
===================================================================
RCS file: /home/cvs/gateway/gw/wapbox.c,v
retrieving revision 1.173
diff -a -u -p -r1.173 wapbox.c
--- gw/wapbox.c	22 Jan 2004 14:08:24 -0000	1.173
+++ gw/wapbox.c	24 Aug 2004 21:56:14 -0000
@@ -95,8 +95,6 @@ enum {
     WTLS_CONNECTION_ORIENTED_PORT = 9203
 };
 
-enum { MAX_SMS_OCTETS = 140 };
-
 enum { DEFAULT_TIMER_FREQ = 1};
 
 static Octstr *bearerbox_host;
Index: gw/smsc/smsc_http.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_http.c,v
retrieving revision 1.36
diff -a -u -p -r1.36 smsc_http.c
--- gw/smsc/smsc_http.c	11 Aug 2004 16:41:29 -0000	1.36
+++ gw/smsc/smsc_http.c	24 Aug 2004 21:56:15 -0000
@@ -313,7 +313,6 @@ static void httpsmsc_send_cb(void *arg)
  */
 
 enum { HEX_NOT_UPPERCASE = 0 };
-enum { MAX_SMS_OCTETS = 140 };
 
 
 static void kannel_send_sms(SMSCConn *conn, Msg *sms)
Index: gw/smsc/smsc_soap.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_soap.c,v
retrieving revision 1.12
diff -a -u -p -r1.12 smsc_soap.c
--- gw/smsc/smsc_soap.c	11 Aug 2004 16:41:29 -0000	1.12
+++ gw/smsc/smsc_soap.c	24 Aug 2004 21:56:17 -0000
@@ -2062,6 +2062,7 @@ static Octstr* soap_convert_token(Msg* m
         if (!octstr_str_compare(name, #fieldname)) \
                 return octstr_duplicate(p->fieldname);
 #define UUID(fieldname) 
+#define VOID(fieldname)
 
 #define MSG(type, stmt) \
         case type: { struct type *p = &msg->type; stmt } break;
Index: utils/mtbatch.c
===================================================================
RCS file: /home/cvs/gateway/utils/mtbatch.c,v
retrieving revision 1.4
diff -a -u -p -r1.4 mtbatch.c
--- utils/mtbatch.c	22 Jan 2004 14:08:25 -0000	1.4
+++ utils/mtbatch.c	24 Aug 2004 21:56:17 -0000
@@ -80,12 +80,6 @@
 #include "shared.h"
 #include "heartbeat.h"
 
-/*
- * Maximum number of octets in an SMS message. Note that this is 8 bit
- * characters, not 7 bit characters.
- */
-enum { MAX_SMS_OCTETS = 140 };
-
 static char *pid_file;
 static Octstr *smsbox_id = NULL;
 static Octstr *content = NULL;
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.