diff --git a/lib/defs.js b/lib/defs.js index 6adca55..bb7ab48 100644 --- a/lib/defs.js +++ b/lib/defs.js @@ -2,7 +2,8 @@ // More or less copied from https://github.com/farhadi/node-smpp -var iconv = require('iconv-lite'), +var log = require('winston'), + iconv = require('iconv-lite'), encodings = {}, filters = {}, tlvsById = {}, @@ -135,7 +136,13 @@ types = { }, write: function(value, buffer, offset) { value = value || 0; - buffer.writeUInt8(value, offset); + + try { + buffer.writeUInt8(value, offset); + } catch (err) { + log.error('larvitsmpp: lib/defs.js - Could not write integer value "' + value + '" on offset "' + offset + '" when buffer length is "' + buffer.length + '"'); + throw err; + } }, size: function() { return 1; diff --git a/lib/session.js b/lib/session.js index 74de912..6970dd6 100644 --- a/lib/session.js +++ b/lib/session.js @@ -357,6 +357,8 @@ function sendSms(smsOptions, callback) { // Check if we must split this message into multiple if (utils.bitCount(smsOptions.message) > 1120) { + log.debug('larvitsmpp: lib/session.js: sendSms() - Message larger than 1120 bits, send it as long message!'); + this.sendLongSms(smsOptions, callback); return; diff --git a/lib/utils.js b/lib/utils.js index f0bfa01..12c5571 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,7 +1,8 @@ 'use strict'; -var log = require('winston'), - defs = require('./defs'); +var log = require('winston'), + defs = require('./defs'), + bundleMsgId = 0; /** * Calcualte cmdLength from object @@ -115,15 +116,19 @@ function writeBuffer(obj, cmdLength, callback) { 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); - 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 { + if (param === 'sm_length') { + log.silly('larvitsmpp: lib/utils.js: writeBuffer() - sm_length is calculated by short_message: "' + obj.params.short_message.toString('hex') + '"'); + } + log.silly('larvitsmpp: lib/utils.js: writeBuffer() - Writing param "' + param + '" with content "' + obj.params[param] + '"'); } + // Write parameter value to buffer using the types method write() + paramType.write(obj.params[param], buff, offset); + // Increase the offset for the next param offset += paramSize; } @@ -424,9 +429,7 @@ function objToPdu(obj, callback) { return; } - writeBuffer(obj, cmdLength, function(err, buff) { - callback(err, buff); - }); + writeBuffer(obj, cmdLength, callback); }); } @@ -582,7 +585,11 @@ function smppDate(jsDateObj) { * @return integer */ function bitCount(msg, encoding) { - if (encoding === 'ASCII' || defs.encodings.ASCII.match(msg)) { + if (defs.encodings[encoding] === undefined) { + encoding = defs.encodings.detect(msg); + } + + if (encoding === 'ASCII') { return defs.encodings.ASCII.encode(msg).length * 7; // * 7 since each character takes up 7 bits } else { return defs.encodings.UCS2.encode(msg).length * 8; // * 8 since its encoded as 16-bits. @@ -596,26 +603,40 @@ 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 = defs.encodings.detect(msg), + var msgPart = '', + msgs = [], + encoding = defs.encodings.detect(msg), + totBitCount = bitCount(msg, encoding), + partBitCount, udh, - i; + i; // A single message could contain up to 1120 bits // Return directly if the message fits into that - if (bitCount(msg) < 1121) { + if (totBitCount < 1121) { + log.silly('larvitsmpp: lib/utils.js: splitMsg() - bitCount below 1121 (' + totBitCount + ') return only one part'); return [defs.encodings[encoding].encode(msg)]; } + bundleMsgId ++; // This will identify this message "bundle" + + if (bundleMsgId === 256) { + bundleMsgId = 1; + } + + log.silly('larvitsmpp: lib/utils.js: splitMsg() - bundleMsgId set to ' + bundleMsgId); + i = 0; while (msg[i] !== undefined) { msgPart += msg[i]; - if (bitCount(msgPart, encoding) > 1072) { + partBitCount = bitCount(msgPart, encoding); + + if (partBitCount > 1072) { // We've reached the message limit + log.debug('larvitsmpp: lib/utils.js: splitMsg() - Msg part defined. partBitCount: ' + partBitCount + ' msgPart: "' + msgPart + '"'); + // Add this msgPart minus the last character to the msgs array as an encoded buffer msgs.push(defs.encodings[encoding].encode(msgPart.slice(0, - 1))); @@ -641,7 +662,7 @@ function splitMsg(msg) { 0x05, // Length of User Data Header, in this case 05. 0x00, // Information Element Identifier, equal to 00 (Concatenated short messages, 8-bit reference number) 0x03, // Length of the header, excluding the first two fields; equal to 03 - msgId, // CSMS reference number, must be same for all the SMS parts in the CSMS + bundleMsgId, // CSMS reference number, must be same for all the SMS parts in the CSMS msgs.length, // Total number of parts. The value shall remain constant for every short message which makes up the concatenated short message. If the value is zero then the receiving entity shall ignore the whole information element i + 1 // This part's number in the sequence. The value shall start at 1 and increment for every short message which makes up the concatenated short message. ]); diff --git a/test/04_session.js b/test/04_session.js index 35bd4f1..1299a06 100644 --- a/test/04_session.js +++ b/test/04_session.js @@ -225,6 +225,72 @@ describe('Sessions', function() { }); }); + it('should try sending a weirder long sms', function(done) { + portfinder.getPort(function(err, freePort) { + assert( ! err, 'Error should not be negative'); + + larvitsmpp.server({ + 'port': freePort + }, function(err, serverSession) { + assert( ! err, 'Error should not be negative'); + + serverSession.on('sms', function(sms) { + sms.smsId = 2344; + + assert(sms.from === 'foo', 'SMS from should be "foo"'); + assert(sms.to === '46709771337', 'SMS to should be "46709771337"'); + assert(sms.message === 'K sKM8NYUuoVbORtCn€swWsvTZjbtYM1TceGJJouolLk4cOtlk7j dxWMI56Domdx2W!dHKjGBR5UsynmUnbp1ysRDCktBri WW2pxIWHv0P7H OVRZNw 6 DBzAqpnd7ZPslwNyi»x OuiNH0R!WM2DTo8ItysNDNe1eNnpLvahhRgv»TC y lvgFrmv4OiUTOP', 'SMS message should be correct'); + assert(sms.dlr === false, 'DLR should be boolean false'); + assert(sms.pduObjs[0].pduObj.cmdId === 4, 'SMS pduObj cmdId should be 4'); + + sms.sendResp(function(err, retPdus) { + assert( ! err, 'Error should not be negative'); + + assert(retPdus[0].toString('hex') === '00000017800000040000000000000002323334342d3100', 'Return PDU 0 is wrong'); + assert(retPdus[1].toString('hex') === '00000017800000040000000000000003323334342d3200', 'Return PDU 1 is wrong'); + assert(retPdus[2].toString('hex') === '00000017800000040000000000000004323334342d3300', 'Return PDU 2 is wrong'); + }); + }); + + serverSession.on('close', function() { + // Manually destroy the server socket + serverSession.sock.destroy(); + }); + }); + + larvitsmpp.client({ + 'port': freePort + }, function(err, clientSession) { + assert( ! err, 'Error should not be negative'); + + clientSession.sendSms({ + 'from': 'foo', + 'to': '46709771337', + 'message': 'K sKM8NYUuoVbORtCn€swWsvTZjbtYM1TceGJJouolLk4cOtlk7j dxWMI56Domdx2W!dHKjGBR5UsynmUnbp1ysRDCktBri WW2pxIWHv0P7H OVRZNw 6 DBzAqpnd7ZPslwNyi»x OuiNH0R!WM2DTo8ItysNDNe1eNnpLvahhRgv»TC y lvgFrmv4OiUTOP' + }, function(err, smsIds, retPduObjs) { + assert( ! err, 'Error should not be negative'); + + assert(smsIds instanceof Array, 'smsIds should be an Array'); + assert(smsIds[0] === '2344-1', 'First smsId should be "2344-1"'); + assert(smsIds[1] === '2344-2', 'Second smsId should be "2344-2"'); + assert(smsIds[2] === '2344-3', 'Third smsId should be "2344-3"'); + assert(smsIds[3] === undefined, 'Fourth smsId should be undefined'); + assert(retPduObjs[0].cmdStatus === 'ESME_ROK', 'Command status should be "ESME_ROK"'); + assert(retPduObjs[0].cmdName === 'submit_sm_resp', 'Command name should be "submit_sm_resp"'); + assert(retPduObjs[1].cmdStatus === 'ESME_ROK', 'Command status should be "ESME_ROK"'); + assert(retPduObjs[1].cmdName === 'submit_sm_resp', 'Command name should be "submit_sm_resp"'); + assert(retPduObjs[2].cmdStatus === 'ESME_ROK', 'Command status should be "ESME_ROK"'); + assert(retPduObjs[2].cmdName === 'submit_sm_resp', 'Command name should be "submit_sm_resp"'); + + // Gracefully close connection + clientSession.unbind(); + + done(); + }); + }); + }); + }); + it('should send a long sms and receive dlrs for it', function(done) { portfinder.getPort(function(err, freePort) { assert( ! err, 'Error should not be negative');