diff --git a/README.md b/README.md index d0e1511..4536042 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ Example code below: }); // Oh, the sms sender wants a dlr (delivery report), send it! - if (sms.dlrRequested === true) { + if (sms.dlr === true) { serverSession.sendDlr(sms); // To send a negative delivery report do: diff --git a/lib/defs.js b/lib/defs.js index 84c0ca2..6adca55 100644 --- a/lib/defs.js +++ b/lib/defs.js @@ -175,12 +175,18 @@ types = { }, write: function(value, buffer, offset) { buffer.writeUInt8(value.length, offset ++); - if (typeof value === 'string' || typeof value === 'number') { + if (typeof value === 'number') { + value = value.toString(); + } + if (typeof value === 'string') { value = new Buffer(value, 'ascii'); } value.copy(buffer, offset); }, size: function(value) { + if (typeof value === 'number') { + value = value.toString(); + } return value.length + 1; }, default: '' @@ -197,7 +203,10 @@ types = { return buffer.toString('ascii', offset, offset + length); }, write: function(value, buffer, offset) { - if (typeof value === 'string' || typeof value === 'number') { + if (typeof value === 'number') { + value = value.toString(); + } + if (typeof value === 'string') { value = new Buffer(value, 'ascii'); } value.copy(buffer, offset); @@ -217,7 +226,10 @@ types = { return buffer.slice(offset, offset + length); }, write: function(value, buffer, offset) { - if (typeof value === 'string' || typeof value === 'number') { + if (typeof value === 'number') { + value = value.toString(); + } + if (typeof value === 'string') { value = new Buffer(value, 'ascii'); } value.copy(buffer, offset); @@ -337,12 +349,18 @@ types.tlv = { return buffer.toString('ascii', offset, offset + length); }, write: function(value, buffer, offset) { - if (typeof value === 'string' || typeof value === 'number') { + if (typeof value === 'number') { + value = value.toString(); + } + if (typeof value === 'string') { value = new Buffer(value, 'ascii'); } value.copy(buffer, offset); }, size: function(value) { + if (typeof value === 'number') { + value = value.toString(); + } return value.length; }, default: '' @@ -352,12 +370,18 @@ types.tlv = { return buffer.slice(offset, offset + length); }, write: function(value, buffer, offset) { - if (typeof value === 'string' || typeof value === 'number') { + if (typeof value === 'number') { + value = value.toString(); + } + if (typeof value === 'string') { value = new Buffer(value, 'ascii'); } value.copy(buffer, offset); }, size: function(value) { + if (typeof value === 'number') { + value = value.toString(); + } return value.length; }, default: null diff --git a/lib/session.js b/lib/session.js index 7a100f2..d6baf40 100644 --- a/lib/session.js +++ b/lib/session.js @@ -110,7 +110,7 @@ function session(sock) { // Make sure the sequence number is set and is correct pduObj.seqNr = returnObj.ourSeqNr; - log.debug('larvitsmpp: lib/session.js: session() - returnObj.send() - Sending PDU to remote. cmdName: ' + pduObj.cmdName + ' seqNr: ' + pduObj.seqNr); + log.debug('larvitsmpp: lib/session.js: session() - returnObj.send() - Sending PDU to remote. pduObj: ' + JSON.stringify(pduObj)); // If closeAndSend is omitted, put callback in its place if (typeof closeAfterSend === 'function') { @@ -207,7 +207,7 @@ function session(sock) { * from - alphanum or international format * to - international format * message - string - * dlrRequested - boolean defaults to false + * dlr - boolean defaults to false * @param func callback(err, smsId, retPduObj) */ returnObj.sendSms = function(smsOptions, callback) { @@ -221,10 +221,12 @@ function session(sock) { }; // Request DLRs! - if (smsOptions.dlrRequested) { + if (smsOptions.dlr) { pduObj.params.registered_delivery = 0x01; } + log.debug('larvitsmpp: lib/session.js: returnObj.sendSms() - pduObj: ' + JSON.stringify(pduObj)); + returnObj.send(pduObj, function(err, retPduObj) { if (typeof callback === 'function') { callback(err, retPduObj.params.message_id, retPduObj); @@ -372,7 +374,7 @@ function session(sock) { 'to': pduObj.params.destination_addr, 'submitTime': new Date(), 'message': pduObj.params.short_message, - 'dlrRequested': Boolean(pduObj.params.registered_delivery) + 'dlr': Boolean(pduObj.params.registered_delivery) }; function smsReceived(smsData) { diff --git a/lib/utils.js b/lib/utils.js index 4da737e..88e94ac 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -116,6 +116,8 @@ function writeBuffer(obj, cmdLength, callback) { // 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] + '"'); + // Increase the offset for the next param offset += paramType.size(obj.params[param]); } @@ -132,7 +134,7 @@ function writeBuffer(obj, cmdLength, callback) { tlvSize = tlvDef.type.size(tlvValue); - log.silly('larvitsmpp: lib/utils.js: objToPdu() - writeBuffer() - Writing TLV "' + tlvName + '" offset: ' + offset + ' value: "' + tlvValue + '"'); + log.silly('larvitsmpp: lib/utils.js: writeBuffer() - Writing TLV "' + tlvName + '" offset: ' + offset + ' value: "' + tlvValue + '"'); buff.writeUInt16BE(tlvId, offset); buff.writeUInt16BE(tlvSize, offset + 2); @@ -178,9 +180,10 @@ function encodeMsg(str) { * Transforms a PDU to an object * * @param buf pdu + * @param bol stupidNullByte - Define if the short_message should be followed by a stupid NULL byte - will be auto resolved if left undefined * @param func callback(err, obj) */ -function pduToObj(pdu, callback) { +function pduToObj(pdu, stupidNullByte, callback) { var retObj = {'params': {}, 'tlvs': {}}, err = null, offset, @@ -191,6 +194,11 @@ function pduToObj(pdu, callback) { tlvValue, paramSize; + if (typeof stupidNullByte === 'function') { + callback = stupidNullByte; + stupidNullByte = undefined; + } + // Returns true if this PDU is a response to another PDU retObj.isResponse = function() { return ! ! (this.cmdId & 0x80000000); @@ -248,9 +256,8 @@ function pduToObj(pdu, callback) { // 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: lib/utils.js: pduToObj() - short_message is followed by a NULL octet, increase paramSize one extra 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 ++; } } @@ -267,7 +274,8 @@ function pduToObj(pdu, callback) { } // If the length is greater than the current offset, there must be TLVs - resolve them! - while (offset < retObj.cmdLength) { + // The minimal size for a TLV is its head, 4 octets + while ((offset + 4) < retObj.cmdLength) { try { tlvCmdId = pdu.readInt16BE(offset); tlvLength = pdu.readInt16BE(offset + 2); @@ -275,6 +283,7 @@ function pduToObj(pdu, callback) { err = new Error('Unable to read TLV at offset "' + offset + '", given cmdLength: "' + retObj.cmdLength + '" pdu: ' + pdu.toString('hex')); log.error('larvitsmpp: lib/utils.js: pduToObj() - ' + err.message); callback(err); + return; } if (defs.tlvsById[tlvCmdId] === undefined) { @@ -299,21 +308,32 @@ function pduToObj(pdu, callback) { 'tagName': defs.tlvsById[tlvCmdId].tag, 'tagValue': tlvValue }; + + log.silly('larvitsmpp: lib/utils.js: pduToObj() - TLV found: "' + defs.tlvsById[tlvCmdId].tag + '" ID: "' + tlvCmdId + '" value: "' + tlvValue + '"'); } offset = offset + 4 + tlvLength; } + if (offset !== retObj.cmdLength && stupidNullByte === undefined) { + log.verbose('larvitsmpp: lib/utils.js: pduToObj() - Offset (' + offset + ') !== cmdLength (' + retObj.cmdLength + ') for seqNr: ' + retObj.seqNr + ' - retry with the stupid NULL byte for short_message'); + + pduToObj(pdu, true, callback); + return; + } + + if (offset !== retObj.cmdLength) { + 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) { retObj.params.short_message = decodeMsg(retObj.params.short_message, retObj.params.data_coding); - - 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 - callback(null, retObj); } + + log.debug('larvitsmpp: lib/utils.js: pduToObj() - Complete decoded PDU: ' + JSON.stringify(retObj)); + + callback(null, retObj); } /** @@ -357,7 +377,7 @@ function objToPdu(obj, callback) { return; } - // All params are mandatory. Set them if they are not set + // Params must be an object if (obj.params === undefined) { obj.params = {}; } @@ -379,6 +399,8 @@ function objToPdu(obj, callback) { log.silly('larvitsmpp: lib/utils.js: objToPdu() - encoding message "' + shortMsg + '" to "' + obj.params.short_message.toString('hex') + '"'); } + log.debug('larvitsmpp: lib/utils.js: objToPdu() - Complete object to encode: ' + JSON.stringify(obj)); + calcCmdLength(obj, function(err, cmdLength) { if (err) { callback(err); @@ -622,4 +644,4 @@ exports.objToPdu = objToPdu; exports.pduReturn = pduReturn; exports.smppDate = smppDate; exports.bitCount = bitCount; -exports.splitMsg = splitMsg; \ No newline at end of file +exports.splitMsg = splitMsg; diff --git a/test/03_pdu.js b/test/03_pdu.js index b74ac6d..fdb3989 100644 --- a/test/03_pdu.js +++ b/test/03_pdu.js @@ -141,6 +141,29 @@ describe('PDU convertion', function() { done(); }); }); + + it('should encode and decode integer cstring params correctly', function(done) { + var pduObj = { + 'cmdName': 'submit_sm_resp', + 'cmdStatus': 'ESME_ROK', + 'seqNr': 2, + 'params': { + 'message_id': 450 + } + }; + + larvitsmpp.utils.objToPdu(pduObj, function(err, pduBuf) { + assert( ! err, 'Error should be negative'); + + larvitsmpp.utils.pduToObj(pduBuf, function(err, retPduObj) { + assert( ! err, 'Error should be negative'); + + assert(retPduObj.params.message_id === '450', 'message_id param should be 450, but as string'); + + done(); + }); + }); + }); }); describe('TLVs', function() { @@ -201,6 +224,49 @@ describe('PDU convertion', function() { }); }); }); + + it('should add some other TLVs to a PDU', function(done) { + var pduObj = { + 'cmdName': 'deliver_sm', + 'params': { + 'source_addr': '46701113311', + 'destination_addr': '46709771337', + 'esm_class': 4, + 'short_message': 'id:450 sub:001 dlvrd:1 submit date:1504031342 done date:1504031342 stat:DELIVRD err:0 text:xxx' + }, + 'tlvs': { + 'receipted_message_id': { + 'tagId': 30, + 'tagName': 'receipted_message_id', + 'tagValue': 450 + }, + 'message_state': { + 'tagId': 1063, + 'tagName': 'message_state', + 'tagValue': 2 + } + }, + 'seqNr': 323 + }; + + larvitsmpp.utils.objToPdu(pduObj, function(err, pduBuf) { + assert( ! err, 'Error should be negative'); + + larvitsmpp.utils.pduToObj(pduBuf, function(err, retPduObj) { + assert( ! err, 'Error should be negative'); + + assert(retPduObj.params.short_message === 'id:450 sub:001 dlvrd:1 submit date:1504031342 done date:1504031342 stat:DELIVRD err:0 text:xxx', 'short_message should be preserved'); + assert(retPduObj.cmdName === 'deliver_sm', 'Command name should be "deliver_sm"'); + assert(retPduObj.tlvs.message_state !== undefined, 'TLV message_state should be set'); + assert(retPduObj.tlvs.message_state.tagValue === 2, 'TLV message_state tagValue should be 2'); + assert(retPduObj.tlvs.receipted_message_id !== undefined, 'TLV receipted_message_id should be set'); + assert(retPduObj.tlvs.receipted_message_id.tagValue === '450', 'TLV receipted_message_id tagValue should be "450"'); + assert(retPduObj.seqNr === 323, 'Sequence number should be 323'); + + done(); + }); + }); + }); }); describe('Return PDUs', function() {