diff --git a/core/ftn_mail_packet.js b/core/ftn_mail_packet.js index f50f8da3..eb7e4a70 100644 --- a/core/ftn_mail_packet.js +++ b/core/ftn_mail_packet.js @@ -1,8 +1,9 @@ /* jslint node: true */ 'use strict'; -var Config = require('./config.js').config; +var MailPacket = require('./mail_packet.js'); var ftn = require('./ftn_util.js'); +var Message = require('./message.js'); var _ = require('lodash'); var assert = require('assert'); @@ -21,16 +22,9 @@ var async = require('async'); // function FTNMailPacket(options) { - // - // Map of networkName -> { zone, net, node, point, ... } - // - // - // :TODO: ... - // options.nodeAddresses + MailPacket.call(this, options); var self = this; - this.nodeAddresses = options.nodeAddresses || {}; - 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) { // :TODO: Look at spec about line endings/etc. Prob \r\n|[\r\n]| + // :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 msgBody = { @@ -183,6 +184,56 @@ function FTNMailPacket(options) { 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) { var nullTermBuf = new Buffer( [ 0 ] ); 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) { async.waterfall( [ @@ -253,9 +331,10 @@ function FTNMailPacket(options) { } ); }; - } +require('util').inherits(FTNMailPacket, MailPacket); + FTNMailPacket.prototype.parse = function(path, cb) { 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( { @@ -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) { console.log(err) -}); \ No newline at end of file +}); +*/ \ No newline at end of file diff --git a/core/ftn_util.js b/core/ftn_util.js index 368aadb6..63f649fd 100644 --- a/core/ftn_util.js +++ b/core/ftn_util.js @@ -12,10 +12,11 @@ var util = require('util'); exports.stringFromFTN = stringFromFTN; exports.getFormattedFTNAddress = getFormattedFTNAddress; +exports.getDateFromFtnDateTime = getDateFromFtnDateTime; // See list here: https://github.com/Mithgol/node-fidonet-jam - +// :TODO: proably move this elsewhere as a general method function stringFromFTN(buf, encoding) { var nullPos = buf.length; 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'); } + +// +// 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) { return util.format('%d:%d/%d', zone, net, node); } diff --git a/core/mail_packet.js b/core/mail_packet.js new file mode 100644 index 00000000..3fb8b2d2 --- /dev/null +++ b/core/mail_packet.js @@ -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)); +} \ No newline at end of file diff --git a/core/message.js b/core/message.js index fe7e6b95..329e718d 100644 --- a/core/message.js +++ b/core/message.js @@ -8,10 +8,10 @@ var async = require('async'); var _ = require('lodash'); var assert = require('assert'); -exports.Message = Message; +module.exports = Message; function Message(options) { - + this.messageId = options.messageId || 0; // always generated @ persist this.areaId = options.areaId || Message.WellKnownAreaIds.Invalid; // 0 = invalid; 1 = private; Everything else is user defined this.uuid = uuid.v1(); diff --git a/mods/fse.js b/mods/fse.js index 62463054..84c6264a 100644 --- a/mods/fse.js +++ b/mods/fse.js @@ -6,7 +6,7 @@ var ViewController = require('../core/view_controller.js').ViewController; var ansi = require('../core/ansi_term.js'); var theme = require('../core/theme.js'); 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 assert = require('assert'); @@ -36,6 +36,8 @@ function FullScreenEditorModule(options) { // :TODO: This needs to be passed in via args: this.editorMode = 'edit'; // view | edit | editMenu | + + // netMail/crashMail | echoMail this.messageAreaId = 'netMail' === this.editorType ? Message.WellKnownAreaIds.Private : options.messageAreaId; this.getFooterName = function(editorMode) {