Pardon the noise. More tab to space conversion!

This commit is contained in:
Bryan Ashby 2018-06-22 21:26:46 -06:00
parent c3635bb26b
commit 1d8be6b014
128 changed files with 8017 additions and 8017 deletions

View file

@ -1,52 +1,52 @@
/* jslint node: true */
'use strict';
const Config = require('./config.js').get;
const Address = require('./ftn_address.js');
const FNV1a = require('./fnv1a.js');
const getCleanEnigmaVersion = require('./misc_util.js').getCleanEnigmaVersion;
const Config = require('./config.js').get;
const Address = require('./ftn_address.js');
const FNV1a = require('./fnv1a.js');
const getCleanEnigmaVersion = require('./misc_util.js').getCleanEnigmaVersion;
const _ = require('lodash');
const iconv = require('iconv-lite');
const moment = require('moment');
const os = require('os');
const _ = require('lodash');
const iconv = require('iconv-lite');
const moment = require('moment');
const os = require('os');
const packageJson = require('../package.json');
const packageJson = require('../package.json');
// :TODO: Remove "Ftn" from most of these -- it's implied in the module
exports.stringToNullPaddedBuffer = stringToNullPaddedBuffer;
exports.getMessageSerialNumber = getMessageSerialNumber;
exports.getDateFromFtnDateTime = getDateFromFtnDateTime;
exports.getDateTimeString = getDateTimeString;
// :TODO: Remove "Ftn" from most of these -- it's implied in the module
exports.stringToNullPaddedBuffer = stringToNullPaddedBuffer;
exports.getMessageSerialNumber = getMessageSerialNumber;
exports.getDateFromFtnDateTime = getDateFromFtnDateTime;
exports.getDateTimeString = getDateTimeString;
exports.getMessageIdentifier = getMessageIdentifier;
exports.getProductIdentifier = getProductIdentifier;
exports.getUTCTimeZoneOffset = getUTCTimeZoneOffset;
exports.getOrigin = getOrigin;
exports.getTearLine = getTearLine;
exports.getVia = getVia;
exports.getIntl = getIntl;
exports.getAbbreviatedNetNodeList = getAbbreviatedNetNodeList;
exports.parseAbbreviatedNetNodeList = parseAbbreviatedNetNodeList;
exports.getUpdatedSeenByEntries = getUpdatedSeenByEntries;
exports.getUpdatedPathEntries = getUpdatedPathEntries;
exports.getMessageIdentifier = getMessageIdentifier;
exports.getProductIdentifier = getProductIdentifier;
exports.getUTCTimeZoneOffset = getUTCTimeZoneOffset;
exports.getOrigin = getOrigin;
exports.getTearLine = getTearLine;
exports.getVia = getVia;
exports.getIntl = getIntl;
exports.getAbbreviatedNetNodeList = getAbbreviatedNetNodeList;
exports.parseAbbreviatedNetNodeList = parseAbbreviatedNetNodeList;
exports.getUpdatedSeenByEntries = getUpdatedSeenByEntries;
exports.getUpdatedPathEntries = getUpdatedPathEntries;
exports.getCharacterSetIdentifierByEncoding = getCharacterSetIdentifierByEncoding;
exports.getEncodingFromCharacterSetIdentifier = getEncodingFromCharacterSetIdentifier;
exports.getCharacterSetIdentifierByEncoding = getCharacterSetIdentifierByEncoding;
exports.getEncodingFromCharacterSetIdentifier = getEncodingFromCharacterSetIdentifier;
exports.getQuotePrefix = getQuotePrefix;
exports.getQuotePrefix = getQuotePrefix;
//
// Namespace for RFC-4122 name based UUIDs generated from
// FTN kludges MSGID + AREA
// Namespace for RFC-4122 name based UUIDs generated from
// FTN kludges MSGID + AREA
//
//const ENIGMA_FTN_MSGID_NAMESPACE = uuid.parse('a5c7ae11-420c-4469-a116-0e9a6d8d2654');
//const ENIGMA_FTN_MSGID_NAMESPACE = uuid.parse('a5c7ae11-420c-4469-a116-0e9a6d8d2654');
// See list here: https://github.com/Mithgol/node-fidonet-jam
// See list here: https://github.com/Mithgol/node-fidonet-jam
function stringToNullPaddedBuffer(s, bufLen) {
let buffer = Buffer.alloc(bufLen);
let enc = iconv.encode(s, 'CP437').slice(0, bufLen);
let buffer = Buffer.alloc(bufLen);
let enc = iconv.encode(s, 'CP437').slice(0, bufLen);
for(let i = 0; i < enc.length; ++i) {
buffer[i] = enc[i];
}
@ -54,37 +54,37 @@ function stringToNullPaddedBuffer(s, bufLen) {
}
//
// Convert a FTN style DateTime string to a Date object
// Convert a FTN style DateTime string to a Date object
//
// :TODO: Name the next couple methods better - for FTN *packets*
// :TODO: Name the next couple methods better - for FTN *packets*
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"
// Examples seen in the wild (Working):
// "12 Sep 88 18:17:59"
// "Tue 01 Jan 80 00:00"
// "27 Feb 15 00:00:03"
//
// :TODO: Use moment.js here
return moment(Date.parse(dateTime)); // Date.parse() allows funky formats
// return (new Date(Date.parse(dateTime))).toISOString();
// :TODO: Use moment.js here
return moment(Date.parse(dateTime)); // Date.parse() allows funky formats
// return (new Date(Date.parse(dateTime))).toISOString();
}
function getDateTimeString(m) {
//
// From http://ftsc.org/docs/fts-0001.016:
// DateTime = (* a character string 20 characters long *)
// (* 01 Jan 86 02:34:56 *)
// DayOfMonth " " Month " " Year " "
// " " HH ":" MM ":" SS
// Null
// From http://ftsc.org/docs/fts-0001.016:
// DateTime = (* a character string 20 characters long *)
// (* 01 Jan 86 02:34:56 *)
// DayOfMonth " " Month " " Year " "
// " " HH ":" MM ":" SS
// Null
//
// DayOfMonth = "01" | "02" | "03" | ... | "31" (* Fido 0 fills *)
// Month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" |
// "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
// Year = "01" | "02" | .. | "85" | "86" | ... | "99" | "00"
// HH = "00" | .. | "23"
// MM = "00" | .. | "59"
// SS = "00" | .. | "59"
// DayOfMonth = "01" | "02" | "03" | ... | "31" (* Fido 0 fills *)
// Month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" |
// "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
// Year = "01" | "02" | .. | "85" | "86" | ... | "99" | "00"
// HH = "00" | .. | "23"
// MM = "00" | .. | "59"
// SS = "00" | .. | "59"
//
if(!moment.isMoment(m)) {
m = moment(m);
@ -95,52 +95,52 @@ function getDateTimeString(m) {
function getMessageSerialNumber(messageId) {
const msSinceEnigmaEpoc = (Date.now() - Date.UTC(2016, 1, 1));
const hash = Math.abs(new FNV1a(msSinceEnigmaEpoc + messageId).value).toString(16);
const hash = Math.abs(new FNV1a(msSinceEnigmaEpoc + messageId).value).toString(16);
return `00000000${hash}`.substr(-8);
}
//
// Return a FTS-0009.001 compliant MSGID value given a message
// See http://ftsc.org/docs/fts-0009.001
// Return a FTS-0009.001 compliant MSGID value given a message
// See http://ftsc.org/docs/fts-0009.001
//
// "A MSGID line consists of the string "^AMSGID:" (where ^A is a
// control-A (hex 01) and the double-quotes are not part of the
// string), followed by a space, the address of the originating
// system, and a serial number unique to that message on the
// originating system, i.e.:
// "A MSGID line consists of the string "^AMSGID:" (where ^A is a
// control-A (hex 01) and the double-quotes are not part of the
// string), followed by a space, the address of the originating
// system, and a serial number unique to that message on the
// originating system, i.e.:
//
// ^AMSGID: origaddr serialno
// ^AMSGID: origaddr serialno
//
// The originating address should be specified in a form that
// constitutes a valid return address for the originating network.
// If the originating address is enclosed in double-quotes, the
// entire string between the beginning and ending double-quotes is
// considered to be the orginating address. A double-quote character
// within a quoted address is represented by by two consecutive
// double-quote characters. The serial number may be any eight
// character hexadecimal number, as long as it is unique - no two
// messages from a given system may have the same serial number
// within a three years. The manner in which this serial number is
// generated is left to the implementor."
// The originating address should be specified in a form that
// constitutes a valid return address for the originating network.
// If the originating address is enclosed in double-quotes, the
// entire string between the beginning and ending double-quotes is
// considered to be the orginating address. A double-quote character
// within a quoted address is represented by by two consecutive
// double-quote characters. The serial number may be any eight
// character hexadecimal number, as long as it is unique - no two
// messages from a given system may have the same serial number
// within a three years. The manner in which this serial number is
// generated is left to the implementor."
//
//
// Examples & Implementations
// Examples & Implementations
//
// Synchronet: <msgNum>.<conf+area>@<ftnAddr> <serial>
// 2606.agora-agn_tst@46:1/142 19609217
// Synchronet: <msgNum>.<conf+area>@<ftnAddr> <serial>
// 2606.agora-agn_tst@46:1/142 19609217
//
// Mystic: <ftnAddress> <serial>
// 46:3/102 46686263
// Mystic: <ftnAddress> <serial>
// 46:3/102 46686263
//
// ENiGMA½: <messageId>.<areaTag>@<5dFtnAddress> <serial>
// ENiGMA½: <messageId>.<areaTag>@<5dFtnAddress> <serial>
//
// 0.0.8-alpha:
// Made compliant with FTN spec *when exporting NetMail* due to
// Mystic rejecting messages with the true-unique version.
// Strangely, Synchronet uses the unique format and Mystic does
// OK with it. Will need to research further. Note also that
// g00r00 was kind enough to fix Mystic to allow for the Sync/Enig
// format, but that will only help when using newer Mystic versions.
// 0.0.8-alpha:
// Made compliant with FTN spec *when exporting NetMail* due to
// Mystic rejecting messages with the true-unique version.
// Strangely, Synchronet uses the unique format and Mystic does
// OK with it. Will need to research further. Note also that
// g00r00 was kind enough to fix Mystic to allow for the Sync/Enig
// format, but that will only help when using newer Mystic versions.
//
function getMessageIdentifier(message, address, isNetMail = false) {
const addrStr = new Address(address).toString('5D');
@ -151,42 +151,42 @@ function getMessageIdentifier(message, address, isNetMail = false) {
}
//
// Return a FSC-0046.005 Product Identifier or "PID"
// http://ftsc.org/docs/fsc-0046.005
// Return a FSC-0046.005 Product Identifier or "PID"
// http://ftsc.org/docs/fsc-0046.005
//
// Note that we use a variant on the spec for <serial>
// in which (<os>; <arch>; <nodeVer>) is used instead
// Note that we use a variant on the spec for <serial>
// in which (<os>; <arch>; <nodeVer>) is used instead
//
function getProductIdentifier() {
const version = getCleanEnigmaVersion();
const nodeVer = process.version.substr(1); // remove 'v' prefix
const nodeVer = process.version.substr(1); // remove 'v' prefix
return `ENiGMA1/2 ${version} (${os.platform()}; ${os.arch()}; ${nodeVer})`;
}
//
// Return a FRL-1004 style time zone offset for a
// 'TZUTC' kludge line
// Return a FRL-1004 style time zone offset for a
// 'TZUTC' kludge line
//
// http://ftsc.org/docs/frl-1004.002
// http://ftsc.org/docs/frl-1004.002
//
function getUTCTimeZoneOffset() {
return moment().format('ZZ').replace(/\+/, '');
}
//
// Get a FSC-0032 style quote prefix
// http://ftsc.org/docs/fsc-0032.001
// Get a FSC-0032 style quote prefix
// http://ftsc.org/docs/fsc-0032.001
//
function getQuotePrefix(name) {
let initials;
const parts = name.split(' ');
if(parts.length > 1) {
// First & Last initials - (Bryan Ashby -> BA)
// First & Last initials - (Bryan Ashby -> BA)
initials = `${parts[0].slice(0, 1)}${parts[parts.length - 1].slice(0, 1)}`.toUpperCase();
} else {
// Just use the first two - (NuSkooler -> Nu)
// Just use the first two - (NuSkooler -> Nu)
initials = _.capitalize(name.slice(0, 2));
}
@ -194,8 +194,8 @@ function getQuotePrefix(name) {
}
//
// Return a FTS-0004 Origin line
// http://ftsc.org/docs/fts-0004.001
// Return a FTS-0004 Origin line
// http://ftsc.org/docs/fts-0004.001
//
function getOrigin(address) {
const config = Config();
@ -208,38 +208,38 @@ function getOrigin(address) {
}
function getTearLine() {
const nodeVer = process.version.substr(1); // remove 'v' prefix
const nodeVer = process.version.substr(1); // remove 'v' prefix
return `--- ENiGMA 1/2 v${packageJson.version} (${os.platform()}; ${os.arch()}; ${nodeVer})`;
}
//
// Return a FRL-1005.001 "Via" line
// http://ftsc.org/docs/frl-1005.001
// Return a FRL-1005.001 "Via" line
// http://ftsc.org/docs/frl-1005.001
//
function getVia(address) {
/*
FRL-1005.001 states teh following format:
FRL-1005.001 states teh following format:
^AVia: <FTN Address> @YYYYMMDD.HHMMSS[.Precise][.Time Zone]
<Program Name> <Version> [Serial Number]<CR>
*/
const addrStr = new Address(address).toString('5D');
const dateTime = moment().utc().format('YYYYMMDD.HHmmSS.SSSS.UTC');
const version = getCleanEnigmaVersion();
^AVia: <FTN Address> @YYYYMMDD.HHMMSS[.Precise][.Time Zone]
<Program Name> <Version> [Serial Number]<CR>
*/
const addrStr = new Address(address).toString('5D');
const dateTime = moment().utc().format('YYYYMMDD.HHmmSS.SSSS.UTC');
const version = getCleanEnigmaVersion();
return `${addrStr} @${dateTime} ENiGMA1/2 ${version}`;
}
//
// Creates a INTL kludge value as per FTS-4001
// http://retro.fidoweb.ru/docs/index=ftsc&doc=FTS-4001&enc=mac
// Creates a INTL kludge value as per FTS-4001
// http://retro.fidoweb.ru/docs/index=ftsc&doc=FTS-4001&enc=mac
//
function getIntl(toAddress, fromAddress) {
//
// INTL differs from 'standard' kludges in that there is no ':' after "INTL"
// INTL differs from 'standard' kludges in that there is no ':' after "INTL"
//
// "<SOH>"INTL "<destination address>" "<origin address><CR>"
// "...These addresses shall be given on the form <zone>:<net>/<node>"
// "<SOH>"INTL "<destination address>" "<origin address><CR>"
// "...These addresses shall be given on the form <zone>:<net>/<node>"
//
return `${toAddress.toString('3D')} ${fromAddress.toString('3D')}`;
}
@ -258,11 +258,11 @@ function getAbbreviatedNetNodeList(netNodes) {
abbrList += `${netNode.node} `;
});
return abbrList.trim(); // remove trailing space
return abbrList.trim(); // remove trailing space
}
//
// Parse an abbreviated net/node list commonly used for SEEN-BY and PATH
// Parse an abbreviated net/node list commonly used for SEEN-BY and PATH
//
function parseAbbreviatedNetNodeList(netNodes) {
const re = /([0-9]+)\/([0-9]+)\s?|([0-9]+)\s?/g;
@ -282,39 +282,39 @@ function parseAbbreviatedNetNodeList(netNodes) {
}
//
// Return a FTS-0004.001 SEEN-BY entry(s) that include
// all pre-existing SEEN-BY entries with the addition
// of |additions|.
// Return a FTS-0004.001 SEEN-BY entry(s) that include
// all pre-existing SEEN-BY entries with the addition
// of |additions|.
//
// See http://ftsc.org/docs/fts-0004.001
// and notes at http://ftsc.org/docs/fsc-0043.002.
// See http://ftsc.org/docs/fts-0004.001
// and notes at http://ftsc.org/docs/fsc-0043.002.
//
// For a great write up, see http://www.skepticfiles.org/aj/basics03.htm
// For a great write up, see http://www.skepticfiles.org/aj/basics03.htm
//
// This method returns an sorted array of values, but
// not the "SEEN-BY" prefix itself
// This method returns an sorted array of values, but
// not the "SEEN-BY" prefix itself
//
function getUpdatedSeenByEntries(existingEntries, additions) {
/*
From FTS-0004:
From FTS-0004:
"There can be many seen-by lines at the end of Conference
Mail messages, and they are the real "meat" of the control
information. They are used to determine the systems to
receive the exported messages. The format of the line is:
"There can be many seen-by lines at the end of Conference
Mail messages, and they are the real "meat" of the control
information. They are used to determine the systems to
receive the exported messages. The format of the line is:
SEEN-BY: 132/101 113 136/601 1014/1
SEEN-BY: 132/101 113 136/601 1014/1
The net/node numbers correspond to the net/node numbers of
the systems having already received the message. In this way
a message is never sent to a system twice. In a conference
with many participants the number of seen-by lines can be
very large. This line is added if it is not already a part
of the message, or added to if it already exists, each time
a message is exported to other systems. This is a REQUIRED
field, and Conference Mail will not function correctly if
this field is not put in place by other Echomail compatible
programs."
The net/node numbers correspond to the net/node numbers of
the systems having already received the message. In this way
a message is never sent to a system twice. In a conference
with many participants the number of seen-by lines can be
very large. This line is added if it is not already a part
of the message, or added to if it already exists, each time
a message is exported to other systems. This is a REQUIRED
field, and Conference Mail will not function correctly if
this field is not put in place by other Echomail compatible
programs."
*/
existingEntries = existingEntries || [];
if(!_.isArray(existingEntries)) {
@ -328,15 +328,15 @@ function getUpdatedSeenByEntries(existingEntries, additions) {
additions = additions.sort(Address.getComparator());
//
// For now, we'll just append a new SEEN-BY entry
// For now, we'll just append a new SEEN-BY entry
//
// :TODO: we should at least try and update what is already there in a smart way
// :TODO: we should at least try and update what is already there in a smart way
existingEntries.push(getAbbreviatedNetNodeList(additions));
return existingEntries;
}
function getUpdatedPathEntries(existingEntries, localAddress) {
// :TODO: append to PATH in a smart way! We shoudl try to fit at least the last existing line
// :TODO: append to PATH in a smart way! We shoudl try to fit at least the last existing line
existingEntries = existingEntries || [];
if(!_.isArray(existingEntries)) {
@ -350,23 +350,23 @@ function getUpdatedPathEntries(existingEntries, localAddress) {
}
//
// Return FTS-5000.001 "CHRS" value
// http://ftsc.org/docs/fts-5003.001
// Return FTS-5000.001 "CHRS" value
// http://ftsc.org/docs/fts-5003.001
//
const ENCODING_TO_FTS_5003_001_CHARS = {
// level 1 - generally should not be used
ascii : [ 'ASCII', 1 ],
'us-ascii' : [ 'ASCII', 1 ],
// level 1 - generally should not be used
ascii : [ 'ASCII', 1 ],
'us-ascii' : [ 'ASCII', 1 ],
// level 2 - 8 bit, ASCII based
cp437 : [ 'CP437', 2 ],
cp850 : [ 'CP850', 2 ],
// level 2 - 8 bit, ASCII based
cp437 : [ 'CP437', 2 ],
cp850 : [ 'CP850', 2 ],
// level 3 - reserved
// level 3 - reserved
// level 4
utf8 : [ 'UTF-8', 4 ],
'utf-8' : [ 'UTF-8', 4 ],
// level 4
utf8 : [ 'UTF-8', 4 ],
'utf-8' : [ 'UTF-8', 4 ],
};
@ -378,47 +378,47 @@ function getCharacterSetIdentifierByEncoding(encodingName) {
function getEncodingFromCharacterSetIdentifier(chrs) {
const ident = chrs.split(' ')[0].toUpperCase();
// :TODO: fill in the rest!!!
// :TODO: fill in the rest!!!
return {
// level 1
'ASCII' : 'iso-646-1',
'DUTCH' : 'iso-646',
'FINNISH' : 'iso-646-10',
'FRENCH' : 'iso-646',
'CANADIAN' : 'iso-646',
'GERMAN' : 'iso-646',
'ITALIAN' : 'iso-646',
'NORWEIG' : 'iso-646',
'PORTU' : 'iso-646',
'SPANISH' : 'iso-656',
'SWEDISH' : 'iso-646-10',
'SWISS' : 'iso-646',
'UK' : 'iso-646',
'ISO-10' : 'iso-646-10',
// level 1
'ASCII' : 'iso-646-1',
'DUTCH' : 'iso-646',
'FINNISH' : 'iso-646-10',
'FRENCH' : 'iso-646',
'CANADIAN' : 'iso-646',
'GERMAN' : 'iso-646',
'ITALIAN' : 'iso-646',
'NORWEIG' : 'iso-646',
'PORTU' : 'iso-646',
'SPANISH' : 'iso-656',
'SWEDISH' : 'iso-646-10',
'SWISS' : 'iso-646',
'UK' : 'iso-646',
'ISO-10' : 'iso-646-10',
// level 2
'CP437' : 'cp437',
'CP850' : 'cp850',
'CP852' : 'cp852',
'CP866' : 'cp866',
'CP848' : 'cp848',
'CP1250' : 'cp1250',
'CP1251' : 'cp1251',
'CP1252' : 'cp1252',
'CP10000' : 'macroman',
'LATIN-1' : 'iso-8859-1',
'LATIN-2' : 'iso-8859-2',
'LATIN-5' : 'iso-8859-9',
'LATIN-9' : 'iso-8859-15',
// level 2
'CP437' : 'cp437',
'CP850' : 'cp850',
'CP852' : 'cp852',
'CP866' : 'cp866',
'CP848' : 'cp848',
'CP1250' : 'cp1250',
'CP1251' : 'cp1251',
'CP1252' : 'cp1252',
'CP10000' : 'macroman',
'LATIN-1' : 'iso-8859-1',
'LATIN-2' : 'iso-8859-2',
'LATIN-5' : 'iso-8859-9',
'LATIN-9' : 'iso-8859-15',
// level 4
'UTF-8' : 'utf8',
// level 4
'UTF-8' : 'utf8',
// deprecated stuff
'IBMPC' : 'cp1250', // :TODO: validate
'+7_FIDO' : 'cp866',
'+7' : 'cp866',
'MAC' : 'macroman', // :TODO: validate
// deprecated stuff
'IBMPC' : 'cp1250', // :TODO: validate
'+7_FIDO' : 'cp866',
'+7' : 'cp866',
'MAC' : 'macroman', // :TODO: validate
}[ident];
}