mirror of
https://github.com/NuSkooler/enigma-bbs.git
synced 2025-07-24 11:38:27 +02:00
Pardon the noise. More tab to space conversion!
This commit is contained in:
parent
c3635bb26b
commit
1d8be6b014
128 changed files with 8017 additions and 8017 deletions
|
@ -1,75 +1,75 @@
|
|||
/* jslint node: true */
|
||||
'use strict';
|
||||
|
||||
const ftn = require('./ftn_util.js');
|
||||
const Message = require('./message.js');
|
||||
const sauce = require('./sauce.js');
|
||||
const Address = require('./ftn_address.js');
|
||||
const strUtil = require('./string_util.js');
|
||||
const Log = require('./logger.js').log;
|
||||
const ansiPrep = require('./ansi_prep.js');
|
||||
const Errors = require('./enig_error.js').Errors;
|
||||
const ftn = require('./ftn_util.js');
|
||||
const Message = require('./message.js');
|
||||
const sauce = require('./sauce.js');
|
||||
const Address = require('./ftn_address.js');
|
||||
const strUtil = require('./string_util.js');
|
||||
const Log = require('./logger.js').log;
|
||||
const ansiPrep = require('./ansi_prep.js');
|
||||
const Errors = require('./enig_error.js').Errors;
|
||||
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const { Parser } = require('binary-parser');
|
||||
const fs = require('graceful-fs');
|
||||
const async = require('async');
|
||||
const iconv = require('iconv-lite');
|
||||
const moment = require('moment');
|
||||
const _ = require('lodash');
|
||||
const assert = require('assert');
|
||||
const { Parser } = require('binary-parser');
|
||||
const fs = require('graceful-fs');
|
||||
const async = require('async');
|
||||
const iconv = require('iconv-lite');
|
||||
const moment = require('moment');
|
||||
|
||||
exports.Packet = Packet;
|
||||
exports.Packet = Packet;
|
||||
|
||||
const FTN_PACKET_HEADER_SIZE = 58; // fixed header size
|
||||
const FTN_PACKET_HEADER_TYPE = 2;
|
||||
const FTN_PACKET_MESSAGE_TYPE = 2;
|
||||
const FTN_PACKET_BAUD_TYPE_2_2 = 2;
|
||||
const FTN_PACKET_HEADER_SIZE = 58; // fixed header size
|
||||
const FTN_PACKET_HEADER_TYPE = 2;
|
||||
const FTN_PACKET_MESSAGE_TYPE = 2;
|
||||
const FTN_PACKET_BAUD_TYPE_2_2 = 2;
|
||||
|
||||
// SAUCE magic header + version ("00")
|
||||
// SAUCE magic header + version ("00")
|
||||
const FTN_MESSAGE_SAUCE_HEADER = Buffer.from('SAUCE00');
|
||||
|
||||
const FTN_MESSAGE_KLUDGE_PREFIX = '\x01';
|
||||
const FTN_MESSAGE_KLUDGE_PREFIX = '\x01';
|
||||
|
||||
class PacketHeader {
|
||||
constructor(origAddr, destAddr, version, createdMoment) {
|
||||
const EMPTY_ADDRESS = {
|
||||
node : 0,
|
||||
net : 0,
|
||||
zone : 0,
|
||||
point : 0,
|
||||
node : 0,
|
||||
net : 0,
|
||||
zone : 0,
|
||||
point : 0,
|
||||
};
|
||||
|
||||
this.version = version || '2+';
|
||||
this.origAddress = origAddr || EMPTY_ADDRESS;
|
||||
this.destAddress = destAddr || EMPTY_ADDRESS;
|
||||
this.created = createdMoment || moment();
|
||||
this.version = version || '2+';
|
||||
this.origAddress = origAddr || EMPTY_ADDRESS;
|
||||
this.destAddress = destAddr || EMPTY_ADDRESS;
|
||||
this.created = createdMoment || moment();
|
||||
|
||||
// uncommon to set the following explicitly
|
||||
this.prodCodeLo = 0xfe; // http://ftsc.org/docs/fta-1005.003
|
||||
this.prodRevLo = 0;
|
||||
this.baud = 0;
|
||||
this.packetType = FTN_PACKET_HEADER_TYPE;
|
||||
this.password = '';
|
||||
this.prodData = 0x47694e45; // "ENiG"
|
||||
// uncommon to set the following explicitly
|
||||
this.prodCodeLo = 0xfe; // http://ftsc.org/docs/fta-1005.003
|
||||
this.prodRevLo = 0;
|
||||
this.baud = 0;
|
||||
this.packetType = FTN_PACKET_HEADER_TYPE;
|
||||
this.password = '';
|
||||
this.prodData = 0x47694e45; // "ENiG"
|
||||
|
||||
this.capWord = 0x0001;
|
||||
this.capWordValidate = ((this.capWord & 0xff) << 8) | ((this.capWord >> 8) & 0xff); // swap
|
||||
this.capWord = 0x0001;
|
||||
this.capWordValidate = ((this.capWord & 0xff) << 8) | ((this.capWord >> 8) & 0xff); // swap
|
||||
|
||||
this.prodCodeHi = 0xfe; // see above
|
||||
this.prodRevHi = 0;
|
||||
this.prodCodeHi = 0xfe; // see above
|
||||
this.prodRevHi = 0;
|
||||
}
|
||||
|
||||
get origAddress() {
|
||||
let addr = new Address({
|
||||
node : this.origNode,
|
||||
zone : this.origZone,
|
||||
node : this.origNode,
|
||||
zone : this.origZone,
|
||||
});
|
||||
|
||||
if(this.origPoint) {
|
||||
addr.point = this.origPoint;
|
||||
addr.net = this.auxNet;
|
||||
addr.point = this.origPoint;
|
||||
addr.net = this.auxNet;
|
||||
} else {
|
||||
addr.net = this.origNet;
|
||||
addr.net = this.origNet;
|
||||
}
|
||||
|
||||
return addr;
|
||||
|
@ -82,29 +82,29 @@ class PacketHeader {
|
|||
|
||||
this.origNode = address.node;
|
||||
|
||||
// See FSC-48
|
||||
// :TODO: disabled for now until we have separate packet writers for 2, 2+, 2+48, and 2.2
|
||||
// See FSC-48
|
||||
// :TODO: disabled for now until we have separate packet writers for 2, 2+, 2+48, and 2.2
|
||||
/*if(address.point) {
|
||||
this.auxNet = address.origNet;
|
||||
this.origNet = -1;
|
||||
} else {
|
||||
this.origNet = address.net;
|
||||
this.auxNet = 0;
|
||||
}
|
||||
*/
|
||||
this.origNet = address.net;
|
||||
this.auxNet = 0;
|
||||
this.auxNet = address.origNet;
|
||||
this.origNet = -1;
|
||||
} else {
|
||||
this.origNet = address.net;
|
||||
this.auxNet = 0;
|
||||
}
|
||||
*/
|
||||
this.origNet = address.net;
|
||||
this.auxNet = 0;
|
||||
|
||||
this.origZone = address.zone;
|
||||
this.origZone2 = address.zone;
|
||||
this.origPoint = address.point || 0;
|
||||
this.origZone = address.zone;
|
||||
this.origZone2 = address.zone;
|
||||
this.origPoint = address.point || 0;
|
||||
}
|
||||
|
||||
get destAddress() {
|
||||
let addr = new Address({
|
||||
node : this.destNode,
|
||||
net : this.destNet,
|
||||
zone : this.destZone,
|
||||
node : this.destNode,
|
||||
net : this.destNet,
|
||||
zone : this.destZone,
|
||||
});
|
||||
|
||||
if(this.destPoint) {
|
||||
|
@ -119,21 +119,21 @@ class PacketHeader {
|
|||
address = Address.fromString(address);
|
||||
}
|
||||
|
||||
this.destNode = address.node;
|
||||
this.destNet = address.net;
|
||||
this.destZone = address.zone;
|
||||
this.destZone2 = address.zone;
|
||||
this.destPoint = address.point || 0;
|
||||
this.destNode = address.node;
|
||||
this.destNet = address.net;
|
||||
this.destZone = address.zone;
|
||||
this.destZone2 = address.zone;
|
||||
this.destPoint = address.point || 0;
|
||||
}
|
||||
|
||||
get created() {
|
||||
return moment({
|
||||
year : this.year,
|
||||
month : this.month - 1, // moment uses 0 indexed months
|
||||
date : this.day,
|
||||
hour : this.hour,
|
||||
minute : this.minute,
|
||||
second : this.second
|
||||
year : this.year,
|
||||
month : this.month - 1, // moment uses 0 indexed months
|
||||
date : this.day,
|
||||
hour : this.hour,
|
||||
minute : this.minute,
|
||||
second : this.second
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -142,28 +142,28 @@ class PacketHeader {
|
|||
momentCreated = moment(momentCreated);
|
||||
}
|
||||
|
||||
this.year = momentCreated.year();
|
||||
this.month = momentCreated.month() + 1; // moment uses 0 indexed months
|
||||
this.day = momentCreated.date(); // day of month
|
||||
this.hour = momentCreated.hour();
|
||||
this.minute = momentCreated.minute();
|
||||
this.second = momentCreated.second();
|
||||
this.year = momentCreated.year();
|
||||
this.month = momentCreated.month() + 1; // moment uses 0 indexed months
|
||||
this.day = momentCreated.date(); // day of month
|
||||
this.hour = momentCreated.hour();
|
||||
this.minute = momentCreated.minute();
|
||||
this.second = momentCreated.second();
|
||||
}
|
||||
}
|
||||
|
||||
exports.PacketHeader = PacketHeader;
|
||||
|
||||
//
|
||||
// Read/Write FTN packets with support for the following formats:
|
||||
// Read/Write FTN packets with support for the following formats:
|
||||
//
|
||||
// * Type 2 FTS-0001 @ http://ftsc.org/docs/fts-0001.016 (Obsolete)
|
||||
// * Type 2.2 FSC-0045 @ http://ftsc.org/docs/fsc-0045.001
|
||||
// * Type 2+ FSC-0039 and FSC-0048 @ http://ftsc.org/docs/fsc-0039.004
|
||||
// and http://ftsc.org/docs/fsc-0048.002
|
||||
// * Type 2 FTS-0001 @ http://ftsc.org/docs/fts-0001.016 (Obsolete)
|
||||
// * Type 2.2 FSC-0045 @ http://ftsc.org/docs/fsc-0045.001
|
||||
// * Type 2+ FSC-0039 and FSC-0048 @ http://ftsc.org/docs/fsc-0039.004
|
||||
// and http://ftsc.org/docs/fsc-0048.002
|
||||
//
|
||||
// Additional resources:
|
||||
// * Writeup on differences between type 2, 2.2, and 2+:
|
||||
// http://walon.org/pub/fidonet/FTSC-nodelists-etc./pkt-types.txt
|
||||
// Additional resources:
|
||||
// * Writeup on differences between type 2, 2.2, and 2+:
|
||||
// http://walon.org/pub/fidonet/FTSC-nodelists-etc./pkt-types.txt
|
||||
//
|
||||
function Packet(options) {
|
||||
var self = this;
|
||||
|
@ -189,13 +189,13 @@ function Packet(options) {
|
|||
.uint16le('origNet')
|
||||
.uint16le('destNet')
|
||||
.int8('prodCodeLo')
|
||||
.int8('prodRevLo') // aka serialNo
|
||||
.buffer('password', { length : 8 }) // can't use string; need CP437 - see https://github.com/keichi/binary-parser/issues/33
|
||||
.int8('prodRevLo') // aka serialNo
|
||||
.buffer('password', { length : 8 }) // can't use string; need CP437 - see https://github.com/keichi/binary-parser/issues/33
|
||||
.uint16le('origZone')
|
||||
.uint16le('destZone')
|
||||
//
|
||||
// The following is "filler" in FTS-0001, specifics in
|
||||
// FSC-0045 and FSC-0048
|
||||
// The following is "filler" in FTS-0001, specifics in
|
||||
// FSC-0045 and FSC-0048
|
||||
//
|
||||
.uint16le('auxNet')
|
||||
.uint16le('capWordValidate')
|
||||
|
@ -212,7 +212,7 @@ function Packet(options) {
|
|||
return Errors.Invalid(`Unable to parse FTN packet header: ${e.message}`);
|
||||
}
|
||||
|
||||
// Convert password from NULL padded array to string
|
||||
// Convert password from NULL padded array to string
|
||||
packetHeader.password = strUtil.stringFromNullTermBuffer(packetHeader.password, 'CP437');
|
||||
|
||||
if(FTN_PACKET_HEADER_TYPE !== packetHeader.packetType) {
|
||||
|
@ -220,50 +220,50 @@ function Packet(options) {
|
|||
}
|
||||
|
||||
//
|
||||
// What kind of packet do we really have here?
|
||||
// What kind of packet do we really have here?
|
||||
//
|
||||
// :TODO: adjust values based on version discovered
|
||||
// :TODO: adjust values based on version discovered
|
||||
if(FTN_PACKET_BAUD_TYPE_2_2 === packetHeader.baud) {
|
||||
packetHeader.version = '2.2';
|
||||
|
||||
// See FSC-0045
|
||||
packetHeader.origPoint = packetHeader.year;
|
||||
packetHeader.destPoint = packetHeader.month;
|
||||
// See FSC-0045
|
||||
packetHeader.origPoint = packetHeader.year;
|
||||
packetHeader.destPoint = packetHeader.month;
|
||||
|
||||
packetHeader.destDomain = packetHeader.origZone2;
|
||||
packetHeader.origDomain = packetHeader.auxNet;
|
||||
packetHeader.origDomain = packetHeader.auxNet;
|
||||
} else {
|
||||
//
|
||||
// See heuristics described in FSC-0048, "Receiving Type-2+ bundles"
|
||||
// See heuristics described in FSC-0048, "Receiving Type-2+ bundles"
|
||||
//
|
||||
const capWordValidateSwapped =
|
||||
((packetHeader.capWordValidate & 0xff) << 8) |
|
||||
((packetHeader.capWordValidate >> 8) & 0xff);
|
||||
((packetHeader.capWordValidate & 0xff) << 8) |
|
||||
((packetHeader.capWordValidate >> 8) & 0xff);
|
||||
|
||||
if(capWordValidateSwapped === packetHeader.capWord &&
|
||||
0 != packetHeader.capWord &&
|
||||
packetHeader.capWord & 0x0001)
|
||||
0 != packetHeader.capWord &&
|
||||
packetHeader.capWord & 0x0001)
|
||||
{
|
||||
packetHeader.version = '2+';
|
||||
|
||||
// See FSC-0048
|
||||
// See FSC-0048
|
||||
if(-1 === packetHeader.origNet) {
|
||||
packetHeader.origNet = packetHeader.auxNet;
|
||||
}
|
||||
} else {
|
||||
packetHeader.version = '2';
|
||||
|
||||
// :TODO: should fill bytes be 0?
|
||||
// :TODO: should fill bytes be 0?
|
||||
}
|
||||
}
|
||||
|
||||
packetHeader.created = moment({
|
||||
year : packetHeader.year,
|
||||
month : packetHeader.month - 1, // moment uses 0 indexed months
|
||||
date : packetHeader.day,
|
||||
hour : packetHeader.hour,
|
||||
minute : packetHeader.minute,
|
||||
second : packetHeader.second
|
||||
year : packetHeader.year,
|
||||
month : packetHeader.month - 1, // moment uses 0 indexed months
|
||||
date : packetHeader.day,
|
||||
hour : packetHeader.hour,
|
||||
minute : packetHeader.minute,
|
||||
second : packetHeader.second
|
||||
});
|
||||
|
||||
const ph = new PacketHeader();
|
||||
|
@ -352,36 +352,36 @@ function Packet(options) {
|
|||
|
||||
this.processMessageBody = function(messageBodyBuffer, cb) {
|
||||
//
|
||||
// From FTS-0001.16:
|
||||
// "Message text is unbounded and null terminated (note exception below).
|
||||
// From FTS-0001.16:
|
||||
// "Message text is unbounded and null terminated (note exception below).
|
||||
//
|
||||
// A 'hard' carriage return, 0DH, marks the end of a paragraph, and must
|
||||
// be preserved.
|
||||
// A 'hard' carriage return, 0DH, marks the end of a paragraph, and must
|
||||
// be preserved.
|
||||
//
|
||||
// So called 'soft' carriage returns, 8DH, may mark a previous
|
||||
// processor's automatic line wrap, and should be ignored. Beware that
|
||||
// they may be followed by linefeeds, or may not.
|
||||
// So called 'soft' carriage returns, 8DH, may mark a previous
|
||||
// processor's automatic line wrap, and should be ignored. Beware that
|
||||
// they may be followed by linefeeds, or may not.
|
||||
//
|
||||
// All linefeeds, 0AH, should be ignored. Systems which display message
|
||||
// text should wrap long lines to suit their application."
|
||||
// All linefeeds, 0AH, should be ignored. Systems which display message
|
||||
// text should wrap long lines to suit their application."
|
||||
//
|
||||
// This can be a bit tricky:
|
||||
// * Decoding as CP437 converts 0x8d -> 0xec, so we'll need to correct for that
|
||||
// * Many kludge lines specify an encoding. If we find one of such lines, we'll
|
||||
// likely need to re-decode as the specified encoding
|
||||
// * SAUCE is binary-ish data, so we need to inspect for it before any
|
||||
// decoding occurs
|
||||
// This can be a bit tricky:
|
||||
// * Decoding as CP437 converts 0x8d -> 0xec, so we'll need to correct for that
|
||||
// * Many kludge lines specify an encoding. If we find one of such lines, we'll
|
||||
// likely need to re-decode as the specified encoding
|
||||
// * SAUCE is binary-ish data, so we need to inspect for it before any
|
||||
// decoding occurs
|
||||
//
|
||||
let messageBodyData = {
|
||||
message : [],
|
||||
kludgeLines : {}, // KLUDGE:[value1, value2, ...] map
|
||||
seenBy : [],
|
||||
message : [],
|
||||
kludgeLines : {}, // KLUDGE:[value1, value2, ...] map
|
||||
seenBy : [],
|
||||
};
|
||||
|
||||
function addKludgeLine(line) {
|
||||
//
|
||||
// We have to special case INTL/TOPT/FMPT as they don't contain
|
||||
// a ':' name/value separator like the rest of the kludge lines... because stupdity.
|
||||
// We have to special case INTL/TOPT/FMPT as they don't contain
|
||||
// a ':' name/value separator like the rest of the kludge lines... because stupdity.
|
||||
//
|
||||
let key = line.substr(0, 4).trim();
|
||||
let value;
|
||||
|
@ -389,13 +389,13 @@ function Packet(options) {
|
|||
value = line.substr(key.length).trim();
|
||||
} else {
|
||||
const sepIndex = line.indexOf(':');
|
||||
key = line.substr(0, sepIndex).toUpperCase();
|
||||
value = line.substr(sepIndex + 1).trim();
|
||||
key = line.substr(0, sepIndex).toUpperCase();
|
||||
value = line.substr(sepIndex + 1).trim();
|
||||
}
|
||||
|
||||
//
|
||||
// Allow mapped value to be either a key:value if there is only
|
||||
// one entry, or key:[value1, value2,...] if there are more
|
||||
// Allow mapped value to be either a key:value if there is only
|
||||
// one entry, or key:[value1, value2,...] if there are more
|
||||
//
|
||||
if(messageBodyData.kludgeLines[key]) {
|
||||
if(!_.isArray(messageBodyData.kludgeLines[key])) {
|
||||
|
@ -412,21 +412,21 @@ function Packet(options) {
|
|||
async.series(
|
||||
[
|
||||
function extractSauce(callback) {
|
||||
// :TODO: This is wrong: SAUCE may not have EOF marker for one, also if it's
|
||||
// present, we need to extract it but keep the rest of hte message intact as it likely
|
||||
// has SEEN-BY, PATH, and other kludge information *appended*
|
||||
// :TODO: This is wrong: SAUCE may not have EOF marker for one, also if it's
|
||||
// present, we need to extract it but keep the rest of hte message intact as it likely
|
||||
// has SEEN-BY, PATH, and other kludge information *appended*
|
||||
const sauceHeaderPosition = messageBodyBuffer.indexOf(FTN_MESSAGE_SAUCE_HEADER);
|
||||
if(sauceHeaderPosition > -1) {
|
||||
sauce.readSAUCE(messageBodyBuffer.slice(sauceHeaderPosition, sauceHeaderPosition + sauce.SAUCE_SIZE), (err, theSauce) => {
|
||||
if(!err) {
|
||||
// we read some SAUCE - don't re-process that portion into the body
|
||||
messageBodyBuffer = messageBodyBuffer.slice(0, sauceHeaderPosition) + messageBodyBuffer.slice(sauceHeaderPosition + sauce.SAUCE_SIZE);
|
||||
// messageBodyBuffer = messageBodyBuffer.slice(0, sauceHeaderPosition);
|
||||
messageBodyData.sauce = theSauce;
|
||||
// we read some SAUCE - don't re-process that portion into the body
|
||||
messageBodyBuffer = messageBodyBuffer.slice(0, sauceHeaderPosition) + messageBodyBuffer.slice(sauceHeaderPosition + sauce.SAUCE_SIZE);
|
||||
// messageBodyBuffer = messageBodyBuffer.slice(0, sauceHeaderPosition);
|
||||
messageBodyData.sauce = theSauce;
|
||||
} else {
|
||||
Log.warn( { error : err.message }, 'Found what looks like to be a SAUCE record, but failed to read');
|
||||
}
|
||||
return callback(null); // failure to read SAUCE is OK
|
||||
return callback(null); // failure to read SAUCE is OK
|
||||
});
|
||||
} else {
|
||||
callback(null);
|
||||
|
@ -434,21 +434,21 @@ function Packet(options) {
|
|||
},
|
||||
function extractChrsAndDetermineEncoding(callback) {
|
||||
//
|
||||
// From FTS-5003.001:
|
||||
// "The CHRS control line is formatted as follows:
|
||||
// From FTS-5003.001:
|
||||
// "The CHRS control line is formatted as follows:
|
||||
//
|
||||
// ^ACHRS: <identifier> <level>
|
||||
// ^ACHRS: <identifier> <level>
|
||||
//
|
||||
// Where <identifier> is a character string of no more than eight (8)
|
||||
// ASCII characters identifying the character set or character encoding
|
||||
// scheme used, and level is a positive integer value describing what
|
||||
// level of CHRS the message is written in."
|
||||
// Where <identifier> is a character string of no more than eight (8)
|
||||
// ASCII characters identifying the character set or character encoding
|
||||
// scheme used, and level is a positive integer value describing what
|
||||
// level of CHRS the message is written in."
|
||||
//
|
||||
// Also according to the spec, the deprecated "CHARSET" value may be used
|
||||
// :TODO: Look into CHARSET more - should we bother supporting it?
|
||||
// :TODO: See encodingFromHeader() for CHRS/CHARSET support @ https://github.com/Mithgol/node-fidonet-jam
|
||||
const FTN_CHRS_PREFIX = Buffer.from( [ 0x01, 0x43, 0x48, 0x52, 0x53, 0x3a, 0x20 ] ); // "\x01CHRS:"
|
||||
const FTN_CHRS_SUFFIX = Buffer.from( [ 0x0d ] );
|
||||
// Also according to the spec, the deprecated "CHARSET" value may be used
|
||||
// :TODO: Look into CHARSET more - should we bother supporting it?
|
||||
// :TODO: See encodingFromHeader() for CHRS/CHARSET support @ https://github.com/Mithgol/node-fidonet-jam
|
||||
const FTN_CHRS_PREFIX = Buffer.from( [ 0x01, 0x43, 0x48, 0x52, 0x53, 0x3a, 0x20 ] ); // "\x01CHRS:"
|
||||
const FTN_CHRS_SUFFIX = Buffer.from( [ 0x0d ] );
|
||||
|
||||
let chrsPrefixIndex = messageBodyBuffer.indexOf(FTN_CHRS_PREFIX);
|
||||
if(chrsPrefixIndex < 0) {
|
||||
|
@ -476,9 +476,9 @@ function Packet(options) {
|
|||
},
|
||||
function extractMessageData(callback) {
|
||||
//
|
||||
// Decode |messageBodyBuffer| using |encoding| defaulted or detected above
|
||||
// Decode |messageBodyBuffer| using |encoding| defaulted or detected above
|
||||
//
|
||||
// :TODO: Look into \xec thing more - document
|
||||
// :TODO: Look into \xec thing more - document
|
||||
let decoded;
|
||||
try {
|
||||
decoded = iconv.decode(messageBodyBuffer, encoding);
|
||||
|
@ -487,8 +487,8 @@ function Packet(options) {
|
|||
decoded = iconv.decode(messageBodyBuffer, 'ascii');
|
||||
}
|
||||
|
||||
const messageLines = strUtil.splitTextAtTerms(decoded.replace(/\xec/g, ''));
|
||||
let endOfMessage = false;
|
||||
const messageLines = strUtil.splitTextAtTerms(decoded.replace(/\xec/g, ''));
|
||||
let endOfMessage = false;
|
||||
|
||||
messageLines.forEach(line => {
|
||||
if(0 === line.length) {
|
||||
|
@ -499,21 +499,21 @@ function Packet(options) {
|
|||
if(line.startsWith('AREA:')) {
|
||||
messageBodyData.area = line.substring(line.indexOf(':') + 1).trim();
|
||||
} else if(line.startsWith('--- ')) {
|
||||
// Tear Lines are tracked allowing for specialized display/etc.
|
||||
// Tear Lines are tracked allowing for specialized display/etc.
|
||||
messageBodyData.tearLine = line;
|
||||
} else if(/^[ ]{1,2}\* Origin: /.test(line)) { // To spec is " * Origin: ..."
|
||||
} else if(/^[ ]{1,2}\* Origin: /.test(line)) { // To spec is " * Origin: ..."
|
||||
messageBodyData.originLine = line;
|
||||
endOfMessage = true; // Anything past origin is not part of the message body
|
||||
endOfMessage = true; // Anything past origin is not part of the message body
|
||||
} else if(line.startsWith('SEEN-BY:')) {
|
||||
endOfMessage = true; // Anything past the first SEEN-BY is not part of the message body
|
||||
endOfMessage = true; // Anything past the first SEEN-BY is not part of the message body
|
||||
messageBodyData.seenBy.push(line.substring(line.indexOf(':') + 1).trim());
|
||||
} else if(FTN_MESSAGE_KLUDGE_PREFIX === line.charAt(0)) {
|
||||
if('PATH:' === line.slice(1, 6)) {
|
||||
endOfMessage = true; // Anything pats the first PATH is not part of the message body
|
||||
endOfMessage = true; // Anything pats the first PATH is not part of the message body
|
||||
}
|
||||
addKludgeLine(line.slice(1));
|
||||
} else if(!endOfMessage) {
|
||||
// regular ol' message line
|
||||
// regular ol' message line
|
||||
messageBodyData.message.push(line);
|
||||
}
|
||||
});
|
||||
|
@ -530,16 +530,16 @@ function Packet(options) {
|
|||
|
||||
this.parsePacketMessages = function(header, packetBuffer, iterator, cb) {
|
||||
//
|
||||
// Check for end-of-messages marker up front before parse so we can easily
|
||||
// tell the difference between end and bad header
|
||||
// Check for end-of-messages marker up front before parse so we can easily
|
||||
// tell the difference between end and bad header
|
||||
//
|
||||
if(packetBuffer.length < 3) {
|
||||
const peek = packetBuffer.slice(0, 2);
|
||||
if(peek.equals(Buffer.from([ 0x00 ])) || peek.equals(Buffer.from( [ 0x00, 0x00 ]))) {
|
||||
// end marker - no more messages
|
||||
// end marker - no more messages
|
||||
return cb(null);
|
||||
}
|
||||
// else fall through & hit exception below to log error
|
||||
// else fall through & hit exception below to log error
|
||||
}
|
||||
|
||||
let msgData;
|
||||
|
@ -552,26 +552,26 @@ function Packet(options) {
|
|||
.uint16le('ftn_msg_dest_net')
|
||||
.uint16le('ftn_attr_flags')
|
||||
.uint16le('ftn_cost')
|
||||
// :TODO: use string() for these if https://github.com/keichi/binary-parser/issues/33 is resolved
|
||||
// :TODO: use string() for these if https://github.com/keichi/binary-parser/issues/33 is resolved
|
||||
.array('modDateTime', {
|
||||
type : 'uint8',
|
||||
readUntil : b => 0x00 === b,
|
||||
type : 'uint8',
|
||||
readUntil : b => 0x00 === b,
|
||||
})
|
||||
.array('toUserName', {
|
||||
type : 'uint8',
|
||||
readUntil : b => 0x00 === b,
|
||||
type : 'uint8',
|
||||
readUntil : b => 0x00 === b,
|
||||
})
|
||||
.array('fromUserName', {
|
||||
type : 'uint8',
|
||||
readUntil : b => 0x00 === b,
|
||||
type : 'uint8',
|
||||
readUntil : b => 0x00 === b,
|
||||
})
|
||||
.array('subject', {
|
||||
type : 'uint8',
|
||||
readUntil : b => 0x00 === b,
|
||||
type : 'uint8',
|
||||
readUntil : b => 0x00 === b,
|
||||
})
|
||||
.array('message', {
|
||||
type : 'uint8',
|
||||
readUntil : b => 0x00 === b,
|
||||
type : 'uint8',
|
||||
readUntil : b => 0x00 === b,
|
||||
})
|
||||
.parse(packetBuffer);
|
||||
} catch(e) {
|
||||
|
@ -583,49 +583,49 @@ function Packet(options) {
|
|||
}
|
||||
|
||||
//
|
||||
// Convert null terminated arrays to strings
|
||||
// Convert null terminated arrays to strings
|
||||
//
|
||||
[ 'modDateTime', 'toUserName', 'fromUserName', 'subject' ].forEach(k => {
|
||||
msgData[k] = strUtil.stringFromNullTermBuffer(msgData[k], 'CP437');
|
||||
});
|
||||
|
||||
// Technically the following fields have length limits as per fts-0001.016:
|
||||
// * modDateTime : 20 bytes
|
||||
// * toUserName : 36 bytes
|
||||
// * fromUserName : 36 bytes
|
||||
// * subject : 72 bytes
|
||||
// Technically the following fields have length limits as per fts-0001.016:
|
||||
// * modDateTime : 20 bytes
|
||||
// * toUserName : 36 bytes
|
||||
// * fromUserName : 36 bytes
|
||||
// * subject : 72 bytes
|
||||
|
||||
//
|
||||
// The message body itself is a special beast as it may
|
||||
// contain an origin line, kludges, SAUCE in the case
|
||||
// of ANSI files, etc.
|
||||
// The message body itself is a special beast as it may
|
||||
// contain an origin line, kludges, SAUCE in the case
|
||||
// of ANSI files, etc.
|
||||
//
|
||||
const msg = new Message( {
|
||||
toUserName : msgData.toUserName,
|
||||
fromUserName : msgData.fromUserName,
|
||||
subject : msgData.subject,
|
||||
modTimestamp : ftn.getDateFromFtnDateTime(msgData.modDateTime),
|
||||
toUserName : msgData.toUserName,
|
||||
fromUserName : msgData.fromUserName,
|
||||
subject : msgData.subject,
|
||||
modTimestamp : ftn.getDateFromFtnDateTime(msgData.modDateTime),
|
||||
});
|
||||
|
||||
// :TODO: When non-private (e.g. EchoMail), attempt to extract SRC from MSGID vs headers, when avail (or Orgin line? research further)
|
||||
// :TODO: When non-private (e.g. EchoMail), attempt to extract SRC from MSGID vs headers, when avail (or Orgin line? research further)
|
||||
msg.meta.FtnProperty = {
|
||||
ftn_orig_node : header.origNode,
|
||||
ftn_dest_node : header.destNode,
|
||||
ftn_orig_network : header.origNet,
|
||||
ftn_dest_network : header.destNet,
|
||||
ftn_orig_node : header.origNode,
|
||||
ftn_dest_node : header.destNode,
|
||||
ftn_orig_network : header.origNet,
|
||||
ftn_dest_network : header.destNet,
|
||||
|
||||
ftn_attr_flags : msgData.ftn_attr_flags,
|
||||
ftn_cost : msgData.ftn_cost,
|
||||
ftn_attr_flags : msgData.ftn_attr_flags,
|
||||
ftn_cost : msgData.ftn_cost,
|
||||
|
||||
ftn_msg_orig_node : msgData.ftn_msg_orig_node,
|
||||
ftn_msg_dest_node : msgData.ftn_msg_dest_node,
|
||||
ftn_msg_orig_net : msgData.ftn_msg_orig_net,
|
||||
ftn_msg_dest_net : msgData.ftn_msg_dest_net,
|
||||
ftn_msg_orig_node : msgData.ftn_msg_orig_node,
|
||||
ftn_msg_dest_node : msgData.ftn_msg_dest_node,
|
||||
ftn_msg_orig_net : msgData.ftn_msg_orig_net,
|
||||
ftn_msg_dest_net : msgData.ftn_msg_dest_net,
|
||||
};
|
||||
|
||||
self.processMessageBody(msgData.message, messageBodyData => {
|
||||
msg.message = messageBodyData.message;
|
||||
msg.meta.FtnKludge = messageBodyData.kludgeLines;
|
||||
msg.message = messageBodyData.message;
|
||||
msg.meta.FtnKludge = messageBodyData.kludgeLines;
|
||||
|
||||
if(messageBodyData.tearLine) {
|
||||
msg.meta.FtnProperty.ftn_tear_line = messageBodyData.tearLine;
|
||||
|
@ -652,21 +652,21 @@ function Packet(options) {
|
|||
}
|
||||
|
||||
//
|
||||
// If we have a UTC offset kludge (e.g. TZUTC) then update
|
||||
// modDateTime with it
|
||||
// If we have a UTC offset kludge (e.g. TZUTC) then update
|
||||
// modDateTime with it
|
||||
//
|
||||
if(_.isString(msg.meta.FtnKludge.TZUTC) && msg.meta.FtnKludge.TZUTC.length > 0) {
|
||||
msg.modDateTime = msg.modTimestamp.utcOffset(msg.meta.FtnKludge.TZUTC);
|
||||
}
|
||||
|
||||
// :TODO: Parser should give is this info:
|
||||
// :TODO: Parser should give is this info:
|
||||
const bytesRead =
|
||||
14 + // fixed header size
|
||||
msgData.modDateTime.length + 1 + // +1 = NULL
|
||||
msgData.toUserName.length + 1 + // +1 = NULL
|
||||
msgData.fromUserName.length + 1 + // +1 = NULL
|
||||
msgData.subject.length + 1 + // +1 = NULL
|
||||
msgData.message.length; // includes NULL
|
||||
14 + // fixed header size
|
||||
msgData.modDateTime.length + 1 + // +1 = NULL
|
||||
msgData.toUserName.length + 1 + // +1 = NULL
|
||||
msgData.fromUserName.length + 1 + // +1 = NULL
|
||||
msgData.subject.length + 1 + // +1 = NULL
|
||||
msgData.message.length; // includes NULL
|
||||
|
||||
const nextBuf = packetBuffer.slice(bytesRead);
|
||||
if(nextBuf.length > 0) {
|
||||
|
@ -710,11 +710,11 @@ function Packet(options) {
|
|||
};
|
||||
|
||||
this.writeMessageHeader = function(message, buf) {
|
||||
// ensure address FtnProperties are numbers
|
||||
// ensure address FtnProperties are numbers
|
||||
self.sanatizeFtnProperties(message);
|
||||
|
||||
const destNode = message.meta.FtnProperty.ftn_msg_dest_node || message.meta.FtnProperty.ftn_dest_node;
|
||||
const destNet = message.meta.FtnProperty.ftn_msg_dest_net || message.meta.FtnProperty.ftn_dest_network;
|
||||
const destNode = message.meta.FtnProperty.ftn_msg_dest_node || message.meta.FtnProperty.ftn_dest_node;
|
||||
const destNet = message.meta.FtnProperty.ftn_msg_dest_net || message.meta.FtnProperty.ftn_dest_network;
|
||||
|
||||
buf.writeUInt16LE(FTN_PACKET_MESSAGE_TYPE, 0);
|
||||
buf.writeUInt16LE(message.meta.FtnProperty.ftn_orig_node, 2);
|
||||
|
@ -751,43 +751,43 @@ function Packet(options) {
|
|||
self.writeMessageHeader(message, basicHeader);
|
||||
|
||||
//
|
||||
// To, from, and subject must be NULL term'd and have max lengths as per spec.
|
||||
// To, from, and subject must be NULL term'd and have max lengths as per spec.
|
||||
//
|
||||
const toUserNameBuf = strUtil.stringToNullTermBuffer(message.toUserName, { encoding : 'cp437', maxBufLen : 36 } );
|
||||
const fromUserNameBuf = strUtil.stringToNullTermBuffer(message.fromUserName, { encoding : 'cp437', maxBufLen : 36 } );
|
||||
const subjectBuf = strUtil.stringToNullTermBuffer(message.subject, { encoding : 'cp437', maxBufLen : 72 } );
|
||||
const toUserNameBuf = strUtil.stringToNullTermBuffer(message.toUserName, { encoding : 'cp437', maxBufLen : 36 } );
|
||||
const fromUserNameBuf = strUtil.stringToNullTermBuffer(message.fromUserName, { encoding : 'cp437', maxBufLen : 36 } );
|
||||
const subjectBuf = strUtil.stringToNullTermBuffer(message.subject, { encoding : 'cp437', maxBufLen : 72 } );
|
||||
|
||||
//
|
||||
// message: unbound length, NULL term'd
|
||||
// message: unbound length, NULL term'd
|
||||
//
|
||||
// We need to build in various special lines - kludges, area,
|
||||
// seen-by, etc.
|
||||
// We need to build in various special lines - kludges, area,
|
||||
// seen-by, etc.
|
||||
//
|
||||
let msgBody = '';
|
||||
|
||||
//
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// AREA:CONFERENCE
|
||||
// Should be first line in a message
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// AREA:CONFERENCE
|
||||
// Should be first line in a message
|
||||
//
|
||||
if(message.meta.FtnProperty.ftn_area) {
|
||||
msgBody += `AREA:${message.meta.FtnProperty.ftn_area}\r`; // note: no ^A (0x01)
|
||||
msgBody += `AREA:${message.meta.FtnProperty.ftn_area}\r`; // note: no ^A (0x01)
|
||||
}
|
||||
|
||||
// :TODO: DRY with similar function in this file!
|
||||
// :TODO: DRY with similar function in this file!
|
||||
Object.keys(message.meta.FtnKludge).forEach(k => {
|
||||
switch(k) {
|
||||
case 'PATH' :
|
||||
break; // skip & save for last
|
||||
break; // skip & save for last
|
||||
|
||||
case 'Via' :
|
||||
case 'FMPT' :
|
||||
case 'TOPT' :
|
||||
case 'INTL' :
|
||||
msgBody += getAppendMeta(`\x01${k}`, message.meta.FtnKludge[k], ''); // no sepChar
|
||||
msgBody += getAppendMeta(`\x01${k}`, message.meta.FtnKludge[k], ''); // no sepChar
|
||||
break;
|
||||
|
||||
default :
|
||||
default :
|
||||
msgBody += getAppendMeta(`\x01${k}`, message.meta.FtnKludge[k]);
|
||||
break;
|
||||
}
|
||||
|
@ -803,10 +803,10 @@ function Packet(options) {
|
|||
ansiPrep(
|
||||
message.message,
|
||||
{
|
||||
cols : 80,
|
||||
rows : 'auto',
|
||||
forceLineTerm : true,
|
||||
exportMode : true,
|
||||
cols : 80,
|
||||
rows : 'auto',
|
||||
forceLineTerm : true,
|
||||
exportMode : true,
|
||||
},
|
||||
(err, preppedMsg) => {
|
||||
return callback(null, basicHeader, toUserNameBuf, fromUserNameBuf, subjectBuf, msgBody, preppedMsg || message.message);
|
||||
|
@ -817,25 +817,25 @@ function Packet(options) {
|
|||
msgBody += preppedMsg + '\r';
|
||||
|
||||
//
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// Tear line should be near the bottom of a message
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// Tear line should be near the bottom of a message
|
||||
//
|
||||
if(message.meta.FtnProperty.ftn_tear_line) {
|
||||
msgBody += `${message.meta.FtnProperty.ftn_tear_line}\r`;
|
||||
}
|
||||
|
||||
//
|
||||
// Origin line should be near the bottom of a message
|
||||
// Origin line should be near the bottom of a message
|
||||
//
|
||||
if(message.meta.FtnProperty.ftn_origin) {
|
||||
msgBody += `${message.meta.FtnProperty.ftn_origin}\r`;
|
||||
}
|
||||
|
||||
//
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// SEEN-BY and PATH should be the last lines of a message
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// SEEN-BY and PATH should be the last lines of a message
|
||||
//
|
||||
msgBody += getAppendMeta('SEEN-BY', message.meta.FtnProperty.ftn_seen_by); // note: no ^A (0x01)
|
||||
msgBody += getAppendMeta('SEEN-BY', message.meta.FtnProperty.ftn_seen_by); // note: no ^A (0x01)
|
||||
msgBody += getAppendMeta('\x01PATH', message.meta.FtnKludge['PATH']);
|
||||
|
||||
let msgBodyEncoded;
|
||||
|
@ -869,28 +869,28 @@ function Packet(options) {
|
|||
|
||||
ws.write(basicHeader);
|
||||
|
||||
// toUserName & fromUserName: up to 36 bytes in length, NULL term'd
|
||||
// :TODO: DRY...
|
||||
// toUserName & fromUserName: up to 36 bytes in length, NULL term'd
|
||||
// :TODO: DRY...
|
||||
let encBuf = iconv.encode(message.toUserName + '\0', 'CP437').slice(0, 36);
|
||||
encBuf[encBuf.length - 1] = '\0'; // ensure it's null term'd
|
||||
encBuf[encBuf.length - 1] = '\0'; // ensure it's null term'd
|
||||
ws.write(encBuf);
|
||||
|
||||
encBuf = iconv.encode(message.fromUserName + '\0', 'CP437').slice(0, 36);
|
||||
encBuf[encBuf.length - 1] = '\0'; // ensure it's null term'd
|
||||
encBuf[encBuf.length - 1] = '\0'; // ensure it's null term'd
|
||||
ws.write(encBuf);
|
||||
|
||||
// subject: up to 72 bytes in length, NULL term'd
|
||||
// subject: up to 72 bytes in length, NULL term'd
|
||||
encBuf = iconv.encode(message.subject + '\0', 'CP437').slice(0, 72);
|
||||
encBuf[encBuf.length - 1] = '\0'; // ensure it's null term'd
|
||||
encBuf[encBuf.length - 1] = '\0'; // ensure it's null term'd
|
||||
ws.write(encBuf);
|
||||
|
||||
//
|
||||
// message: unbound length, NULL term'd
|
||||
// message: unbound length, NULL term'd
|
||||
//
|
||||
// We need to build in various special lines - kludges, area,
|
||||
// seen-by, etc.
|
||||
// We need to build in various special lines - kludges, area,
|
||||
// seen-by, etc.
|
||||
//
|
||||
// :TODO: Put this in it's own method
|
||||
// :TODO: Put this in it's own method
|
||||
let msgBody = '';
|
||||
|
||||
function appendMeta(k, m, sepChar=':') {
|
||||
|
@ -906,54 +906,54 @@ function Packet(options) {
|
|||
}
|
||||
|
||||
//
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// AREA:CONFERENCE
|
||||
// Should be first line in a message
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// AREA:CONFERENCE
|
||||
// Should be first line in a message
|
||||
//
|
||||
if(message.meta.FtnProperty.ftn_area) {
|
||||
msgBody += `AREA:${message.meta.FtnProperty.ftn_area}\r`; // note: no ^A (0x01)
|
||||
msgBody += `AREA:${message.meta.FtnProperty.ftn_area}\r`; // note: no ^A (0x01)
|
||||
}
|
||||
|
||||
Object.keys(message.meta.FtnKludge).forEach(k => {
|
||||
switch(k) {
|
||||
case 'PATH' : break; // skip & save for last
|
||||
case 'PATH' : break; // skip & save for last
|
||||
|
||||
case 'Via' :
|
||||
case 'FMPT' :
|
||||
case 'TOPT' :
|
||||
case 'INTL' : appendMeta(`\x01${k}`, message.meta.FtnKludge[k], ''); break; // no sepChar
|
||||
case 'INTL' : appendMeta(`\x01${k}`, message.meta.FtnKludge[k], ''); break; // no sepChar
|
||||
|
||||
default : appendMeta(`\x01${k}`, message.meta.FtnKludge[k]); break;
|
||||
default : appendMeta(`\x01${k}`, message.meta.FtnKludge[k]); break;
|
||||
}
|
||||
});
|
||||
|
||||
msgBody += message.message + '\r';
|
||||
|
||||
//
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// Tear line should be near the bottom of a message
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// Tear line should be near the bottom of a message
|
||||
//
|
||||
if(message.meta.FtnProperty.ftn_tear_line) {
|
||||
msgBody += `${message.meta.FtnProperty.ftn_tear_line}\r`;
|
||||
}
|
||||
|
||||
//
|
||||
// Origin line should be near the bottom of a message
|
||||
// Origin line should be near the bottom of a message
|
||||
//
|
||||
if(message.meta.FtnProperty.ftn_origin) {
|
||||
msgBody += `${message.meta.FtnProperty.ftn_origin}\r`;
|
||||
}
|
||||
|
||||
//
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// SEEN-BY and PATH should be the last lines of a message
|
||||
// FTN-0004.001 @ http://ftsc.org/docs/fts-0004.001
|
||||
// SEEN-BY and PATH should be the last lines of a message
|
||||
//
|
||||
appendMeta('SEEN-BY', message.meta.FtnProperty.ftn_seen_by); // note: no ^A (0x01)
|
||||
appendMeta('SEEN-BY', message.meta.FtnProperty.ftn_seen_by); // note: no ^A (0x01)
|
||||
|
||||
appendMeta('\x01PATH', message.meta.FtnKludge['PATH']);
|
||||
|
||||
//
|
||||
// :TODO: We should encode based on config and add the proper kludge here!
|
||||
// :TODO: We should encode based on config and add the proper kludge here!
|
||||
ws.write(iconv.encode(msgBody + '\0', options.encoding));
|
||||
};
|
||||
|
||||
|
@ -981,35 +981,35 @@ function Packet(options) {
|
|||
callback);
|
||||
}
|
||||
],
|
||||
cb // complete
|
||||
cb // complete
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Message attributes defined in FTS-0001.016
|
||||
// http://ftsc.org/docs/fts-0001.016
|
||||
// Message attributes defined in FTS-0001.016
|
||||
// http://ftsc.org/docs/fts-0001.016
|
||||
//
|
||||
// See also:
|
||||
// * http://www.skepticfiles.org/aj/basics03.htm
|
||||
// See also:
|
||||
// * http://www.skepticfiles.org/aj/basics03.htm
|
||||
//
|
||||
Packet.Attribute = {
|
||||
Private : 0x0001, // Private message / NetMail
|
||||
Crash : 0x0002,
|
||||
Received : 0x0004,
|
||||
Sent : 0x0008,
|
||||
FileAttached : 0x0010,
|
||||
InTransit : 0x0020,
|
||||
Orphan : 0x0040,
|
||||
KillSent : 0x0080,
|
||||
Local : 0x0100, // Message is from *this* system
|
||||
Hold : 0x0200,
|
||||
Reserved0 : 0x0400,
|
||||
FileRequest : 0x0800,
|
||||
ReturnReceiptRequest : 0x1000,
|
||||
ReturnReceipt : 0x2000,
|
||||
AuditRequest : 0x4000,
|
||||
FileUpdateRequest : 0x8000,
|
||||
Private : 0x0001, // Private message / NetMail
|
||||
Crash : 0x0002,
|
||||
Received : 0x0004,
|
||||
Sent : 0x0008,
|
||||
FileAttached : 0x0010,
|
||||
InTransit : 0x0020,
|
||||
Orphan : 0x0040,
|
||||
KillSent : 0x0080,
|
||||
Local : 0x0100, // Message is from *this* system
|
||||
Hold : 0x0200,
|
||||
Reserved0 : 0x0400,
|
||||
FileRequest : 0x0800,
|
||||
ReturnReceiptRequest : 0x1000,
|
||||
ReturnReceipt : 0x2000,
|
||||
AuditRequest : 0x4000,
|
||||
FileUpdateRequest : 0x8000,
|
||||
};
|
||||
Object.freeze(Packet.Attribute);
|
||||
|
||||
|
@ -1051,10 +1051,10 @@ Packet.prototype.writeMessageEntry = function(ws, msgEntry) {
|
|||
|
||||
Packet.prototype.writeTerminator = function(ws) {
|
||||
//
|
||||
// From FTS-0001.016:
|
||||
// "A pseudo-message beginning with the word 0000H signifies the end of the packet."
|
||||
// From FTS-0001.016:
|
||||
// "A pseudo-message beginning with the word 0000H signifies the end of the packet."
|
||||
//
|
||||
ws.write(Buffer.from( [ 0x00, 0x00 ] )); // final extra null term
|
||||
ws.write(Buffer.from( [ 0x00, 0x00 ] )); // final extra null term
|
||||
return 2;
|
||||
};
|
||||
|
||||
|
@ -1074,7 +1074,7 @@ Packet.prototype.writeStream = function(ws, messages, options) {
|
|||
});
|
||||
|
||||
if(true === options.terminatePacket) {
|
||||
ws.write(Buffer.from( [ 0 ] )); // final extra null term
|
||||
ws.write(Buffer.from( [ 0 ] )); // final extra null term
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1083,10 +1083,10 @@ Packet.prototype.write = function(path, packetHeader, messages, options) {
|
|||
messages = [ messages ];
|
||||
}
|
||||
|
||||
options = options || { encoding : 'utf8' }; // utf-8 = 'CHRS UTF-8 4'
|
||||
options = options || { encoding : 'utf8' }; // utf-8 = 'CHRS UTF-8 4'
|
||||
|
||||
this.writeStream(
|
||||
fs.createWriteStream(path), // :TODO: specify mode/etc.
|
||||
fs.createWriteStream(path), // :TODO: specify mode/etc.
|
||||
messages,
|
||||
Object.assign( { packetHeader : packetHeader, terminatePacket : true }, options)
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue