* MailPacket class: read() and write() with event emits

* FTNMailPacket WIP derived from MailPacket
This commit is contained in:
Bryan Ashby 2015-07-15 23:51:00 -06:00
parent 7fa27e46a5
commit aebf494ae7
5 changed files with 189 additions and 15 deletions

View File

@ -1,8 +1,9 @@
/* jslint node: true */ /* jslint node: true */
'use strict'; 'use strict';
var Config = require('./config.js').config; var MailPacket = require('./mail_packet.js');
var ftn = require('./ftn_util.js'); var ftn = require('./ftn_util.js');
var Message = require('./message.js');
var _ = require('lodash'); var _ = require('lodash');
var assert = require('assert'); var assert = require('assert');
@ -21,16 +22,9 @@ var async = require('async');
// //
function FTNMailPacket(options) { function FTNMailPacket(options) {
// MailPacket.call(this, options);
// Map of networkName -> { zone, net, node, point, ... }
//
//
// :TODO: ...
// options.nodeAddresses
var self = this; var self = this;
this.nodeAddresses = options.nodeAddresses || {};
self.KLUDGE_PREFIX = '\x01'; self.KLUDGE_PREFIX = '\x01';
/* /*
@ -123,9 +117,16 @@ function FTNMailPacket(options) {
}); });
}; };
self.getMessageMeta = function(msgData) {
return {}; // :TODO: convert msgData kludges/etc. -> Message meta
};
this.parseFtnMessageBody = function(msgBodyBuffer, cb) { this.parseFtnMessageBody = function(msgBodyBuffer, cb) {
// :TODO: Look at spec about line endings/etc. Prob \r\n|[\r\n]|<someOlderAndAmigaStuff> // :TODO: Look at spec about line endings/etc. Prob \r\n|[\r\n]|<someOlderAndAmigaStuff>
// :TODO: Use the proper encoding here. There appear to be multiple specs and/or
// stuff people do with this... some specs kludge lines, which is kinda durpy since
// to get to that point, one must read the file (and decode) to find said kludge...
var msgLines = msgBodyBuffer.toString().split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g); var msgLines = msgBodyBuffer.toString().split(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g);
var msgBody = { var msgBody = {
@ -183,6 +184,56 @@ function FTNMailPacket(options) {
cb(null, msgBody); cb(null, msgBody);
}; };
this.extractMessages = function(buffer, cb) {
var nullTermBuf = new Buffer( [ 0 ] );
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);
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();
});
self.parseFtnMessageBody(msgData.message, function msgBodyParsed(err, msgBody) {
//
// Now, create a Message object
//
var msg = new Message( {
toUserName : msgData.toUserName,
fromUserName : msgData.fromUserName,
subject : msgData.subject,
message : msgBody.message.join('\n'), // :TODO: \r\n is better?
modTimestamp : ftn.getDateFromFtnDateTime(msgData.modDateTime),
meta : self.getMessageMeta(msgData),
});
self.emit('message', msg); // :TODO: Placeholder
});
});
});
};
this.parseFtnMessages = function(buffer, cb) { this.parseFtnMessages = function(buffer, cb) {
var nullTermBuf = new Buffer( [ 0 ] ); var nullTermBuf = new Buffer( [ 0 ] );
var fidoMessages = []; var fidoMessages = [];
@ -223,6 +274,33 @@ function FTNMailPacket(options) {
}); });
}; };
this.extractMesssagesFromPacketBuffer = function(packetBuffer, cb) {
async.waterfall(
[
function parseHeader(callback) {
self.parseFtnPacketHeader(packetBuffer, function headerParsed(err, packetHeader) {
self.packetHeader = packetHeader;
callback(err);
});
},
function validateDesinationAddress(callback) {
self.localNetworkName = self.getNetworkNameForAddress(self.getPacketHeaderAddress());
self.localNetworkName = 'AllowAnyNetworkForDebugging';
callback(self.localNetworkName ? null : new Error('Packet not addressed do this system'));
},
function extractEmbeddedMessages(callback) {
// note: packet header is 58 bytes in length
self.extractMessages(packetBuffer.slice(58), function extracted(err) {
callback(err);
});
}
],
function complete(err) {
cb(err);
}
);
};
this.loadMessagesFromPacketBuffer = function(packetBuffer, cb) { this.loadMessagesFromPacketBuffer = function(packetBuffer, cb) {
async.waterfall( async.waterfall(
[ [
@ -253,9 +331,10 @@ function FTNMailPacket(options) {
} }
); );
}; };
} }
require('util').inherits(FTNMailPacket, MailPacket);
FTNMailPacket.prototype.parse = function(path, cb) { FTNMailPacket.prototype.parse = function(path, cb) {
var self = this; var self = this;
@ -278,6 +357,40 @@ FTNMailPacket.prototype.parse = function(path, cb) {
); );
}; };
FTNMailPacket.prototype.read = function(options) {
FTNMailPacket.super_.prototype.read.call(this, options);
var self = this;
if(_.isString(options.packetPath)) {
async.waterfall(
[
function readPacketFile(callback) {
fs.readFile(options.packetPath, function packetData(err, data) {
callback(err, data);
});
},
function extractMessages(data, callback) {
self.extractMesssagesFromPacketBuffer(data, function extracted(err) {
callback(err);
});
}
],
function complete(err) {
if(err) {
self.emit('error', err);
}
}
);
} else if(Buffer.isBuffer(options.packetBuffer)) {
}
};
FTNMailPacket.prototype.write = function(options) {
FTNMailPacket.super_.prototype.write.call(this, options);
};
var mailPacket = new FTNMailPacket( var mailPacket = new FTNMailPacket(
{ {
@ -293,6 +406,14 @@ var mailPacket = new FTNMailPacket(
} }
); );
mailPacket.on('message', function msgParsed(msg) {
console.log(msg);
});
mailPacket.read( { packetPath : '/home/nuskooler/ownCloud/Projects/ENiGMA½ BBS/FTNPackets/BAD_BNDL.007' } );
/*
mailPacket.parse('/home/nuskooler/ownCloud/Projects/ENiGMA½ BBS/FTNPackets/BAD_BNDL.007', function parsed(err, messages) { mailPacket.parse('/home/nuskooler/ownCloud/Projects/ENiGMA½ BBS/FTNPackets/BAD_BNDL.007', function parsed(err, messages) {
console.log(err) console.log(err)
}); });
*/

View File

@ -12,10 +12,11 @@ var util = require('util');
exports.stringFromFTN = stringFromFTN; exports.stringFromFTN = stringFromFTN;
exports.getFormattedFTNAddress = getFormattedFTNAddress; exports.getFormattedFTNAddress = getFormattedFTNAddress;
exports.getDateFromFtnDateTime = getDateFromFtnDateTime;
// See list here: https://github.com/Mithgol/node-fidonet-jam // See list here: https://github.com/Mithgol/node-fidonet-jam
// :TODO: proably move this elsewhere as a general method
function stringFromFTN(buf, encoding) { function stringFromFTN(buf, encoding) {
var nullPos = buf.length; var nullPos = buf.length;
for(var i = 0; i < buf.length; ++i) { for(var i = 0; i < buf.length; ++i) {
@ -28,6 +29,20 @@ function stringFromFTN(buf, encoding) {
return buf.slice(0, nullPos).toString(encoding || 'utf-8'); return buf.slice(0, nullPos).toString(encoding || 'utf-8');
} }
//
// Convert a FTN style DateTime string to a Date object
//
function getDateFromFtnDateTime(dateTime) {
//
// Examples seen in the wild (Working):
// "12 Sep 88 18:17:59"
// "Tue 01 Jan 80 00:00"
// "27 Feb 15 00:00:03"
//
return (new Date(Date.parse(dateTime))).toISOString();
}
function getFormattedFTNAddress3D(zone, net, node) { function getFormattedFTNAddress3D(zone, net, node) {
return util.format('%d:%d/%d', zone, net, node); return util.format('%d:%d/%d', zone, net, node);
} }

36
core/mail_packet.js Normal file
View File

@ -0,0 +1,36 @@
/* jslint node: true */
'use strict';
var events = require('events');
var assert = require('assert');
var _ = require('lodash');
module.exports = MailPacket;
function MailPacket(options) {
events.EventEmitter.call(this);
// map of network name -> address obj ( { zone, net, node, point, domain } )
this.nodeAddresses = options.nodeAddresses || {};
}
require('util').inherits(MailPacket, events.EventEmitter);
MailPacket.prototype.read = function(options) {
//
// options.packetPath | opts.packetBuffer: supplies a path-to-file
// or a buffer containing packet data
//
// emits 'message' event per message read
//
assert(_.isString(options.packetPath) || Buffer.isBuffer(options.packetBuffer));
};
MailPacket.prototype.write = function(options) {
//
// options.messages[]: array of message(s) to create packets from
//
// emits 'packet' event per packet constructed
//
assert(_.isArray(options.messages));
}

View File

@ -8,7 +8,7 @@ var async = require('async');
var _ = require('lodash'); var _ = require('lodash');
var assert = require('assert'); var assert = require('assert');
exports.Message = Message; module.exports = Message;
function Message(options) { function Message(options) {

View File

@ -6,7 +6,7 @@ var ViewController = require('../core/view_controller.js').ViewController;
var ansi = require('../core/ansi_term.js'); var ansi = require('../core/ansi_term.js');
var theme = require('../core/theme.js'); var theme = require('../core/theme.js');
var MultiLineEditTextView = require('../core/multi_line_edit_text_view.js').MultiLineEditTextView; var MultiLineEditTextView = require('../core/multi_line_edit_text_view.js').MultiLineEditTextView;
var Message = require('../core/message.js').Message; var Message = require('../core/message.js');
var async = require('async'); var async = require('async');
var assert = require('assert'); var assert = require('assert');
@ -36,6 +36,8 @@ function FullScreenEditorModule(options) {
// :TODO: This needs to be passed in via args: // :TODO: This needs to be passed in via args:
this.editorMode = 'edit'; // view | edit | editMenu | this.editorMode = 'edit'; // view | edit | editMenu |
// netMail/crashMail | echoMail
this.messageAreaId = 'netMail' === this.editorType ? Message.WellKnownAreaIds.Private : options.messageAreaId; this.messageAreaId = 'netMail' === this.editorType ? Message.WellKnownAreaIds.Private : options.messageAreaId;
this.getFooterName = function(editorMode) { this.getFooterName = function(editorMode) {