From 0d5ba73e8af516a34574dafd924a05696835aa29 Mon Sep 17 00:00:00 2001 From: lilleman Date: Sun, 10 May 2015 15:35:12 +0200 Subject: [PATCH] Added support for sending long messages via splitted short_message parameter --- larvitsmpp.js | 3 +- lib/session.js | 88 +++++++++++++++++++++++++++++++++++++++----------- lib/utils.js | 35 +++++++++++--------- test/03_pdu.js | 32 ++++++++++++++++++ 4 files changed, 122 insertions(+), 36 deletions(-) diff --git a/larvitsmpp.js b/larvitsmpp.js index a8f4b9a..5a95866 100644 --- a/larvitsmpp.js +++ b/larvitsmpp.js @@ -2,4 +2,5 @@ exports.server = require('./lib/server'); exports.client = require('./lib/client'); -exports.utils = require('./lib/utils'); \ No newline at end of file +exports.utils = require('./lib/utils'); +exports.defs = require('./lib/defs'); \ No newline at end of file diff --git a/lib/session.js b/lib/session.js index d6baf40..f7a8dfa 100644 --- a/lib/session.js +++ b/lib/session.js @@ -1,9 +1,9 @@ 'use strict'; -var log = require('winston'), - events = require('events'), - smppUtils = require('./utils'), - defs = require('./defs'); +var log = require('winston'), + events = require('events'), + utils = require('./utils'), + defs = require('./defs'); /** * Generic session function @@ -57,7 +57,7 @@ function session(sock) { */ returnObj.sockWrite = function(pdu, closeAfterSend) { if ( ! Buffer.isBuffer(pdu)) { - smppUtils.objToPdu(pdu, function(err, buffer) { + utils.objToPdu(pdu, function(err, buffer) { if (err) { log.warn('larvitsmpp: lib/session.js: session() - sockWrite() - Could not convert PDU to buffer'); returnObj.closeSocket(); @@ -96,7 +96,7 @@ function session(sock) { // Make sure the pdu is an object if (Buffer.isBuffer(pdu)) { - smppUtils.pduToObj(pdu, function(err, pduObj) { + utils.pduToObj(pdu, function(err, pduObj) { if (err) { callback(err); return; @@ -180,7 +180,7 @@ function session(sock) { closeAfterSend = undefined; } - smppUtils.pduReturn(pdu, status, params, function(err, retPdu) { + utils.pduReturn(pdu, status, params, function(err, retPdu) { if (err) { log.error('larvitsmpp: lib/session.js: session() - returnObj.sendReturn() - Could not create return PDU: ' + err.message); returnObj.closeSocket(); @@ -200,6 +200,53 @@ function session(sock) { }); }; + /** + * Send a longer SMS than 1120 bits + * + * @param obj smsOptions + * from - alphanum or international format + * to - international format + * message - string + * dlr - boolean defaults to false + * @param func callback(err, smsId, retPduObj) + */ + returnObj.sendLongSms = function(smsOptions, callback) { + var msgs = utils.splitMsg(smsOptions.message), + encoding = defs.encodings.detect(smsOptions.message); // Set encoding once for all message parts + + function sendPart(i) { + var pduObj = { + 'cmdName': 'submit_sm', + 'params': { + 'source_addr_ton': 1, // Default to international format + 'esm_class': 0x40, // This indicates that there is a UDH in the short_message + 'source_addr': smsOptions.from, + 'destination_addr': smsOptions.to, + 'data_coding': defs.consts.ENCODING[encoding], + 'short_message': msgs[i], + 'sm_length': msgs[i].length + } + }; + + // Request DLRs! + if (smsOptions.dlr) { + pduObj.params.registered_delivery = 0x01; + } + + log.debug('larvitsmpp: lib/session.js: returnObj.sendLongSms() - pduObj: ' + JSON.stringify(pduObj)); + + returnObj.send(pduObj, function(err, retPduObj) { + if (typeof callback === 'function' && msgs[i + 1] === undefined) { + callback(err, retPduObj.params.message_id, retPduObj); + } else if (msgs[i + 1] !== undefined) { + sendPart(i + 1); + } + }); + } + + sendPart(0); + }; + /** * Send an SMS * @@ -215,6 +262,7 @@ function session(sock) { pduObj.cmdName = 'submit_sm'; pduObj.params = { + 'source_addr_ton': 1, // Default to international format 'source_addr': smsOptions.from, 'destination_addr': smsOptions.to, 'short_message': smsOptions.message @@ -225,6 +273,13 @@ function session(sock) { pduObj.params.registered_delivery = 0x01; } + // Check if we must split this message into multiple + if (utils.bitCount(smsOptions.message) > 1120) { + returnObj.sendLongSms(smsOptions, callback); + + return; + } + log.debug('larvitsmpp: lib/session.js: returnObj.sendSms() - pduObj: ' + JSON.stringify(pduObj)); returnObj.send(pduObj, function(err, retPduObj) { @@ -243,7 +298,6 @@ function session(sock) { */ returnObj.sendDlr = function(sms, status, callback) { var shortMessage = 'id:' + sms.smsId + ' sub:001 ', - err, dlrPduObj; if (typeof status === 'function') { @@ -261,8 +315,8 @@ function session(sock) { shortMessage += 'dlvrd:0 '; } - shortMessage += 'submit date:' + smppUtils.smppDate(sms.submitTime); - shortMessage += ' done date:' + smppUtils.smppDate(new Date()); + shortMessage += 'submit date:' + utils.smppDate(sms.submitTime); + shortMessage += ' done date:' + utils.smppDate(new Date()); if (status) { shortMessage += ' stat:DELIVRD err:0 text:xxx'; @@ -298,15 +352,12 @@ function session(sock) { dlrPduObj.seqNr = 323; - console.log('before:'); - console.log(dlrPduObj); - - smppUtils.objToPdu(dlrPduObj, function(err, pdu) { + utils.objToPdu(dlrPduObj, function(err, pdu) { if (err) { throw err; } - smppUtils.pduToObj(pdu, function(err, newObj) { + utils.pduToObj(pdu, function(err, newObj) { if (err) { throw err; } @@ -316,14 +367,13 @@ function session(sock) { }); }); - return; returnObj.send(dlrPduObj, function(err, retPduObj) { console.log('WHATTA WHATTA'); console.log(retPduObj); - /*if (typeof callback === 'function') { + if (typeof callback === 'function') { callback(err, retPduObj.params.message_id, retPduObj); - }*/ + } }); }; @@ -423,7 +473,7 @@ function session(sock) { log.silly('larvitsmpp: lib/session.js: session() - sock.on(data) - Incoming PDU: ' + pduBuf.toString('hex')); - smppUtils.pduToObj(pduBuf, function(err, pduObj) { + utils.pduToObj(pduBuf, function(err, pduObj) { if (err) { log.warn('larvitsmpp: lib/session.js: session() - Invalid PDU. ' + err.message); diff --git a/lib/utils.js b/lib/utils.js index 88e94ac..a34d625 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -73,6 +73,7 @@ function writeBuffer(obj, cmdLength, callback) { buff, param, paramType, + paramSize, tlvId, tlvName, tlvValue, @@ -112,14 +113,19 @@ function writeBuffer(obj, cmdLength, callback) { // Cycle through the defs list to make sure the params are in the right order for (param in defs.cmds[obj.cmdName].params) { paramType = defs.cmds[obj.cmdName].params[param].type; + paramSize = paramType.size(obj.params[param]); // Write parameter value to buffer using the types method write() paramType.write(obj.params[param], buff, offset); - log.silly('larvitsmpp: lib/utils.js: writeBuffer() - Writing param "' + param + '" with content "' + obj.params[param] + '"'); + if (Buffer.isBuffer(obj.params[param])) { + log.silly('larvitsmpp: lib/utils.js: writeBuffer() - Writing param "' + param + '" with content "' + obj.params[param].toString('hex') + '" and size "' + paramSize + '"'); + } else { + log.silly('larvitsmpp: lib/utils.js: writeBuffer() - Writing param "' + param + '" with content "' + obj.params[param] + '"'); + } // Increase the offset for the next param - offset += paramType.size(obj.params[param]); + offset += paramSize; } // Cycle through the tlvs @@ -143,6 +149,8 @@ function writeBuffer(obj, cmdLength, callback) { offset += tlvDef.type.size(tlvValue) + 4; } + log.silly('larvitsmpp: lib/utils.js: writeBuffer() - Complete PDU: "' + buff.toString('hex') + '"'); + callback(null, buff); } @@ -255,7 +263,6 @@ function pduToObj(pdu, stupidNullByte, callback) { if (param === 'short_message') { // Check if we have a trailing NULL octet after the short_message. Some idiot thought that would be a good idea // in some implementations, so we need to account for that. - if (stupidNullByte === true) { log.silly('larvitsmpp: lib/utils.js: pduToObj() - stupidNullByte is set, so short_message is followed by a NULL octet, increase paramSize one extra to account for that'); paramSize ++; @@ -326,8 +333,10 @@ function pduToObj(pdu, stupidNullByte, callback) { log.warn('larvitsmpp: lib/utils.js: pduToObj() - Offset (' + offset + ') !== cmdLength (' + retObj.cmdLength + ') for seqNr: ' + retObj.seqNr); } - // Decode the short message if it is set - if (retObj.params.short_message !== undefined) { + // Decode the short message if it is set and esm_class is 0 + // The esm_class 0x40 (64 int) means the short_message have a UDH + // Thats why we return the short_message as a buffer + if (retObj.params.short_message !== undefined && retObj.params.esm_class !== 0x40) { retObj.params.short_message = decodeMsg(retObj.params.short_message, retObj.params.data_coding); } @@ -395,6 +404,7 @@ function objToPdu(obj, callback) { // Acutally encode the string shortMsg = obj.params.short_message; obj.params.short_message = encodeMsg(obj.params.short_message); + obj.params.sm_length = obj.params.short_message.length; log.silly('larvitsmpp: lib/utils.js: objToPdu() - encoding message "' + shortMsg + '" to "' + obj.params.short_message.toString('hex') + '"'); } @@ -571,20 +581,13 @@ function bitCount(msg, encoding) { * @return array of buffers */ function splitMsg(msg) { - var msgPart = '', - msgs = [], - msgId = Math.floor(Math.random() * (255 - 0)), // This will identify this message "bundle" - encoding, + var msgPart = '', + msgs = [], + msgId = Math.floor(Math.random() * (255 - 0)), // This will identify this message "bundle" + encoding = defs.encodings.detect(msg), udh, i; - // Build a buffer from the correct encoding - if (defs.encodings.ASCII.match(msg)) { - encoding = 'ASCII'; - } else { - encoding = 'UCS2'; - } - // A single message could contain up to 1120 bits // Return directly if the message fits into that if (bitCount(msg) < 1121) { diff --git a/test/03_pdu.js b/test/03_pdu.js index fdb3989..88b24d8 100644 --- a/test/03_pdu.js +++ b/test/03_pdu.js @@ -142,6 +142,38 @@ describe('PDU convertion', function() { }); }); + it('should create a submit_sm PDU with esm_class 0x40 and a short_message with UDH in it', function(done) { + var msg = 'hej världen', + msgBuf = Buffer.concat([new Buffer('050003010101', 'hex'), new Buffer(msg)]), + encoding = larvitsmpp.defs.encodings.detect(msg); + + larvitsmpp.utils.objToPdu({ + 'cmdName': 'submit_sm', + 'cmdStatus': 'ESME_ROK', + 'seqNr': 12, + 'params': { + 'esm_class': 0x40, + 'source_addr': '46701113311', + 'destination_addr': '46709771337', + 'sm_length': msgBuf.length, + 'data_coding': larvitsmpp.defs.consts.ENCODING[encoding], + 'short_message': msgBuf + } + }, function(err, pdu) { + assert( ! err, 'Error should be negative'); + + larvitsmpp.utils.pduToObj(pdu, function(err, retObj) { + assert( ! err, 'Error should be negative'); + + assert(retObj.params.short_message.toString('hex') === '05000301010168656a2076c3a4726c64656e', 'short_message should be correct'); + + done(); + }); + + }); + }); + + it('should encode and decode integer cstring params correctly', function(done) { var pduObj = { 'cmdName': 'submit_sm_resp',