mirror of
https://github.com/NuSkooler/enigma-bbs.git
synced 2025-06-06 12:47:13 +02:00
* Some WIP FTN packet processing
This commit is contained in:
parent
f51b6c7098
commit
5e55ce4bee
4 changed files with 288 additions and 84 deletions
|
@ -123,5 +123,19 @@ function getDefaultConfig() {
|
||||||
dsaPrivateKey : paths.join(__dirname, './../misc/default_key.dsa'),
|
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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
//
|
|
||||||
|
|
||||||
}
|
|
190
core/ftn_mail_packet.js
Normal file
190
core/ftn_mail_packet.js
Normal file
|
@ -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) {
|
||||||
|
|
||||||
|
});
|
84
core/ftn_util.js
Normal file
84
core/ftn_util.js
Normal file
|
@ -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() + ')';
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue