Monstrous amount of changes. Bad commit disciplin.

This commit is contained in:
2015-05-10 23:42:11 +02:00
parent 2307800d22
commit 80c0153b57
4 changed files with 292 additions and 107 deletions
+5 -5
View File
@@ -11,7 +11,7 @@ This will setup a client that connects to localhost, port 2775 without username
var larvitsmpp = require('larvitsmpp');
larvitsmpp.client(function(err, clientSession) {
clientSession.send({
clientSession.sendSms({
'from': '46701113311',
'to': '46709771337',
'message': 'Hello world'
@@ -45,7 +45,7 @@ This will setup a client that connects to given host, port with username and pas
throw err;
}
console.log('SMS sent, smsId: ' + smsId);
console.log('SMS sent, smsId(s): ' + smsId.join(', '));
console.log('Return PDU object:');
console.log(retPduObj);
});
@@ -101,10 +101,10 @@ Example code below:
}
// Incoming SMS!
serverSession.on('sms', function(sms, callback) {
serverSession.on('sms', function(sms) {
// It is important to run the callback since this is a part of the protocol
callback({
// It is important to run the sms.resp() since this is a part of the protocol
sms.resp({
// Decimal value status code
// Default is 0 == no error
+271 -95
View File
@@ -2,8 +2,167 @@
var log = require('winston'),
events = require('events'),
moment = require('moment'),
utils = require('./utils'),
defs = require('./defs');
defs = require('./defs'),
async = require('async');
/*
* Send a response to an sms
* This must be called from an sms object context
*
* @param str status - see list at defs.errors - defaults to 'ESME_ROK' - no error (OPTIONAL)
* @param func callback(err, [retPdu, ...])
*/
function smsResp(status, callback) {
var sms = this,
err,
i,
tasks = [];
if (typeof status === 'function') {
callback = status;
status = true;
}
if (typeof callback !== 'function') {
callback = function() {};
}
if (sms.smsId === undefined) {
sms.smsId = '';
}
// Accept a generic positive status
if (status === 'true' || status === true || status === 0) {
status = 'ESME_ROK';
}
// Accept a generic negative status
if (status === 'false' || status === false || status === 1) {
status = 'ESME_RUNKNOWNERR'; // Set to unknown error in this case
}
if (sms.pduObjs === undefined && sms.pduObj === undefined) {
err = new Error('No pdu objects found to base return PDU upon');
log.warn('larvitsmpp: lib/session.js: smsResp() - ' + err.message);
callback(err);
return;
}
if (sms.pduObjs === undefined) {
sms.pduObjs = [sms.pduObjs];
}
// Build async tasks to run the responses in parallel
i = 0;
while (sms.pduObjs[i] !== undefined) {
tasks.push(function(callback) {
sms.session.sendReturn(
sms.pduObj,
status,
{'message_id': sms.smsId},
false,
callback
);
});
i ++;
}
sms.session.sendReturn(
sms.pduObj,
status,
{'message_id': sms.smsId},
false,
callback
);
}
/**
* Send a dlr to an sms
* This must be called from an sms object context
*
* @param str status - see list at defs.consts.MESSAGE_STATE - defaults to 'DELIVERED'
* @param func callback(err, retPdu)
*/
function smsDlr(status, callback) {
var shortMessage,
dlrPduObj,
err,
sms = this;
if (typeof status === 'function') {
callback = status;
status = undefined;
}
if (status === undefined || status === true || status === 2 || status === 'true') {
status = 2;
} else if (defs.consts.MESSAGE_STATE[status] !== undefined) {
status = defs.consts.MESSAGE_STATE[status];
} else if (defs.constsById.MESSAGE_STATE[status]) {
status = parseInt(status);
} else {
status = 5; // UNDELIVERABLE
}
if (typeof callback !== 'function') {
callback = function() {};
}
if (sms.smsId === undefined) {
err = new Error('Trying to send DLR with no smsId.');
log.warn('larvitsmpp: lib/session.js: smsDlr() - ' + err.message);
callback(err);
return;
}
shortMessage = 'id:' + sms.smsId + ' sub:001 ';
if (status === 2) {
shortMessage += 'dlvrd:1 ';
} else {
shortMessage += 'dlvrd:0 ';
}
shortMessage += 'submit date:' + utils.smppDate(sms.submitTime);
shortMessage += ' done date:' + utils.smppDate(new Date());
if (status === 2) {
shortMessage += ' stat:DELIVRD err:0 text:xxx';
} else {
shortMessage += ' stat:UNDELIVERABLE err:1 text:xxx';
}
dlrPduObj = {
'cmdName': 'deliver_sm',
'params': {
'source_addr': sms.from,
'destination_addr': sms.to,
'esm_class': 4,
'short_message': shortMessage
},
'tlvs': {
'receipted_message_id': {
'tagId': 0x001E,
'tagName': 'receipted_message_id',
'tagValue': sms.smsId
},
'message_state': {
'tagId': 0x0427,
'tagName': 'message_state',
'tagValue': status
}
}
};
sms.session.send(dlrPduObj, false, callback);
}
/**
* Generic session function
@@ -136,14 +295,14 @@ function session(sock) {
log.debug('larvitsmpp: lib/session.js: session() - returnObj.send() - returnObj.on(incomingPduObj) - cmdName: ' + incPduObj.cmdName + ' seqNr: ' + incPduObj.seqNr + ' cmdStatus: ' + incPduObj.cmdStatus);
// Make sure this is the actual response to the sent PDU
if (incPduObj.isResponse() && incPduObj.seqNr === pduObj.seqNr) {
if (incPduObj.isResp() && incPduObj.seqNr === pduObj.seqNr) {
callback(null, incPduObj);
if (closeAfterSend) {
returnObj.closeSocket();
}
} else {
err = new Error('larvitsmpp: lib/session.js: session() - returnObj.send() - returnObj.on(incomingPduObj) - Event triggered but incoming PDU is not a response or seqNr does not match. isResponse: ' + incPduObj.isResponse().toString() + ' incSeqNr: ' + incPduObj.seqNr + ' expected seqNr: ' + pduObj.seqNr);
err = new Error('larvitsmpp: lib/session.js: session() - returnObj.send() - returnObj.on(incomingPduObj) - Event triggered but incoming PDU is not a response or seqNr does not match. isResp: ' + incPduObj.isResp().toString() + ' incSeqNr: ' + incPduObj.seqNr + ' expected seqNr: ' + pduObj.seqNr);
log.warn(err.message);
callback(err);
}
@@ -189,6 +348,8 @@ function session(sock) {
return;
}
log.silly('larvitsmpp: lib/session.js: session() - returnObj.sendReturn() - Sending return PDU: ' + retPdu.toString('hex'));
returnObj.sockWrite(retPdu, closeAfterSend);
if (typeof callback === 'function') {
@@ -208,7 +369,8 @@ function session(sock) {
* @param func callback(err, smsId, retPduObj)
*/
returnObj.sendLongSms = function(smsOptions, callback) {
var msgs = utils.splitMsg(smsOptions.message),
var smsIds = [],
msgs = utils.splitMsg(smsOptions.message),
encoding = defs.encodings.detect(smsOptions.message); // Set encoding once for all message parts
function sendPart(i) {
@@ -233,8 +395,10 @@ function session(sock) {
log.debug('larvitsmpp: lib/session.js: returnObj.sendLongSms() - pduObj: ' + JSON.stringify(pduObj));
returnObj.send(pduObj, function(err, retPduObj) {
smsIds.push(retPduObj.params.message_id);
if (typeof callback === 'function' && msgs[i + 1] === undefined) {
callback(err, retPduObj.params.message_id, retPduObj);
callback(err, smsIds, retPduObj);
} else if (msgs[i + 1] !== undefined) {
sendPart(i + 1);
}
@@ -281,97 +445,106 @@ function session(sock) {
returnObj.send(pduObj, function(err, retPduObj) {
if (typeof callback === 'function') {
callback(err, retPduObj.params.message_id, retPduObj);
callback(err, [retPduObj.params.message_id], retPduObj);
}
});
};
/**
* Send a DLR
*
* @param obj sms - sms object
* @param bol status - defaults to true
* @param func callback(err)
*/
returnObj.sendDlr = function(sms, status, callback) {
var shortMessage = 'id:' + sms.smsId + ' sub:001 ',
dlrPduObj;
// Temporary storage for long sms parts
// These should be cleared if they lingre to long to avoid memory leaks
returnObj.longSmses = {};
if (typeof status === 'function') {
callback = status;
status = true;
}
// Store long smses in the temporary storage
returnObj.longSms = function(pduObj) {
var smsGroupId = pduObj.params.short_message[3],
smsParts = pduObj.params.short_message[4],
longSmsId = pduObj.params.source_addr + '_' + pduObj.params.destination_addr + '_' + smsGroupId;
if (status === undefined) {
status = true;
}
if (status) {
shortMessage += 'dlvrd:1 ';
if (returnObj.longSmses[longSmsId] === undefined) {
returnObj.longSmses[longSmsId] = {
'created': new Date(),
'partsCount': parseInt(smsParts),
'pduObjs': [{
'partNr': pduObj.params.short_message[5], // We save this here to easier sort the array later on
'pduObj': pduObj
}]
};
} else {
shortMessage += 'dlvrd:0 ';
}
shortMessage += 'submit date:' + utils.smppDate(sms.submitTime);
shortMessage += ' done date:' + utils.smppDate(new Date());
if (status) {
shortMessage += ' stat:DELIVRD err:0 text:xxx';
} else {
shortMessage += ' stat:UNDELIVERABLE err:1 text:xxx';
}
dlrPduObj = {
'cmdName': 'deliver_sm',
'params': {
'source_addr': sms.from,
'destination_addr': sms.to,
'esm_class': 4,
'short_message': shortMessage
},
'tlvs': {
'receipted_message_id': {
'tagId': 0x001E,
'tagName': 'receipted_message_id',
'tagValue': sms.smsId
},
'message_state': {
'tagId': 0x0427,
'tagName': 'message_state',
'tagValue': 2
}
returnObj.longSmses[longSmsId].pduObjs.push(pduObj);
}
};
if ( ! status) {
dlrPduObj.tlvs.message_state.tagValue = 5;
// Walk through the long sms storage to investigate if we can send complete messages along
// or should remove old ones
returnObj.checkLongSmses = function() {
var smsGroupId,
smsGroup,
smsObj,
i,
curPduObj;
// Sort function to sort group parts
function sortLongSmsPdus(a, b) {
if (a.partNr < b.partNr) {
return - 1;
}
dlrPduObj.seqNr = 323;
utils.objToPdu(dlrPduObj, function(err, pdu) {
if (err) {
throw err;
if (a.partNr > b.partNr) {
return 1;
}
utils.pduToObj(pdu, function(err, newObj) {
if (err) {
throw err;
return 0;
}
console.log('newObj:');
console.log(newObj);
});
});
returnObj.send(dlrPduObj, function(err, retPduObj) {
console.log('WHATTA WHATTA');
console.log(retPduObj);
if (typeof callback === 'function') {
callback(err, retPduObj.params.message_id, retPduObj);
// Callback function for emitted sms event
function smsReceived(smsData) {
delete returnObj.longSmses[smsGroupId];
}
for (smsGroupId in returnObj.longSmses) {
smsGroup = returnObj.longSmses[smsGroupId];
// All parts are accounted for! Emit sms event and clear from tmp storage
if (smsGroup.partsCount === smsGroup.pduObjs.length) {
log.debug('larvitsmpp: lib/session.js: session() - returnObj.checkLongSmses() - All parts accounted for in smsGroupId "' + smsGroupId + '", emitting sms event.');
smsObj = {
// These are needed for references here and there in functions
'session': returnObj,
'pduObjs': smsGroup.pduObjs,
'from': smsGroup.pduObjs[0].params.source_addr,
'to': smsGroup.pduObjs[0].params.destination_addr,
'submitTime': new Date(),
'message': '',
'dlr': Boolean(smsGroup.pduObjs[0].params.registered_delivery),
'sendResp': smsResp,
'sendDlr': smsDlr
};
// Concatenate all the parts messages to one and set references to the session
// First we need to sort the parts, since they can come in random order
smsObj.pduObjs.sort(sortLongSmsPdus);
i = 0;
while (smsObj.pduObjs[i] !== undefined) {
curPduObj = smsObj.pduObjs[i];
curPduObj.session = returnObj;
smsObj.message += utils.decode(curPduObj.params.short_message, curPduObj.params.data_coding, curPduObj.params.short_message[0]);
i ++;
}
returnObj.emit('sms', smsObj);
} else if (moment(new Date()).diff(smsGroup.created, 'hours') > 24) {
log.info('larvitsmpp: lib/session.js: session() - returnObj.checkLongSmses() - smsGroupId "' + smsGroupId + '" is removed from returnObj.longSmses due to being older than 24 hours.');
delete returnObj.longSmses[smsGroupId];
}
}
});
};
// Handle incomming commands.
@@ -384,7 +557,7 @@ function session(sock) {
// TLV message_state must exists
if (pduObj.tlvs.message_state === undefined) {
log.info('larvitsmpp: lib/session.js: session() - returnObj.deliverSm() - TLV message_state is missing. SeqNr: ' + pduObj.seqNr);
log.info('larvitsmpp: lib/session.js: session() - returnObj.handleCmd.deliver_sm() - TLV message_state is missing. SeqNr: ' + pduObj.seqNr);
returnObj.sendReturn(pduObj, 'ESME_RINVTLVSTREAM');
return;
@@ -392,7 +565,7 @@ function session(sock) {
// TLV message_state needs to be valid
if (defs.constsById.MESSAGE_STATE[pduObj.tlvs.message_state.tagValue] === undefined) {
log.info('larvitsmpp: lib/session.js: session() - returnObj.deliverSm() - Invalid TLV message_state: "' + pduObj.tlvs.message_state.tagValue + '". SeqNr: ' + pduObj.seqNr);
log.info('larvitsmpp: lib/session.js: session() - returnObj.handleCmd.deliver_sm() - Invalid TLV message_state: "' + pduObj.tlvs.message_state.tagValue + '". SeqNr: ' + pduObj.seqNr);
returnObj.sendReturn(pduObj, 'ESME_RINVTLVSTREAM');
return;
@@ -400,7 +573,7 @@ function session(sock) {
// TLV receipted_message_id must exist
if (pduObj.tlvs.receipted_message_id === undefined) {
log.info('larvitsmpp: lib/session.js: session() - returnObj.deliverSm() - TLV receipted_message_id is missing. SeqNr: ' + pduObj.seqNr);
log.info('larvitsmpp: lib/session.js: session() - returnObj.handleCmd.deliver_sm() - TLV receipted_message_id is missing. SeqNr: ' + pduObj.seqNr);
returnObj.sendReturn(pduObj, 'ESME_RINVTLVSTREAM');
return;
@@ -427,25 +600,28 @@ function session(sock) {
returnObj.handleCmd.submit_sm = function(pduObj) {
var smsObj = {};
// If esm_class is 0x40 it means this is just a part of a larger message
if (pduObj.params.esm_class === 0x40) {
returnObj.longSms(pduObj);
return; // Long messages should not get handled here at all, so cancel execution here
}
smsObj = {
// These are needed for references here and there in functions
'session': returnObj,
'pduObj': pduObj,
'from': pduObj.params.source_addr,
'to': pduObj.params.destination_addr,
'submitTime': new Date(),
'message': pduObj.params.short_message,
'dlr': Boolean(pduObj.params.registered_delivery)
'dlr': Boolean(pduObj.params.registered_delivery),
'sendResp': smsResp,
'sendDlr': smsDlr
};
function smsReceived(smsData) {
if (smsData === undefined) {
smsData = {};
}
smsObj.smsId = smsData.smsId;
returnObj.sendReturn(pduObj, 'ESME_ROK', {'message_id': smsObj.smsId});
}
returnObj.emit('sms', smsObj, smsReceived);
returnObj.emit('sms', smsObj);
};
// Handle incoming unbind
@@ -534,7 +710,7 @@ function session(sock) {
} else {
log.verbose('larvitsmpp: lib/session.js: session() - returnObj.on(incomingPdu) - Incoming PDU parsed. Seqnr: ' + pduObj.seqNr + ' cmd: ' + pduObj.cmdName + ' cmdStatus: ' + pduObj.cmdStatus + ' hex: ' + pdu.toString('hex'));
if (pduObj.isResponse()) {
if (pduObj.isResp()) {
// We do this so we can remove the dynamic event listeners to not have a memory leak
returnObj.emit('incomingPduObj' + pduObj.seqNr, pduObj);
+8 -3
View File
@@ -159,11 +159,16 @@ function writeBuffer(obj, cmdLength, callback) {
*
* @param buf buffer
* @param str encoding 'ASCII', 'LATIN1' or 'UCS2' or hex values
* @param int offset - defaults to 0
* @return str in utf8 format
*/
function decodeMsg(buffer, encoding) {
function decodeMsg(buffer, encoding, offset) {
var checkEnc;
if (offset === undefined) {
offset = 0;
}
for (checkEnc in defs.consts.ENCODING) {
if (parseInt(encoding) === defs.consts.ENCODING[checkEnc] || encoding === checkEnc) {
encoding = checkEnc;
@@ -175,7 +180,7 @@ function decodeMsg(buffer, encoding) {
encoding = 'ASCII';
}
return defs.encodings[encoding].decode(buffer);
return defs.encodings[encoding].decode(buffer.slice(offset, buffer.length));
}
function encodeMsg(str) {
@@ -208,7 +213,7 @@ function pduToObj(pdu, stupidNullByte, callback) {
}
// Returns true if this PDU is a response to another PDU
retObj.isResponse = function() {
retObj.isResp = function() {
return ! ! (this.cmdId & 0x80000000);
};
+7 -3
View File
@@ -9,7 +9,9 @@
"dependencies": {
"winston": "latest",
"utils-merge": "latest",
"iconv-lite": "latest"
"iconv-lite": "latest",
"moment": "latest",
"async": "latest"
},
"description": "Simplified SMPP implementation",
"devDependencies": {
@@ -27,12 +29,14 @@
"url": "https://github.com/larvit/larvitsmpp",
"type": "git"
},
"version": "0.0.2beta",
"version": "0.0.3",
"readmeFilename": "README.md",
"readme": "larvitsmpp",
"bugs": {
"url": "https://github.com/larvit/larvitsmpp/issues"
},
"homepage": "https://github.com/larvit/larvitsmpp",
"scripts": {}
"scripts": {
"test": "mocha"
}
}