tpdu
Version:
SMS T-PDU Parser and Builder
428 lines (367 loc) • 9.73 kB
JavaScript
var constants = require('./constants');
var builderTable = new WeakMap();
var Builder = function(tpdu) {
builderTable.set(this, tpdu || {});
};
/**
* Service Center Address
*
* @param addressType
* @param numberingPlan
* @param address
* @returns {Builder}
*/
Builder.prototype.sca = function(addressType, numberingPlan, address) {
var tpdu = builderTable.get(this);
tpdu.sca = {
addressType: addressType,
numberingPlan: numberingPlan,
address: address
};
return this;
};
/**
* Direction
*
* @param direction
* @returns {Builder}
*/
Builder.prototype.direction = function(direction) {
var tpdu = builderTable.get(this);
tpdu.direction = direction;
return this;
};
/**
* Message Type Indicator
*
* @param tpmti
* @returns {Builder}
*/
Builder.prototype.tpmti = function(tpmti) {
var tpdu = builderTable.get(this);
tpdu.tpmti = tpmti;
return this;
};
/**
* More Messages to Send
*
* @param tpmms
* @returns {Builder}
*/
Builder.prototype.tpmms = function(tpmms) {
var tpdu = builderTable.get(this);
tpdu.tpmms = tpmms;
return this;
};
/**
* Reject Duplicates
*
* @param tprd
* @returns {Builder}
*/
Builder.prototype.tprd = function(tprd) {
var tpdu = builderTable.get(this);
tpdu.tprd = tprd;
return this;
};
/**
* Validity Period Format
*
* @param tpvpf
* @returns {Builder}
*/
Builder.prototype.tpvpf = function(tpvpf) {
var tpdu = builderTable.get(this);
tpdu.tpvpf = tpvpf;
return this;
}
/**
* Status Report Request
*
* @param tpsrr
* @returns {Builder}
*/
Builder.prototype.tpsrr = function(tpsrr) {
var tpdu = builderTable.get(this);
tpdu.tpsrr = tpsrr;
return this;
};
/**
* Status Report Indication
*
* @param tpsri
* @returns {Builder}
*/
Builder.prototype.tpsri = function(tpsri) {
var tpdu = builderTable.get(this);
tpdu.tpsri = tpsri;
return this;
};
/**
* User Header Data Indicator
*
* @param uhdi
* @returns {Builder}
*/
Builder.prototype.tpudhi = function(tpudhi) {
var tpdu = builderTable.get(this);
tpdu.tpudhi = tpudhi;
return this;
};
/**
* Reply Path
*
* @param tprp
* @returns {Builder}
*/
Builder.prototype.tprp = function(tprp) {
var tpdu = builderTable.get(this);
tpdu.tprp = tprp;
return this;
};
/**
* Message Reference
*
* @param tpmr
* @returns {Builder}
*/
Builder.prototype.tpmr = function(tpmr) {
var tpdu = builderTable.get(this);
tpdu.tpmr = tpmr;
return this;
};
/**
* Originating Address
*
* @param tpoa
* @returns {Builder}
*/
Builder.prototype.tpoa = function(addressType, numberingPlan, address) {
var tpdu = builderTable.get(this);
tpdu.tpoa = {
addressType: addressType,
numberingPlan: numberingPlan,
address: address
};
return this;
};
/**
* Destination Address
*
* @param addressType
* @param numberingPlan
* @param address
* @returns {Builder}
*/
Builder.prototype.tpda = function(addressType, numberingPlan, address) {
var tpdu = builderTable.get(this);
tpdu.tpda = {
addressType: addressType,
numberingPlan: numberingPlan,
address: address
};
return this;
};
/**
* Protocol Identifier
*
* @param tppid
* @returns {Builder}
*/
Builder.prototype.tppid = function(tppid) {
var tpdu = builderTable.get(this);
tpdu.tppid = tppid;
return this;
};
/**
* Data Coding Scheme
*
* @param tpdcs
* @returns {Builder}
*/
Builder.prototype.tpdcs = function(tpdcs) {
var tpdu = builderTable.get(this);
tpdu.tpdcs = tpdcs;
return this;
};
/**
* Validity Period
*
* @param tpvp
* @returns {Builder}
*/
Builder.prototype.tpvp = function(tpvp) {
var tpdu = builderTable.get(this);
tpdu.tpvp = tpvp;
return this;
};
/**
* Service Center Time Stamp
*
* @param tpscts
* @returns {Builder}
*/
Builder.prototype.tpscts = function(tpscts) {
var tpdu = builderTable.get(this);
tpdu.tpscts = tpscts;
return this;
};
/**
* User Data Length
*
* @param tpudl
* @returns {Builder}
*/
Builder.prototype.tpudl = function(tpudl) {
var tpdu = builderTable.get(this);
tpdu.tpudl = tpudl;
return this;
};
/**
* User Data
*
* @param tpud
* @returns {Builder}
*/
Builder.prototype.tpud = function(tpud) {
var tpdu = builderTable.get(this);
tpdu.tpud = tpud;
return this;
};
var build = {
address: function(address, sca) {
if(sca) {
if(!address || address.address.length === 0) {
var buffer = new Buffer(1);
buffer.writeUInt8(0, 0);
return buffer;
}
var length = (1 + Math.ceil(address.address.length / 2)) | 0;
} else {
var length = Math.ceil(address.address.length) | 0;
}
var type = address.numberingPlan | (address.addressType << 4) | 0x80;
var digits = [length, type];
if(sca) {
for (var i = 1; i < length; i++) {
var ln = parseInt(address.address.charAt((i - 1) * 2), 16) & 0xf;
var hn = parseInt(address.address.charAt((i - 1) * 2 + 1) || 'f', 16) & 0xf;
digits.push(ln | hn << 4);
}
} else {
for (var i = 0; i < length; i += 2) {
var ln = parseInt(address.address.charAt(i), 16) & 0xf;
var hn = parseInt(address.address.charAt(i + 1) || 'f', 16) & 0xf;
digits.push(ln | hn << 4);
}
}
return new Buffer(digits, 'raw');
},
write7bit: function(payload) {
var buffer = new Buffer(Math.ceil((payload.length * 7) / 8)), c = 0;
buffer.fill(0);
for(var i = 0; i < payload.length; i++) {
var char = constants.ALPHABET_7BIT.indexOf(payload.charAt(i)) & 0x7f;
var ls = char << ((8 - (i % 8)) % 8);
var hs = (ls & 0xff00) >>> 8;
ls &= 0xff;
var index = ((i * 7) / 8) | 0;
buffer.writeUInt8(buffer.readUInt8(index) | ls, index);
if(index + 1 < buffer.length) buffer.writeUInt8(buffer.readUInt8(index + 1) | hs, index + 1);
}
return buffer;
},
writeTimestamp: function(timestamp) {
var year = parseInt(timestamp.getUTCFullYear().toString().substring(2), 10);
var month = (timestamp.getUTCMonth() + 1);
var day = (timestamp.getUTCDate());
var hour = (timestamp.getUTCHours());
var minute = (timestamp.getUTCMinutes());
var second = (timestamp.getUTCSeconds());
var buffer = new Buffer(7);
buffer.writeUInt8(((year % 10) << 4) | (year / 10), 0);
buffer.writeUInt8(((month % 10) << 4) | (month / 10), 1);
buffer.writeUInt8(((day % 10) << 4) | (day / 10), 2);
buffer.writeUInt8(((hour % 10) << 4) | (hour / 10), 3);
buffer.writeUInt8(((minute % 10) << 4) | (minute / 10), 4);
buffer.writeUInt8(((second % 10) << 4) | (second / 10), 5);
buffer.writeUInt8(0, 6);
return buffer;
},
SMS_DELIVER: function(tpdu) {
var sca = build.address(tpdu.sca, true);
var mti = new Buffer(1);
mti.writeUInt8(
(tpdu.tpmti & 0x3) |
((tpdu.tpmms & 0x1) << 2) |
((tpdu.tpsri & 0x01) << 5) |
((tpdu.tpudhi & 0x01) << 6) |
((tpdu.tprp & 0x01) << 7),
0);
var oa = build.address(tpdu.tpoa);
var pid = new Buffer(1);
pid.writeUInt8(tpdu.tppid);
var dcs = new Buffer(1);
dcs.writeUInt8(tpdu.tpdcs);
var scts = build.writeTimestamp(tpdu.tpscts);
var udl = new Buffer(1);
udl.writeUInt8(tpdu.tpudl);
var ud = build.write7bit(tpdu.tpud);
return Buffer.concat([sca, mti, oa, pid, dcs, scts, udl, ud]);
},
SMS_SUBMIT: function(tpdu) {
var sca = build.address(tpdu.sca, true);
var mti = new Buffer(1);
mti.writeUInt8(
(tpdu.tpmti & 0x3) |
((tpdu.tprd & 0x1) << 2) |
((tpdu.tpvpf & 0x3) << 3) |
((tpdu.tpsrr & 0x01) << 5) |
((tpdu.tpudhi & 0x01) << 6) |
((tpdu.tprp & 0x01) << 7),
0);
var mr = new Buffer(1);
mr.writeUInt8(tpdu.tpmr);
var da = build.address(tpdu.tpda);
var pid = new Buffer(1);
pid.writeUInt8(tpdu.tppid);
var dcs = new Buffer(1);
dcs.writeUInt8(tpdu.tpdcs);
var vp = new Buffer(1);
vp.writeUInt8(tpdu.tpvp);
var udl = new Buffer(1);
udl.writeUInt8(tpdu.tpudl);
var ud = build.write7bit(tpdu.tpud);
return Buffer.concat([sca, mti, mr, da, pid, dcs, vp, udl, ud]);
}
};
/**
* Builds a T-PDU for the current Builder
*
* @returns {String}
*/
Builder.prototype.build = function() {
var tpdu = builderTable.get(this);
switch(tpdu.direction) {
case 'inbound':
switch(tpdu.tpmti) {
case constants.SMS_DELIVER:
var buffer = build.SMS_DELIVER(tpdu);
break
default:
throw new Error("Unsupported TP-MTI: 0x" + tpdu.tpmti.toString(16));
}
break;
case 'outbound':
switch(tpdu.tpmti) {
case constants.SMS_SUBMIT:
var buffer = build.SMS_SUBMIT(tpdu);
break
default:
throw new Error("Unsupported TP-MTI: 0x" + tpdu.tpmti.toString(16));
}
break;
}
return buffer.toString('hex').toUpperCase();
};
module.exports = Builder;