Added support for DLRs
This commit is contained in:
@@ -2,12 +2,15 @@
|
||||
|
||||
// More or less copied from https://github.com/farhadi/node-smpp
|
||||
|
||||
var iconv = require('iconv-lite'),
|
||||
encodings = {},
|
||||
filters = {},
|
||||
tlvsById = {},
|
||||
var iconv = require('iconv-lite'),
|
||||
encodings = {},
|
||||
filters = {},
|
||||
tlvsById = {},
|
||||
constsById = {},
|
||||
types,
|
||||
consts,
|
||||
constGrp,
|
||||
constName,
|
||||
tlvs,
|
||||
tag,
|
||||
cmds,
|
||||
@@ -111,6 +114,16 @@ consts = {
|
||||
}
|
||||
};
|
||||
|
||||
for (constGrp in consts) {
|
||||
if (constsById[constGrp] === undefined) {
|
||||
constsById[constGrp] = {};
|
||||
}
|
||||
|
||||
for (constName in consts[constGrp]) {
|
||||
constsById[constGrp][consts[constGrp][constName]] = constName;
|
||||
}
|
||||
}
|
||||
|
||||
types = {
|
||||
int8: {
|
||||
read: function(buffer, offset) {
|
||||
@@ -1423,6 +1436,7 @@ for (error in errors) {
|
||||
exports.encodings = encodings;
|
||||
exports.filters = filters;
|
||||
exports.consts = consts;
|
||||
exports.constsById = constsById;
|
||||
exports.cmds = cmds;
|
||||
exports.cmdsById = cmdsById;
|
||||
exports.types = types;
|
||||
|
||||
+113
-67
@@ -135,17 +135,25 @@ function pduToObj(pdu, callback) {
|
||||
if (defs.tlvsById[tlvCmdId] === undefined) {
|
||||
tlvValue = pdu.slice(offset + 4, offset + 4 + tlvLength).toString('hex');
|
||||
|
||||
retObj.tlvs['0x' + tlvCmdId.toString(16)] = tlvValue;
|
||||
retObj.tlvs[tlvCmdId] = {
|
||||
'tagId': tlvCmdId,
|
||||
'tagName': undefined,
|
||||
'tagValue': tlvValue
|
||||
};
|
||||
|
||||
log.info('larvitsmpp: pduToObj() - Unknown TLV found. Hex ID: ' + tlvCmdId.toString(16) + ' length: ' + tlvLength + ' hex value: ' + tlvValue);
|
||||
log.verbose('larvitsmpp: pduToObj() - Unknown TLV found. Hex ID: ' + tlvCmdId.toString(16) + ' length: ' + tlvLength + ' hex value: ' + tlvValue);
|
||||
} else {
|
||||
tlvValue = defs.tlvsById[tlvCmdId].type.read(pdu, offset + 4, tlvLength);
|
||||
tlvValue = defs.tlvsById[tlvCmdId].type.read(pdu, offset + 4, tlvLength);
|
||||
|
||||
if (Buffer.isBuffer(tlvValue)) {
|
||||
tlvValue = tlvValue.toString('hex');
|
||||
}
|
||||
|
||||
retObj.tlvs[defs.tlvsById[tlvCmdId].tag] = tlvValue;
|
||||
retObj.tlvs[defs.tlvsById[tlvCmdId].tag] = {
|
||||
'tagId': tlvCmdId,
|
||||
'tagName': defs.tlvsById[tlvCmdId].tag,
|
||||
'tagValue': tlvValue
|
||||
};
|
||||
}
|
||||
|
||||
offset = offset + 4 + tlvLength;
|
||||
@@ -360,30 +368,30 @@ function pduReturn(pdu, status, callback) {
|
||||
* Generic session function
|
||||
*
|
||||
* @param obj sock - socket object
|
||||
* @return obj (sessionEmitter)
|
||||
* @return obj (returnObj)
|
||||
*/
|
||||
function session(sock) {
|
||||
var sessionEmitter = new events.EventEmitter();
|
||||
var returnObj = new events.EventEmitter();
|
||||
|
||||
log.silly('larvitsmpp: session() - New session started from ' + sock.remoteAddress + ':' + sock.remotePort);
|
||||
|
||||
sessionEmitter.loggedIn = false;
|
||||
returnObj.loggedIn = false;
|
||||
|
||||
// Sequence number used for commands initiated from us
|
||||
sessionEmitter.ourSeqNr = 1;
|
||||
returnObj.ourSeqNr = 1;
|
||||
|
||||
// Make the socket transparent via the returned emitter
|
||||
sessionEmitter.sock = sock;
|
||||
returnObj.sock = sock;
|
||||
|
||||
/**
|
||||
* Increase our sequence number
|
||||
*/
|
||||
sessionEmitter.incOurSeqNr = function() {
|
||||
sessionEmitter.ourSeqNr = sessionEmitter.ourSeqNr + 1;
|
||||
returnObj.incOurSeqNr = function() {
|
||||
returnObj.ourSeqNr = returnObj.ourSeqNr + 1;
|
||||
|
||||
// If we pass the maximum, start over at 1
|
||||
if (sessionEmitter.ourSeqNr > 2147483646) {
|
||||
sessionEmitter.ourSeqNr = 1;
|
||||
if (returnObj.ourSeqNr > 2147483646) {
|
||||
returnObj.ourSeqNr = 1;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -391,10 +399,10 @@ function session(sock) {
|
||||
* Close the socket
|
||||
* Always use this function to close the socket so we get it on log
|
||||
*/
|
||||
sessionEmitter.closeSocket = function() {
|
||||
returnObj.closeSocket = function() {
|
||||
log.verbose('larvitsmpp: session() - closeSocket() - Closing socket for ' + sock.remoteAddress + ':' + sock.remotePort);
|
||||
if (sessionEmitter.enqLinkTimer) {
|
||||
clearTimeout(sessionEmitter.enqLinkTimer);
|
||||
if (returnObj.enqLinkTimer) {
|
||||
clearTimeout(returnObj.enqLinkTimer);
|
||||
}
|
||||
sock.destroy();
|
||||
};
|
||||
@@ -405,16 +413,16 @@ function session(sock) {
|
||||
* @param buf or obj pdu - can also take PDU object
|
||||
* @param bol closeAfterSend - if true will close the socket after sending
|
||||
*/
|
||||
sessionEmitter.sockWrite = function(pdu, closeAfterSend) {
|
||||
returnObj.sockWrite = function(pdu, closeAfterSend) {
|
||||
if ( ! Buffer.isBuffer(pdu)) {
|
||||
objToPdu(pdu, function(err, buffer) {
|
||||
if (err) {
|
||||
log.warn('larvitsmpp: session() - sockWrite() - Could not convert PDU to buffer');
|
||||
sessionEmitter.closeSocket();
|
||||
returnObj.closeSocket();
|
||||
return;
|
||||
}
|
||||
|
||||
sessionEmitter.sockWrite(buffer);
|
||||
returnObj.sockWrite(buffer);
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -423,7 +431,7 @@ function session(sock) {
|
||||
sock.write(pdu);
|
||||
|
||||
if (closeAfterSend) {
|
||||
sessionEmitter.closeSocket();
|
||||
returnObj.closeSocket();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -434,13 +442,13 @@ function session(sock) {
|
||||
* @param bol closeAfterSend - Will close after return is fetched. Defaults to false (OPTIONAL)
|
||||
* @param func callback(err, retPdu) (OPTIONAL)
|
||||
*/
|
||||
sessionEmitter.send = function(pdu, closeAfterSend, callback) {
|
||||
returnObj.send = function(pdu, closeAfterSend, callback) {
|
||||
var pduObj = pdu,
|
||||
err = null;
|
||||
|
||||
// Make sure the sequence number is set and is correct
|
||||
if ( ! Buffer.isBuffer(pdu)) {
|
||||
pdu.seqNr = sessionEmitter.ourSeqNr;
|
||||
pdu.seqNr = returnObj.ourSeqNr;
|
||||
}
|
||||
|
||||
// Make sure the pdu is an object
|
||||
@@ -451,12 +459,12 @@ function session(sock) {
|
||||
return;
|
||||
}
|
||||
|
||||
sessionEmitter.send(pduObj, closeAfterSend, callback);
|
||||
returnObj.send(pduObj, closeAfterSend, callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug('larvitsmpp: session() - sessionEmitter.send() - Sending PDU to remote. cmdName: ' + pdu.cmdName + ' seqNr: ' + pdu.seqNr);
|
||||
log.debug('larvitsmpp: session() - returnObj.send() - Sending PDU to remote. cmdName: ' + pdu.cmdName + ' seqNr: ' + pdu.seqNr);
|
||||
|
||||
// If closeAndSend is omitted, put callback in its place
|
||||
if (typeof closeAfterSend === 'function') {
|
||||
@@ -471,38 +479,38 @@ function session(sock) {
|
||||
|
||||
// Response PDUs are not allowed with the send() command, they should use the sendReturn()
|
||||
if (pduObj.cmdName.substring(pduObj.cmdName - 5) === '_resp') {
|
||||
err = new Error('larvitsmpp: session() - sessionEmitter.send() - Given pduObj is a response, use sendReturn() instead. cmdName: ' + pduObj.cmdName);
|
||||
err = new Error('larvitsmpp: session() - returnObj.send() - Given pduObj is a response, use sendReturn() instead. cmdName: ' + pduObj.cmdName);
|
||||
callback(err);
|
||||
return;
|
||||
}
|
||||
|
||||
// When the return is fetched, call the callback
|
||||
sessionEmitter.on('incomingPdu' + pduObj.seqNr, function(incPduObj) {
|
||||
returnObj.on('incomingPdu' + pduObj.seqNr, function(incPduObj) {
|
||||
|
||||
log.debug('larvitsmpp: session() - sessionEmitter.send() - sessionEmitter.on(incomingPdu) - cmdName: ' + incPduObj.cmdName + ' seqNr: ' + incPduObj.seqNr + ' cmdStatus: ' + incPduObj.cmdStatus);
|
||||
log.debug('larvitsmpp: session() - returnObj.send() - returnObj.on(incomingPdu) - cmdName: ' + incPduObj.cmdName + ' seqNr: ' + incPduObj.seqNr + ' cmdStatus: ' + incPduObj.cmdStatus);
|
||||
|
||||
// Clean up by removing this listener or else it will lurk along forever
|
||||
sessionEmitter.removeAllListeners('incomingPdu' + pduObj.seqNr);
|
||||
returnObj.removeAllListeners('incomingPdu' + pduObj.seqNr);
|
||||
|
||||
// Make sure this is the actual response to the sent PDU
|
||||
if (incPduObj.isResponse() && incPduObj.seqNr === pduObj.seqNr) {
|
||||
callback(null, incPduObj);
|
||||
|
||||
if (closeAfterSend) {
|
||||
sessionEmitter.closeSocket();
|
||||
returnObj.closeSocket();
|
||||
}
|
||||
} else {
|
||||
err = new Error('larvitsmpp: session() - sessionEmitter.send() - sessionEmitter.on(incomingPdu) - 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: session() - returnObj.send() - returnObj.on(incomingPdu) - 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);
|
||||
log.warn(err.message);
|
||||
callback(err);
|
||||
}
|
||||
});
|
||||
|
||||
// Increase our internal sequence number
|
||||
sessionEmitter.incOurSeqNr();
|
||||
returnObj.incOurSeqNr();
|
||||
|
||||
// Write the PDU to socket
|
||||
sessionEmitter.sockWrite(pduObj);
|
||||
returnObj.sockWrite(pduObj);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -513,7 +521,7 @@ function session(sock) {
|
||||
* @param bol closeAfterSend - if true will close the socket after sending (OPTIONAL)
|
||||
* @param func callback(err)
|
||||
*/
|
||||
sessionEmitter.sendReturn = function(pdu, status, closeAfterSend, callback) {
|
||||
returnObj.sendReturn = function(pdu, status, closeAfterSend, callback) {
|
||||
if (typeof closeAfterSend === 'function') {
|
||||
callback = closeAfterSend;
|
||||
closeAfterSend = undefined;
|
||||
@@ -521,8 +529,8 @@ function session(sock) {
|
||||
|
||||
pduReturn(pdu, status, function(err, retPdu) {
|
||||
if (err) {
|
||||
log.error('larvitsmpp: session() - sessionEmitter.sendReturn() - Could not create return PDU: ' + err.message);
|
||||
sessionEmitter.closeSocket();
|
||||
log.error('larvitsmpp: session() - returnObj.sendReturn() - Could not create return PDU: ' + err.message);
|
||||
returnObj.closeSocket();
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(err);
|
||||
@@ -531,7 +539,7 @@ function session(sock) {
|
||||
return;
|
||||
}
|
||||
|
||||
sessionEmitter.sockWrite(retPdu, closeAfterSend);
|
||||
returnObj.sockWrite(retPdu, closeAfterSend);
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
@@ -547,9 +555,9 @@ function session(sock) {
|
||||
* to - international format
|
||||
* message - string
|
||||
* dlr - boolean defaults to false
|
||||
* @param func callback(err, retPduObj)
|
||||
* @param func callback(err, smsId, retPduObj)
|
||||
*/
|
||||
sessionEmitter.sendSms = function(smsOptions, callback) {
|
||||
returnObj.sendSms = function(smsOptions, callback) {
|
||||
var pduObj = {};
|
||||
|
||||
pduObj.cmdName = 'submit_sm';
|
||||
@@ -564,54 +572,92 @@ function session(sock) {
|
||||
pduObj.params.registered_delivery = 0x01;
|
||||
}
|
||||
|
||||
sessionEmitter.send(pduObj, callback);
|
||||
returnObj.send(pduObj, function(err, retPduObj) {
|
||||
if (typeof callback === 'function') {
|
||||
callback(err, retPduObj.params.message_id, retPduObj);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
sessionEmitter.deliverSm = function(pduObj) {
|
||||
console.log(pduObj);
|
||||
sessionEmitter.sendReturn(pduObj);
|
||||
// Handle incoming deliver_sm
|
||||
returnObj.deliverSm = function(pduObj) {
|
||||
var dlrObj;
|
||||
|
||||
// TLV message_state must exists
|
||||
if (pduObj.tlvs.message_state === undefined) {
|
||||
log.info('larvitsmpp: session() - returnObj.deliverSm() - TLV message_state is missing. SeqNr: ' + pduObj.seqNr);
|
||||
returnObj.sendReturn(pduObj, 'ESME_RINVTLVSTREAM');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TLV message_state needs to be valid
|
||||
if (defs.constsById.MESSAGE_STATE[pduObj.tlvs.message_state.tagValue] === undefined) {
|
||||
log.info('larvitsmpp: session() - returnObj.deliverSm() - Invalid TLV message_state: "' + pduObj.tlvs.message_state.tagValue + '". SeqNr: ' + pduObj.seqNr);
|
||||
returnObj.sendReturn(pduObj, 'ESME_RINVTLVSTREAM');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// TLV receipted_message_id must exist
|
||||
if (pduObj.tlvs.receipted_message_id === undefined) {
|
||||
log.info('larvitsmpp: session() - returnObj.deliverSm() - TLV receipted_message_id is missing. SeqNr: ' + pduObj.seqNr);
|
||||
returnObj.sendReturn(pduObj, 'ESME_RINVTLVSTREAM');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dlrObj = {
|
||||
'statusMsg': defs.constsById.MESSAGE_STATE[pduObj.tlvs.message_state.tagValue],
|
||||
'statusId': pduObj.tlvs.message_state.tagValue,
|
||||
'smsId': pduObj.tlvs.receipted_message_id.tagValue
|
||||
};
|
||||
|
||||
returnObj.emit('dlr', dlrObj, pduObj);
|
||||
returnObj.sendReturn(pduObj);
|
||||
};
|
||||
|
||||
sessionEmitter.submitSm = function(pduObj) {
|
||||
sessionEmitter.emit('sms', {
|
||||
returnObj.submitSm = function(pduObj) {
|
||||
returnObj.emit('sms', {
|
||||
'from': pduObj.params.source_addr,
|
||||
'to': pduObj.params.destination_addr,
|
||||
'message': pduObj.params.short_message,
|
||||
'dlrRequested': Boolean(pduObj.params.registered_delivery)
|
||||
});
|
||||
sessionEmitter.sendReturn(pduObj);
|
||||
'dlrRequested': Boolean(pduObj.params.registered_delivery),
|
||||
'smsId': pduObj.params.message_id
|
||||
}, pduObj);
|
||||
returnObj.sendReturn(pduObj);
|
||||
};
|
||||
|
||||
// Dummy, should be extended by serverSession or clientSession
|
||||
sessionEmitter.login = function() {
|
||||
returnObj.login = function() {
|
||||
log.info('larvitsmpp: session() - login() - Dummy login function ran, this might be a mistake');
|
||||
sessionEmitter.loggedIn = true;
|
||||
returnObj.loggedIn = true;
|
||||
};
|
||||
|
||||
// Dummy method - should be used by serverSession or clientSession
|
||||
sessionEmitter.resetEnqLinkTimer = function() {
|
||||
returnObj.resetEnqLinkTimer = function() {
|
||||
log.silly('larvitsmpp: session() - resetEnqLinkTimer() - Resetting the kill timer');
|
||||
};
|
||||
|
||||
sessionEmitter.enquireLink = function(pduObj) {
|
||||
returnObj.enquireLink = function(pduObj) {
|
||||
log.silly('larvitsmpp: session() - enquireLink() - Enquiring link');
|
||||
sessionEmitter.resetEnqLinkTimer();
|
||||
sessionEmitter.sendReturn(pduObj);
|
||||
returnObj.resetEnqLinkTimer();
|
||||
returnObj.sendReturn(pduObj);
|
||||
};
|
||||
|
||||
sessionEmitter.unbind = function() {
|
||||
sessionEmitter.send({
|
||||
returnObj.unbind = function() {
|
||||
returnObj.send({
|
||||
'cmdName': 'unbind'
|
||||
}, true);
|
||||
};
|
||||
|
||||
// Add a 'data' event handler to this instance of socket
|
||||
sock.on('data', function(pduBuf) {
|
||||
// Pass the data along to the sessionEmitter
|
||||
sessionEmitter.emit('data', pduBuf);
|
||||
// Pass the data along to the returnObj
|
||||
returnObj.emit('data', pduBuf);
|
||||
|
||||
// Reset the enquire link timer
|
||||
sessionEmitter.resetEnqLinkTimer();
|
||||
returnObj.resetEnqLinkTimer();
|
||||
|
||||
log.silly('larvitsmpp: session() - sock.on(data) - Incoming PDU: ' + pduBuf.toString('hex'));
|
||||
|
||||
@@ -619,15 +665,15 @@ function session(sock) {
|
||||
if (err) {
|
||||
log.warn('larvitsmpp: session() - Invalid PDU. ' + err.message);
|
||||
|
||||
sessionEmitter.closeSocket();
|
||||
returnObj.closeSocket();
|
||||
} else {
|
||||
log.verbose('larvitsmpp: session() - sock.on(data) - Incoming PDU. Seqnr: ' + pduObj.seqNr + ' cmd: ' + pduObj.cmdName + ' cmdStatus: ' + pduObj.cmdStatus + ' hex: ' + pduBuf.toString('hex'));
|
||||
|
||||
if (pduObj.isResponse()) {
|
||||
// We do this so we can remove the dynamic event listeners to not have a memory leak
|
||||
sessionEmitter.emit('incomingPdu' + pduObj.seqNr, pduObj);
|
||||
returnObj.emit('incomingPdu' + pduObj.seqNr, pduObj);
|
||||
} else {
|
||||
sessionEmitter.emit('incomingPdu', pduObj);
|
||||
returnObj.emit('incomingPdu', pduObj);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -635,11 +681,11 @@ function session(sock) {
|
||||
|
||||
// Add a 'close' event handler to this instance of socket
|
||||
sock.on('close', function() {
|
||||
sessionEmitter.emit('close');
|
||||
returnObj.emit('close');
|
||||
log.debug('larvitsmpp: session() - socket closed');
|
||||
});
|
||||
|
||||
return sessionEmitter;
|
||||
return returnObj;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -647,7 +693,7 @@ function session(sock) {
|
||||
*
|
||||
* @param obj sock - socket object
|
||||
* @param obj options - as derived from server()
|
||||
* @return obj (sessionEmitter)
|
||||
* @return obj (returnObj)
|
||||
*/
|
||||
function serverSession(sock, options) {
|
||||
var parent = session(sock);
|
||||
@@ -754,7 +800,7 @@ function serverSession(sock, options) {
|
||||
*
|
||||
* @param obj sock - socket object
|
||||
* @param obj options - as derived from client()
|
||||
* @return obj (sessionEmitter)
|
||||
* @return obj (returnObj)
|
||||
*/
|
||||
function clientSession(sock, options) {
|
||||
var parent = session(sock);
|
||||
@@ -828,12 +874,12 @@ function server(options, callback) {
|
||||
// The function passed to net.createServer() becomes the event handler for the 'connection' event
|
||||
// The sock object the callback function receives UNIQUE for each connection
|
||||
net.createServer(function(sock) {
|
||||
var sessionEmitter = serverSession(sock, options);
|
||||
var returnObj = serverSession(sock, options);
|
||||
|
||||
// We have a connection - a socket object is assigned to the connection automatically
|
||||
log.verbose('larvitsmpp: server() - Incomming connection! From: ' + sock.remoteAddress + ':' + sock.remotePort);
|
||||
|
||||
callback(null, sessionEmitter);
|
||||
callback(null, returnObj);
|
||||
}).listen(options.port, options.host);
|
||||
|
||||
log.info('larvitsmpp: server() - Up and running at ' + options.host + ':' + options.port);
|
||||
|
||||
+5
-5
@@ -150,11 +150,11 @@ describe('PDU convertion', function() {
|
||||
larvitsmpp.pduToObj(pdu, function(err, obj) {
|
||||
assert( ! err, 'Error should be negative');
|
||||
|
||||
assert(obj.cmdId.toString(16) === '5', 'Command ID should be 0x00000005 (5)');
|
||||
assert(obj.cmdStatus === 'ESME_ROK', 'Command status should be "ESME_ROK"');
|
||||
assert(obj.seqNr === 44576390, 'Sequence number should be 44576390');
|
||||
assert(obj.params.destination_addr === '46701113311', 'Param destination_addr should be "46701113311"');
|
||||
assert(obj.tlvs.receipted_message_id === '155040621242143585', 'TLV receipted_message_id should be "155040621242143585"');
|
||||
assert(obj.cmdId.toString(16) === '5', 'Command ID should be 0x00000005 (5)');
|
||||
assert(obj.cmdStatus === 'ESME_ROK', 'Command status should be "ESME_ROK"');
|
||||
assert(obj.seqNr === 44576390, 'Sequence number should be 44576390');
|
||||
assert(obj.params.destination_addr === '46701113311', 'Param destination_addr should be "46701113311"');
|
||||
assert(obj.tlvs.receipted_message_id.tagValue === '155040621242143585', 'TLV receipted_message_id should be "155040621242143585", but is "' + obj.tlvs.receipted_message_id.tagValue + '"');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user