diff --git a/defs.js b/defs.js index 87e9189..a474a94 100644 --- a/defs.js +++ b/defs.js @@ -192,30 +192,20 @@ types = { default: '' }, buffer: { - read: function(buffer, offset) { - var length = buffer.readUInt8(offset ++); + read: function(buffer, offset, length) { return buffer.slice(offset, offset + length); }, write: function(value, buffer, offset) { - buffer.writeUInt8(value.length, offset ++); if (typeof value === 'string') { value = new Buffer(value, 'ascii'); } value.copy(buffer, offset); }, - size: function(buffer, offset) { - var length; - - if (isNaN(offset)) { - offset = 0; - } - - length = buffer.readUInt8(offset ++); - - if (buffer[offset + length + 1] !== undefined && buffer.slice(offset + length + 1).toString('hex') === '00') { - return offset + length + 1; + size: function(buffer) { + if (buffer[buffer.length - 1] === 0x00) { + return buffer.length - 1; } else { - return offset + length; + return buffer.length; } }, default: new Buffer(0) @@ -1325,7 +1315,7 @@ cmds = { replace_if_present_flag: {type: types.int8}, data_coding: {type: types.int8}, sm_default_msg_id: {type: types.int8}, - //sm_length: {type: types.int8}, + sm_length: {type: types.int8}, short_message: {type: types.buffer, filter: filters.message} } }, diff --git a/larvitsmpp.js b/larvitsmpp.js index e663471..549bb14 100644 --- a/larvitsmpp.js +++ b/larvitsmpp.js @@ -11,11 +11,10 @@ var log = require('winston'), * * @param buf buffer * @param str encoding 'ASCII', 'LATIN1' or 'UCS2' or hex values - * @param func callback(err, str) - str will be in utf8 format + * @return str in utf8 format */ -function decodeMsg(buffer, encoding, callback) { - var err = null, - checkEnc; +function decodeMsg(buffer, encoding) { + var checkEnc; for (checkEnc in defs.consts.ENCODING) { if (parseInt(encoding) === defs.consts.ENCODING[checkEnc] || encoding === checkEnc) { @@ -28,18 +27,14 @@ function decodeMsg(buffer, encoding, callback) { encoding = 'ASCII'; } - callback(err, defs.encodings[encoding].decode(buffer)); + return defs.encodings[encoding].decode(buffer); } -/* -function encodeMsg(str, callback) { - var encoding = defs.encodings.detect(str), - err = null, - buff; - buff = defs.encodings[encoding].encode(str); +function encodeMsg(str) { + var encoding = defs.encodings.detect(str); - callback(err, buff); -}*/ + return defs.encodings[encoding].encode(str); +} /** * Transforms a PDU to an object @@ -104,17 +99,22 @@ function pduToObj(pdu, callback) { // Get the parameter value by using the definition type read() function try { - retObj.params[param] = command.params[param].type.read(pdu, offset); - - // Short message seems to sometimes be terminated with a NULL and sometimes not. Very ugly and needs special care. - if (param === 'short_message') { - paramSize = command.params[param].type.size(pdu, offset); - } else { - paramSize = command.params[param].type.size(retObj.params[param]); - } + retObj.params[param] = command.params[param].type.read(pdu, offset, retObj.params.sm_length); + paramSize = command.params[param].type.size(retObj.params[param]); log.silly('larvitsmpp: pduToObj() - Reading param "' + param + '" at offset ' + offset + ' with calculated size: ' + paramSize + ' content in hex: ' + pdu.slice(offset, offset + paramSize).toString('hex')); + 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 (pdu.slice(offset + retObj.params.sm_length, offset + retObj.params.sm_length + 1).toString('hex') === '00') { + log.silly('larvitsmpp: pduToObj() - short_message is followed by a NULL octet, increase paramSize one extra to account for that'); + + paramSize ++; + } + } + // Increase the offset by the current params length offset += paramSize; } catch (e) { @@ -145,17 +145,9 @@ function pduToObj(pdu, callback) { // Decode the short message if it is set if (retObj.params.short_message !== undefined) { + retObj.params.short_message = decodeMsg(retObj.params.short_message, retObj.params.data_coding); - decodeMsg(retObj.params.short_message, retObj.params.data_coding, function(err, decodedMsg) { - if (err) { - callback(err); - return; - } - - retObj.params.short_message = decodedMsg; - - callback(null, retObj); - }); + callback(null, retObj); } else { // We need to do the standard callback in an else statement since // this always would be called before the above callback if we didn't @@ -172,6 +164,7 @@ function pduToObj(pdu, callback) { function objToPdu(obj, callback) { var cmdLength = 16, // All commands are at least 16 octets long err = null, + shortMsg, param, paramType, buff, @@ -232,6 +225,28 @@ function objToPdu(obj, callback) { return; } + // All params are mandatory. Set them if they are not set + if (obj.params === undefined) { + obj.params = {}; + } + + // If param "short_message" exists, encode it and set parameter "data_coding" accordingly + if (obj.params.short_message !== undefined && ! Buffer.isBuffer(obj.params.short_message)) { + // Detect encoding + obj.params.data_coding = defs.encodings.detect(obj.params.short_message); + + log.silly('larvitsmpp: objToPdu() - data_coding "' + obj.params.data_coding + '" detected'); + + // Now set the hex value + obj.params.data_coding = defs.consts.ENCODING[obj.params.data_coding]; + + // 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: objToPdu() - encoding message "' + shortMsg + '" to "' + obj.params.short_message.toString('hex') + '"'); + } + // Handle params - All command params should always exists, even if they do not contain data. for (param in defs.cmds[obj.cmdName].params) { @@ -239,11 +254,6 @@ function objToPdu(obj, callback) { // This is needed so we can calculate length etc paramType = defs.cmds[obj.cmdName].params[param].type; - // All params are mandatory. Set them if they are not set - if (obj.params === undefined) { - obj.params = {}; - } - if (obj.params[param] === undefined) { obj.params[param] = paramType.default; } @@ -521,6 +531,29 @@ function session(sock) { }); }; + /** + * Send an SMS + * + * @param obj smsOptions + * from - alphanum or international format + * to - international format + * message - string + * dlr - boolean defaults to false + * @param func callback(err, retPduObj) + */ + sessionEmitter.sendSms = function(smsOptions, callback) { + var pduObj = {}; + + pduObj.cmdName = 'submti_sm'; + pduObj.params = { + 'source_addr': smsOptions.from, + 'destination_addr': smsOptions.to, + 'short_message': smsOptions.message + }; + + sessionEmitter.send(pduObj, callback); + }; + sessionEmitter.deliverSm = function(pduObj) { console.log(pduObj); sessionEmitter.sendReturn(pduObj); diff --git a/test/test.js b/test/test.js index b266008..3d2a187 100644 --- a/test/test.js +++ b/test/test.js @@ -117,6 +117,25 @@ describe('PDU convertion', function() { done(); }); }); + + it('should create a very simple submit_sm PDU', function(done) { + larvitsmpp.objToPdu({ + 'cmdName': 'submit_sm', + 'cmdStatus': 'ESME_ROK', + 'seqNr': 12, + 'params': { + 'source_addr': '46701113311', + 'destination_addr': '46709771337', + 'short_message': 'Hello world' + } + }, function(err, pdu) { + assert( ! err, 'Error should be negative'); + + assert(pdu.toString('hex') === '0000004200000004000000000000000c00000034363730313131333331310000003436373039373731333337000000000000000001000b48656c6c6f20776f726c64'); + + done(); + }); + }); }); });