From 8f33a9a0672f71be33c8aac50f19c7fc4bbf0c2b Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 14 Jul 2015 17:08:52 -0600 Subject: [PATCH] * Progress on parsing/extracting FTN packets --- core/ftn_mail_packet.js | 142 +++++++++++++++++++++++++++++++++------- core/ftn_util.js | 19 +++++- 2 files changed, 136 insertions(+), 25 deletions(-) diff --git a/core/ftn_mail_packet.js b/core/ftn_mail_packet.js index 392ea573..a5e85e1d 100644 --- a/core/ftn_mail_packet.js +++ b/core/ftn_mail_packet.js @@ -53,7 +53,7 @@ function FTNMailPacket(options) { }; }; - this.getNetworkForAddress = function(addr) { + this.getNetworkNameForAddress = function(addr) { var nodeAddr; for(var network in self.nodeAddresses) { nodeAddr = self.nodeAddresses[network]; @@ -67,7 +67,7 @@ function FTNMailPacket(options) { } }; - this.loadPacketHeader = function(packetBuffer, cb) { + this.parseFtnPacketHeader = function(packetBuffer, cb) { assert(Buffer.isBuffer(packetBuffer)); if(packetBuffer.length < 58) { @@ -93,7 +93,7 @@ function FTNMailPacket(options) { .buffer('password', 8) // null terminated C style string .word16lu('origZone') .word16lu('destZone') - // where is the rest of the spec? + // :TODO: Document the various specs/fields more .word16lu('auxNet') .word16lu('capWordA') .word8('prodCodeHi') @@ -107,37 +107,131 @@ function FTNMailPacket(options) { .tap(function tapped(packetHeader) { packetHeader.password = ftn.stringFromFTN(packetHeader.password); + // :TODO: Don't hard code magic # here + if(2 !== packetHeader.packetType) { + cb(new Error('Packet is not Type-2')); + return; + } + + // :TODO: validate & pass error if failure cb(null, packetHeader); }); }; - this.loadMessagesFromPacketBuffer = function(packetBuffer, cb) { - async.series( + this.parseFtnMessageBody = function(msgBodyBuffer, cb) { + + }; + + this.parseFtnMessages = function(buffer, cb) { + var nullTermBuf = new Buffer( [ 0 ] ); + var fidoMessages = []; + + binary.stream(buffer).loop(function looper(end, vars) { + this + .word16lu('messageType') + .word16lu('originNode') + .word16lu('destNode') + .word16lu('originNet') + .word16lu('destNet') + .word8('attrFlags1') + .word8('attrFlags2') + .word16lu('cost') + .scan('modDateTime', nullTermBuf) + .scan('toUserName', nullTermBuf) + .scan('fromUserName', nullTermBuf) + .scan('subject', nullTermBuf) + .scan('message', nullTermBuf) + .tap(function tapped(msgData) { + if(!msgData.originNode) { + end(); + cb(null, fidoMessages); + return; + } + + // buffer to string conversion + // :TODO: What is the real encoding here? + [ 'modDateTime', 'toUserName', 'fromUserName', 'subject', ].forEach(function field(f) { + msgData[f] = msgData[f].toString(); + }); + + + + fidoMessages.push(_.clone(msgData)); + }); + }); + }; + + /* + this.loadMessageHeader = function(msgHeaderBuffer, cb) { + assert(Buffer.isBuffer(msgHeaderBuffer)); + + if(msgHeaderBuffer.length < 14) { + cb(new Error('Buffer too small')); + return; + } + + binary.parse(msgHeaderBuffer) + .word16lu('messageType') + .word16lu('originNode') + .word16lu('destNode') + .word16lu('originNet') + .word16lu('destNet') + .word8('attrFlags1') + .word8('attrFlags2') + .word16lu('cost') + .tap(function tapped(msgHeader) { + console.log(msgHeader) + + var nullTermBuf = new Buffer( [ 0 ] ); + var offset = 14; + binary.parse(msgHeaderBuffer.slice(offset)) + .scan('modDateTime', nullTermBuf) + .scan('toUserName', nullTermBuf) + .tap(function tapped(varMsgHeader) { + console.log(varMsgHeader.modDateTime.toString()) + console.log(varMsgHeader.toUserName.toString()) + }); + + cb(null, msgHeader); + }); + }; + + this.loadMessage = function(buf, cb) { + var bufPosition = 0; + async.waterfall( [ function loadHdr(callback) { - self.loadPacketHeader(packetBuffer, function headerLoaded(err, packetHeader) { + self.loadMessageHeader(buf.slice(bufPosition), function headerLoaded(err, msgHeader) { + callback(err, msgHeader); + }); + } + ] + ); + }; + */ + + this.loadMessagesFromPacketBuffer = function(packetBuffer, cb) { + async.waterfall( + [ + function parseHeader(callback) { + self.parseFtnPacketHeader(packetBuffer, function headerParsed(err, packetHeader) { self.packetHeader = packetHeader; callback(err); }); }, - function validateType(callback) { - // :TODO: don't use a magic # here.... - if(2 !== self.packetHeader.packetType) { - callback(new Error('Packet is not Type-2')); - } else { - callback(null); - } + function validateDesinationAddress(callback) { + self.localNetworkName = self.getNetworkNameForAddress(self.getPacketHeaderAddress()); + callback(self.localNetworkName ? null : new Error('Packet not addressed do this system')); }, - function checkAddress(callback) { - /* - if(0 !== self.packetHeader.destPoint) { - self.packetNodeAddress = ftn.getFormattedFTNAddress(self.getPacketHeaderAddress(), '4D'); - } else { - self.packetNodeAddress = ftn.getFormattedFTNAddress(self.getPacketHeaderAddress(), '3D'); - }*/ - - var network = self.getNetworkForAddress(self.getPacketHeaderAddress()); - callback(network ? null : new Error('Packet not addressed do this system')); + function parseMessages(callback) { + self.parseFtnMessages(packetBuffer.slice(58), function messagesParsed(err, fidoMessages) { + callback(err, fidoMessages); + }); + }, + function createMessageObjects(fidoMessages, callback) { + fidoMessages.forEach(function msg(fmsg) { + console.log(fmsg.subject); + }); } ], function complete(err) { @@ -185,6 +279,6 @@ var mailPacket = new FTNMailPacket( } ); -mailPacket.parse('/home/nuskooler/ownCloud/Projects/ENiGMA½ BBS/FTNPackets/27000425.pkt', function parsed(err, messages) { +mailPacket.parse('/home/bashby/ownCloud/Projects/ENiGMA½ BBS/FTNPackets/27000425.pkt', function parsed(err, messages) { }); \ No newline at end of file diff --git a/core/ftn_util.js b/core/ftn_util.js index ba14bcd9..368aadb6 100644 --- a/core/ftn_util.js +++ b/core/ftn_util.js @@ -13,6 +13,8 @@ var util = require('util'); exports.stringFromFTN = stringFromFTN; exports.getFormattedFTNAddress = getFormattedFTNAddress; +// See list here: https://github.com/Mithgol/node-fidonet-jam + function stringFromFTN(buf, encoding) { var nullPos = buf.length; @@ -67,7 +69,7 @@ function getFormattedFTNAddress(address, dimensions) { return addr; } -function getFTNMessageSerialNumber(messageId) { +function getFtnMessageSerialNumber(messageId) { return ((Math.floor((Date.now() - Date.UTC(2015, 1, 1)) / 1000) + messageId)).toString(16); } @@ -75,6 +77,21 @@ function getFTNMessageID(messageId, areaId) { return messageId + '.' + areaId + '@' + getFTNAddress() + ' ' + getFTNMessageSerialNumber(messageId) } + +// +// Specs: +// * http://ftsc.org/docs/fts-0009.001 +// * +// +function getFtnMsgIdKludgeLine(origAddress, messageId) { + if(_.isObject(origAddress)) { + origAddress = getFormattedFTNAddress(origAddress, '5D'); + } + + return '\x01MSGID: ' + origAddress + ' ' + getFtnMessageSerialNumber(messageId); +} + + function getFTNOriginLine() { // // Specs: