Added support for sending long messages via splitted short_message parameter

This commit is contained in:
2015-05-10 15:35:12 +02:00
parent 931dac203d
commit 0d5ba73e8a
4 changed files with 122 additions and 36 deletions
+2 -1
View File
@@ -2,4 +2,5 @@
exports.server = require('./lib/server'); exports.server = require('./lib/server');
exports.client = require('./lib/client'); exports.client = require('./lib/client');
exports.utils = require('./lib/utils'); exports.utils = require('./lib/utils');
exports.defs = require('./lib/defs');
+69 -19
View File
@@ -1,9 +1,9 @@
'use strict'; 'use strict';
var log = require('winston'), var log = require('winston'),
events = require('events'), events = require('events'),
smppUtils = require('./utils'), utils = require('./utils'),
defs = require('./defs'); defs = require('./defs');
/** /**
* Generic session function * Generic session function
@@ -57,7 +57,7 @@ function session(sock) {
*/ */
returnObj.sockWrite = function(pdu, closeAfterSend) { returnObj.sockWrite = function(pdu, closeAfterSend) {
if ( ! Buffer.isBuffer(pdu)) { if ( ! Buffer.isBuffer(pdu)) {
smppUtils.objToPdu(pdu, function(err, buffer) { utils.objToPdu(pdu, function(err, buffer) {
if (err) { if (err) {
log.warn('larvitsmpp: lib/session.js: session() - sockWrite() - Could not convert PDU to buffer'); log.warn('larvitsmpp: lib/session.js: session() - sockWrite() - Could not convert PDU to buffer');
returnObj.closeSocket(); returnObj.closeSocket();
@@ -96,7 +96,7 @@ function session(sock) {
// Make sure the pdu is an object // Make sure the pdu is an object
if (Buffer.isBuffer(pdu)) { if (Buffer.isBuffer(pdu)) {
smppUtils.pduToObj(pdu, function(err, pduObj) { utils.pduToObj(pdu, function(err, pduObj) {
if (err) { if (err) {
callback(err); callback(err);
return; return;
@@ -180,7 +180,7 @@ function session(sock) {
closeAfterSend = undefined; closeAfterSend = undefined;
} }
smppUtils.pduReturn(pdu, status, params, function(err, retPdu) { utils.pduReturn(pdu, status, params, function(err, retPdu) {
if (err) { if (err) {
log.error('larvitsmpp: lib/session.js: session() - returnObj.sendReturn() - Could not create return PDU: ' + err.message); log.error('larvitsmpp: lib/session.js: session() - returnObj.sendReturn() - Could not create return PDU: ' + err.message);
returnObj.closeSocket(); 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 * Send an SMS
* *
@@ -215,6 +262,7 @@ function session(sock) {
pduObj.cmdName = 'submit_sm'; pduObj.cmdName = 'submit_sm';
pduObj.params = { pduObj.params = {
'source_addr_ton': 1, // Default to international format
'source_addr': smsOptions.from, 'source_addr': smsOptions.from,
'destination_addr': smsOptions.to, 'destination_addr': smsOptions.to,
'short_message': smsOptions.message 'short_message': smsOptions.message
@@ -225,6 +273,13 @@ function session(sock) {
pduObj.params.registered_delivery = 0x01; 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)); log.debug('larvitsmpp: lib/session.js: returnObj.sendSms() - pduObj: ' + JSON.stringify(pduObj));
returnObj.send(pduObj, function(err, retPduObj) { returnObj.send(pduObj, function(err, retPduObj) {
@@ -243,7 +298,6 @@ function session(sock) {
*/ */
returnObj.sendDlr = function(sms, status, callback) { returnObj.sendDlr = function(sms, status, callback) {
var shortMessage = 'id:' + sms.smsId + ' sub:001 ', var shortMessage = 'id:' + sms.smsId + ' sub:001 ',
err,
dlrPduObj; dlrPduObj;
if (typeof status === 'function') { if (typeof status === 'function') {
@@ -261,8 +315,8 @@ function session(sock) {
shortMessage += 'dlvrd:0 '; shortMessage += 'dlvrd:0 ';
} }
shortMessage += 'submit date:' + smppUtils.smppDate(sms.submitTime); shortMessage += 'submit date:' + utils.smppDate(sms.submitTime);
shortMessage += ' done date:' + smppUtils.smppDate(new Date()); shortMessage += ' done date:' + utils.smppDate(new Date());
if (status) { if (status) {
shortMessage += ' stat:DELIVRD err:0 text:xxx'; shortMessage += ' stat:DELIVRD err:0 text:xxx';
@@ -298,15 +352,12 @@ function session(sock) {
dlrPduObj.seqNr = 323; dlrPduObj.seqNr = 323;
console.log('before:'); utils.objToPdu(dlrPduObj, function(err, pdu) {
console.log(dlrPduObj);
smppUtils.objToPdu(dlrPduObj, function(err, pdu) {
if (err) { if (err) {
throw err; throw err;
} }
smppUtils.pduToObj(pdu, function(err, newObj) { utils.pduToObj(pdu, function(err, newObj) {
if (err) { if (err) {
throw err; throw err;
} }
@@ -316,14 +367,13 @@ function session(sock) {
}); });
}); });
return;
returnObj.send(dlrPduObj, function(err, retPduObj) { returnObj.send(dlrPduObj, function(err, retPduObj) {
console.log('WHATTA WHATTA'); console.log('WHATTA WHATTA');
console.log(retPduObj); console.log(retPduObj);
/*if (typeof callback === 'function') { if (typeof callback === 'function') {
callback(err, retPduObj.params.message_id, retPduObj); 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')); 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) { if (err) {
log.warn('larvitsmpp: lib/session.js: session() - Invalid PDU. ' + err.message); log.warn('larvitsmpp: lib/session.js: session() - Invalid PDU. ' + err.message);
+19 -16
View File
@@ -73,6 +73,7 @@ function writeBuffer(obj, cmdLength, callback) {
buff, buff,
param, param,
paramType, paramType,
paramSize,
tlvId, tlvId,
tlvName, tlvName,
tlvValue, 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 // Cycle through the defs list to make sure the params are in the right order
for (param in defs.cmds[obj.cmdName].params) { for (param in defs.cmds[obj.cmdName].params) {
paramType = defs.cmds[obj.cmdName].params[param].type; paramType = defs.cmds[obj.cmdName].params[param].type;
paramSize = paramType.size(obj.params[param]);
// Write parameter value to buffer using the types method write() // Write parameter value to buffer using the types method write()
paramType.write(obj.params[param], buff, offset); 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 // Increase the offset for the next param
offset += paramType.size(obj.params[param]); offset += paramSize;
} }
// Cycle through the tlvs // Cycle through the tlvs
@@ -143,6 +149,8 @@ function writeBuffer(obj, cmdLength, callback) {
offset += tlvDef.type.size(tlvValue) + 4; offset += tlvDef.type.size(tlvValue) + 4;
} }
log.silly('larvitsmpp: lib/utils.js: writeBuffer() - Complete PDU: "' + buff.toString('hex') + '"');
callback(null, buff); callback(null, buff);
} }
@@ -255,7 +263,6 @@ function pduToObj(pdu, stupidNullByte, callback) {
if (param === 'short_message') { 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 // 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. // in some implementations, so we need to account for that.
if (stupidNullByte === true) { 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'); 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 ++; 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); log.warn('larvitsmpp: lib/utils.js: pduToObj() - Offset (' + offset + ') !== cmdLength (' + retObj.cmdLength + ') for seqNr: ' + retObj.seqNr);
} }
// Decode the short message if it is set // Decode the short message if it is set and esm_class is 0
if (retObj.params.short_message !== undefined) { // 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); 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 // Acutally encode the string
shortMsg = obj.params.short_message; shortMsg = obj.params.short_message;
obj.params.short_message = encodeMsg(obj.params.short_message); obj.params.short_message = encodeMsg(obj.params.short_message);
obj.params.sm_length = obj.params.short_message.length; 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') + '"'); 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 * @return array of buffers
*/ */
function splitMsg(msg) { function splitMsg(msg) {
var msgPart = '', var msgPart = '',
msgs = [], msgs = [],
msgId = Math.floor(Math.random() * (255 - 0)), // This will identify this message "bundle" msgId = Math.floor(Math.random() * (255 - 0)), // This will identify this message "bundle"
encoding, encoding = defs.encodings.detect(msg),
udh, udh,
i; 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 // A single message could contain up to 1120 bits
// Return directly if the message fits into that // Return directly if the message fits into that
if (bitCount(msg) < 1121) { if (bitCount(msg) < 1121) {
+32
View File
@@ -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) { it('should encode and decode integer cstring params correctly', function(done) {
var pduObj = { var pduObj = {
'cmdName': 'submit_sm_resp', 'cmdName': 'submit_sm_resp',