NetMail avail to oputil & export - WIP
This commit is contained in:
parent
1c5a00313b
commit
fc40641eeb
|
@ -24,6 +24,7 @@
|
|||
"error",
|
||||
"always"
|
||||
],
|
||||
"comma-dangle": 0
|
||||
"comma-dangle": 0,
|
||||
"no-trailing-spaces" :"warn"
|
||||
}
|
||||
}
|
|
@ -72,6 +72,20 @@
|
|||
}
|
||||
}
|
||||
|
||||
loginSequenceFlavorSelect: {
|
||||
art: LOGINSEL
|
||||
mci: {
|
||||
TM1: {
|
||||
argName: promptValue
|
||||
items: [ "yes", "no" ]
|
||||
focus: true
|
||||
focusItemIndex: 1
|
||||
hotKeys: { Y: 0, N: 1 }
|
||||
hotKeySubmit: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loginGlobalNewScan: {
|
||||
art: GNSPMPT
|
||||
mci: {
|
||||
|
|
|
@ -19,7 +19,7 @@ function hasMessageConferenceAndArea(config) {
|
|||
assert(_.isObject(config.messageConferences)); // we create one ourself!
|
||||
|
||||
const nonInternalConfs = Object.keys(config.messageConferences).filter(confTag => {
|
||||
return 'system_internal' !== confTag;
|
||||
return 'system_internal' !== confTag;
|
||||
});
|
||||
|
||||
if(0 === nonInternalConfs.length) {
|
||||
|
@ -53,12 +53,12 @@ function init(configPath, options, cb) {
|
|||
if(!_.isString(configPath)) {
|
||||
return callback(null, { } );
|
||||
}
|
||||
|
||||
|
||||
fs.readFile(configPath, { encoding : 'utf8' }, (err, configData) => {
|
||||
if(err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
|
||||
let configJson;
|
||||
try {
|
||||
configJson = hjson.parse(configData, options);
|
||||
|
@ -70,7 +70,7 @@ function init(configPath, options, cb) {
|
|||
});
|
||||
},
|
||||
function mergeWithDefaultConfig(configJson, callback) {
|
||||
|
||||
|
||||
const mergedConfig = _.mergeWith(
|
||||
getDefaultConfig(),
|
||||
configJson, (conf1, conf2) => {
|
||||
|
@ -616,10 +616,11 @@ function getDefaultConfig() {
|
|||
scannerTossers : {
|
||||
ftn_bso : {
|
||||
paths : {
|
||||
outbound : paths.join(__dirname, './../mail/ftn_out/'),
|
||||
inbound : paths.join(__dirname, './../mail/ftn_in/'),
|
||||
secInbound : paths.join(__dirname, './../mail/ftn_secin/'),
|
||||
reject : paths.join(__dirname, './../mail/reject/'), // bad pkt, bundles, TIC attachments that fail any check, etc.
|
||||
outbound : paths.join(__dirname, './../mail/ftn_out/'),
|
||||
inbound : paths.join(__dirname, './../mail/ftn_in/'),
|
||||
secInbound : paths.join(__dirname, './../mail/ftn_secin/'),
|
||||
reject : paths.join(__dirname, './../mail/reject/'), // bad pkt, bundles, TIC attachments that fail any check, etc.
|
||||
outboundNetMail : paths.join(__dirname, './../mail/ftn_netmail_out/'),
|
||||
// set 'retain' to a valid path to keep good pkt files
|
||||
},
|
||||
|
||||
|
|
|
@ -393,9 +393,19 @@ function Packet(options) {
|
|||
};
|
||||
|
||||
function addKludgeLine(line) {
|
||||
const sepIndex = line.indexOf(':');
|
||||
const key = line.substr(0, sepIndex).toUpperCase();
|
||||
const value = line.substr(sepIndex + 1).trim();
|
||||
//
|
||||
// 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);
|
||||
let value;
|
||||
if( ['INTL', 'TOPT', 'FMPT' ].includes(key)) {
|
||||
value = line.substr(4).trim();
|
||||
} else {
|
||||
const sepIndex = line.indexOf(':');
|
||||
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
|
||||
|
@ -639,7 +649,7 @@ function Packet(options) {
|
|||
|
||||
this.getMessageEntryBuffer = function(message, options, cb) {
|
||||
|
||||
function getAppendMeta(k, m) {
|
||||
function getAppendMeta(k, m, sepChar=':') {
|
||||
let append = '';
|
||||
if(m) {
|
||||
let a = m;
|
||||
|
@ -647,7 +657,7 @@ function Packet(options) {
|
|||
a = [ a ];
|
||||
}
|
||||
a.forEach(v => {
|
||||
append += `${k}: ${v}\r`;
|
||||
append += `${k}${sepChar} ${v}\r`;
|
||||
});
|
||||
}
|
||||
return append;
|
||||
|
@ -693,10 +703,21 @@ function Packet(options) {
|
|||
msgBody += `AREA:${message.meta.FtnProperty.ftn_area}\r`; // note: no ^A (0x01)
|
||||
}
|
||||
|
||||
// :TODO: DRY with similar function in this file!
|
||||
Object.keys(message.meta.FtnKludge).forEach(k => {
|
||||
// we want PATH to be last
|
||||
if('PATH' !== k) {
|
||||
msgBody += getAppendMeta(`\x01${k}`, message.meta.FtnKludge[k]);
|
||||
switch(k) {
|
||||
case 'PATH' :
|
||||
break; // skip & save for last
|
||||
|
||||
case 'FMPT' :
|
||||
case 'TOPT' :
|
||||
case 'INTL' :
|
||||
msgBody += getAppendMeta(`\x01${k}`, message.meta.FtnKludge[k], ''); // no sepChar
|
||||
break;
|
||||
|
||||
default :
|
||||
msgBody += getAppendMeta(`\x01${k}`, message.meta.FtnKludge[k]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -810,14 +831,14 @@ function Packet(options) {
|
|||
// :TODO: Put this in it's own method
|
||||
let msgBody = '';
|
||||
|
||||
function appendMeta(k, m) {
|
||||
function appendMeta(k, m, sepChar=':') {
|
||||
if(m) {
|
||||
let a = m;
|
||||
if(!_.isArray(a)) {
|
||||
a = [ a ];
|
||||
}
|
||||
a.forEach(v => {
|
||||
msgBody += `${k}: ${v}\r`;
|
||||
msgBody += `${k}${sepChar} ${v}\r`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -832,9 +853,14 @@ function Packet(options) {
|
|||
}
|
||||
|
||||
Object.keys(message.meta.FtnKludge).forEach(k => {
|
||||
// we want PATH to be last
|
||||
if('PATH' !== k) {
|
||||
appendMeta(`\x01${k}`, message.meta.FtnKludge[k]);
|
||||
switch(k) {
|
||||
case 'PATH' : break; // skip & save for last
|
||||
|
||||
case 'FMPT' :
|
||||
case 'TOPT' :
|
||||
case 'INTL' : appendMeta(`\x01${k}`, message.meta.FtnKludge[k], ''); break; // no sepChar
|
||||
|
||||
default : appendMeta(`\x01${k}`, message.meta.FtnKludge[k]); break;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ exports.getUTCTimeZoneOffset = getUTCTimeZoneOffset;
|
|||
exports.getOrigin = getOrigin;
|
||||
exports.getTearLine = getTearLine;
|
||||
exports.getVia = getVia;
|
||||
exports.getIntl = getIntl;
|
||||
exports.getAbbreviatedNetNodeList = getAbbreviatedNetNodeList;
|
||||
exports.parseAbbreviatedNetNodeList = parseAbbreviatedNetNodeList;
|
||||
exports.getUpdatedSeenByEntries = getUpdatedSeenByEntries;
|
||||
|
@ -222,6 +223,20 @@ function getVia(address) {
|
|||
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
|
||||
//
|
||||
function getIntl(toAddress, fromAddress) {
|
||||
//
|
||||
// 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>"
|
||||
//
|
||||
return `${toAddress.toString('3D')} ${fromAddress.toString('3D')}`;
|
||||
}
|
||||
|
||||
function getAbbreviatedNetNodeList(netNodes) {
|
||||
let abbrList = '';
|
||||
let currNet;
|
||||
|
|
|
@ -124,11 +124,13 @@ Message.FtnPropertyNames = {
|
|||
// Note: kludges are stored with their names as-is
|
||||
|
||||
Message.prototype.setLocalToUserId = function(userId) {
|
||||
this.meta.System.local_to_user_id = userId;
|
||||
this.meta.System = this.meta.System || {};
|
||||
this.meta.System[Message.SystemMetaNames.LocalToUserID] = userId;
|
||||
};
|
||||
|
||||
Message.prototype.setLocalFromUserId = function(userId) {
|
||||
this.meta.System.local_from_user_id = userId;
|
||||
this.meta.System = this.meta.System || {};
|
||||
this.meta.System[Message.SystemMetaNames.LocalFromUserID] = userId;
|
||||
};
|
||||
|
||||
Message.createMessageUUID = function(areaTag, modTimestamp, subject, body) {
|
||||
|
|
|
@ -8,7 +8,7 @@ const argv = require('./oputil_common.js').argv;
|
|||
const initConfigAndDatabases = require('./oputil_common.js').initConfigAndDatabases;
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
const getAreaAndStorage = require('./oputil_common.js').getAreaAndStorage;
|
||||
const Errors = require('../../core/enig_error.js').Errors;
|
||||
const Errors = require('../enig_error.js').Errors;
|
||||
|
||||
const async = require('async');
|
||||
const fs = require('graceful-fs');
|
||||
|
|
|
@ -84,6 +84,14 @@ general information:
|
|||
FILENAME_WC filename with * and ? wildcard support. may match 0:n entries
|
||||
SHA full or partial SHA-256
|
||||
FILE_ID a file identifier. see file.sqlite3
|
||||
`,
|
||||
MessageBase :
|
||||
`usage: oputil.js mb <action> [<args>]
|
||||
|
||||
actions:
|
||||
areafix CMD1 CMD2 ... ADDR sends an AreaFix NetMail to ADDR with the supplied command(s)
|
||||
one or more commands may be supplied. commands that are multi
|
||||
part such as "%COMPRESS ZIP" should be quoted.
|
||||
`
|
||||
};
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ const argv = require('./oputil_common.js').argv;
|
|||
const printUsageAndSetExitCode = require('./oputil_common.js').printUsageAndSetExitCode;
|
||||
const handleUserCommand = require('./oputil_user.js').handleUserCommand;
|
||||
const handleFileBaseCommand = require('./oputil_file_base.js').handleFileBaseCommand;
|
||||
const handleMessageBaseCommand = require('./oputil_message_base.js').handleMessageBaseCommand;
|
||||
const handleConfigCommand = require('./oputil_config.js').handleConfigCommand;
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
|
||||
|
@ -26,19 +27,10 @@ module.exports = function() {
|
|||
}
|
||||
|
||||
switch(argv._[0]) {
|
||||
case 'user' :
|
||||
handleUserCommand();
|
||||
break;
|
||||
|
||||
case 'config' :
|
||||
handleConfigCommand();
|
||||
break;
|
||||
|
||||
case 'fb' :
|
||||
handleFileBaseCommand();
|
||||
break;
|
||||
|
||||
default:
|
||||
return printUsageAndSetExitCode(getHelpFor('General'), ExitCodes.BAD_COMMAND);
|
||||
case 'user' : return handleUserCommand();
|
||||
case 'config' : return handleConfigCommand();
|
||||
case 'fb' : return handleFileBaseCommand();
|
||||
case 'mb' : return handleMessageBaseCommand();
|
||||
default : return printUsageAndSetExitCode(getHelpFor('General'), ExitCodes.BAD_COMMAND);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
/* jslint node: true */
|
||||
/* eslint-disable no-console */
|
||||
'use strict';
|
||||
|
||||
const printUsageAndSetExitCode = require('./oputil_common.js').printUsageAndSetExitCode;
|
||||
const ExitCodes = require('./oputil_common.js').ExitCodes;
|
||||
const argv = require('./oputil_common.js').argv;
|
||||
const initConfigAndDatabases = require('./oputil_common.js').initConfigAndDatabases;
|
||||
const getHelpFor = require('./oputil_help.js').getHelpFor;
|
||||
const Address = require('../ftn_address.js');
|
||||
const Errors = require('../enig_error.js').Errors;
|
||||
|
||||
// deps
|
||||
const async = require('async');
|
||||
|
||||
exports.handleMessageBaseCommand = handleMessageBaseCommand;
|
||||
|
||||
function areaFix() {
|
||||
//
|
||||
// oputil mb areafix CMD1 CMD2 ... ADDR [--password PASS]
|
||||
//
|
||||
if(argv._.length < 3) {
|
||||
return printUsageAndSetExitCode(
|
||||
getHelpFor('MessageBase'),
|
||||
ExitCodes.ERROR
|
||||
);
|
||||
}
|
||||
|
||||
async.waterfall(
|
||||
[
|
||||
function init(callback) {
|
||||
return initConfigAndDatabases(callback);
|
||||
},
|
||||
function validateAddress(callback) {
|
||||
const addrArg = argv._.slice(-1)[0];
|
||||
const ftnAddr = Address.fromString(addrArg);
|
||||
|
||||
if(!ftnAddr) {
|
||||
return callback(Errors.Invalid(`"${addrArg}" is not a valid FTN address`));
|
||||
}
|
||||
|
||||
//
|
||||
// We need to validate the address targets a system we know unless
|
||||
// the --force option is used
|
||||
//
|
||||
// :TODO:
|
||||
return callback(null, ftnAddr);
|
||||
},
|
||||
function fetchFromUser(ftnAddr, callback) {
|
||||
//
|
||||
// --from USER || +op from system
|
||||
//
|
||||
// If possible, we want the user ID of the supplied user as well
|
||||
//
|
||||
const User = require('../user.js');
|
||||
|
||||
if(argv.from) {
|
||||
User.getUserIdAndName(argv.from, (err, userId, fromName) => {
|
||||
if(err) {
|
||||
return callback(null, ftnAddr, argv.from, 0);
|
||||
}
|
||||
|
||||
// fromName is the same as argv.from, but case may be differnet (yet correct)
|
||||
return callback(null, ftnAddr, fromName, userId);
|
||||
});
|
||||
} else {
|
||||
User.getUserName(User.RootUserID, (err, fromName) => {
|
||||
return callback(null, ftnAddr, fromName || 'SysOp', err ? 0 : User.RootUserID);
|
||||
});
|
||||
}
|
||||
},
|
||||
function createMessage(ftnAddr, fromName, fromUserId, callback) {
|
||||
//
|
||||
// Build message as commands separated by line feed
|
||||
//
|
||||
// We need to remove quotes from arguments. These are required
|
||||
// in the case of e.g. removing an area: "-SOME_AREA" would end
|
||||
// up confusing minimist, therefor they must be quoted: "'-SOME_AREA'"
|
||||
//
|
||||
const messageBody = argv._.slice(2, -1).map(arg => {
|
||||
return arg.replace(/["']/g, '');
|
||||
}).join('\n') + '\n';
|
||||
|
||||
const Message = require('../message.js');
|
||||
|
||||
const message = new Message({
|
||||
toUserName : argv.to || 'AreaFix',
|
||||
fromUserName : fromName,
|
||||
subject : argv.password || '',
|
||||
message : messageBody,
|
||||
areaTag : Message.WellKnownAreaTags.Private, // mark private
|
||||
meta : {
|
||||
FtnProperty : {
|
||||
[ Message.FtnPropertyNames.FtnDestZone ] : ftnAddr.zone,
|
||||
[ Message.FtnPropertyNames.FtnDestNetwork ] : ftnAddr.net,
|
||||
[ Message.FtnPropertyNames.FtnDestNode ] : ftnAddr.node,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if(ftnAddr.point) {
|
||||
message.meta.FtnProperty[Message.FtnPropertyNames.FtnDestPoint] = ftnAddr.point;
|
||||
}
|
||||
|
||||
if(0 !== fromUserId) {
|
||||
message.setLocalFromUserId(fromUserId);
|
||||
}
|
||||
|
||||
return callback(null, message);
|
||||
},
|
||||
function persistMessage(message, callback) {
|
||||
//
|
||||
// :TODO:
|
||||
// - Persist message in private outgoing (sysop out box)
|
||||
// - Make necessary changes such that the message is exported properly
|
||||
//
|
||||
console.log(message);
|
||||
message.persist(err => {
|
||||
return callback(err);
|
||||
});
|
||||
}
|
||||
],
|
||||
err => {
|
||||
if(err) {
|
||||
process.exitCode = ExitCodes.ERROR;
|
||||
console.error(`${err.message}${err.reason ? ': ' + err.reason : ''}`);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function handleMessageBaseCommand() {
|
||||
|
||||
function errUsage() {
|
||||
return printUsageAndSetExitCode(
|
||||
getHelpFor('MessageBase'),
|
||||
ExitCodes.ERROR
|
||||
);
|
||||
}
|
||||
|
||||
if(true === argv.help) {
|
||||
return errUsage();
|
||||
}
|
||||
|
||||
const action = argv._[1];
|
||||
|
||||
return({
|
||||
areafix : areaFix,
|
||||
}[action] || errUsage)();
|
||||
}
|
File diff suppressed because it is too large
Load Diff
16
package.json
16
package.json
|
@ -27,32 +27,32 @@
|
|||
"buffers": "NuSkooler/node-buffers",
|
||||
"bunyan": "^1.8.12",
|
||||
"exiftool": "^0.0.3",
|
||||
"fs-extra": "^4.0.1",
|
||||
"fs-extra": "^5.0.0",
|
||||
"glob": "^7.1.2",
|
||||
"graceful-fs": "^4.1.11",
|
||||
"hashids": "^1.1.1",
|
||||
"hjson": "^3.1.0",
|
||||
"iconv-lite": "^0.4.18",
|
||||
"inquirer": "^3.2.3",
|
||||
"inquirer": "^4.0.1",
|
||||
"later": "1.2.0",
|
||||
"lodash": "^4.17.4",
|
||||
"mime-types": "^2.1.17",
|
||||
"minimist": "1.2.x",
|
||||
"moment": "^2.18.1",
|
||||
"node-glob": "^1.2.0",
|
||||
"nodemailer": "^4.1.0",
|
||||
"moment": "^2.20.0",
|
||||
"nodemailer": "^4.4.1",
|
||||
"ptyw.js": "NuSkooler/ptyw.js",
|
||||
"rlogin": "^1.0.0",
|
||||
"sane": "^2.2.0",
|
||||
"sanitize-filename": "^1.6.1",
|
||||
"sqlite3": "^3.1.9",
|
||||
"sqlite3-trans" : "^1.2.0",
|
||||
"sqlite3-trans": "^1.2.0",
|
||||
"ssh2": "^0.5.5",
|
||||
"temptmp": "^1.0.0",
|
||||
"uuid": "^3.1.0",
|
||||
"uuid-parse": "^1.0.0",
|
||||
"ws": "^3.1.0",
|
||||
"ws": "^3.3.3",
|
||||
"xxhash": "^0.2.4",
|
||||
"yazl" : "^2.4.2"
|
||||
"yazl": "^2.4.2"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"engines": {
|
||||
|
|
Loading…
Reference in New Issue