From 5e55ce4bee458e448a4ea57a264d940a0b16c309 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Tue, 14 Jul 2015 00:13:29 -0600 Subject: [PATCH] * Some WIP FTN packet processing --- core/config.js | 14 +++ core/fidonet.js | 84 ------------------ core/ftn_mail_packet.js | 190 ++++++++++++++++++++++++++++++++++++++++ core/ftn_util.js | 84 ++++++++++++++++++ 4 files changed, 288 insertions(+), 84 deletions(-) delete mode 100644 core/fidonet.js create mode 100644 core/ftn_mail_packet.js create mode 100644 core/ftn_util.js diff --git a/core/config.js b/core/config.js index a3db7efc..615d0dc0 100644 --- a/core/config.js +++ b/core/config.js @@ -123,5 +123,19 @@ function getDefaultConfig() { dsaPrivateKey : paths.join(__dirname, './../misc/default_key.dsa'), } }, + + networks : { + /* + networkName : { // e.g. fidoNet + address : { + zone : 0, + net : 0, + node : 0, + point : 0, + domain : 'l33t.codes' + } + } + */ + }, }; } diff --git a/core/fidonet.js b/core/fidonet.js deleted file mode 100644 index 939ec364..00000000 --- a/core/fidonet.js +++ /dev/null @@ -1,84 +0,0 @@ -/* jslint node: true */ -'use strict'; - -var Config = require('./config.js').config; - - -var _ = require('lodash'); -var assert = require('assert'); -var binary = require('binary'); - -function getFTNAddress = function() { - return 'TODO'; -} - - -function getFTNMessageSerialNumber(messageId) { - return ((Math.floor((Date.now() - Date.UTC(2015, 01, 01)) / 1000) + messageId)).toString(16); -} - -function getFTNMessageID(messageId, areaId) { - return messageId + '.' + areaId + '@' + getFTNAddress() + ' ' + getFTNMessageSerialNumber(messageId) -} - -function getFTNOriginLine = function() { - // - // Specs: - // http://ftsc.org/docs/fts-0004.001 - // - return ' * Origin: ' + Config.general.boardName + '(' + getFidoNetAddress() + ')'; -} - -// -// References -// https://github.com/M-griffin/PyPacketMail/blob/master/PyPacketMail.py -// -function extractMessageFromFTNPacketBuffer(options, cb) { - // options.networkAddress - // options.packetBuffer - assert(_.isBuffer(options.packetBuffer)); - - // :TODO: check size - // :TODO: big/little endian? - binary.parse(options.packetBuffer) - .word16('origNode') - .word16('destNode') - .word16('year') - .word16('month') - .word16('day') - .word16('hour') - .word16('minute') - .word16('second') - .word16('baud') - .word16('packetType') - .word16('originNet') - .word16('destNet') - .word8('prodCodeLo') - .word8('revisionMajor') // aka serialNo - .buffer('password', 8) - .word16('origZone') - .word16('destZone') - // where is the rest of the spec? - .word16('auxNet') - .word16('capWordA') - .word8('prodCodeHi') - .word8('revisionMinor') - .word16('capWordB') - .word16('originZone2') - .word16('destZone2') - .word16('originPoint') - .word16('destPoint') - .word32u('prodData') - .tap(function tapped(vars) { - - } - ); -} - -function extractMessagesFromFTNPacketFile(options) { - // - // options.path - // options.networkAddress - // - -} \ No newline at end of file diff --git a/core/ftn_mail_packet.js b/core/ftn_mail_packet.js new file mode 100644 index 00000000..392ea573 --- /dev/null +++ b/core/ftn_mail_packet.js @@ -0,0 +1,190 @@ +/* jslint node: true */ +'use strict'; + +var Config = require('./config.js').config; +var ftn = require('./ftn_util.js'); + +var _ = require('lodash'); +var assert = require('assert'); +var binary = require('binary'); +var fs = require('fs'); +var util = require('util'); +var async = require('async'); + +// +// References +// https://github.com/M-griffin/PyPacketMail/blob/master/PyPacketMail.py +// + +function FTNMailPacket(options) { + + // + // Map of networkName -> { zone, net, node, point, ... } + // + // + // :TODO: ... + // options.nodeAddresses + + var self = this; + this.nodeAddresses = options.nodeAddresses || {}; + + /* + this.loadNodeAddresses = function() { + if(Config.networks) { + for(var name in Config.networks) { + if(!Config.networks[name].address) { + continue; + } + + this.nodeAddresses[name] = Config.networks[name].address; + } + } + }; + + this.loadNodeAddresses(); + */ + + this.getPacketHeaderAddress = function() { + return { + zone : self.packetHeader.destZone, + net : self.packetHeader.destNet, + node : self.packetHeader.destNode, + point : self.packetHeader.destPoint, + }; + }; + + this.getNetworkForAddress = function(addr) { + var nodeAddr; + for(var network in self.nodeAddresses) { + nodeAddr = self.nodeAddresses[network]; + if(nodeAddr.zone === addr.zone && + nodeAddr.net === addr.net && + nodeAddr.node === addr.node && + nodeAddr.point === addr.point) + { + return network; + } + } + }; + + this.loadPacketHeader = function(packetBuffer, cb) { + assert(Buffer.isBuffer(packetBuffer)); + + if(packetBuffer.length < 58) { + cb(new Error('Buffer too small')); + return; + } + + binary.parse(packetBuffer) + .word16lu('origNode') + .word16lu('destNode') + .word16lu('year') + .word16lu('month') + .word16lu('day') + .word16lu('hour') + .word16lu('minute') + .word16lu('second') + .word16lu('baud') + .word16lu('packetType') + .word16lu('originNet') + .word16lu('destNet') + .word8('prodCodeLo') + .word8('revisionMajor') // aka serialNo + .buffer('password', 8) // null terminated C style string + .word16lu('origZone') + .word16lu('destZone') + // where is the rest of the spec? + .word16lu('auxNet') + .word16lu('capWordA') + .word8('prodCodeHi') + .word8('revisionMinor') + .word16lu('capWordB') + .word16lu('originZone2') + .word16lu('destZone2') + .word16lu('originPoint') + .word16lu('destPoint') + .word32lu('prodData') + .tap(function tapped(packetHeader) { + packetHeader.password = ftn.stringFromFTN(packetHeader.password); + + cb(null, packetHeader); + }); + }; + + this.loadMessagesFromPacketBuffer = function(packetBuffer, cb) { + async.series( + [ + function loadHdr(callback) { + self.loadPacketHeader(packetBuffer, function headerLoaded(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 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 complete(err) { + cb(err); + } + ); + }; + +} + +FTNMailPacket.prototype.parse = function(path, cb) { + var self = this; + + async.waterfall( + [ + function readFromFile(callback) { + fs.readFile(path, function packetData(err, data) { + callback(err, data); + }); + }, + function extractMessages(data, callback) { + self.loadMessagesFromPacketBuffer(data, function extracted(err, messages) { + callback(err, messages); + }); + } + ], + function complete(err, messages) { + cb(err, messages); + } + ); +}; + + +var mailPacket = new FTNMailPacket( + { + nodeAddresses : { + fidoNet : { + zone : 46, + net : 1, + node : 140, + point : 0, + domain : '' + } + } + } +); + +mailPacket.parse('/home/nuskooler/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 new file mode 100644 index 00000000..ba14bcd9 --- /dev/null +++ b/core/ftn_util.js @@ -0,0 +1,84 @@ +/* jslint node: true */ +'use strict'; + +var Config = require('./config.js').config; + + +var _ = require('lodash'); +var assert = require('assert'); +var binary = require('binary'); +var fs = require('fs'); +var util = require('util'); + +exports.stringFromFTN = stringFromFTN; +exports.getFormattedFTNAddress = getFormattedFTNAddress; + + +function stringFromFTN(buf, encoding) { + var nullPos = buf.length; + for(var i = 0; i < buf.length; ++i) { + if(0x00 === buf[i]) { + nullPos = i; + break; + } + } + + return buf.slice(0, nullPos).toString(encoding || 'utf-8'); +} + +function getFormattedFTNAddress3D(zone, net, node) { + return util.format('%d:%d/%d', zone, net, node); +} + +function getFormattedFTNAddress4D(zone, net, node, point) { + return util.format('%d:%d/%d.%d', zone, net, node, point); +} + +function getFormattedFTNAddress5D(zone, net, node, point, domain) { + // :TODO: +} + +function getFormattedFTNAddress(address, dimensions) { + var addr = util.format('%d:%d', address.zone, address.net); + switch(dimensions) { + case 2 : + case '2D' : + // above + break; + + case 3 : + case '3D' : + addr += util.format('/%d', address.node); + break; + + case 4 : + case '4D': + addr += util.format('.%d', address.point || 0); // missing and 0 are equiv for point + break; + + case 5 : + case '5D' : + if(address.domain) { + addr += util.format('@%s', address.domain); + } + break; + } + + return addr; +} + +function getFTNMessageSerialNumber(messageId) { + return ((Math.floor((Date.now() - Date.UTC(2015, 1, 1)) / 1000) + messageId)).toString(16); +} + +function getFTNMessageID(messageId, areaId) { + return messageId + '.' + areaId + '@' + getFTNAddress() + ' ' + getFTNMessageSerialNumber(messageId) +} + +function getFTNOriginLine() { + // + // Specs: + // http://ftsc.org/docs/fts-0004.001 + // + return ' * Origin: ' + Config.general.boardName + '(' + getFidoNetAddress() + ')'; +}