Index: gw/smsc/smsc_smpp.c
===================================================================
RCS file: /home/cvs/gateway/gw/smsc/smsc_smpp.c,v
retrieving revision 1.123
diff -u -r1.123 smsc_smpp.c
--- gw/smsc/smsc_smpp.c	2 Sep 2009 13:10:50 -0000	1.123
+++ gw/smsc/smsc_smpp.c	16 Jan 2010 16:25:28 -0000
@@ -168,6 +168,7 @@
     long connection_timeout;
     long wait_ack;
     int wait_ack_action;
+    int send_binary_as_data_sm;
     Load *load;
     SMSCConn *conn;
 } SMPP;
@@ -221,7 +222,8 @@
                          Octstr *my_number, int smpp_msg_id_type,
                          int autodetect_addr, Octstr *alt_charset, Octstr *alt_addr_charset,
                          Octstr *service_type, long connection_timeout,
-                         long wait_ack, int wait_ack_action)
+                         long wait_ack, int wait_ack_action,
+                         int send_binary_as_data_sm)
 {
     SMPP *smpp;
 
@@ -262,6 +264,7 @@
     smpp->connection_timeout = connection_timeout;
     smpp->wait_ack = wait_ack;
     smpp->wait_ack_action = wait_ack_action;
+    smpp->send_binary_as_data_sm = send_binary_as_data_sm;
     smpp->bind_addr_ton = 0;
     smpp->bind_addr_npi = 0;
     smpp->use_ssl = 0;
@@ -747,7 +750,7 @@
 }
 
 
-static SMPP_PDU *msg_to_pdu(SMPP *smpp, Msg *msg)
+static SMPP_PDU *msg_to_submit_pdu(SMPP *smpp, Msg *msg)
 {
     SMPP_PDU *pdu;
     int validity;
@@ -957,6 +960,253 @@
     return pdu;
 }
 
+#define GETLONGFROMUDHDATA(pos) (long)((long)octstr_get_char(msg->sms.udhdata, pos + 1) + (0x100L * (long)octstr_get_char(msg->sms.udhdata, pos)))
+
+#define GETSHORTFROMUDHDATA(pos) (octstr_get_char(msg->sms.udhdata, pos))
+
+/* changed this function submit_sm -> data_sm */
+static SMPP_PDU *msg_to_data_pdu(SMPP *smpp, Msg *msg)
+{
+    SMPP_PDU *pdu;
+    /* RK: Binary (UDH) values that go in optional parameters */
+    long source_port = 0x0000, destination_port = 0x0000;
+    int  sar_msg_ref_num = 0x01, sar_total_segments = 0x01, sar_segment_seqnum = 0x01;
+    long udhlen, udhcurrent, udhtype;
+
+    pdu = smpp_pdu_create(data_sm,
+                          counter_increase(smpp->message_id_counter));
+
+    pdu->u.data_sm.source_addr = octstr_duplicate(msg->sms.sender);
+    error(0, "SMPP: Source address: %s", octstr_get_cstr(pdu->u.data_sm.source_addr));
+    pdu->u.data_sm.destination_addr = octstr_duplicate(msg->sms.receiver);
+
+    /* Set the service type of the outgoing message. We'll use the config 
+     * directive as default and 'binfo' as specific parameter. */
+    pdu->u.data_sm.service_type = octstr_len(msg->sms.binfo) ? 
+        octstr_duplicate(msg->sms.binfo) : octstr_duplicate(smpp->service_type);
+
+    /* Check for manual override of source ton and npi values */
+    if(smpp->source_addr_ton > -1 && smpp->source_addr_npi > -1) {
+        pdu->u.data_sm.source_addr_ton = smpp->source_addr_ton;
+        pdu->u.data_sm.source_addr_npi = smpp->source_addr_npi;
+        debug("bb.sms.smpp", 0, "SMPP[%s]: Manually forced source addr ton = %d, source add npi = %d",
+              octstr_get_cstr(smpp->conn->id), smpp->source_addr_ton,
+              smpp->source_addr_npi);
+    } else {
+        /* setup default values */
+        pdu->u.data_sm.source_addr_ton = GSM_ADDR_TON_NATIONAL; /* national */
+        pdu->u.data_sm.source_addr_npi = GSM_ADDR_NPI_E164; /* ISDN number plan */
+    }
+
+    if (smpp->autodetect_addr) {
+        /* lets see if its international or alphanumeric sender */
+        if (octstr_get_char(pdu->u.data_sm.source_addr, 0) == '+') {
+            if (!octstr_check_range(pdu->u.data_sm.source_addr, 1, 256, gw_isdigit)) {
+                pdu->u.data_sm.source_addr_ton = GSM_ADDR_TON_ALPHANUMERIC; /* alphanum */
+                pdu->u.data_sm.source_addr_npi = GSM_ADDR_NPI_UNKNOWN;    /* short code */
+            } else {
+               /* numeric sender address with + in front -> international (remove the +) */
+               octstr_delete(pdu->u.data_sm.source_addr, 0, 1);
+               pdu->u.data_sm.source_addr_ton = GSM_ADDR_TON_INTERNATIONAL;
+            }
+        } else {
+            if (!octstr_check_range(pdu->u.data_sm.source_addr,0, 256, gw_isdigit)) {
+                pdu->u.data_sm.source_addr_ton = GSM_ADDR_TON_ALPHANUMERIC;
+                pdu->u.data_sm.source_addr_npi = GSM_ADDR_NPI_UNKNOWN;
+            }
+        }
+    }
+
+    /* Check for manual override of destination ton and npi values */
+    if (smpp->dest_addr_ton > -1 && smpp->dest_addr_npi > -1) {
+        pdu->u.data_sm.dest_addr_ton = smpp->dest_addr_ton;
+        pdu->u.data_sm.dest_addr_npi = smpp->dest_addr_npi;
+        debug("bb.sms.smpp", 0, "SMPP[%s]: Manually forced dest addr ton = %d, dest add npi = %d",
+              octstr_get_cstr(smpp->conn->id), smpp->dest_addr_ton,
+              smpp->dest_addr_npi);
+    } else {
+        pdu->u.data_sm.dest_addr_ton = GSM_ADDR_TON_NATIONAL; /* national */
+        pdu->u.data_sm.dest_addr_npi = GSM_ADDR_NPI_E164; /* ISDN number plan */
+    }
+
+    /*
+     * if its a international number starting with +, lets remove the
+     * '+' and set number type to international instead
+     */
+    if (octstr_get_char(pdu->u.data_sm.destination_addr,0) == '+') {
+        octstr_delete(pdu->u.data_sm.destination_addr, 0,1);
+        pdu->u.data_sm.dest_addr_ton = GSM_ADDR_TON_INTERNATIONAL;
+    }
+
+    /* check length of src/dst address */
+    if (octstr_len(pdu->u.data_sm.destination_addr) > 20 ||
+        octstr_len(pdu->u.data_sm.source_addr) > 20) {
+        smpp_pdu_destroy(pdu);
+        return NULL;
+    }
+
+    /*
+     * set the data coding scheme (DCS) field
+     * check if we have a forced value for this from the smsc-group.
+     * Note: if message class is set, then we _must_ force alt_dcs otherwise
+     * dcs has reserved values (e.g. mclass=2, dcs=0x11). We check MWI flag
+     * first here, because MWI and MCLASS can not be set at the same time and
+     * function fields_to_dcs check MWI first, so we have no need to force alt_dcs
+     * if MWI is set.
+     */
+    if (msg->sms.mwi == MWI_UNDEF && msg->sms.mclass != MC_UNDEF)
+        pdu->u.data_sm.data_coding = fields_to_dcs(msg, 1); /* force alt_dcs */
+    else
+        pdu->u.data_sm.data_coding = fields_to_dcs(msg,
+            (msg->sms.alt_dcs != SMS_PARAM_UNDEFINED ?
+             msg->sms.alt_dcs : smpp->conn->alt_dcs));
+
+    /* set protocol id */
+    /*
+    RK data_sm doesnt have protocol id
+    if(msg->sms.pid != SMS_PARAM_UNDEFINED)
+        pdu->u.data_sm.protocol_id = msg->sms.pid;
+    */
+
+    /*
+     * set the esm_class field
+     * default is store and forward, plus udh and rpi if requested
+     */
+    pdu->u.data_sm.esm_class = ESM_CLASS_SUBMIT_STORE_AND_FORWARD_MODE;
+    /* RK: We dont send an UDH in payload of data_sm
+    if (octstr_len(msg->sms.udhdata))
+        pdu->u.data_sm.esm_class = pdu->u.data_sm.esm_class |
+            ESM_CLASS_SUBMIT_UDH_INDICATOR;
+    */
+    if (msg->sms.rpi > 0)
+        pdu->u.data_sm.esm_class = pdu->u.data_sm.esm_class |
+            ESM_CLASS_SUBMIT_RPI;
+
+    /*
+     * set data segments and length
+     */
+
+    /* RK: short messages goes into message_payload */
+    pdu->u.data_sm.message_payload = octstr_duplicate(msg->sms.msgdata);
+
+    /*
+     * only re-encoding if using default smsc charset that is defined via
+     * alt-charset in smsc group and if MT is not binary
+     */
+    if (msg->sms.coding == DC_7BIT || (msg->sms.coding == DC_UNDEF && octstr_len(msg->sms.udhdata))) {
+        /* 
+         * consider 3 cases: 
+         *  a) data_coding 0xFX: encoding should always be GSM 03.38 charset 
+         *  b) data_coding 0x00: encoding may be converted according to alt-charset 
+         *  c) data_coding 0x00: assume GSM 03.38 charset if alt-charset is not defined
+         */
+        if ((pdu->u.data_sm.data_coding & 0xF0) ||
+            (!smpp->alt_charset && pdu->u.data_sm.data_coding == 0)) {
+            charset_latin1_to_gsm(pdu->u.data_sm.message_payload);
+        }
+        else if (pdu->u.data_sm.data_coding == 0 && smpp->alt_charset) {
+            /*
+             * convert to the given alternative charset
+             */
+            if (charset_convert(pdu->u.data_sm.message_payload, "ISO-8859-1",
+                                octstr_get_cstr(smpp->alt_charset)) != 0)
+                error(0, "Failed to convert msgdata from charset <%s> to <%s>, will send as is.",
+                             "ISO-8859-1", octstr_get_cstr(smpp->alt_charset));
+        }
+    }
+
+    /* prepend udh if present */
+    /* RK: udh data is not prepended to payload
+    if (octstr_len(msg->sms.udhdata)) {
+        octstr_insert(pdu->u.data_sm.short_message, msg->sms.udhdata, 0);
+    }
+    RK: sm_lenght is not a data_sm member.
+    pdu->u.data_sm.sm_length = octstr_len(pdu->u.data_sm.short_message);
+    */
+
+    /*
+     * check for validity and defered settings
+     */
+    /* RK: TODO: This should be either qos_time_to_live or ms_validity
+    if (msg->sms.validity >= 0) {
+        struct tm tm = gw_gmtime(time(NULL) + msg->sms.validity * 60);
+        pdu->u.data_sm.validity_period = octstr_format("%02d%02d%02d%02d%02d%02d000+",
+                tm.tm_year % 100, tm.tm_mon + 1, tm.tm_mday,
+                tm.tm_hour, tm.tm_min, tm.tm_sec);
+    }
+    */
+
+    /*
+    RK: TODO: Which field?
+    if (msg->sms.deferred >= 0) {
+        struct tm tm = gw_gmtime(time(NULL) + msg->sms.deferred * 60);
+        pdu->u.data_sm.schedule_delivery_time = octstr_format("%02d%02d%02d%02d%02d%02d000+",
+                tm.tm_year % 100, tm.tm_mon + 1, tm.tm_mday,
+                tm.tm_hour, tm.tm_min, tm.tm_sec);
+    }
+    */
+    /* RK: added code: fill in optional parameters */
+    error(0, "SMPP: UDHLEN: %ld", octstr_len(msg->sms.udhdata));
+    if (octstr_len(msg->sms.udhdata) > 0) {
+        udhcurrent = 1;
+        udhlen = octstr_len(msg->sms.udhdata) - 1;
+        while (udhcurrent < udhlen) {
+                error(0, "SMPP: Processing: %i", octstr_get_char(msg->sms.udhdata, udhcurrent));
+                udhtype = GETLONGFROMUDHDATA(udhcurrent);
+                error(0, "SMPP: UDHTYPE: %x", udhtype);
+                udhcurrent += 2;
+                switch (udhtype) {
+                case 0x0003:
+                        /* catenation header */
+                        sar_msg_ref_num = GETSHORTFROMUDHDATA(udhcurrent++);
+                        sar_total_segments = GETSHORTFROMUDHDATA(udhcurrent++);
+                        sar_segment_seqnum = GETSHORTFROMUDHDATA(udhcurrent++);
+                        break;
+                case 0x0504:
+                        /* binary header */
+                        destination_port = GETLONGFROMUDHDATA(udhcurrent);
+                        udhcurrent += 2;
+                        source_port = GETLONGFROMUDHDATA(udhcurrent);
+                        udhcurrent += 2;
+                        error(0, "SMPP: Source: %x, Destination: %x", source_port, destination_port);
+                        break;
+                default:
+                        /* oops... we have a header value we dont know about */
+                        udhcurrent = udhlen;
+                        break;
+                }
+        }
+        if (source_port) pdu->u.data_sm.source_port = source_port;
+        if (destination_port) pdu->u.data_sm.destination_port = destination_port;
+        /* we should also send sar data if only one segment */
+        pdu->u.data_sm.sar_msg_ref_num = sar_msg_ref_num;
+        pdu->u.data_sm.sar_total_segments = sar_total_segments;
+        pdu->u.data_sm.sar_segment_seqnum = sar_segment_seqnum;
+        pdu->u.data_sm.payload_type = 0; /* set to 0, default */
+    }
+
+    /* ask for the delivery reports if needed */
+    if (DLR_IS_SUCCESS_OR_FAIL(msg->sms.dlr_mask))
+        pdu->u.data_sm.registered_delivery = 1;
+    else if (DLR_IS_FAIL(msg->sms.dlr_mask) && !DLR_IS_SUCCESS(msg->sms.dlr_mask))
+        pdu->u.data_sm.registered_delivery = 2;
+
+    /* set priority */
+    /*
+    RK: Dont have such a thing in data_sm
+    if (msg->sms.priority >= 0 && msg->sms.priority <= 3)
+        pdu->u.data_sm.priority_flag = msg->sms.priority;
+    else
+        pdu->u.data_sm.priority_flag = smpp->priority;
+    */
+
+    /* set more messages to send */
+    if (smpp->version > 0x33 && msg->sms.msg_left > 0)
+        pdu->u.data_sm.more_messages_to_send = 1;
+
+    return pdu;
+}
+
 
 static int send_enquire_link(SMPP *smpp, Connection *conn, long *last_sent)
 {
@@ -1034,7 +1284,7 @@
         /* it's not a error if we still have data buffered */
         ret = (ret == 1) ? 0 : ret;
     } else
-	ret = -1;
+        ret = -1;
     octstr_destroy(os);
     return ret;
 }
@@ -1050,6 +1300,8 @@
         return 0;
 
     while (*pending_submits < smpp->max_pending_submits) {
+        int send_as_data_sm;
+
         /* check our throughput */
         if (smpp->conn->throughput > 0 && load_get(smpp->load, 0) >= smpp->conn->throughput) {
             debug("bb.sms.smpp", 0, "SMPP[%s]: throughput limit exceeded (%.02f,%.02f)",
@@ -1059,13 +1311,19 @@
         debug("bb.sms.smpp", 0, "SMPP[%s]: throughput (%.02f,%.02f)",
               octstr_get_cstr(smpp->conn->id), load_get(smpp->load, 0), smpp->conn->throughput);
 
-    	/* Get next message, quit if none to be sent */
+            /* Get next message, quit if none to be sent */
     	msg = gw_prioqueue_remove(smpp->msgs_to_send);
         if (msg == NULL)
             break;
 
         /* Send PDU, record it as waiting for ack from SMS center */
-        pdu = msg_to_pdu(smpp, msg);
+        /* RK: todo: make this a setting in smpp.conf */
+        send_as_data_sm = ((smpp->send_binary_as_data_sm) && (octstr_len(msg->sms.udhdata) > 2) && (GETLONGFROMUDHDATA(1) == 0x0504));
+        if (send_as_data_sm) {
+            pdu = msg_to_data_pdu(smpp, msg);
+        } else {
+            pdu = msg_to_submit_pdu(smpp, msg);
+        }
         if (pdu == NULL) {
             bb_smscconn_send_failed(smpp->conn, msg, SMSCCONN_FAILED_MALFORMED, octstr_create("MALFORMED SMS"));
             continue;
@@ -1073,7 +1331,11 @@
         /* check for write errors */
         if (send_pdu(conn, smpp->conn->id, pdu) == 0) {
             struct smpp_msg *smpp_msg = smpp_msg_create(msg);
-            os = octstr_format("%ld", pdu->u.submit_sm.sequence_number);
+            if (send_as_data_sm) {
+                os = octstr_format("%ld", pdu->u.data_sm.sequence_number);
+            } else {
+                os = octstr_format("%ld", pdu->u.submit_sm.sequence_number);
+            }
             dict_put(smpp->sent_msgs, os, smpp_msg);
             smpp_pdu_destroy(pdu);
             octstr_destroy(os);
@@ -1365,14 +1627,14 @@
          * Obey which SMPP msg_id type this SMSC is using, where we
          * have the following semantics for the variable smpp_msg_id:
          *
-         * bit 1: type for submit_sm_resp, bit 2: type for deliver_sm
+         * bit 1: type for submit_sm/data_sm, bit 2: type for deliver_sm
          *
          * if bit is set value is hex otherwise dec
          *
-         * 0x00 deliver_sm dec, submit_sm_resp dec
-         * 0x01 deliver_sm dec, submit_sm_resp hex
-         * 0x02 deliver_sm hex, submit_sm_resp dec
-         * 0x03 deliver_sm hex, submit_sm_resp hex
+         * 0x00 deliver_sm dec, submit_sm/data_sm dec
+         * 0x01 deliver_sm dec, submit_sm/data_sm hex
+         * 0x02 deliver_sm hex, submit_sm/data_sm dec
+         * 0x03 deliver_sm hex, submit_sm/data_sm hex
          *
          * Default behaviour is SMPP spec compliant, which means
          * msg_ids should be C strings and hence non modified.
@@ -1643,6 +1905,68 @@
             } /* end if for SMSC ACK */
             break;
 
+        case data_sm_resp:
+            os = octstr_format("%ld", pdu->u.data_sm_resp.sequence_number);
+            smpp_msg = dict_remove(smpp->sent_msgs, os);
+            octstr_destroy(os);
+            if (smpp_msg == NULL) {
+                warning(0, "SMPP[%s]: SMSC sent data_sm_resp "
+                        "with wrong sequence number 0x%08lx",
+                        octstr_get_cstr(smpp->conn->id),
+                        pdu->u.data_sm_resp.sequence_number);
+                break;
+            }
+            msg = smpp_msg->msg;
+            smpp_msg_destroy(smpp_msg, 0);
+            if (pdu->u.data_sm_resp.command_status != 0) {
+                error(0, "SMPP[%s]: SMSC returned error code 0x%08lx (%s) "
+                      "in response to data_sm.",
+                      octstr_get_cstr(smpp->conn->id),
+                      pdu->u.data_sm_resp.command_status,
+		      smpp_error_to_string(pdu->u.data_sm_resp.command_status));
+                reason = smpp_status_to_smscconn_failure_reason(
+                            pdu->u.data_sm_resp.command_status);
+
+                /*
+                 * check to see if we got a "throttling error", in which case we'll just
+                 * sleep for a while
+                 */
+                if (pdu->u.data_sm_resp.command_status == SMPP_ESME_RTHROTTLED)
+                    time(&(smpp->throttling_err_time));
+                else
+                    smpp->throttling_err_time = 0;
+
+                bb_smscconn_send_failed(smpp->conn, msg, reason, 
+                        octstr_format("%ld/%s", pdu->u.data_sm_resp.command_status,
+                            smpp_error_to_string(pdu->u.data_sm_resp.command_status)));
+                --(*pending_submits);
+            } else {
+                Octstr *tmp;
+
+                /* check if msg_id is C string, decimal or hex for this SMSC */
+                if (smpp->smpp_msg_id_type == -1) {
+                    /* the default, C string */
+                    tmp = octstr_duplicate(pdu->u.data_sm_resp.message_id);
+                } else {
+                    if (smpp->smpp_msg_id_type & 0x01) {
+                        tmp = octstr_format("%ld", strtol(  /* hex */
+                            octstr_get_cstr(pdu->u.data_sm_resp.message_id), NULL, 16));
+                    } else {
+                        tmp = octstr_format("%ld", strtol(  /* decimal */
+                            octstr_get_cstr(pdu->u.data_sm_resp.message_id), NULL, 10));
+                    }
+                }
+
+                /* SMSC ACK.. now we have the message id. */
+                if (DLR_IS_ENABLED_DEVICE(msg->sms.dlr_mask))
+                    dlr_add(smpp->conn->id, tmp, msg);
+
+                octstr_destroy(tmp);
+                bb_smscconn_sent(smpp->conn, msg, NULL);
+                --(*pending_submits);
+            } /* end if for SMSC ACK */
+            break;
+
         case bind_transmitter_resp:
             if (pdu->u.bind_transmitter_resp.command_status != 0 &&
                 pdu->u.bind_transmitter_resp.command_status != SMPP_ESME_RALYNBD) {
@@ -1746,7 +2070,7 @@
                 msg = smpp_msg->msg;
                 smpp_msg_destroy(smpp_msg, 0);
 
-                error(0, "SMPP[%s]: SMSC returned error code 0x%08lx (%s) in response to submit_sm.",
+                error(0, "SMPP[%s]: SMSC returned error code 0x%08lx (%s) in response to submit_sm/data_sm.",
                       octstr_get_cstr(smpp->conn->id),
                       cmd_stat,
                 smpp_error_to_string(cmd_stat));
@@ -2190,6 +2514,7 @@
     Octstr *alt_charset;
     Octstr *alt_addr_charset;
     long connection_timeout, wait_ack, wait_ack_action;
+    int send_binary_as_data_sm;
 
     my_number = alt_addr_charset = alt_charset = NULL;
     transceiver_mode = 0;
@@ -2303,6 +2628,8 @@
         if (smpp_msg_id_type < 0 || smpp_msg_id_type > 3)
             panic(0,"SMPP: Invalid value for msg-id-type directive in configuraton");
     }
+    if (cfg_get_bool(&send_binary_as_data_sm, grp, octstr_imm("send-binary-as-datasm")) == -1)
+        send_binary_as_data_sm = 0;
 
     /* check for an alternative charset */
     alt_charset = cfg_get(grp, octstr_imm("alt-charset"));
@@ -2328,7 +2655,8 @@
                        dest_addr_npi, enquire_link_interval,
                        max_pending_submits, version, priority, validity, my_number,
                        smpp_msg_id_type, autodetect_addr, alt_charset, alt_addr_charset,
-                       service_type, connection_timeout, wait_ack, wait_ack_action);
+                       service_type, connection_timeout, wait_ack, wait_ack_action,
+                       send_binary_as_data_sm);
 
     cfg_get_integer(&smpp->bind_addr_ton, grp, octstr_imm("bind-addr-ton"));
     cfg_get_integer(&smpp->bind_addr_npi, grp, octstr_imm("bind-addr-npi"));
Index: gwlib/cfg.def
===================================================================
RCS file: /home/cvs/gateway/gwlib/cfg.def,v
retrieving revision 1.142
diff -u -r1.142 cfg.def
--- gwlib/cfg.def	6 Dec 2009 17:24:14 -0000	1.142
+++ gwlib/cfg.def	16 Jan 2010 16:25:28 -0000
@@ -387,6 +387,7 @@
     OCTSTR(status-permfail-regex)
     OCTSTR(status-tempfail-regex)
     OCTSTR(max-sms-octets)
+    OCTSTR(send-binary-as-datasm)
     OCTSTR(login-prompt)
     OCTSTR(password-prompt)
     OCTSTR(generic-param-username)
