From 1d8be6b014cd7ee71336e98f6af2f7a79cb2b9e5 Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Fri, 22 Jun 2018 21:26:46 -0600 Subject: [PATCH] Pardon the noise. More tab to space conversion! --- core/abracadabra.js | 148 ++--- core/acs.js | 30 +- core/ansi_escape_parser.js | 304 ++++----- core/ansi_prep.js | 76 +-- core/ansi_term.js | 458 +++++++------- core/archive_util.js | 98 +-- core/art.js | 172 ++--- core/asset.js | 38 +- core/bbs_link.js | 110 ++-- core/bbs_list.js | 162 ++--- core/button_view.js | 26 +- core/client.js | 414 ++++++------ core/client_connections.js | 70 +-- core/client_term.js | 100 +-- core/color_codes.js | 244 +++---- core/combatnet.js | 34 +- core/conf_area_util.js | 14 +- core/config.js | 824 ++++++++++++------------ core/config_cache.js | 12 +- core/config_util.js | 20 +- core/connect.js | 68 +- core/crc.js | 8 +- core/database.js | 350 +++++------ core/descript_ion_file.js | 32 +- core/door.js | 72 +-- core/door_party.js | 56 +- core/download_queue.js | 20 +- core/dropfile.js | 246 ++++---- core/edit_text_view.js | 34 +- core/email.js | 18 +- core/enig_error.js | 40 +- core/enigma_assert.js | 10 +- core/erc_client.js | 66 +- core/event_scheduler.js | 92 +-- core/events.js | 20 +- core/exodus.js | 160 ++--- core/file_area_filter_edit.js | 108 ++-- core/file_area_list.js | 264 ++++---- core/file_area_web.js | 154 ++--- core/file_base_area.js | 330 +++++----- core/file_base_area_select.js | 32 +- core/file_base_download_manager.js | 78 +-- core/file_base_filter.js | 28 +- core/file_base_list_export.js | 198 +++--- core/file_base_search.js | 62 +- core/file_base_user_list_export.js | 146 ++--- core/file_base_web_download_manager.js | 88 +-- core/file_entry.js | 250 ++++---- core/file_transfer.js | 246 ++++---- core/file_transfer_protocol_select.js | 60 +- core/file_util.js | 44 +- core/fnv1a.js | 8 +- core/fse.js | 418 ++++++------ core/ftn_address.js | 66 +- core/ftn_mail_packet.js | 590 ++++++++--------- core/ftn_util.js | 392 ++++++------ core/horizontal_menu_view.js | 36 +- core/key_entry_view.js | 20 +- core/last_callers.js | 74 +-- core/listening_server.js | 24 +- core/logger.js | 30 +- core/login_server_module.js | 30 +- core/mail_packet.js | 18 +- core/mail_util.js | 34 +- core/mask_edit_text_view.js | 78 +-- core/mci_view_factory.js | 112 ++-- core/menu_module.js | 126 ++-- core/menu_stack.js | 62 +- core/menu_util.js | 78 +-- core/menu_view.js | 68 +- core/message.js | 440 ++++++------- core/message_area.js | 216 +++---- core/message_base_search.js | 68 +- core/mime_util.js | 24 +- core/misc_util.js | 24 +- core/mod_mixins.js | 10 +- core/module_util.js | 38 +- core/msg_area_list.js | 84 +-- core/msg_area_post_fse.js | 20 +- core/msg_area_reply_fse.js | 10 +- core/msg_area_view_fse.js | 76 +-- core/msg_conf_list.js | 60 +- core/msg_list.js | 124 ++-- core/msg_network.js | 20 +- core/msg_scan_toss_module.js | 6 +- core/multi_line_edit_text_view.js | 462 +++++++------- core/new_scan.js | 102 +-- core/nua.js | 72 +-- core/onelinerz.js | 144 ++--- core/plugin_module.js | 2 +- core/predefined_mci.js | 222 +++---- core/rumorz.js | 90 +-- core/sauce.js | 150 ++--- core/scanner_tossers/ftn_bso.js | 838 ++++++++++++------------- core/server_module.js | 4 +- core/servers/content/gopher.js | 144 ++--- core/servers/content/web.js | 100 +-- core/servers/login/ssh.js | 96 +-- core/servers/login/telnet.js | 468 +++++++------- core/servers/login/websocket.js | 88 +-- core/set_newscan_date.js | 122 ++-- core/show_art.js | 46 +- core/spinner_menu_view.js | 30 +- core/standard_menu.js | 10 +- core/stat_log.js | 108 ++-- core/string_format.js | 148 ++--- core/string_util.js | 248 ++++---- core/system_events.js | 34 +- core/system_menu_method.js | 78 +-- core/system_view_validate.js | 64 +- core/telnet_bridge.js | 82 +-- core/text_view.js | 184 +++--- core/theme.js | 164 ++--- core/tic_file_info.js | 106 ++-- core/toggle_menu_view.js | 28 +- core/upload.js | 230 +++---- core/user.js | 162 ++--- core/user_config.js | 100 +-- core/user_group.js | 24 +- core/user_list.js | 68 +- core/user_login.js | 52 +- core/uuid_util.js | 18 +- core/vertical_menu_view.js | 90 +-- core/view.js | 136 ++-- core/view_controller.js | 276 ++++---- core/web_password_reset.js | 128 ++-- core/whos_online.js | 46 +- core/word_wrap.js | 52 +- 128 files changed, 8017 insertions(+), 8017 deletions(-) diff --git a/core/abracadabra.js b/core/abracadabra.js index 77a5e4c3..3a1507b6 100644 --- a/core/abracadabra.js +++ b/core/abracadabra.js @@ -1,62 +1,62 @@ /* jslint node: true */ 'use strict'; -const MenuModule = require('./menu_module.js').MenuModule; -const DropFile = require('./dropfile.js').DropFile; -const door = require('./door.js'); -const theme = require('./theme.js'); -const ansi = require('./ansi_term.js'); +const MenuModule = require('./menu_module.js').MenuModule; +const DropFile = require('./dropfile.js').DropFile; +const door = require('./door.js'); +const theme = require('./theme.js'); +const ansi = require('./ansi_term.js'); -const async = require('async'); -const assert = require('assert'); -const paths = require('path'); -const _ = require('lodash'); -const mkdirs = require('fs-extra').mkdirs; +const async = require('async'); +const assert = require('assert'); +const paths = require('path'); +const _ = require('lodash'); +const mkdirs = require('fs-extra').mkdirs; -// :TODO: This should really be a system module... needs a little work to allow for such +// :TODO: This should really be a system module... needs a little work to allow for such const activeDoorNodeInstances = {}; exports.moduleInfo = { - name : 'Abracadabra', - desc : 'External BBS Door Module', - author : 'NuSkooler', + name : 'Abracadabra', + desc : 'External BBS Door Module', + author : 'NuSkooler', }; /* - Example configuration for LORD under DOSEMU: + Example configuration for LORD under DOSEMU: - { - config: { - name: PimpWars - dropFileType: DORINFO - cmd: qemu-system-i386 - args: [ - "-localtime", - "freedos.img", - "-chardev", - "socket,port={srvPort},nowait,host=localhost,id=s0", - "-device", - "isa-serial,chardev=s0" - ] - io: socket - } - } + { + config: { + name: PimpWars + dropFileType: DORINFO + cmd: qemu-system-i386 + args: [ + "-localtime", + "freedos.img", + "-chardev", + "socket,port={srvPort},nowait,host=localhost,id=s0", + "-device", + "isa-serial,chardev=s0" + ] + io: socket + } + } - listen: socket | stdio + listen: socket | stdio - { - "config" : { - "name" : "LORD", - "dropFileType" : "DOOR", - "cmd" : "/usr/bin/dosemu", - "args" : [ "-quiet", "-f", "/etc/dosemu/dosemu.conf", "X:\\PW\\START.BAT {dropfile} {node}" ] ], - "nodeMax" : 32, - "tooManyArt" : "toomany-lord.ans" - } - } + { + "config" : { + "name" : "LORD", + "dropFileType" : "DOOR", + "cmd" : "/usr/bin/dosemu", + "args" : [ "-quiet", "-f", "/etc/dosemu/dosemu.conf", "X:\\PW\\START.BAT {dropfile} {node}" ] ], + "nodeMax" : 32, + "tooManyArt" : "toomany-lord.ans" + } + } - :TODO: See Mystic & others for other arg options that we may need to support + :TODO: See Mystic & others for other arg options that we may need to support */ exports.getModule = class AbracadabraModule extends MenuModule { @@ -64,21 +64,21 @@ exports.getModule = class AbracadabraModule extends MenuModule { super(options); this.config = options.menuConfig.config; - // :TODO: MenuModule.validateConfig(cb) -- validate config section gracefully instead of asserts! -- { key : type, key2 : type2, ... } - assert(_.isString(this.config.name, 'Config \'name\' is required')); - assert(_.isString(this.config.dropFileType, 'Config \'dropFileType\' is required')); - assert(_.isString(this.config.cmd, 'Config \'cmd\' is required')); + // :TODO: MenuModule.validateConfig(cb) -- validate config section gracefully instead of asserts! -- { key : type, key2 : type2, ... } + assert(_.isString(this.config.name, 'Config \'name\' is required')); + assert(_.isString(this.config.dropFileType, 'Config \'dropFileType\' is required')); + assert(_.isString(this.config.cmd, 'Config \'cmd\' is required')); - this.config.nodeMax = this.config.nodeMax || 0; - this.config.args = this.config.args || []; + this.config.nodeMax = this.config.nodeMax || 0; + this.config.args = this.config.args || []; } /* - :TODO: - * disconnecting wile door is open leaves dosemu - * http://bbslink.net/sysop.php support - * Font support ala all other menus... or does this just work? - */ + :TODO: + * disconnecting wile door is open leaves dosemu + * http://bbslink.net/sysop.php support + * Font support ala all other menus... or does this just work? + */ initSequence() { const self = this; @@ -87,12 +87,12 @@ exports.getModule = class AbracadabraModule extends MenuModule { [ function validateNodeCount(callback) { if(self.config.nodeMax > 0 && - _.isNumber(activeDoorNodeInstances[self.config.name]) && - activeDoorNodeInstances[self.config.name] + 1 > self.config.nodeMax) + _.isNumber(activeDoorNodeInstances[self.config.name]) && + activeDoorNodeInstances[self.config.name] + 1 > self.config.nodeMax) { self.client.log.info( { - name : self.config.name, + name : self.config.name, activeCount : activeDoorNodeInstances[self.config.name] }, 'Too many active instances'); @@ -106,13 +106,13 @@ exports.getModule = class AbracadabraModule extends MenuModule { } else { self.client.term.write('\nToo many active instances. Try again later.\n'); - // :TODO: Use MenuModule.pausePrompt() + // :TODO: Use MenuModule.pausePrompt() self.pausePrompt( () => { callback(new Error('Too many active instances')); }); } } else { - // :TODO: JS elegant way to do this? + // :TODO: JS elegant way to do this? if(activeDoorNodeInstances[self.config.name]) { activeDoorNodeInstances[self.config.name] += 1; } else { @@ -123,8 +123,8 @@ exports.getModule = class AbracadabraModule extends MenuModule { } }, function generateDropfile(callback) { - self.dropFile = new DropFile(self.client, self.config.dropFileType); - var fullPath = self.dropFile.fullPath; + self.dropFile = new DropFile(self.client, self.config.dropFileType); + var fullPath = self.dropFile.fullPath; mkdirs(paths.dirname(fullPath), function dirCreated(err) { if(err) { @@ -152,28 +152,28 @@ exports.getModule = class AbracadabraModule extends MenuModule { runDoor() { const exeInfo = { - cmd : this.config.cmd, - args : this.config.args, - io : this.config.io || 'stdio', - encoding : this.config.encoding || this.client.term.outputEncoding, - dropFile : this.dropFile.fileName, - node : this.client.node, - //inhSocket : this.client.output._handle.fd, + cmd : this.config.cmd, + args : this.config.args, + io : this.config.io || 'stdio', + encoding : this.config.encoding || this.client.term.outputEncoding, + dropFile : this.dropFile.fileName, + node : this.client.node, + //inhSocket : this.client.output._handle.fd, }; const doorInstance = new door.Door(this.client, exeInfo); doorInstance.once('finished', () => { // - // Try to clean up various settings such as scroll regions that may - // have been set within the door + // Try to clean up various settings such as scroll regions that may + // have been set within the door // this.client.term.rawWrite( ansi.normal() + - ansi.goto(this.client.term.termHeight, this.client.term.termWidth) + - ansi.setScrollRegion() + - ansi.goto(this.client.term.termHeight, 0) + - '\r\n\r\n' + ansi.goto(this.client.term.termHeight, this.client.term.termWidth) + + ansi.setScrollRegion() + + ansi.goto(this.client.term.termHeight, 0) + + '\r\n\r\n' ); this.prevMenu(); diff --git a/core/acs.js b/core/acs.js index b1461400..66f13a08 100644 --- a/core/acs.js +++ b/core/acs.js @@ -1,13 +1,13 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const checkAcs = require('./acs_parser.js').parse; -const Log = require('./logger.js').log; +// ENiGMA½ +const checkAcs = require('./acs_parser.js').parse; +const Log = require('./logger.js').log; -// deps -const assert = require('assert'); -const _ = require('lodash'); +// deps +const assert = require('assert'); +const _ = require('lodash'); class ACS { constructor(client) { @@ -26,7 +26,7 @@ class ACS { } // - // Message Conferences & Areas + // Message Conferences & Areas // hasMessageConfRead(conf) { return this.check(conf.acs, 'read', ACS.Defaults.MessageConfRead); @@ -37,7 +37,7 @@ class ACS { } // - // File Base / Areas + // File Base / Areas // hasFileAreaRead(area) { return this.check(area.acs, 'read', ACS.Defaults.FileAreaRead); @@ -53,7 +53,7 @@ class ACS { getConditionalValue(condArray, memberName) { if(!Array.isArray(condArray)) { - // no cond array, just use the value + // no cond array, just use the value return condArray; } @@ -68,7 +68,7 @@ class ACS { return false; } } else { - return true; // no acs check req. + return true; // no acs check req. } }); @@ -79,12 +79,12 @@ class ACS { } ACS.Defaults = { - MessageAreaRead : 'GM[users]', - MessageConfRead : 'GM[users]', + MessageAreaRead : 'GM[users]', + MessageConfRead : 'GM[users]', - FileAreaRead : 'GM[users]', - FileAreaWrite : 'GM[sysops]', - FileAreaDownload : 'GM[users]', + FileAreaRead : 'GM[users]', + FileAreaWrite : 'GM[sysops]', + FileAreaDownload : 'GM[users]', }; module.exports = ACS; diff --git a/core/ansi_escape_parser.js b/core/ansi_escape_parser.js index 49001363..9786ea4c 100644 --- a/core/ansi_escape_parser.js +++ b/core/ansi_escape_parser.js @@ -1,16 +1,16 @@ /* jslint node: true */ 'use strict'; -const miscUtil = require('./misc_util.js'); -const ansi = require('./ansi_term.js'); -const Log = require('./logger.js').log; +const miscUtil = require('./misc_util.js'); +const ansi = require('./ansi_term.js'); +const Log = require('./logger.js').log; -// deps -const events = require('events'); -const util = require('util'); -const _ = require('lodash'); +// deps +const events = require('events'); +const util = require('util'); +const _ = require('lodash'); -exports.ANSIEscapeParser = ANSIEscapeParser; +exports.ANSIEscapeParser = ANSIEscapeParser; const CR = 0x0d; const LF = 0x0a; @@ -20,76 +20,76 @@ function ANSIEscapeParser(options) { events.EventEmitter.call(this); - this.column = 1; - this.row = 1; - this.scrollBack = 0; - this.graphicRendition = {}; + this.column = 1; + this.row = 1; + this.scrollBack = 0; + this.graphicRendition = {}; this.parseState = { - re : /(?:\x1b\x5b)([?=;0-9]*?)([ABCDHJKfhlmnpsu])/g, // eslint-disable-line no-control-regex + re : /(?:\x1b\x5b)([?=;0-9]*?)([ABCDHJKfhlmnpsu])/g, // eslint-disable-line no-control-regex }; options = miscUtil.valueWithDefault(options, { - mciReplaceChar : '', - termHeight : 25, - termWidth : 80, - trailingLF : 'default', // default|omit|no|yes, ... + mciReplaceChar : '', + termHeight : 25, + termWidth : 80, + trailingLF : 'default', // default|omit|no|yes, ... }); - this.mciReplaceChar = miscUtil.valueWithDefault(options.mciReplaceChar, ''); - this.termHeight = miscUtil.valueWithDefault(options.termHeight, 25); - this.termWidth = miscUtil.valueWithDefault(options.termWidth, 80); - this.trailingLF = miscUtil.valueWithDefault(options.trailingLF, 'default'); + this.mciReplaceChar = miscUtil.valueWithDefault(options.mciReplaceChar, ''); + this.termHeight = miscUtil.valueWithDefault(options.termHeight, 25); + this.termWidth = miscUtil.valueWithDefault(options.termWidth, 80); + this.trailingLF = miscUtil.valueWithDefault(options.trailingLF, 'default'); self.moveCursor = function(cols, rows) { - self.column += cols; - self.row += rows; + self.column += cols; + self.row += rows; - self.column = Math.max(self.column, 1); - self.column = Math.min(self.column, self.termWidth); // can't move past term width - self.row = Math.max(self.row, 1); + self.column = Math.max(self.column, 1); + self.column = Math.min(self.column, self.termWidth); // can't move past term width + self.row = Math.max(self.row, 1); self.positionUpdated(); }; self.saveCursorPosition = function() { self.savedPosition = { - row : self.row, - column : self.column + row : self.row, + column : self.column }; }; self.restoreCursorPosition = function() { - self.row = self.savedPosition.row; - self.column = self.savedPosition.column; + self.row = self.savedPosition.row; + self.column = self.savedPosition.column; delete self.savedPosition; self.positionUpdated(); - // self.rowUpdated(); + // self.rowUpdated(); }; self.clearScreen = function() { - // :TODO: should be doing something with row/column? + // :TODO: should be doing something with row/column? self.emit('clear screen'); }; /* - self.rowUpdated = function() { - self.emit('row update', self.row + self.scrollBack); - };*/ + self.rowUpdated = function() { + self.emit('row update', self.row + self.scrollBack); + };*/ self.positionUpdated = function() { self.emit('position update', self.row, self.column); }; function literal(text) { - const len = text.length; - let pos = 0; - let start = 0; + const len = text.length; + let pos = 0; + let start = 0; let charCode; while(pos < len) { - charCode = text.charCodeAt(pos) & 0xff; // 8bit clean + charCode = text.charCodeAt(pos) & 0xff; // 8bit clean switch(charCode) { case CR : @@ -116,7 +116,7 @@ function ANSIEscapeParser(options) { start = pos + 1; self.column = 1; - self.row += 1; + self.row += 1; self.positionUpdated(); } else { @@ -129,11 +129,11 @@ function ANSIEscapeParser(options) { } // - // Finalize this chunk + // Finalize this chunk // if(self.column > self.termWidth) { self.column = 1; - self.row += 1; + self.row += 1; self.positionUpdated(); } @@ -145,7 +145,7 @@ function ANSIEscapeParser(options) { } function parseMCI(buffer) { - // :TODO: move this to "constants" seciton @ top + // :TODO: move this to "constants" seciton @ top var mciRe = /%([A-Z]{2})([0-9]{1,2})?(?:\(([0-9A-Za-z,]+)\))*/g; var pos = 0; var match; @@ -154,16 +154,16 @@ function ANSIEscapeParser(options) { var id; do { - pos = mciRe.lastIndex; - match = mciRe.exec(buffer); + pos = mciRe.lastIndex; + match = mciRe.exec(buffer); if(null !== match) { if(match.index > pos) { literal(buffer.slice(pos, match.index)); } - mciCode = match[1]; - id = match[2] || null; + mciCode = match[1]; + id = match[2] || null; if(match[3]) { args = match[3].split(','); @@ -171,7 +171,7 @@ function ANSIEscapeParser(options) { args = []; } - // if MCI codes are changing, save off the current color + // if MCI codes are changing, save off the current color var fullMciCode = mciCode + (id || ''); if(self.lastMciCode !== fullMciCode) { @@ -182,10 +182,10 @@ function ANSIEscapeParser(options) { self.emit('mci', { - mci : mciCode, - id : id ? parseInt(id, 10) : null, - args : args, - SGR : ansi.getSGRFromGraphicRendition(self.graphicRendition, true) + mci : mciCode, + id : id ? parseInt(id, 10) : null, + args : args, + SGR : ansi.getSGRFromGraphicRendition(self.graphicRendition, true) }); if(self.mciReplaceChar.length > 0) { @@ -208,10 +208,10 @@ function ANSIEscapeParser(options) { self.reset = function(input) { self.parseState = { - // ignore anything past EOF marker, if any - buffer : input.split(String.fromCharCode(0x1a), 1)[0], - re : /(?:\x1b\x5b)([?=;0-9]*?)([ABCDHJKfhlmnpsu])/g, // eslint-disable-line no-control-regex - stop : false, + // ignore anything past EOF marker, if any + buffer : input.split(String.fromCharCode(0x1a), 1)[0], + re : /(?:\x1b\x5b)([?=;0-9]*?)([ABCDHJKfhlmnpsu])/g, // eslint-disable-line no-control-regex + stop : false, }; }; @@ -224,13 +224,13 @@ function ANSIEscapeParser(options) { self.reset(input); } - // :TODO: ensure this conforms to ANSI-BBS / CTerm / bansi.txt for movement/etc. + // :TODO: ensure this conforms to ANSI-BBS / CTerm / bansi.txt for movement/etc. var pos; var match; var opCode; var args; - var re = self.parseState.re; - var buffer = self.parseState.buffer; + var re = self.parseState.re; + var buffer = self.parseState.buffer; self.parseState.stop = false; @@ -239,16 +239,16 @@ function ANSIEscapeParser(options) { return; } - pos = re.lastIndex; - match = re.exec(buffer); + pos = re.lastIndex; + match = re.exec(buffer); if(null !== match) { if(match.index > pos) { parseMCI(buffer.slice(pos, match.index)); } - opCode = match[2]; - args = match[1].split(';').map(v => parseInt(v, 10)); // convert to array of ints + opCode = match[2]; + args = match[1].split(';').map(v => parseInt(v, 10)); // convert to array of ints escape(opCode, args); @@ -260,13 +260,13 @@ function ANSIEscapeParser(options) { if(pos < buffer.length) { var lastBit = buffer.slice(pos); - // :TODO: check for various ending LF's, not just DOS \r\n + // :TODO: check for various ending LF's, not just DOS \r\n if('\r\n' === lastBit.slice(-2).toString()) { switch(self.trailingLF) { case 'default' : // - // Default is to *not* omit the trailing LF - // if we're going to end on termHeight + // Default is to *not* omit the trailing LF + // if we're going to end on termHeight // if(this.termHeight === self.row) { lastBit = lastBit.slice(0, -2); @@ -288,100 +288,100 @@ function ANSIEscapeParser(options) { }; /* - self.parse = function(buffer, savedRe) { - // :TODO: ensure this conforms to ANSI-BBS / CTerm / bansi.txt for movement/etc. - // :TODO: move this to "constants" section @ top - var re = /(?:\x1b\x5b)([\?=;0-9]*?)([ABCDHJKfhlmnpsu])/g; - var pos = 0; - var match; - var opCode; - var args; + self.parse = function(buffer, savedRe) { + // :TODO: ensure this conforms to ANSI-BBS / CTerm / bansi.txt for movement/etc. + // :TODO: move this to "constants" section @ top + var re = /(?:\x1b\x5b)([\?=;0-9]*?)([ABCDHJKfhlmnpsu])/g; + var pos = 0; + var match; + var opCode; + var args; - // ignore anything past EOF marker, if any - buffer = buffer.split(String.fromCharCode(0x1a), 1)[0]; + // ignore anything past EOF marker, if any + buffer = buffer.split(String.fromCharCode(0x1a), 1)[0]; - do { - pos = re.lastIndex; - match = re.exec(buffer); + do { + pos = re.lastIndex; + match = re.exec(buffer); - if(null !== match) { - if(match.index > pos) { - parseMCI(buffer.slice(pos, match.index)); - } + if(null !== match) { + if(match.index > pos) { + parseMCI(buffer.slice(pos, match.index)); + } - opCode = match[2]; - args = getArgArray(match[1].split(';')); + opCode = match[2]; + args = getArgArray(match[1].split(';')); - escape(opCode, args); + escape(opCode, args); - self.emit('chunk', match[0]); - } + self.emit('chunk', match[0]); + } - } while(0 !== re.lastIndex); + } while(0 !== re.lastIndex); - if(pos < buffer.length) { - parseMCI(buffer.slice(pos)); - } + if(pos < buffer.length) { + parseMCI(buffer.slice(pos)); + } - self.emit('complete'); - }; - */ + self.emit('complete'); + }; + */ function escape(opCode, args) { let arg; switch(opCode) { - // cursor up + // cursor up case 'A' : //arg = args[0] || 1; arg = isNaN(args[0]) ? 1 : args[0]; self.moveCursor(0, -arg); break; - // cursor down + // cursor down case 'B' : //arg = args[0] || 1; arg = isNaN(args[0]) ? 1 : args[0]; self.moveCursor(0, arg); break; - // cursor forward/right + // cursor forward/right case 'C' : //arg = args[0] || 1; arg = isNaN(args[0]) ? 1 : args[0]; self.moveCursor(arg, 0); break; - // cursor back/left + // cursor back/left case 'D' : //arg = args[0] || 1; arg = isNaN(args[0]) ? 1 : args[0]; self.moveCursor(-arg, 0); break; - case 'f' : // horiz & vertical - case 'H' : // cursor position - //self.row = args[0] || 1; - //self.column = args[1] || 1; - self.row = isNaN(args[0]) ? 1 : args[0]; - self.column = isNaN(args[1]) ? 1 : args[1]; + case 'f' : // horiz & vertical + case 'H' : // cursor position + //self.row = args[0] || 1; + //self.column = args[1] || 1; + self.row = isNaN(args[0]) ? 1 : args[0]; + self.column = isNaN(args[1]) ? 1 : args[1]; //self.rowUpdated(); self.positionUpdated(); break; - // save position + // save position case 's' : self.saveCursorPosition(); break; - // restore position + // restore position case 'u' : self.restoreCursorPosition(); break; - // set graphic rendition + // set graphic rendition case 'm' : self.graphicRendition.reset = false; @@ -395,7 +395,7 @@ function ANSIEscapeParser(options) { } else if(ANSIEscapeParser.styles[arg]) { switch(arg) { case 0 : - // clear out everything + // clear out everything delete self.graphicRendition.intensity; delete self.graphicRendition.underline; delete self.graphicRendition.blink; @@ -445,13 +445,13 @@ function ANSIEscapeParser(options) { } self.emit('sgr update', self.graphicRendition); - break; // m + break; // m - // :TODO: s, u, K + // :TODO: s, u, K - // erase display/screen + // erase display/screen case 'J' : - // :TODO: Handle other 'J' types! + // :TODO: Handle other 'J' types! if(2 === args[0]) { self.clearScreen(); } @@ -463,62 +463,62 @@ function ANSIEscapeParser(options) { util.inherits(ANSIEscapeParser, events.EventEmitter); ANSIEscapeParser.foregroundColors = { - 30 : 'black', - 31 : 'red', - 32 : 'green', - 33 : 'yellow', - 34 : 'blue', - 35 : 'magenta', - 36 : 'cyan', - 37 : 'white', - 39 : 'default', // same as white for most implementations + 30 : 'black', + 31 : 'red', + 32 : 'green', + 33 : 'yellow', + 34 : 'blue', + 35 : 'magenta', + 36 : 'cyan', + 37 : 'white', + 39 : 'default', // same as white for most implementations - 90 : 'grey' + 90 : 'grey' }; Object.freeze(ANSIEscapeParser.foregroundColors); ANSIEscapeParser.backgroundColors = { - 40 : 'black', - 41 : 'red', - 42 : 'green', - 43 : 'yellow', - 44 : 'blue', - 45 : 'magenta', - 46 : 'cyan', - 47 : 'white', - 49 : 'default', // same as black for most implementations + 40 : 'black', + 41 : 'red', + 42 : 'green', + 43 : 'yellow', + 44 : 'blue', + 45 : 'magenta', + 46 : 'cyan', + 47 : 'white', + 49 : 'default', // same as black for most implementations }; Object.freeze(ANSIEscapeParser.backgroundColors); -// :TODO: ensure these names all align with that of ansi_term.js +// :TODO: ensure these names all align with that of ansi_term.js // -// See the following specs: -// * http://www.ansi-bbs.org/ansi-bbs-core-server.html -// * http://www.vt100.net/docs/vt510-rm/SGR -// * https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt +// See the following specs: +// * http://www.ansi-bbs.org/ansi-bbs-core-server.html +// * http://www.vt100.net/docs/vt510-rm/SGR +// * https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt // -// Note that these are intentionally not in order such that they -// can be grouped by concept here in code. +// Note that these are intentionally not in order such that they +// can be grouped by concept here in code. // ANSIEscapeParser.styles = { - 0 : 'default', // Everything disabled + 0 : 'default', // Everything disabled - 1 : 'intensityBright', // aka bold - 2 : 'intensityDim', - 22 : 'intensityNormal', + 1 : 'intensityBright', // aka bold + 2 : 'intensityDim', + 22 : 'intensityNormal', - 4 : 'underlineOn', // Not supported by most BBS-like terminals - 24 : 'underlineOff', // Not supported by most BBS-like terminals + 4 : 'underlineOn', // Not supported by most BBS-like terminals + 24 : 'underlineOff', // Not supported by most BBS-like terminals - 5 : 'blinkSlow', // blinkSlow & blinkFast are generally treated the same - 6 : 'blinkFast', // blinkSlow & blinkFast are generally treated the same - 25 : 'blinkOff', + 5 : 'blinkSlow', // blinkSlow & blinkFast are generally treated the same + 6 : 'blinkFast', // blinkSlow & blinkFast are generally treated the same + 25 : 'blinkOff', - 7 : 'negativeImageOn', // Generally not supported or treated as "reverse FG & BG" - 27 : 'negativeImageOff', // Generally not supported or treated as "reverse FG & BG" + 7 : 'negativeImageOn', // Generally not supported or treated as "reverse FG & BG" + 27 : 'negativeImageOff', // Generally not supported or treated as "reverse FG & BG" - 8 : 'invisibleOn', // FG set to BG - 28 : 'invisibleOff', // Not supported by most BBS-like terminals + 8 : 'invisibleOn', // FG set to BG + 28 : 'invisibleOff', // Not supported by most BBS-like terminals }; Object.freeze(ANSIEscapeParser.styles); diff --git a/core/ansi_prep.js b/core/ansi_prep.js index 3eb05b08..09c9bbf6 100644 --- a/core/ansi_prep.js +++ b/core/ansi_prep.js @@ -1,38 +1,38 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const ANSIEscapeParser = require('./ansi_escape_parser.js').ANSIEscapeParser; -const ANSI = require('./ansi_term.js'); +// ENiGMA½ +const ANSIEscapeParser = require('./ansi_escape_parser.js').ANSIEscapeParser; +const ANSI = require('./ansi_term.js'); const { splitTextAtTerms, renderStringLength -} = require('./string_util.js'); +} = require('./string_util.js'); -// deps -const _ = require('lodash'); +// deps +const _ = require('lodash'); module.exports = function ansiPrep(input, options, cb) { if(!input) { return cb(null, ''); } - options.termWidth = options.termWidth || 80; - options.termHeight = options.termHeight || 25; - options.cols = options.cols || options.termWidth || 80; - options.rows = options.rows || options.termHeight || 'auto'; - options.startCol = options.startCol || 1; - options.exportMode = options.exportMode || false; - options.fillLines = _.get(options, 'fillLines', true); - options.indent = options.indent || 0; + options.termWidth = options.termWidth || 80; + options.termHeight = options.termHeight || 25; + options.cols = options.cols || options.termWidth || 80; + options.rows = options.rows || options.termHeight || 'auto'; + options.startCol = options.startCol || 1; + options.exportMode = options.exportMode || false; + options.fillLines = _.get(options, 'fillLines', true); + options.indent = options.indent || 0; - // in auto we start out at 25 rows, but can always expand for more + // in auto we start out at 25 rows, but can always expand for more const canvas = Array.from( { length : 'auto' === options.rows ? 25 : options.rows }, () => Array.from( { length : options.cols}, () => new Object() ) ); const parser = new ANSIEscapeParser( { termHeight : options.termHeight, termWidth : options.termWidth } ); const state = { - row : 0, - col : 0, + row : 0, + col : 0, }; let lastRow = 0; @@ -46,19 +46,19 @@ module.exports = function ansiPrep(input, options, cb) { } parser.on('position update', (row, col) => { - state.row = row - 1; - state.col = col - 1; + state.row = row - 1; + state.col = col - 1; if(0 === state.col) { state.initialSgr = state.lastSgr; } - lastRow = Math.max(state.row, lastRow); + lastRow = Math.max(state.row, lastRow); }); parser.on('literal', literal => { // - // CR/LF are handled for 'position update'; we don't need the chars themselves + // CR/LF are handled for 'position update'; we don't need the chars themselves // literal = literal.replace(/\r?\n|[\r\u2028\u2029]/g, ''); @@ -73,9 +73,9 @@ module.exports = function ansiPrep(input, options, cb) { canvas[state.row][state.col].char = c; if(state.sgr) { - canvas[state.row][state.col].sgr = _.clone(state.sgr); - state.lastSgr = canvas[state.row][state.col].sgr; - state.sgr = null; + canvas[state.row][state.col].sgr = _.clone(state.sgr); + state.lastSgr = canvas[state.row][state.col].sgr; + state.sgr = null; } } @@ -87,8 +87,8 @@ module.exports = function ansiPrep(input, options, cb) { ensureRow(state.row); if(state.col < options.cols) { - canvas[state.row][state.col].sgr = _.clone(sgr); - state.lastSgr = canvas[state.row][state.col].sgr; + canvas[state.row][state.col].sgr = _.clone(sgr); + state.lastSgr = canvas[state.row][state.col].sgr; } else { state.sgr = sgr; } @@ -147,16 +147,16 @@ module.exports = function ansiPrep(input, options, cb) { if(options.exportMode) { // - // If we're in export mode, we do some additional hackery: + // If we're in export mode, we do some additional hackery: // - // * Hard wrap ALL lines at <= 79 *characters* (not visible columns) - // if a line must wrap early, we'll place a ESC[A ESC[C where - // represents chars to get back to the position we were previously at + // * Hard wrap ALL lines at <= 79 *characters* (not visible columns) + // if a line must wrap early, we'll place a ESC[A ESC[C where + // represents chars to get back to the position we were previously at // - // * Replace contig spaces with ESC[C as well to save... space. + // * Replace contig spaces with ESC[C as well to save... space. // - // :TODO: this would be better to do as part of the processing above, but this will do for now - const MAX_CHARS = 79 - 8; // 79 max, - 8 for max ESC seq's we may prefix a line with + // :TODO: this would be better to do as part of the processing above, but this will do for now + const MAX_CHARS = 79 - 8; // 79 max, - 8 for max ESC seq's we may prefix a line with let exportOutput = ''; let m; @@ -176,16 +176,16 @@ module.exports = function ansiPrep(input, options, cb) { afterSeq = m.index + m[0].length; if(afterSeq < MAX_CHARS) { - // after current seq + // after current seq splitAt = afterSeq; } else { if(m.index < MAX_CHARS) { - // before last found seq + // before last found seq splitAt = m.index; - wantMore = false; // can't eat up any more + wantMore = false; // can't eat up any more } - break; // seq's beyond this point are >= MAX_CHARS + break; // seq's beyond this point are >= MAX_CHARS } } @@ -202,7 +202,7 @@ module.exports = function ansiPrep(input, options, cb) { renderStart += renderStringLength(part); exportOutput += `${part}\r\n`; - if(fullLine.length > 0) { // more to go for this line? + if(fullLine.length > 0) { // more to go for this line? exportOutput += `${ANSI.up()}${ANSI.right(renderStart)}`; } else { exportOutput += ANSI.up(); diff --git a/core/ansi_term.js b/core/ansi_term.js index dc3399a6..44c82464 100644 --- a/core/ansi_term.js +++ b/core/ansi_term.js @@ -2,191 +2,191 @@ 'use strict'; // -// ANSI Terminal Support Resources +// ANSI Terminal Support Resources // -// ANSI-BBS -// * http://ansi-bbs.org/ +// ANSI-BBS +// * http://ansi-bbs.org/ // -// CTerm / SyncTERM -// * https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt +// CTerm / SyncTERM +// * https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt // -// BananaCom -// * http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt +// BananaCom +// * http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt // -// ANSI.SYS -// * http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/ansisys.txt -// * http://academic.evergreen.edu/projects/biophysics/technotes/program/ansi_esc.htm +// ANSI.SYS +// * http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/ansisys.txt +// * http://academic.evergreen.edu/projects/biophysics/technotes/program/ansi_esc.htm // -// VTX -// * https://github.com/codewar65/VTX_ClientServer/blob/master/vtx.txt +// VTX +// * https://github.com/codewar65/VTX_ClientServer/blob/master/vtx.txt // -// General -// * http://en.wikipedia.org/wiki/ANSI_escape_code -// * http://www.inwap.com/pdp10/ansicode.txt +// General +// * http://en.wikipedia.org/wiki/ANSI_escape_code +// * http://www.inwap.com/pdp10/ansicode.txt // -// Other Implementations -// * https://github.com/chjj/term.js/blob/master/src/term.js +// Other Implementations +// * https://github.com/chjj/term.js/blob/master/src/term.js // // -// For a board, we need to support the semi-standard ANSI-BBS "spec" which -// is bastardized mix of DOS ANSI.SYS, cterm.txt, bansi.txt and a little other. -// This gives us NetRunner, SyncTERM, EtherTerm, most *nix terminals, compatibilitiy -// with legit oldschool DOS terminals, and so on. +// For a board, we need to support the semi-standard ANSI-BBS "spec" which +// is bastardized mix of DOS ANSI.SYS, cterm.txt, bansi.txt and a little other. +// This gives us NetRunner, SyncTERM, EtherTerm, most *nix terminals, compatibilitiy +// with legit oldschool DOS terminals, and so on. // -// ENiGMA½ -const miscUtil = require('./misc_util.js'); +// ENiGMA½ +const miscUtil = require('./misc_util.js'); -// deps -const assert = require('assert'); -const _ = require('lodash'); +// deps +const assert = require('assert'); +const _ = require('lodash'); -exports.getFullMatchRegExp = getFullMatchRegExp; -exports.getFGColorValue = getFGColorValue; -exports.getBGColorValue = getBGColorValue; -exports.sgr = sgr; -exports.getSGRFromGraphicRendition = getSGRFromGraphicRendition; -exports.clearScreen = clearScreen; -exports.resetScreen = resetScreen; -exports.normal = normal; -exports.goHome = goHome; -exports.disableVT100LineWrapping = disableVT100LineWrapping; -exports.setSyncTERMFont = setSyncTERMFont; -exports.getSyncTERMFontFromAlias = getSyncTERMFontFromAlias; -exports.setSyncTermFontWithAlias = setSyncTermFontWithAlias; -exports.setCursorStyle = setCursorStyle; -exports.setEmulatedBaudRate = setEmulatedBaudRate; -exports.vtxHyperlink = vtxHyperlink; +exports.getFullMatchRegExp = getFullMatchRegExp; +exports.getFGColorValue = getFGColorValue; +exports.getBGColorValue = getBGColorValue; +exports.sgr = sgr; +exports.getSGRFromGraphicRendition = getSGRFromGraphicRendition; +exports.clearScreen = clearScreen; +exports.resetScreen = resetScreen; +exports.normal = normal; +exports.goHome = goHome; +exports.disableVT100LineWrapping = disableVT100LineWrapping; +exports.setSyncTERMFont = setSyncTERMFont; +exports.getSyncTERMFontFromAlias = getSyncTERMFontFromAlias; +exports.setSyncTermFontWithAlias = setSyncTermFontWithAlias; +exports.setCursorStyle = setCursorStyle; +exports.setEmulatedBaudRate = setEmulatedBaudRate; +exports.vtxHyperlink = vtxHyperlink; // -// See also -// https://github.com/TooTallNate/ansi.js/blob/master/lib/ansi.js +// See also +// https://github.com/TooTallNate/ansi.js/blob/master/lib/ansi.js -const ESC_CSI = '\u001b['; +const ESC_CSI = '\u001b['; const CONTROL = { - up : 'A', - down : 'B', + up : 'A', + down : 'B', - forward : 'C', - right : 'C', + forward : 'C', + right : 'C', - back : 'D', - left : 'D', + back : 'D', + left : 'D', - nextLine : 'E', - prevLine : 'F', - horizAbsolute : 'G', + nextLine : 'E', + prevLine : 'F', + horizAbsolute : 'G', // - // CSI [ p1 ] J - // Erase in Page / Erase Data - // Defaults: p1 = 0 - // Erases from the current screen according to the value of p1 - // 0 - Erase from the current position to the end of the screen. - // 1 - Erase from the current position to the start of the screen. - // 2 - Erase entire screen. As a violation of ECMA-048, also moves - // the cursor to position 1/1 as a number of BBS programs assume - // this behaviour. - // Erased characters are set to the current attribute. + // CSI [ p1 ] J + // Erase in Page / Erase Data + // Defaults: p1 = 0 + // Erases from the current screen according to the value of p1 + // 0 - Erase from the current position to the end of the screen. + // 1 - Erase from the current position to the start of the screen. + // 2 - Erase entire screen. As a violation of ECMA-048, also moves + // the cursor to position 1/1 as a number of BBS programs assume + // this behaviour. + // Erased characters are set to the current attribute. // - // Support: - // * SyncTERM: Works as expected - // * NetRunner: Always clears a screen *height* (e.g. 25) regardless of p1 - // and screen remainder + // Support: + // * SyncTERM: Works as expected + // * NetRunner: Always clears a screen *height* (e.g. 25) regardless of p1 + // and screen remainder // - eraseData : 'J', + eraseData : 'J', - eraseLine : 'K', - insertLine : 'L', + eraseLine : 'K', + insertLine : 'L', // - // CSI [ p1 ] M - // Delete Line(s) / "ANSI" Music - // Defaults: p1 = 1 - // Deletes the current line and the p1 - 1 lines after it scrolling the - // first non-deleted line up to the current line and filling the newly - // empty lines at the end of the screen with the current attribute. - // If "ANSI" Music is fully enabled (CSI = 2 M), performs "ANSI" music - // instead. - // See "ANSI" MUSIC section for more details. + // CSI [ p1 ] M + // Delete Line(s) / "ANSI" Music + // Defaults: p1 = 1 + // Deletes the current line and the p1 - 1 lines after it scrolling the + // first non-deleted line up to the current line and filling the newly + // empty lines at the end of the screen with the current attribute. + // If "ANSI" Music is fully enabled (CSI = 2 M), performs "ANSI" music + // instead. + // See "ANSI" MUSIC section for more details. // - // Support: - // * SyncTERM: Works as expected - // * NetRunner: + // Support: + // * SyncTERM: Works as expected + // * NetRunner: // - // General Notes: - // See also notes in bansi.txt and cterm.txt about the various - // incompatibilities & oddities around this sequence. ANSI-BBS - // states that it *should* work with any value of p1. + // General Notes: + // See also notes in bansi.txt and cterm.txt about the various + // incompatibilities & oddities around this sequence. ANSI-BBS + // states that it *should* work with any value of p1. // - deleteLine : 'M', - ansiMusic : 'M', + deleteLine : 'M', + ansiMusic : 'M', - scrollUp : 'S', - scrollDown : 'T', - setScrollRegion : 'r', - savePos : 's', - restorePos : 'u', - queryPos : '6n', - queryScreenSize : '255n', // See bansi.txt - goto : 'H', // row Pr, column Pc -- same as f - gotoAlt : 'f', // same as H + scrollUp : 'S', + scrollDown : 'T', + setScrollRegion : 'r', + savePos : 's', + restorePos : 'u', + queryPos : '6n', + queryScreenSize : '255n', // See bansi.txt + goto : 'H', // row Pr, column Pc -- same as f + gotoAlt : 'f', // same as H blinkToBrightIntensity : '?33h', - blinkNormal : '?33l', + blinkNormal : '?33l', - emulationSpeed : '*r', // Set output emulation speed. See cterm.txt + emulationSpeed : '*r', // Set output emulation speed. See cterm.txt - hideCursor : '?25l', // Nonstandard - cterm.txt - showCursor : '?25h', // Nonstandard - cterm.txt + hideCursor : '?25l', // Nonstandard - cterm.txt + showCursor : '?25h', // Nonstandard - cterm.txt - queryDeviceAttributes : 'c', // Nonstandard - cterm.txt + queryDeviceAttributes : 'c', // Nonstandard - cterm.txt - // :TODO: see https://code.google.com/p/conemu-maximus5/wiki/AnsiEscapeCodes - // apparently some terms can report screen size and text area via 18t and 19t + // :TODO: see https://code.google.com/p/conemu-maximus5/wiki/AnsiEscapeCodes + // apparently some terms can report screen size and text area via 18t and 19t }; // -// Select Graphics Rendition -// See http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/src/conio/cterm.txt +// Select Graphics Rendition +// See http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/src/conio/cterm.txt // const SGRValues = { - reset : 0, - bold : 1, - dim : 2, - blink : 5, - fastBlink : 6, - negative : 7, - hidden : 8, + reset : 0, + bold : 1, + dim : 2, + blink : 5, + fastBlink : 6, + negative : 7, + hidden : 8, - normal : 22, // - steady : 25, - positive : 27, + normal : 22, // + steady : 25, + positive : 27, - black : 30, - red : 31, - green : 32, - yellow : 33, - blue : 34, - magenta : 35, - cyan : 36, - white : 37, + black : 30, + red : 31, + green : 32, + yellow : 33, + blue : 34, + magenta : 35, + cyan : 36, + white : 37, - blackBG : 40, - redBG : 41, - greenBG : 42, - yellowBG : 43, - blueBG : 44, - magentaBG : 45, - cyanBG : 46, - whiteBG : 47, + blackBG : 40, + redBG : 41, + greenBG : 42, + yellowBG : 43, + blueBG : 44, + magentaBG : 45, + cyanBG : 46, + whiteBG : 47, }; function getFullMatchRegExp(flags = 'g') { - // :TODO: expand this a bit - see strip-ansi/etc. - // :TODO: \u009b ? - return new RegExp(/[\u001b][[()#;?]*([0-9]{1,4}(?:;[0-9]{0,4})*)?([0-9A-ORZcf-npqrsuy=><])/, flags); // eslint-disable-line no-control-regex + // :TODO: expand this a bit - see strip-ansi/etc. + // :TODO: \u009b ? + return new RegExp(/[\u001b][[()#;?]*([0-9]{1,4}(?:;[0-9]{0,4})*)?([0-9A-ORZcf-npqrsuy=><])/, flags); // eslint-disable-line no-control-regex } function getFGColorValue(name) { @@ -198,20 +198,20 @@ function getBGColorValue(name) { } -// See http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/src/conio/cterm.txt -// :TODO: document -// :TODO: Create mappings for aliases... maybe make this a map to values instead -// :TODO: Break this up in to two parts: -// 1) FONT_AND_CODE_PAGES (e.g. SyncTERM/cterm) -// 2) SAUCE_FONT_MAP: Sauce name(s) -> items in FONT_AND_CODE_PAGES. -// ...we can then have getFontFromSAUCEName(sauceFontName) -// Also, create a SAUCE_ENCODING_MAP: SAUCE font name -> encodings +// See http://cvs.synchro.net/cgi-bin/viewcvs.cgi/*checkout*/src/conio/cterm.txt +// :TODO: document +// :TODO: Create mappings for aliases... maybe make this a map to values instead +// :TODO: Break this up in to two parts: +// 1) FONT_AND_CODE_PAGES (e.g. SyncTERM/cterm) +// 2) SAUCE_FONT_MAP: Sauce name(s) -> items in FONT_AND_CODE_PAGES. +// ...we can then have getFontFromSAUCEName(sauceFontName) +// Also, create a SAUCE_ENCODING_MAP: SAUCE font name -> encodings // -// An array of CTerm/SyncTERM font/encoding values. Each entry's index -// corresponds to it's escape sequence value (e.g. cp437 = 0) +// An array of CTerm/SyncTERM font/encoding values. Each entry's index +// corresponds to it's escape sequence value (e.g. cp437 = 0) // -// See https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt +// See https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt // const SYNCTERM_FONT_AND_ENCODING_TABLE = [ 'cp437', @@ -260,54 +260,54 @@ const SYNCTERM_FONT_AND_ENCODING_TABLE = [ ]; // -// A map of various font name/aliases such as those used -// in SAUCE records to SyncTERM/CTerm names +// A map of various font name/aliases such as those used +// in SAUCE records to SyncTERM/CTerm names // -// This table contains lowercased entries with any spaces -// replaced with '_' for lookup purposes. +// This table contains lowercased entries with any spaces +// replaced with '_' for lookup purposes. // const FONT_ALIAS_TO_SYNCTERM_MAP = { - 'cp437' : 'cp437', - 'ibm_vga' : 'cp437', - 'ibmpc' : 'cp437', - 'ibm_pc' : 'cp437', - 'pc' : 'cp437', - 'cp437_art' : 'cp437', - 'ibmpcart' : 'cp437', - 'ibmpc_art' : 'cp437', - 'ibm_pc_art' : 'cp437', - 'msdos_art' : 'cp437', - 'msdosart' : 'cp437', - 'pc_art' : 'cp437', - 'pcart' : 'cp437', + 'cp437' : 'cp437', + 'ibm_vga' : 'cp437', + 'ibmpc' : 'cp437', + 'ibm_pc' : 'cp437', + 'pc' : 'cp437', + 'cp437_art' : 'cp437', + 'ibmpcart' : 'cp437', + 'ibmpc_art' : 'cp437', + 'ibm_pc_art' : 'cp437', + 'msdos_art' : 'cp437', + 'msdosart' : 'cp437', + 'pc_art' : 'cp437', + 'pcart' : 'cp437', - 'ibm_vga50' : 'cp437', - 'ibm_vga25g' : 'cp437', - 'ibm_ega' : 'cp437', - 'ibm_ega43' : 'cp437', + 'ibm_vga50' : 'cp437', + 'ibm_vga25g' : 'cp437', + 'ibm_ega' : 'cp437', + 'ibm_ega43' : 'cp437', - 'topaz' : 'topaz', - 'amiga_topaz_1' : 'topaz', - 'amiga_topaz_1+' : 'topaz_plus', - 'topazplus' : 'topaz_plus', - 'topaz_plus' : 'topaz_plus', - 'amiga_topaz_2' : 'topaz', - 'amiga_topaz_2+' : 'topaz_plus', - 'topaz2plus' : 'topaz_plus', + 'topaz' : 'topaz', + 'amiga_topaz_1' : 'topaz', + 'amiga_topaz_1+' : 'topaz_plus', + 'topazplus' : 'topaz_plus', + 'topaz_plus' : 'topaz_plus', + 'amiga_topaz_2' : 'topaz', + 'amiga_topaz_2+' : 'topaz_plus', + 'topaz2plus' : 'topaz_plus', - 'pot_noodle' : 'pot_noodle', - 'p0tnoodle' : 'pot_noodle', - 'amiga_p0t-noodle' : 'pot_noodle', + 'pot_noodle' : 'pot_noodle', + 'p0tnoodle' : 'pot_noodle', + 'amiga_p0t-noodle' : 'pot_noodle', - 'mo_soul' : 'mo_soul', - 'mosoul' : 'mo_soul', - 'mO\'sOul' : 'mo_soul', + 'mo_soul' : 'mo_soul', + 'mosoul' : 'mo_soul', + 'mO\'sOul' : 'mo_soul', - 'amiga_microknight' : 'microknight', - 'amiga_microknight+' : 'microknight_plus', + 'amiga_microknight' : 'microknight', + 'amiga_microknight+' : 'microknight_plus', - 'atari' : 'atari', - 'atarist' : 'atari', + 'atari' : 'atari', + 'atarist' : 'atari', }; @@ -334,13 +334,13 @@ function setSyncTermFontWithAlias(nameOrAlias) { } const DEC_CURSOR_STYLE = { - 'blinking block' : 0, - 'default' : 1, - 'steady block' : 2, - 'blinking underline' : 3, - 'steady underline' : 4, - 'blinking bar' : 5, - 'steady bar' : 6, + 'blinking block' : 0, + 'default' : 1, + 'steady block' : 2, + 'blinking underline' : 3, + 'steady underline' : 4, + 'blinking bar' : 5, + 'steady bar' : 6, }; function setCursorStyle(cursorStyle) { @@ -352,21 +352,21 @@ function setCursorStyle(cursorStyle) { } -// Create methods such as up(), nextLine(),... +// Create methods such as up(), nextLine(),... Object.keys(CONTROL).forEach(function onControlName(name) { const code = CONTROL[name]; exports[name] = function() { let c = code; if(arguments.length > 0) { - // arguments are array like -- we want an array + // arguments are array like -- we want an array c = Array.prototype.slice.call(arguments).map(Math.round).join(';') + code; } return `${ESC_CSI}${c}`; }; }); -// Create various color methods such as white(), yellowBG(), reset(), ... +// Create various color methods such as white(), yellowBG(), reset(), ... Object.keys(SGRValues).forEach( name => { const code = SGRValues[name]; @@ -377,16 +377,16 @@ Object.keys(SGRValues).forEach( name => { function sgr() { // - // - Allow an single array or variable number of arguments - // - Each element can be either a integer or string found in SGRValues - // which in turn maps to a integer + // - Allow an single array or variable number of arguments + // - Each element can be either a integer or string found in SGRValues + // which in turn maps to a integer // if(arguments.length <= 0) { return ''; } - let result = []; - const args = Array.isArray(arguments[0]) ? arguments[0] : arguments; + let result = []; + const args = Array.isArray(arguments[0]) ? arguments[0] : arguments; for(let i = 0; i < args.length; ++i) { const arg = args[i]; @@ -401,12 +401,12 @@ function sgr() { } // -// Converts a Graphic Rendition object used elsewhere -// to a ANSI SGR sequence. +// Converts a Graphic Rendition object used elsewhere +// to a ANSI SGR sequence. // function getSGRFromGraphicRendition(graphicRendition, initialReset) { - let sgrSeq = []; - let styleCount = 0; + let sgrSeq = []; + let styleCount = 0; [ 'intensity', 'underline', 'blink', 'negative', 'invisible' ].forEach( s => { if(graphicRendition[s]) { @@ -431,7 +431,7 @@ function getSGRFromGraphicRendition(graphicRendition, initialReset) { } /////////////////////////////////////////////////////////////////////////////// -// Shortcuts for common functions +// Shortcuts for common functions /////////////////////////////////////////////////////////////////////////////// function clearScreen() { @@ -447,20 +447,20 @@ function normal() { } function goHome() { - return exports.goto(); // no params = home = 1,1 + return exports.goto(); // no params = home = 1,1 } // -// Disable auto line wraping @ termWidth +// Disable auto line wraping @ termWidth // -// See: -// http://stjarnhimlen.se/snippets/vt100.txt -// https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt +// See: +// http://stjarnhimlen.se/snippets/vt100.txt +// https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt // -// WARNING: -// * Not honored by all clients -// * If it is honored, ANSI's that rely on this (e.g. do not have \r\n endings -// and use term width -- generally 80 columns -- will display garbled! +// WARNING: +// * Not honored by all clients +// * If it is honored, ANSI's that rely on this (e.g. do not have \r\n endings +// and use term width -- generally 80 columns -- will display garbled! // function disableVT100LineWrapping() { return `${ESC_CSI}?7l`; @@ -468,20 +468,20 @@ function disableVT100LineWrapping() { function setEmulatedBaudRate(rate) { const speed = { - unlimited : 0, - off : 0, - 0 : 0, - 300 : 1, - 600 : 2, - 1200 : 3, - 2400 : 4, - 4800 : 5, - 9600 : 6, - 19200 : 7, - 38400 : 8, - 57600 : 9, - 76800 : 10, - 115200 : 11, + unlimited : 0, + off : 0, + 0 : 0, + 300 : 1, + 600 : 2, + 1200 : 3, + 2400 : 4, + 4800 : 5, + 9600 : 6, + 19200 : 7, + 38400 : 8, + 57600 : 9, + 76800 : 10, + 115200 : 11, }[rate] || 0; return 0 === speed ? exports.emulationSpeed() : exports.emulationSpeed(1, speed); } diff --git a/core/archive_util.js b/core/archive_util.js index d59a2609..c0076dba 100644 --- a/core/archive_util.js +++ b/core/archive_util.js @@ -1,26 +1,26 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('./config.js').get; -const stringFormat = require('./string_format.js'); -const Errors = require('./enig_error.js').Errors; -const resolveMimeType = require('./mime_util.js').resolveMimeType; +// ENiGMA½ +const Config = require('./config.js').get; +const stringFormat = require('./string_format.js'); +const Errors = require('./enig_error.js').Errors; +const resolveMimeType = require('./mime_util.js').resolveMimeType; -// base/modules -const fs = require('graceful-fs'); -const _ = require('lodash'); -const pty = require('node-pty'); -const paths = require('path'); +// base/modules +const fs = require('graceful-fs'); +const _ = require('lodash'); +const pty = require('node-pty'); +const paths = require('path'); let archiveUtil; class Archiver { constructor(config) { - this.compress = config.compress; - this.decompress = config.decompress; - this.list = config.list; - this.extract = config.extract; + this.compress = config.compress; + this.decompress = config.decompress; + this.list = config.list; + this.extract = config.extract; } ok() { @@ -37,7 +37,7 @@ class Archiver { canCompress() { return this.can('compress'); } canDecompress() { return this.can('decompress'); } - canList() { return this.can('list'); } // :TODO: validate entryMatch + canList() { return this.can('list'); } // :TODO: validate entryMatch canExtract() { return this.can('extract'); } } @@ -48,7 +48,7 @@ module.exports = class ArchiveUtil { this.longestSignature = 0; } - // singleton access + // singleton access static getInstance() { if(!archiveUtil) { archiveUtil = new ArchiveUtil(); @@ -59,17 +59,17 @@ module.exports = class ArchiveUtil { init() { // - // Load configuration + // Load configuration // const config = Config(); if(_.has(config, 'archives.archivers')) { Object.keys(config.archives.archivers).forEach(archKey => { - const archConfig = config.archives.archivers[archKey]; - const archiver = new Archiver(archConfig); + const archConfig = config.archives.archivers[archKey]; + const archiver = new Archiver(archConfig); if(!archiver.ok()) { - // :TODO: Log warning - bad archiver/config + // :TODO: Log warning - bad archiver/config } this.archivers[archKey] = archiver; @@ -78,10 +78,10 @@ module.exports = class ArchiveUtil { if(_.isObject(config.fileTypes)) { const updateSig = (ft) => { - ft.sig = Buffer.from(ft.sig, 'hex'); - ft.offset = ft.offset || 0; + ft.sig = Buffer.from(ft.sig, 'hex'); + ft.offset = ft.offset || 0; - // :TODO: this is broken: sig is NOT this long, it's sig.length long; offset needs to allow for -negative values as well + // :TODO: this is broken: sig is NOT this long, it's sig.length long; offset needs to allow for -negative values as well const sigLen = ft.offset + ft.sig.length; if(sigLen > this.longestSignature) { this.longestSignature = sigLen; @@ -106,7 +106,7 @@ module.exports = class ArchiveUtil { getArchiver(mimeTypeOrExtension, justExtention) { const mimeType = resolveMimeType(mimeTypeOrExtension); - if(!mimeType) { // lookup returns false on failure + if(!mimeType) { // lookup returns false on failure return; } @@ -115,10 +115,10 @@ module.exports = class ArchiveUtil { if(Array.isArray(fileType)) { if(!justExtention) { - // need extention for lookup; ambiguous as-is :( + // need extention for lookup; ambiguous as-is :( return; } - // further refine by extention + // further refine by extention fileType = fileType.find(ft => justExtention === ft.ext); } @@ -135,11 +135,11 @@ module.exports = class ArchiveUtil { return this.getArchiver(archType) ? true : false; } - // :TODO: implement me: + // :TODO: implement me: /* - detectTypeWithBuf(buf, cb) { - } - */ + detectTypeWithBuf(buf, cb) { + } + */ detectType(path, cb) { fs.open(path, 'r', (err, fd) => { @@ -177,8 +177,8 @@ module.exports = class ArchiveUtil { } spawnHandler(proc, action, cb) { - // pty.js doesn't currently give us a error when things fail, - // so we have this horrible, horrible hack: + // pty.js doesn't currently give us a error when things fail, + // so we have this horrible, horrible hack: let err; proc.once('data', d => { if(_.isString(d) && d.startsWith('execvp(3) failed.')) { @@ -199,8 +199,8 @@ module.exports = class ArchiveUtil { } const fmtObj = { - archivePath : archivePath, - fileList : files.join(' '), // :TODO: probably need same hack as extractTo here! + archivePath : archivePath, + fileList : files.join(' '), // :TODO: probably need same hack as extractTo here! }; const args = archiver.compress.args.map( arg => stringFormat(arg, fmtObj) ); @@ -233,25 +233,25 @@ module.exports = class ArchiveUtil { } const fmtObj = { - archivePath : archivePath, - extractPath : extractPath, + archivePath : archivePath, + extractPath : extractPath, }; let action = haveFileList ? 'extract' : 'decompress'; if('extract' === action && !_.isObject(archiver[action])) { - // we're forced to do a full decompress + // we're forced to do a full decompress action = 'decompress'; haveFileList = false; } - // we need to treat {fileList} special in that it should be broken up to 0:n args + // we need to treat {fileList} special in that it should be broken up to 0:n args const args = archiver[action].args.map( arg => { return '{fileList}' === arg ? arg : stringFormat(arg, fmtObj); }); const fileListPos = args.indexOf('{fileList}'); if(fileListPos > -1) { - // replace {fileList} with 0:n sep file list arguments + // replace {fileList} with 0:n sep file list arguments args.splice.apply(args, [fileListPos, 1].concat(fileList)); } @@ -273,10 +273,10 @@ module.exports = class ArchiveUtil { } const fmtObj = { - archivePath : archivePath, + archivePath : archivePath, }; - const args = archiver.list.args.map( arg => stringFormat(arg, fmtObj) ); + const args = archiver.list.args.map( arg => stringFormat(arg, fmtObj) ); let proc; try { @@ -287,7 +287,7 @@ module.exports = class ArchiveUtil { let output = ''; proc.on('data', data => { - // :TODO: hack for: execvp(3) failed.: No such file or directory + // :TODO: hack for: execvp(3) failed.: No such file or directory output += data; }); @@ -304,8 +304,8 @@ module.exports = class ArchiveUtil { let m; while((m = entryMatchRe.exec(output))) { entries.push({ - byteSize : parseInt(m[entryGroupOrder.byteSize]), - fileName : m[entryGroupOrder.fileName].trim(), + byteSize : parseInt(m[entryGroupOrder.byteSize]), + fileName : m[entryGroupOrder.fileName].trim(), }); } @@ -315,15 +315,15 @@ module.exports = class ArchiveUtil { getPtyOpts(extractPath) { const opts = { - name : 'enigma-archiver', - cols : 80, - rows : 24, - env : process.env, + name : 'enigma-archiver', + cols : 80, + rows : 24, + env : process.env, }; if(extractPath) { opts.cwd = extractPath; } - // :TODO: set cwd to supplied temp path if not sepcific extract + // :TODO: set cwd to supplied temp path if not sepcific extract return opts; } }; diff --git a/core/art.js b/core/art.js index 3a1949b1..315950e1 100644 --- a/core/art.js +++ b/core/art.js @@ -1,43 +1,43 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('./config.js').get; -const miscUtil = require('./misc_util.js'); -const ansi = require('./ansi_term.js'); -const aep = require('./ansi_escape_parser.js'); -const sauce = require('./sauce.js'); +// ENiGMA½ +const Config = require('./config.js').get; +const miscUtil = require('./misc_util.js'); +const ansi = require('./ansi_term.js'); +const aep = require('./ansi_escape_parser.js'); +const sauce = require('./sauce.js'); -// deps -const fs = require('graceful-fs'); -const paths = require('path'); -const assert = require('assert'); -const iconv = require('iconv-lite'); -const _ = require('lodash'); -const xxhash = require('xxhash'); +// deps +const fs = require('graceful-fs'); +const paths = require('path'); +const assert = require('assert'); +const iconv = require('iconv-lite'); +const _ = require('lodash'); +const xxhash = require('xxhash'); -exports.getArt = getArt; -exports.getArtFromPath = getArtFromPath; -exports.display = display; -exports.defaultEncodingFromExtension = defaultEncodingFromExtension; +exports.getArt = getArt; +exports.getArtFromPath = getArtFromPath; +exports.display = display; +exports.defaultEncodingFromExtension = defaultEncodingFromExtension; -// :TODO: Return MCI code information -// :TODO: process SAUCE comments -// :TODO: return font + font mapped information from SAUCE +// :TODO: Return MCI code information +// :TODO: process SAUCE comments +// :TODO: return font + font mapped information from SAUCE const SUPPORTED_ART_TYPES = { - // :TODO: the defualt encoding are really useless if they are all the same ... - // perhaps .ansamiga and .ascamiga could be supported as well as overrides via conf - '.ans' : { name : 'ANSI', defaultEncoding : 'cp437', eof : 0x1a }, - '.asc' : { name : 'ASCII', defaultEncoding : 'cp437', eof : 0x1a }, - '.pcb' : { name : 'PCBoard', defaultEncoding : 'cp437', eof : 0x1a }, - '.bbs' : { name : 'Wildcat', defaultEncoding : 'cp437', eof : 0x1a }, + // :TODO: the defualt encoding are really useless if they are all the same ... + // perhaps .ansamiga and .ascamiga could be supported as well as overrides via conf + '.ans' : { name : 'ANSI', defaultEncoding : 'cp437', eof : 0x1a }, + '.asc' : { name : 'ASCII', defaultEncoding : 'cp437', eof : 0x1a }, + '.pcb' : { name : 'PCBoard', defaultEncoding : 'cp437', eof : 0x1a }, + '.bbs' : { name : 'Wildcat', defaultEncoding : 'cp437', eof : 0x1a }, - '.amiga' : { name : 'Amiga', defaultEncoding : 'amiga', eof : 0x1a }, - '.txt' : { name : 'Amiga Text', defaultEncoding : 'cp437', eof : 0x1a }, - // :TODO: extentions for wwiv, renegade, celerity, syncronet, ... - // :TODO: extension for atari - // :TODO: extension for topaz ansi/ascii. + '.amiga' : { name : 'Amiga', defaultEncoding : 'amiga', eof : 0x1a }, + '.txt' : { name : 'Amiga Text', defaultEncoding : 'cp437', eof : 0x1a }, + // :TODO: extentions for wwiv, renegade, celerity, syncronet, ... + // :TODO: extension for atari + // :TODO: extension for topaz ansi/ascii. }; function getFontNameFromSAUCE(sauce) { @@ -47,8 +47,8 @@ function getFontNameFromSAUCE(sauce) { } function sliceAtEOF(data, eofMarker) { - let eof = data.length; - const stopPos = Math.max(data.length - (256), 0); // 256 = 2 * sizeof(SAUCE) + let eof = data.length; + const stopPos = Math.max(data.length - (256), 0); // 256 = 2 * sizeof(SAUCE) for(let i = eof - 1; i > stopPos; i--) { if(eofMarker === data[i]) { @@ -66,12 +66,12 @@ function getArtFromPath(path, options, cb) { } // - // Convert from encodedAs -> j + // Convert from encodedAs -> j // - const ext = paths.extname(path).toLowerCase(); - const encoding = options.encodedAs || defaultEncodingFromExtension(ext); + const ext = paths.extname(path).toLowerCase(); + const encoding = options.encodedAs || defaultEncodingFromExtension(ext); - // :TODO: how are BOM's currently handled if present? Are they removed? Do we need to? + // :TODO: how are BOM's currently handled if present? Are they removed? Do we need to? function sliceOfData() { if(options.fullFile === true) { @@ -84,8 +84,8 @@ function getArtFromPath(path, options, cb) { function getResult(sauce) { const result = { - data : sliceOfData(), - fromPath : path, + data : sliceOfData(), + fromPath : path, }; if(sauce) { @@ -102,18 +102,18 @@ function getArtFromPath(path, options, cb) { } // - // If a encoding was not provided & we have a mapping from - // the information provided by SAUCE, use that. + // If a encoding was not provided & we have a mapping from + // the information provided by SAUCE, use that. // if(!options.encodedAs) { /* - if(sauce.Character && sauce.Character.fontName) { - var enc = SAUCE_FONT_TO_ENCODING_HINT[sauce.Character.fontName]; - if(enc) { - encoding = enc; - } - } - */ + if(sauce.Character && sauce.Character.fontName) { + var enc = SAUCE_FONT_TO_ENCODING_HINT[sauce.Character.fontName]; + if(enc) { + encoding = enc; + } + } + */ } return cb(null, getResult(sauce)); }); @@ -126,10 +126,10 @@ function getArtFromPath(path, options, cb) { function getArt(name, options, cb) { const ext = paths.extname(name); - options.basePath = miscUtil.valueWithDefault(options.basePath, Config().paths.art); - options.asAnsi = miscUtil.valueWithDefault(options.asAnsi, true); + options.basePath = miscUtil.valueWithDefault(options.basePath, Config().paths.art); + options.asAnsi = miscUtil.valueWithDefault(options.asAnsi, true); - // :TODO: make use of asAnsi option and convert from supported -> ansi + // :TODO: make use of asAnsi option and convert from supported -> ansi if('' !== ext) { options.types = [ ext.toLowerCase() ]; @@ -141,7 +141,7 @@ function getArt(name, options, cb) { } } - // If an extension is provided, just read the file now + // If an extension is provided, just read the file now if('' !== ext) { const directPath = paths.join(options.basePath, name); return getArtFromPath(directPath, options, cb); @@ -225,10 +225,10 @@ function defaultEofFromExtension(ext) { } } -// :TODO: Implement the following -// * Pause (disabled | termHeight | keyPress ) -// * Cancel (disabled | ) -// * Resume from pause -> continous (disabled | ) +// :TODO: Implement the following +// * Pause (disabled | termHeight | keyPress ) +// * Cancel (disabled | ) +// * Resume from pause -> continous (disabled | ) function display(client, art, options, cb) { if(_.isFunction(options) && !cb) { cb = options; @@ -239,25 +239,25 @@ function display(client, art, options, cb) { return cb(new Error('Empty art')); } - options.mciReplaceChar = options.mciReplaceChar || ' '; - options.disableMciCache = options.disableMciCache || false; + options.mciReplaceChar = options.mciReplaceChar || ' '; + options.disableMciCache = options.disableMciCache || false; - // :TODO: this is going to be broken into two approaches controlled via options: - // 1) Standard - use internal tracking of locations for MCI -- no CPR's/etc. - // 2) CPR driven + // :TODO: this is going to be broken into two approaches controlled via options: + // 1) Standard - use internal tracking of locations for MCI -- no CPR's/etc. + // 2) CPR driven if(!_.isBoolean(options.iceColors)) { - // try to detect from SAUCE + // try to detect from SAUCE if(_.has(options, 'sauce.ansiFlags') && (options.sauce.ansiFlags & (1 << 0))) { options.iceColors = true; } } const ansiParser = new aep.ANSIEscapeParser({ - mciReplaceChar : options.mciReplaceChar, - termHeight : client.term.termHeight, - termWidth : client.term.termWidth, - trailingLF : options.trailingLF, + mciReplaceChar : options.mciReplaceChar, + termHeight : client.term.termHeight, + termWidth : client.term.termWidth, + trailingLF : options.trailingLF, }); let parseComplete = false; @@ -273,12 +273,12 @@ function display(client, art, options, cb) { } if(!options.disableMciCache && !mciMapFromCache) { - // cache our MCI findings... + // cache our MCI findings... client.mciCache[artHash] = mciMap; client.log.trace( { artHash : artHash.toString(16), mciMap : mciMap }, 'Added MCI map to cache'); } - ansiParser.removeAllListeners(); // :TODO: Necessary??? + ansiParser.removeAllListeners(); // :TODO: Necessary??? const extraInfo = { height : ansiParser.row - 1, @@ -288,11 +288,11 @@ function display(client, art, options, cb) { } if(!options.disableMciCache) { - artHash = xxhash.hash(Buffer.from(art), 0xCAFEBABE); + artHash = xxhash.hash(Buffer.from(art), 0xCAFEBABE); - // see if we have a mciMap cached for this art + // see if we have a mciMap cached for this art if(client.mciCache) { - mciMap = client.mciCache[artHash]; + mciMap = client.mciCache[artHash]; } } @@ -300,7 +300,7 @@ function display(client, art, options, cb) { mciMapFromCache = true; client.log.trace( { artHash : artHash.toString(16), mciMap : mciMap }, 'Loaded MCI map from cache'); } else { - // no cached MCI info + // no cached MCI info mciMap = {}; cprListener = function(pos) { @@ -318,20 +318,20 @@ function display(client, art, options, cb) { let generatedId = 100; ansiParser.on('mci', mciInfo => { - // :TODO: ensure generatedId's do not conflict with any existing |id| - const id = _.isNumber(mciInfo.id) ? mciInfo.id : generatedId; - const mapKey = `${mciInfo.mci}${id}`; - const mapEntry = mciMap[mapKey]; + // :TODO: ensure generatedId's do not conflict with any existing |id| + const id = _.isNumber(mciInfo.id) ? mciInfo.id : generatedId; + const mapKey = `${mciInfo.mci}${id}`; + const mapEntry = mciMap[mapKey]; if(mapEntry) { - mapEntry.focusSGR = mciInfo.SGR; - mapEntry.focusArgs = mciInfo.args; + mapEntry.focusSGR = mciInfo.SGR; + mapEntry.focusArgs = mciInfo.args; } else { mciMap[mapKey] = { - args : mciInfo.args, - SGR : mciInfo.SGR, - code : mciInfo.mci, - id : id, + args : mciInfo.args, + SGR : mciInfo.SGR, + code : mciInfo.mci, + id : id, }; if(!mciInfo.id) { @@ -366,10 +366,10 @@ function display(client, art, options, cb) { } // - // Set SyncTERM font if we're switching only. Most terminals - // that support this ESC sequence can only show *one* font - // at a time. This applies to detection only (e.g. SAUCE). - // If explicit, we'll set it no matter what (above) + // Set SyncTERM font if we're switching only. Most terminals + // that support this ESC sequence can only show *one* font + // at a time. This applies to detection only (e.g. SAUCE). + // If explicit, we'll set it no matter what (above) // if(fontName && client.term.currentSyncFont != fontName) { client.term.currentSyncFont = fontName; diff --git a/core/asset.js b/core/asset.js index ece05cfc..2204b1da 100644 --- a/core/asset.js +++ b/core/asset.js @@ -1,21 +1,21 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('./config.js').get; -const StatLog = require('./stat_log.js'); +// ENiGMA½ +const Config = require('./config.js').get; +const StatLog = require('./stat_log.js'); -// deps -const _ = require('lodash'); -const assert = require('assert'); +// deps +const _ = require('lodash'); +const assert = require('assert'); -exports.parseAsset = parseAsset; -exports.getAssetWithShorthand = getAssetWithShorthand; -exports.getArtAsset = getArtAsset; -exports.getModuleAsset = getModuleAsset; -exports.resolveConfigAsset = resolveConfigAsset; -exports.resolveSystemStatAsset = resolveSystemStatAsset; -exports.getViewPropertyAsset = getViewPropertyAsset; +exports.parseAsset = parseAsset; +exports.getAssetWithShorthand = getAssetWithShorthand; +exports.getArtAsset = getArtAsset; +exports.getModuleAsset = getModuleAsset; +exports.resolveConfigAsset = resolveConfigAsset; +exports.resolveSystemStatAsset = resolveSystemStatAsset; +exports.getViewPropertyAsset = getViewPropertyAsset; const ALL_ASSETS = [ 'art', @@ -39,9 +39,9 @@ function parseAsset(s) { if(m[3]) { result.location = m[2]; - result.asset = m[3]; + result.asset = m[3]; } else { - result.asset = m[2]; + result.asset = m[2]; } return result; @@ -61,8 +61,8 @@ function getAssetWithShorthand(spec, defaultType) { } return { - type : defaultType, - asset : spec, + type : defaultType, + asset : spec, }; } @@ -94,8 +94,8 @@ function resolveConfigAsset(spec) { if(asset) { assert('config' === asset.type); - const path = asset.asset.split('.'); - let conf = Config(); + const path = asset.asset.split('.'); + let conf = Config(); for(let i = 0; i < path.length; ++i) { if(_.isUndefined(conf[path[i]])) { return spec; diff --git a/core/bbs_link.js b/core/bbs_link.js index 4034b383..cbb7c036 100644 --- a/core/bbs_link.js +++ b/core/bbs_link.js @@ -1,54 +1,54 @@ /* jslint node: true */ 'use strict'; -const MenuModule = require('./menu_module.js').MenuModule; -const resetScreen = require('./ansi_term.js').resetScreen; +const MenuModule = require('./menu_module.js').MenuModule; +const resetScreen = require('./ansi_term.js').resetScreen; -const async = require('async'); -const _ = require('lodash'); -const http = require('http'); -const net = require('net'); -const crypto = require('crypto'); +const async = require('async'); +const _ = require('lodash'); +const http = require('http'); +const net = require('net'); +const crypto = require('crypto'); -const packageJson = require('../package.json'); +const packageJson = require('../package.json'); /* - Expected configuration block: + Expected configuration block: - { - module: bbs_link - ... - config: { - sysCode: XXXXX - authCode: XXXXX - schemeCode: XXXX - door: lord + { + module: bbs_link + ... + config: { + sysCode: XXXXX + authCode: XXXXX + schemeCode: XXXX + door: lord - // default hoss: games.bbslink.net - host: games.bbslink.net + // default hoss: games.bbslink.net + host: games.bbslink.net - // defualt port: 23 - port: 23 - } - } + // defualt port: 23 + port: 23 + } + } */ -// :TODO: BUG: When a client disconnects, it's not handled very well -- the log is spammed with tons of errors -// :TODO: ENH: Support nodeMax and tooManyArt +// :TODO: BUG: When a client disconnects, it's not handled very well -- the log is spammed with tons of errors +// :TODO: ENH: Support nodeMax and tooManyArt exports.moduleInfo = { - name : 'BBSLink', - desc : 'BBSLink Access Module', - author : 'NuSkooler', + name : 'BBSLink', + desc : 'BBSLink Access Module', + author : 'NuSkooler', }; exports.getModule = class BBSLinkModule extends MenuModule { constructor(options) { super(options); - this.config = options.menuConfig.config; - this.config.host = this.config.host || 'games.bbslink.net'; - this.config.port = this.config.port || 23; + this.config = options.menuConfig.config; + this.config.host = this.config.host || 'games.bbslink.net'; + this.config.port = this.config.port || 23; } initSequence() { @@ -61,9 +61,9 @@ exports.getModule = class BBSLinkModule extends MenuModule { [ function validateConfig(callback) { if(_.isString(self.config.sysCode) && - _.isString(self.config.authCode) && - _.isString(self.config.schemeCode) && - _.isString(self.config.door)) + _.isString(self.config.authCode) && + _.isString(self.config.schemeCode) && + _.isString(self.config.door)) { callback(null); } else { @@ -72,7 +72,7 @@ exports.getModule = class BBSLinkModule extends MenuModule { }, function acquireToken(callback) { // - // Acquire an authentication token + // Acquire an authentication token // crypto.randomBytes(16, function rand(ex, buf) { if(ex) { @@ -93,19 +93,19 @@ exports.getModule = class BBSLinkModule extends MenuModule { }, function authenticateToken(callback) { // - // Authenticate the token we acquired previously + // Authenticate the token we acquired previously // var headers = { - 'X-User' : self.client.user.userId.toString(), - 'X-System' : self.config.sysCode, - 'X-Auth' : crypto.createHash('md5').update(self.config.authCode + token).digest('hex'), - 'X-Code' : crypto.createHash('md5').update(self.config.schemeCode + token).digest('hex'), - 'X-Rows' : self.client.term.termHeight.toString(), - 'X-Key' : randomKey, - 'X-Door' : self.config.door, - 'X-Token' : token, - 'X-Type' : 'enigma-bbs', - 'X-Version' : packageJson.version, + 'X-User' : self.client.user.userId.toString(), + 'X-System' : self.config.sysCode, + 'X-Auth' : crypto.createHash('md5').update(self.config.authCode + token).digest('hex'), + 'X-Code' : crypto.createHash('md5').update(self.config.schemeCode + token).digest('hex'), + 'X-Rows' : self.client.term.termHeight.toString(), + 'X-Key' : randomKey, + 'X-Door' : self.config.door, + 'X-Token' : token, + 'X-Type' : 'enigma-bbs', + 'X-Version' : packageJson.version, }; self.simpleHttpRequest('/auth.php?key=' + randomKey, headers, function resp(err, body) { @@ -120,12 +120,12 @@ exports.getModule = class BBSLinkModule extends MenuModule { }, function createTelnetBridge(callback) { // - // Authentication with BBSLink successful. Now, we need to create a telnet - // bridge from us to them + // Authentication with BBSLink successful. Now, we need to create a telnet + // bridge from us to them // var connectOpts = { - port : self.config.port, - host : self.config.host, + port : self.config.port, + host : self.config.host, }; var clientTerminated; @@ -151,8 +151,8 @@ exports.getModule = class BBSLinkModule extends MenuModule { }; bridgeConnection.on('data', function incomingData(data) { - // pass along - // :TODO: just pipe this as well + // pass along + // :TODO: just pipe this as well self.client.term.rawWrite(data); }); @@ -182,9 +182,9 @@ exports.getModule = class BBSLinkModule extends MenuModule { simpleHttpRequest(path, headers, cb) { const getOpts = { - host : this.config.host, - path : path, - headers : headers, + host : this.config.host, + path : path, + headers : headers, }; const req = http.get(getOpts, function response(resp) { diff --git a/core/bbs_list.js b/core/bbs_list.js index b0da3dbb..2f9b6084 100644 --- a/core/bbs_list.js +++ b/core/bbs_list.js @@ -1,73 +1,73 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; const { getModDatabasePath, getTransactionDatabase -} = require('./database.js'); +} = require('./database.js'); -const ViewController = require('./view_controller.js').ViewController; -const ansi = require('./ansi_term.js'); -const theme = require('./theme.js'); -const User = require('./user.js'); -const stringFormat = require('./string_format.js'); +const ViewController = require('./view_controller.js').ViewController; +const ansi = require('./ansi_term.js'); +const theme = require('./theme.js'); +const User = require('./user.js'); +const stringFormat = require('./string_format.js'); -// deps -const async = require('async'); -const sqlite3 = require('sqlite3'); -const _ = require('lodash'); +// deps +const async = require('async'); +const sqlite3 = require('sqlite3'); +const _ = require('lodash'); -// :TODO: add notes field +// :TODO: add notes field const moduleInfo = exports.moduleInfo = { - name : 'BBS List', - desc : 'List of other BBSes', - author : 'Andrew Pamment', - packageName : 'com.magickabbs.enigma.bbslist' + name : 'BBS List', + desc : 'List of other BBSes', + author : 'Andrew Pamment', + packageName : 'com.magickabbs.enigma.bbslist' }; const MciViewIds = { view : { - BBSList : 1, - SelectedBBSName : 2, - SelectedBBSSysOp : 3, - SelectedBBSTelnet : 4, - SelectedBBSWww : 5, - SelectedBBSLoc : 6, - SelectedBBSSoftware : 7, - SelectedBBSNotes : 8, - SelectedBBSSubmitter : 9, + BBSList : 1, + SelectedBBSName : 2, + SelectedBBSSysOp : 3, + SelectedBBSTelnet : 4, + SelectedBBSWww : 5, + SelectedBBSLoc : 6, + SelectedBBSSoftware : 7, + SelectedBBSNotes : 8, + SelectedBBSSubmitter : 9, }, add : { - BBSName : 1, - Sysop : 2, - Telnet : 3, - Www : 4, - Location : 5, - Software : 6, - Notes : 7, - Error : 8, + BBSName : 1, + Sysop : 2, + Telnet : 3, + Www : 4, + Location : 5, + Software : 6, + Notes : 7, + Error : 8, } }; const FormIds = { - View : 0, - Add : 1, + View : 0, + Add : 1, }; const SELECTED_MCI_NAME_TO_ENTRY = { - SelectedBBSName : 'bbsName', - SelectedBBSSysOp : 'sysOp', - SelectedBBSTelnet : 'telnet', - SelectedBBSWww : 'www', - SelectedBBSLoc : 'location', - SelectedBBSSoftware : 'software', - SelectedBBSSubmitter : 'submitter', - SelectedBBSSubmitterId : 'submitterUserId', - SelectedBBSNotes : 'notes', + SelectedBBSName : 'bbsName', + SelectedBBSSysOp : 'sysOp', + SelectedBBSTelnet : 'telnet', + SelectedBBSWww : 'www', + SelectedBBSLoc : 'location', + SelectedBBSSoftware : 'software', + SelectedBBSSubmitter : 'submitter', + SelectedBBSSubmitterId : 'submitterUserId', + SelectedBBSNotes : 'notes', }; exports.getModule = class BBSListModule extends MenuModule { @@ -77,7 +77,7 @@ exports.getModule = class BBSListModule extends MenuModule { const self = this; this.menuMethods = { // - // Validators + // Validators // viewValidationListener : function(err, cb) { const errMsgView = self.viewControllers.add.getView(MciViewIds.add.Error); @@ -93,7 +93,7 @@ exports.getModule = class BBSListModule extends MenuModule { }, // - // Key & submit handlers + // Key & submit handlers // addBBS : function(formData, extraArgs, cb) { self.displayAddScreen(cb); @@ -106,7 +106,7 @@ exports.getModule = class BBSListModule extends MenuModule { const entriesView = self.viewControllers.view.getView(MciViewIds.view.BBSList); if(self.entries[self.selectedBBS].submitterUserId !== self.client.user.userId && !self.client.user.isSysOp()) { - // must be owner or +op + // must be owner or +op return cb(null); } @@ -117,7 +117,7 @@ exports.getModule = class BBSListModule extends MenuModule { self.database.run( `DELETE FROM bbs_list - WHERE id=?;`, + WHERE id=?;`, [ entry.id ], err => { if (err) { @@ -147,13 +147,13 @@ exports.getModule = class BBSListModule extends MenuModule { } }); if(!ok) { - // validators should prevent this! + // validators should prevent this! return cb(null); } self.database.run( `INSERT INTO bbs_list (bbs_name, sysop, telnet, www, location, software, submitter_user_id, notes) - VALUES(?, ?, ?, ?, ?, ?, ?, ?);`, + VALUES(?, ?, ?, ?, ?, ?, ?, ?);`, [ formData.value.name, formData.value.sysop, formData.value.telnet, formData.value.www, formData.value.location, formData.value.software, self.client.user.userId, formData.value.notes @@ -188,7 +188,7 @@ exports.getModule = class BBSListModule extends MenuModule { ], err => { if(err) { - // :TODO: Handle me -- initSequence() should really take a completion callback + // :TODO: Handle me -- initSequence() should really take a completion callback } self.finishedLoading(); } @@ -218,9 +218,9 @@ exports.getModule = class BBSListModule extends MenuModule { } setEntries(entriesView) { - const config = this.menuConfig.config; - const listFormat = config.listFormat || '{bbsName}'; - const focusListFormat = config.focusListFormat || '{bbsName}'; + const config = this.menuConfig.config; + const listFormat = config.listFormat || '{bbsName}'; + const focusListFormat = config.focusListFormat || '{bbsName}'; entriesView.setItems(this.entries.map( e => stringFormat(listFormat, e) ) ); entriesView.setFocusItems(this.entries.map( e => stringFormat(focusListFormat, e) ) ); @@ -255,9 +255,9 @@ exports.getModule = class BBSListModule extends MenuModule { ); const loadOpts = { - callingMenu : self, - mciMap : artData.mciMap, - formId : FormIds.View, + callingMenu : self, + mciMap : artData.mciMap, + formId : FormIds.View, }; return vc.loadFromMenuConfig(loadOpts, callback); @@ -273,19 +273,19 @@ exports.getModule = class BBSListModule extends MenuModule { self.database.each( `SELECT id, bbs_name, sysop, telnet, www, location, software, submitter_user_id, notes - FROM bbs_list;`, + FROM bbs_list;`, (err, row) => { if (!err) { self.entries.push({ - id : row.id, - bbsName : row.bbs_name, - sysOp : row.sysop, - telnet : row.telnet, - www : row.www, - location : row.location, - software : row.software, - submitterUserId : row.submitter_user_id, - notes : row.notes, + id : row.id, + bbsName : row.bbs_name, + sysOp : row.sysop, + telnet : row.telnet, + www : row.www, + location : row.location, + software : row.software, + submitterUserId : row.submitter_user_id, + notes : row.notes, }); } }, @@ -371,9 +371,9 @@ exports.getModule = class BBSListModule extends MenuModule { ); const loadOpts = { - callingMenu : self, - mciMap : artData.mciMap, - formId : FormIds.Add, + callingMenu : self, + mciMap : artData.mciMap, + formId : FormIds.Add, }; return vc.loadFromMenuConfig(loadOpts, callback); @@ -414,16 +414,16 @@ exports.getModule = class BBSListModule extends MenuModule { self.database.serialize( () => { self.database.run( `CREATE TABLE IF NOT EXISTS bbs_list ( - id INTEGER PRIMARY KEY, - bbs_name VARCHAR NOT NULL, - sysop VARCHAR NOT NULL, - telnet VARCHAR NOT NULL, - www VARCHAR, - location VARCHAR, - software VARCHAR, - submitter_user_id INTEGER NOT NULL, - notes VARCHAR - );` + id INTEGER PRIMARY KEY, + bbs_name VARCHAR NOT NULL, + sysop VARCHAR NOT NULL, + telnet VARCHAR NOT NULL, + www VARCHAR, + location VARCHAR, + software VARCHAR, + submitter_user_id INTEGER NOT NULL, + notes VARCHAR + );` ); }); callback(null); diff --git a/core/button_view.js b/core/button_view.js index 6c86b5c3..63de435a 100644 --- a/core/button_view.js +++ b/core/button_view.js @@ -1,17 +1,17 @@ /* jslint node: true */ 'use strict'; -const TextView = require('./text_view.js').TextView; -const miscUtil = require('./misc_util.js'); -const util = require('util'); +const TextView = require('./text_view.js').TextView; +const miscUtil = require('./misc_util.js'); +const util = require('util'); -exports.ButtonView = ButtonView; +exports.ButtonView = ButtonView; function ButtonView(options) { - options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true); - options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true); - options.justify = miscUtil.valueWithDefault(options.justify, 'center'); - options.cursor = miscUtil.valueWithDefault(options.cursor, 'hide'); + options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true); + options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true); + options.justify = miscUtil.valueWithDefault(options.justify, 'center'); + options.cursor = miscUtil.valueWithDefault(options.cursor, 'hide'); TextView.call(this, options); } @@ -29,12 +29,12 @@ ButtonView.prototype.onKeyPress = function(ch, key) { }; /* ButtonView.prototype.onKeyPress = function(ch, key) { - // allow space = submit - if(' ' === ch) { - this.emit('action', 'accept'); - } + // allow space = submit + if(' ' === ch) { + this.emit('action', 'accept'); + } - ButtonView.super_.prototype.onKeyPress.call(this, ch, key); + ButtonView.super_.prototype.onKeyPress.call(this, ch, key); }; */ diff --git a/core/client.js b/core/client.js index 4347c0b2..a2eb4bc3 100644 --- a/core/client.js +++ b/core/client.js @@ -2,69 +2,69 @@ 'use strict'; /* - Portions of this code for key handling heavily inspired from the following: - https://github.com/chjj/blessed/blob/master/lib/keys.js + Portions of this code for key handling heavily inspired from the following: + https://github.com/chjj/blessed/blob/master/lib/keys.js - chji's blessed is MIT licensed: + chji's blessed is MIT licensed: - ----/snip/---------------------- - The MIT License (MIT) + ----/snip/---------------------- + The MIT License (MIT) - Copyright (c) + Copyright (c) - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - ----/snip/---------------------- + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + ----/snip/---------------------- */ -// ENiGMA½ -const term = require('./client_term.js'); -const ansi = require('./ansi_term.js'); -const User = require('./user.js'); -const Config = require('./config.js').get; -const MenuStack = require('./menu_stack.js'); -const ACS = require('./acs.js'); -const Events = require('./events.js'); +// ENiGMA½ +const term = require('./client_term.js'); +const ansi = require('./ansi_term.js'); +const User = require('./user.js'); +const Config = require('./config.js').get; +const MenuStack = require('./menu_stack.js'); +const ACS = require('./acs.js'); +const Events = require('./events.js'); -// deps -const stream = require('stream'); -const assert = require('assert'); -const _ = require('lodash'); +// deps +const stream = require('stream'); +const assert = require('assert'); +const _ = require('lodash'); -exports.Client = Client; +exports.Client = Client; -// :TODO: Move all of the key stuff to it's own module +// :TODO: Move all of the key stuff to it's own module // -// Resources & Standards: -// * http://www.ansi-bbs.org/ansi-bbs-core-server.html +// Resources & Standards: +// * http://www.ansi-bbs.org/ansi-bbs-core-server.html // -const RE_DSR_RESPONSE_ANYWHERE = /(?:\u001b\[)([0-9;]+)(R)/; -const RE_DEV_ATTR_RESPONSE_ANYWHERE = /(?:\u001b\[)[=?]([0-9a-zA-Z;]+)(c)/; -const RE_META_KEYCODE_ANYWHERE = /(?:\u001b)([a-zA-Z0-9])/; -const RE_META_KEYCODE = new RegExp('^' + RE_META_KEYCODE_ANYWHERE.source + '$'); -const RE_FUNCTION_KEYCODE_ANYWHERE = new RegExp('(?:\u001b+)(O|N|\\[|\\[\\[)(?:' + [ +const RE_DSR_RESPONSE_ANYWHERE = /(?:\u001b\[)([0-9;]+)(R)/; +const RE_DEV_ATTR_RESPONSE_ANYWHERE = /(?:\u001b\[)[=?]([0-9a-zA-Z;]+)(c)/; +const RE_META_KEYCODE_ANYWHERE = /(?:\u001b)([a-zA-Z0-9])/; +const RE_META_KEYCODE = new RegExp('^' + RE_META_KEYCODE_ANYWHERE.source + '$'); +const RE_FUNCTION_KEYCODE_ANYWHERE = new RegExp('(?:\u001b+)(O|N|\\[|\\[\\[)(?:' + [ '(\\d+)(?:;(\\d+))?([~^$])', - '(?:M([@ #!a`])(.)(.))', // mouse stuff + '(?:M([@ #!a`])(.)(.))', // mouse stuff '(?:1;)?(\\d+)?([a-zA-Z@])' ].join('|') + ')'); -const RE_FUNCTION_KEYCODE = new RegExp('^' + RE_FUNCTION_KEYCODE_ANYWHERE.source); -const RE_ESC_CODE_ANYWHERE = new RegExp( [ +const RE_FUNCTION_KEYCODE = new RegExp('^' + RE_FUNCTION_KEYCODE_ANYWHERE.source); +const RE_ESC_CODE_ANYWHERE = new RegExp( [ RE_FUNCTION_KEYCODE_ANYWHERE.source, RE_META_KEYCODE_ANYWHERE.source, RE_DSR_RESPONSE_ANYWHERE.source, @@ -76,14 +76,14 @@ const RE_ESC_CODE_ANYWHERE = new RegExp( [ function Client(/*input, output*/) { stream.call(this); - const self = this; + const self = this; - this.user = new User(); - this.currentTheme = { info : { name : 'N/A', description : 'None' } }; - this.lastKeyPressMs = Date.now(); - this.menuStack = new MenuStack(this); - this.acs = new ACS(this); - this.mciCache = {}; + this.user = new User(); + this.currentTheme = { info : { name : 'N/A', description : 'None' } }; + this.lastKeyPressMs = Date.now(); + this.menuStack = new MenuStack(this); + this.acs = new ACS(this); + this.mciCache = {}; this.clearMciCache = function() { this.mciCache = {}; @@ -119,34 +119,34 @@ function Client(/*input, output*/) { // - // Peek at incoming |data| and emit events for any special - // handling that may include: - // * Keyboard input - // * ANSI CSR's and the like + // Peek at incoming |data| and emit events for any special + // handling that may include: + // * Keyboard input + // * ANSI CSR's and the like // - // References: - // * http://www.ansi-bbs.org/ansi-bbs-core-server.html - // * Christopher Jeffrey's Blessed library @ https://github.com/chjj/blessed/ + // References: + // * http://www.ansi-bbs.org/ansi-bbs-core-server.html + // * Christopher Jeffrey's Blessed library @ https://github.com/chjj/blessed/ // this.getTermClient = function(deviceAttr) { let termClient = { // - // See http://www.fbl.cz/arctel/download/techman.pdf + // See http://www.fbl.cz/arctel/download/techman.pdf // - // Known clients: - // * Irssi ConnectBot (Android) + // Known clients: + // * Irssi ConnectBot (Android) // - '63;1;2' : 'arctel', - '50;86;84;88' : 'vtx', + '63;1;2' : 'arctel', + '50;86;84;88' : 'vtx', }[deviceAttr]; if(!termClient) { if(_.startsWith(deviceAttr, '67;84;101;114;109')) { // - // See https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt + // See https://github.com/protomouse/synchronet/blob/master/src/conio/cterm.txt // - // Known clients: - // * SyncTERM + // Known clients: + // * SyncTERM // termClient = 'cterm'; } @@ -156,18 +156,18 @@ function Client(/*input, output*/) { }; this.isMouseInput = function(data) { - return /\x1b\[M/.test(data) || // eslint-disable-line no-control-regex - /\u001b\[M([\x00\u0020-\uffff]{3})/.test(data) || // eslint-disable-line no-control-regex - /\u001b\[(\d+;\d+;\d+)M/.test(data) || - /\u001b\[<(\d+;\d+;\d+)([mM])/.test(data) || - /\u001b\[<(\d+;\d+;\d+;\d+)&w/.test(data) || - /\u001b\[24([0135])~\[(\d+),(\d+)\]\r/.test(data) || - /\u001b\[(O|I)/.test(data); + return /\x1b\[M/.test(data) || // eslint-disable-line no-control-regex + /\u001b\[M([\x00\u0020-\uffff]{3})/.test(data) || // eslint-disable-line no-control-regex + /\u001b\[(\d+;\d+;\d+)M/.test(data) || + /\u001b\[<(\d+;\d+;\d+)([mM])/.test(data) || + /\u001b\[<(\d+;\d+;\d+;\d+)&w/.test(data) || + /\u001b\[24([0135])~\[(\d+),(\d+)\]\r/.test(data) || + /\u001b\[(O|I)/.test(data); }; this.getKeyComponentsFromCode = function(code) { return { - // xterm/gnome + // xterm/gnome 'OP' : { name : 'f1' }, 'OQ' : { name : 'f2' }, 'OR' : { name : 'f3' }, @@ -181,93 +181,93 @@ function Client(/*input, output*/) { 'OF' : { name : 'end' }, 'OH' : { name : 'home' }, - // xterm/rxvt - '[11~' : { name : 'f1' }, - '[12~' : { name : 'f2' }, - '[13~' : { name : 'f3' }, - '[14~' : { name : 'f4' }, + // xterm/rxvt + '[11~' : { name : 'f1' }, + '[12~' : { name : 'f2' }, + '[13~' : { name : 'f3' }, + '[14~' : { name : 'f4' }, - '[1~' : { name : 'home' }, - '[2~' : { name : 'insert' }, - '[3~' : { name : 'delete' }, - '[4~' : { name : 'end' }, - '[5~' : { name : 'page up' }, - '[6~' : { name : 'page down' }, + '[1~' : { name : 'home' }, + '[2~' : { name : 'insert' }, + '[3~' : { name : 'delete' }, + '[4~' : { name : 'end' }, + '[5~' : { name : 'page up' }, + '[6~' : { name : 'page down' }, - // Cygwin & libuv - '[[A' : { name : 'f1' }, - '[[B' : { name : 'f2' }, - '[[C' : { name : 'f3' }, - '[[D' : { name : 'f4' }, - '[[E' : { name : 'f5' }, + // Cygwin & libuv + '[[A' : { name : 'f1' }, + '[[B' : { name : 'f2' }, + '[[C' : { name : 'f3' }, + '[[D' : { name : 'f4' }, + '[[E' : { name : 'f5' }, - // Common impls - '[15~' : { name : 'f5' }, - '[17~' : { name : 'f6' }, - '[18~' : { name : 'f7' }, - '[19~' : { name : 'f8' }, - '[20~' : { name : 'f9' }, - '[21~' : { name : 'f10' }, - '[23~' : { name : 'f11' }, - '[24~' : { name : 'f12' }, + // Common impls + '[15~' : { name : 'f5' }, + '[17~' : { name : 'f6' }, + '[18~' : { name : 'f7' }, + '[19~' : { name : 'f8' }, + '[20~' : { name : 'f9' }, + '[21~' : { name : 'f10' }, + '[23~' : { name : 'f11' }, + '[24~' : { name : 'f12' }, - // xterm - '[A' : { name : 'up arrow' }, - '[B' : { name : 'down arrow' }, - '[C' : { name : 'right arrow' }, - '[D' : { name : 'left arrow' }, - '[E' : { name : 'clear' }, - '[F' : { name : 'end' }, - '[H' : { name : 'home' }, + // xterm + '[A' : { name : 'up arrow' }, + '[B' : { name : 'down arrow' }, + '[C' : { name : 'right arrow' }, + '[D' : { name : 'left arrow' }, + '[E' : { name : 'clear' }, + '[F' : { name : 'end' }, + '[H' : { name : 'home' }, - // PuTTY - '[[5~' : { name : 'page up' }, - '[[6~' : { name : 'page down' }, + // PuTTY + '[[5~' : { name : 'page up' }, + '[[6~' : { name : 'page down' }, - // rvxt - '[7~' : { name : 'home' }, - '[8~' : { name : 'end' }, + // rvxt + '[7~' : { name : 'home' }, + '[8~' : { name : 'end' }, - // rxvt with modifiers - '[a' : { name : 'up arrow', shift : true }, - '[b' : { name : 'down arrow', shift : true }, - '[c' : { name : 'right arrow', shift : true }, - '[d' : { name : 'left arrow', shift : true }, - '[e' : { name : 'clear', shift : true }, + // rxvt with modifiers + '[a' : { name : 'up arrow', shift : true }, + '[b' : { name : 'down arrow', shift : true }, + '[c' : { name : 'right arrow', shift : true }, + '[d' : { name : 'left arrow', shift : true }, + '[e' : { name : 'clear', shift : true }, - '[2$' : { name : 'insert', shift : true }, - '[3$' : { name : 'delete', shift : true }, - '[5$' : { name : 'page up', shift : true }, - '[6$' : { name : 'page down', shift : true }, - '[7$' : { name : 'home', shift : true }, - '[8$' : { name : 'end', shift : true }, + '[2$' : { name : 'insert', shift : true }, + '[3$' : { name : 'delete', shift : true }, + '[5$' : { name : 'page up', shift : true }, + '[6$' : { name : 'page down', shift : true }, + '[7$' : { name : 'home', shift : true }, + '[8$' : { name : 'end', shift : true }, - 'Oa' : { name : 'up arrow', ctrl : true }, - 'Ob' : { name : 'down arrow', ctrl : true }, - 'Oc' : { name : 'right arrow', ctrl : true }, - 'Od' : { name : 'left arrow', ctrl : true }, - 'Oe' : { name : 'clear', ctrl : true }, + 'Oa' : { name : 'up arrow', ctrl : true }, + 'Ob' : { name : 'down arrow', ctrl : true }, + 'Oc' : { name : 'right arrow', ctrl : true }, + 'Od' : { name : 'left arrow', ctrl : true }, + 'Oe' : { name : 'clear', ctrl : true }, - '[2^' : { name : 'insert', ctrl : true }, - '[3^' : { name : 'delete', ctrl : true }, - '[5^' : { name : 'page up', ctrl : true }, - '[6^' : { name : 'page down', ctrl : true }, - '[7^' : { name : 'home', ctrl : true }, - '[8^' : { name : 'end', ctrl : true }, + '[2^' : { name : 'insert', ctrl : true }, + '[3^' : { name : 'delete', ctrl : true }, + '[5^' : { name : 'page up', ctrl : true }, + '[6^' : { name : 'page down', ctrl : true }, + '[7^' : { name : 'home', ctrl : true }, + '[8^' : { name : 'end', ctrl : true }, - // SyncTERM / EtherTerm - '[K' : { name : 'end' }, - '[@' : { name : 'insert' }, - '[V' : { name : 'page up' }, - '[U' : { name : 'page down' }, + // SyncTERM / EtherTerm + '[K' : { name : 'end' }, + '[@' : { name : 'insert' }, + '[V' : { name : 'page up' }, + '[U' : { name : 'page down' }, - // other - '[Z' : { name : 'tab', shift : true }, + // other + '[Z' : { name : 'tab', shift : true }, }[code]; }; this.on('data', function clientData(data) { - // create a uniform format that can be parsed below + // create a uniform format that can be parsed below if(data[0] > 127 && undefined === data[1]) { data[0] -= 128; data = '\u001b' + data.toString('utf-8'); @@ -287,15 +287,15 @@ function Client(/*input, output*/) { data = data.slice(m.index + m[0].length); } - buf = buf.concat(data.split('')); // remainder + buf = buf.concat(data.split('')); // remainder buf.forEach(function bufPart(s) { var key = { - seq : s, - name : undefined, - ctrl : false, - meta : false, - shift : false, + seq : s, + name : undefined, + ctrl : false, + meta : false, + shift : false, }; var parts; @@ -325,55 +325,55 @@ function Client(/*input, output*/) { key.name = 'tab'; } else if('\x7f' === s) { // - // Backspace vs delete is a crazy thing, especially in *nix. - // - ANSI-BBS uses 0x7f for DEL - // - xterm et. al clients send 0x7f for backspace... ugg. + // Backspace vs delete is a crazy thing, especially in *nix. + // - ANSI-BBS uses 0x7f for DEL + // - xterm et. al clients send 0x7f for backspace... ugg. // - // See http://www.hypexr.org/linux_ruboff.php - // And a great discussion @ https://lists.debian.org/debian-i18n/1998/04/msg00015.html + // See http://www.hypexr.org/linux_ruboff.php + // And a great discussion @ https://lists.debian.org/debian-i18n/1998/04/msg00015.html // if(self.term.isNixTerm()) { - key.name = 'backspace'; + key.name = 'backspace'; } else { - key.name = 'delete'; + key.name = 'delete'; } } else if ('\b' === s || '\x1b\x7f' === s || '\x1b\b' === s) { - // backspace, CTRL-H - key.name = 'backspace'; - key.meta = ('\x1b' === s.charAt(0)); + // backspace, CTRL-H + key.name = 'backspace'; + key.meta = ('\x1b' === s.charAt(0)); } else if('\x1b' === s || '\x1b\x1b' === s) { - key.name = 'escape'; - key.meta = (2 === s.length); + key.name = 'escape'; + key.meta = (2 === s.length); } else if (' ' === s || '\x1b ' === s) { - // rather annoying that space can come in other than just " " - key.name = 'space'; - key.meta = (2 === s.length); + // rather annoying that space can come in other than just " " + key.name = 'space'; + key.meta = (2 === s.length); } else if(1 === s.length && s <= '\x1a') { - // CTRL- - key.name = String.fromCharCode(s.charCodeAt(0) + 'a'.charCodeAt(0) - 1); - key.ctrl = true; + // CTRL- + key.name = String.fromCharCode(s.charCodeAt(0) + 'a'.charCodeAt(0) - 1); + key.ctrl = true; } else if(1 === s.length && s >= 'a' && s <= 'z') { - // normal, lowercased letter - key.name = s; + // normal, lowercased letter + key.name = s; } else if(1 === s.length && s >= 'A' && s <= 'Z') { - key.name = s.toLowerCase(); - key.shift = true; + key.name = s.toLowerCase(); + key.shift = true; } else if ((parts = RE_META_KEYCODE.exec(s))) { - // meta with character key - key.name = parts[1].toLowerCase(); - key.meta = true; - key.shift = /^[A-Z]$/.test(parts[1]); + // meta with character key + key.name = parts[1].toLowerCase(); + key.meta = true; + key.shift = /^[A-Z]$/.test(parts[1]); } else if((parts = RE_FUNCTION_KEYCODE.exec(s))) { var code = - (parts[1] || '') + (parts[2] || '') + - (parts[4] || '') + (parts[9] || ''); + (parts[1] || '') + (parts[2] || '') + + (parts[4] || '') + (parts[9] || ''); var modifier = (parts[3] || parts[8] || 1) - 1; - key.ctrl = !!(modifier & 4); - key.meta = !!(modifier & 10); - key.shift = !!(modifier & 1); - key.code = code; + key.ctrl = !!(modifier & 4); + key.meta = !!(modifier & 10); + key.shift = !!(modifier & 1); + key.code = code; _.assign(key, self.getKeyComponentsFromCode(code)); } @@ -382,7 +382,7 @@ function Client(/*input, output*/) { if(1 === s.length) { ch = s; } else if('space' === key.name) { - // stupid hack to always get space as a regular char + // stupid hack to always get space as a regular char ch = ' '; } @@ -390,18 +390,18 @@ function Client(/*input, output*/) { key = undefined; } else { // - // Adjust name for CTRL/Shift/Meta modifiers + // Adjust name for CTRL/Shift/Meta modifiers // key.name = - (key.ctrl ? 'ctrl + ' : '') + - (key.meta ? 'meta + ' : '') + - (key.shift ? 'shift + ' : '') + - key.name; + (key.ctrl ? 'ctrl + ' : '') + + (key.meta ? 'meta + ' : '') + + (key.shift ? 'shift + ' : '') + + key.name; } if(key || ch) { if(Config().logging.traceUserKeyboardInput) { - self.log.trace( { key : key, ch : escape(ch) }, 'User keyboard input'); // jshint ignore:line + self.log.trace( { key : key, ch : escape(ch) }, 'User keyboard input'); // jshint ignore:line } self.lastKeyPressMs = Date.now(); @@ -417,15 +417,15 @@ function Client(/*input, output*/) { require('util').inherits(Client, stream); Client.prototype.setInputOutput = function(input, output) { - this.input = input; - this.output = output; + this.input = input; + this.output = output; - this.term = new term.ClientTerminal(this.output); + this.term = new term.ClientTerminal(this.output); }; Client.prototype.setTermType = function(termType) { - this.term.env.TERM = termType; - this.term.termType = termType; + this.term.env.TERM = termType; + this.term.termType = termType; this.log.debug( { termType : termType }, 'Set terminal type'); }; @@ -434,10 +434,10 @@ Client.prototype.startIdleMonitor = function() { this.lastKeyPressMs = Date.now(); // - // Every 1m, check for idle. + // Every 1m, check for idle. // this.idleCheck = setInterval( () => { - const nowMs = Date.now(); + const nowMs = Date.now(); const idleLogoutSeconds = this.user.isAuthenticated() ? Config().misc.idleLogoutSeconds : @@ -468,12 +468,12 @@ Client.prototype.end = function () { try { // - // We can end up calling 'end' before TTY/etc. is established, e.g. with SSH + // We can end up calling 'end' before TTY/etc. is established, e.g. with SSH // - // :TODO: is this OK? + // :TODO: is this OK? return this.output.end.apply(this.output, arguments); } catch(e) { - // TypeError + // TypeError } }; @@ -492,15 +492,15 @@ Client.prototype.waitForKeyPress = function(cb) { }; Client.prototype.isLocal = function() { - // :TODO: Handle ipv6 better + // :TODO: Handle ipv6 better return [ '127.0.0.1', '::ffff:127.0.0.1' ].includes(this.remoteAddress); }; /////////////////////////////////////////////////////////////////////////////// -// Default error handlers +// Default error handlers /////////////////////////////////////////////////////////////////////////////// -// :TODO: getDefaultHandler(name) -- handlers in default_handlers.js or something +// :TODO: getDefaultHandler(name) -- handlers in default_handlers.js or something Client.prototype.defaultHandlerMissingMod = function() { var self = this; @@ -516,7 +516,7 @@ Client.prototype.defaultHandlerMissingMod = function() { //self.term.write(err); //if(miscUtil.isDevelopment() && err.stack) { - // self.term.write('\n' + err.stack + '\n'); + // self.term.write('\n' + err.stack + '\n'); //} self.end(); @@ -530,7 +530,7 @@ Client.prototype.terminalSupports = function(query) { switch(query) { case 'vtx_audio' : - // https://github.com/codewar65/VTX_ClientServer/blob/master/vtx.txt + // https://github.com/codewar65/VTX_ClientServer/blob/master/vtx.txt return 'vtx' === termClient; case 'vtx_hyperlink' : diff --git a/core/client_connections.js b/core/client_connections.js index bdeb4539..4c16d610 100644 --- a/core/client_connections.js +++ b/core/client_connections.js @@ -1,23 +1,23 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const logger = require('./logger.js'); +// ENiGMA½ +const logger = require('./logger.js'); const Events = require('./events.js'); -// deps -const _ = require('lodash'); -const moment = require('moment'); -const hashids = require('hashids'); +// deps +const _ = require('lodash'); +const moment = require('moment'); +const hashids = require('hashids'); -exports.getActiveConnections = getActiveConnections; -exports.getActiveNodeList = getActiveNodeList; -exports.addNewClient = addNewClient; -exports.removeClient = removeClient; -exports.getConnectionByUserId = getConnectionByUserId; +exports.getActiveConnections = getActiveConnections; +exports.getActiveNodeList = getActiveNodeList; +exports.addNewClient = addNewClient; +exports.removeClient = removeClient; +exports.getConnectionByUserId = getConnectionByUserId; const clientConnections = []; -exports.clientConnections = clientConnections; +exports.clientConnections = clientConnections; function getActiveConnections() { return clientConnections; } @@ -35,48 +35,48 @@ function getActiveNodeList(authUsersOnly) { return _.map(activeConnections, ac => { const entry = { - node : ac.node, - authenticated : ac.user.isAuthenticated(), - userId : ac.user.userId, - action : _.has(ac, 'currentMenuModule.menuConfig.desc') ? ac.currentMenuModule.menuConfig.desc : 'Unknown', + node : ac.node, + authenticated : ac.user.isAuthenticated(), + userId : ac.user.userId, + action : _.has(ac, 'currentMenuModule.menuConfig.desc') ? ac.currentMenuModule.menuConfig.desc : 'Unknown', }; // - // There may be a connection, but not a logged in user as of yet + // There may be a connection, but not a logged in user as of yet // if(ac.user.isAuthenticated()) { - entry.userName = ac.user.username; - entry.realName = ac.user.properties.real_name; - entry.location = ac.user.properties.location; - entry.affils = ac.user.properties.affiliation; + entry.userName = ac.user.username; + entry.realName = ac.user.properties.real_name; + entry.location = ac.user.properties.location; + entry.affils = ac.user.properties.affiliation; - const diff = now.diff(moment(ac.user.properties.last_login_timestamp), 'minutes'); - entry.timeOn = moment.duration(diff, 'minutes'); + const diff = now.diff(moment(ac.user.properties.last_login_timestamp), 'minutes'); + entry.timeOn = moment.duration(diff, 'minutes'); } return entry; }); } function addNewClient(client, clientSock) { - const id = client.session.id = clientConnections.push(client) - 1; - const remoteAddress = client.remoteAddress = clientSock.remoteAddress; + const id = client.session.id = clientConnections.push(client) - 1; + const remoteAddress = client.remoteAddress = clientSock.remoteAddress; - // create a uniqe identifier one-time ID for this session + // create a uniqe identifier one-time ID for this session client.session.uniqueId = new hashids('ENiGMA½ClientSession').encode([ id, moment().valueOf() ]); - // Create a client specific logger - // Note that this will be updated @ login with additional information + // Create a client specific logger + // Note that this will be updated @ login with additional information client.log = logger.log.child( { clientId : id, sessionId : client.session.uniqueId } ); const connInfo = { - remoteAddress : remoteAddress, - serverName : client.session.serverName, - isSecure : client.session.isSecure, + remoteAddress : remoteAddress, + serverName : client.session.serverName, + isSecure : client.session.isSecure, }; if(client.log.debug()) { - connInfo.port = clientSock.localPort; - connInfo.family = clientSock.localFamily; + connInfo.port = clientSock.localPort; + connInfo.family = clientSock.localFamily; } client.log.info(connInfo, 'Client connected'); @@ -98,8 +98,8 @@ function removeClient(client) { logger.log.info( { - connectionCount : clientConnections.length, - clientId : client.session.id + connectionCount : clientConnections.length, + clientId : client.session.id }, 'Client disconnected' ); diff --git a/core/client_term.js b/core/client_term.js index 77196586..eb1ee4f9 100644 --- a/core/client_term.js +++ b/core/client_term.js @@ -1,39 +1,39 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -var Log = require('./logger.js').log; -var enigmaToAnsi = require('./color_codes.js').enigmaToAnsi; -var renegadeToAnsi = require('./color_codes.js').renegadeToAnsi; +// ENiGMA½ +var Log = require('./logger.js').log; +var enigmaToAnsi = require('./color_codes.js').enigmaToAnsi; +var renegadeToAnsi = require('./color_codes.js').renegadeToAnsi; -var iconv = require('iconv-lite'); -var assert = require('assert'); -var _ = require('lodash'); +var iconv = require('iconv-lite'); +var assert = require('assert'); +var _ = require('lodash'); -exports.ClientTerminal = ClientTerminal; +exports.ClientTerminal = ClientTerminal; function ClientTerminal(output) { - this.output = output; + this.output = output; var outputEncoding = 'cp437'; assert(iconv.encodingExists(outputEncoding)); - // convert line feeds such as \n -> \r\n - this.convertLF = true; + // convert line feeds such as \n -> \r\n + this.convertLF = true; // - // Some terminal we handle specially - // They can also be found in this.env{} + // Some terminal we handle specially + // They can also be found in this.env{} // - var termType = 'unknown'; - var termHeight = 0; - var termWidth = 0; - var termClient = 'unknown'; + var termType = 'unknown'; + var termHeight = 0; + var termWidth = 0; + var termClient = 'unknown'; - this.currentSyncFont = 'not_set'; + this.currentSyncFont = 'not_set'; - // Raw values set by e.g. telnet NAWS, ENVIRONMENT, etc. - this.env = {}; + // Raw values set by e.g. telnet NAWS, ENVIRONMENT, etc. + this.env = {}; Object.defineProperty(this, 'outputEncoding', { get : function() { @@ -58,13 +58,13 @@ function ClientTerminal(output) { if(this.isANSI()) { this.outputEncoding = 'cp437'; } else { - // :TODO: See how x84 does this -- only set if local/remote are binary + // :TODO: See how x84 does this -- only set if local/remote are binary this.outputEncoding = 'utf8'; } - // :TODO: according to this: http://mud-dev.wikidot.com/article:telnet-client-identification - // Windows telnet will send "VTNT". If so, set termClient='windows' - // there are some others on the page as well + // :TODO: according to this: http://mud-dev.wikidot.com/article:telnet-client-identification + // Windows telnet will send "VTNT". If so, set termClient='windows' + // there are some others on the page as well Log.debug( { encoding : this.outputEncoding }, 'Set output encoding due to terminal type change'); } @@ -110,7 +110,7 @@ ClientTerminal.prototype.disconnect = function() { ClientTerminal.prototype.isNixTerm = function() { // - // Standard *nix type terminals + // Standard *nix type terminals // if(this.termType.startsWith('xterm')) { return true; @@ -121,40 +121,40 @@ ClientTerminal.prototype.isNixTerm = function() { ClientTerminal.prototype.isANSI = function() { // - // ANSI terminals should be encoded to CP437 + // ANSI terminals should be encoded to CP437 // - // Some terminal types provided by Mercyful Fate / Enthral: - // ANSI-BBS - // PC-ANSI - // QANSI - // SCOANSI - // VT100 - // QNX + // Some terminal types provided by Mercyful Fate / Enthral: + // ANSI-BBS + // PC-ANSI + // QANSI + // SCOANSI + // VT100 + // QNX // - // Reports from various terminals + // Reports from various terminals // - // syncterm: - // * SyncTERM + // syncterm: + // * SyncTERM // - // xterm: - // * PuTTY + // xterm: + // * PuTTY // - // ansi-bbs: - // * fTelnet + // ansi-bbs: + // * fTelnet // - // pcansi: - // * ZOC + // pcansi: + // * ZOC // - // screen: - // * ConnectBot (Android) + // screen: + // * ConnectBot (Android) // - // linux: - // * JuiceSSH (note: TERM=linux also) + // linux: + // * JuiceSSH (note: TERM=linux also) // return [ 'ansi', 'pcansi', 'pc-ansi', 'ansi-bbs', 'qansi', 'scoansi', 'syncterm' ].includes(this.termType); }; -// :TODO: probably need to update these to convert IAC (0xff) -> IACIAC (escape it) +// :TODO: probably need to update these to convert IAC (0xff) -> IACIAC (escape it) ClientTerminal.prototype.write = function(s, convertLineFeeds, cb) { this.rawWrite(this.encode(s, convertLineFeeds), cb); @@ -178,11 +178,11 @@ ClientTerminal.prototype.pipeWrite = function(s, spec, cb) { spec = spec || 'renegade'; var conv = { - enigma : enigmaToAnsi, - renegade : renegadeToAnsi, + enigma : enigmaToAnsi, + renegade : renegadeToAnsi, }[spec] || renegadeToAnsi; - this.write(conv(s, this), null, cb); // null = use default for |convertLineFeeds| + this.write(conv(s, this), null, cb); // null = use default for |convertLineFeeds| }; ClientTerminal.prototype.encode = function(s, convertLineFeeds) { diff --git a/core/color_codes.js b/core/color_codes.js index b04d2c0b..272b611a 100644 --- a/core/color_codes.js +++ b/core/color_codes.js @@ -1,39 +1,39 @@ /* jslint node: true */ 'use strict'; -var ansi = require('./ansi_term.js'); -var getPredefinedMCIValue = require('./predefined_mci.js').getPredefinedMCIValue; +var ansi = require('./ansi_term.js'); +var getPredefinedMCIValue = require('./predefined_mci.js').getPredefinedMCIValue; -var assert = require('assert'); -var _ = require('lodash'); +var assert = require('assert'); +var _ = require('lodash'); -exports.enigmaToAnsi = enigmaToAnsi; -exports.stripPipeCodes = exports.stripEnigmaCodes = stripEnigmaCodes; -exports.pipeStrLen = exports.enigmaStrLen = enigmaStrLen; -exports.pipeToAnsi = exports.renegadeToAnsi = renegadeToAnsi; -exports.controlCodesToAnsi = controlCodesToAnsi; +exports.enigmaToAnsi = enigmaToAnsi; +exports.stripPipeCodes = exports.stripEnigmaCodes = stripEnigmaCodes; +exports.pipeStrLen = exports.enigmaStrLen = enigmaStrLen; +exports.pipeToAnsi = exports.renegadeToAnsi = renegadeToAnsi; +exports.controlCodesToAnsi = controlCodesToAnsi; -// :TODO: Not really happy with the module name of "color_codes". Would like something better +// :TODO: Not really happy with the module name of "color_codes". Would like something better -// Also add: -// * fromCelerity(): | -// * fromPCBoard(): (@X) -// * fromWildcat(): (@@ (same as PCBoard without 'X' prefix and '@' suffix) -// * fromWWIV(): <0-7> -// * fromSyncronet(): -// See http://wiki.synchro.net/custom:colors +// Also add: +// * fromCelerity(): | +// * fromPCBoard(): (@X) +// * fromWildcat(): (@@ (same as PCBoard without 'X' prefix and '@' suffix) +// * fromWWIV(): <0-7> +// * fromSyncronet(): +// See http://wiki.synchro.net/custom:colors -// :TODO: rid of enigmaToAnsi() -- never really use. Instead, create bbsToAnsi() that supports renegade, PCB, WWIV, etc... +// :TODO: rid of enigmaToAnsi() -- never really use. Instead, create bbsToAnsi() that supports renegade, PCB, WWIV, etc... function enigmaToAnsi(s, client) { if(-1 == s.indexOf('|')) { - return s; // no pipe codes present + return s; // no pipe codes present } - var result = ''; - var re = /\|([A-Z\d]{2}|\|)/g; + var result = ''; + var re = /\|([A-Z\d]{2}|\|)/g; var m; var lastIndex = 0; while((m = re.exec(s))) { @@ -44,14 +44,14 @@ function enigmaToAnsi(s, client) { continue; } - // convert to number + // convert to number val = parseInt(val, 10); if(isNaN(val)) { // - // ENiGMA MCI code? Only available if |client| - // is supplied. + // ENiGMA MCI code? Only available if |client| + // is supplied. // - val = getPredefinedMCIValue(client, m[1]) || ('|' + m[1]); // value itself or literal + val = getPredefinedMCIValue(client, m[1]) || ('|' + m[1]); // value itself or literal } if(_.isString(val)) { @@ -89,51 +89,51 @@ function enigmaStrLen(s) { function ansiSgrFromRenegadeColorCode(cc) { return ansi.sgr({ - 0 : [ 'reset', 'black' ], - 1 : [ 'reset', 'blue' ], - 2 : [ 'reset', 'green' ], - 3 : [ 'reset', 'cyan' ], - 4 : [ 'reset', 'red' ], - 5 : [ 'reset', 'magenta' ], - 6 : [ 'reset', 'yellow' ], - 7 : [ 'reset', 'white' ], + 0 : [ 'reset', 'black' ], + 1 : [ 'reset', 'blue' ], + 2 : [ 'reset', 'green' ], + 3 : [ 'reset', 'cyan' ], + 4 : [ 'reset', 'red' ], + 5 : [ 'reset', 'magenta' ], + 6 : [ 'reset', 'yellow' ], + 7 : [ 'reset', 'white' ], - 8 : [ 'bold', 'black' ], - 9 : [ 'bold', 'blue' ], - 10 : [ 'bold', 'green' ], - 11 : [ 'bold', 'cyan' ], - 12 : [ 'bold', 'red' ], - 13 : [ 'bold', 'magenta' ], - 14 : [ 'bold', 'yellow' ], - 15 : [ 'bold', 'white' ], + 8 : [ 'bold', 'black' ], + 9 : [ 'bold', 'blue' ], + 10 : [ 'bold', 'green' ], + 11 : [ 'bold', 'cyan' ], + 12 : [ 'bold', 'red' ], + 13 : [ 'bold', 'magenta' ], + 14 : [ 'bold', 'yellow' ], + 15 : [ 'bold', 'white' ], - 16 : [ 'blackBG' ], - 17 : [ 'blueBG' ], - 18 : [ 'greenBG' ], - 19 : [ 'cyanBG' ], - 20 : [ 'redBG' ], - 21 : [ 'magentaBG' ], - 22 : [ 'yellowBG' ], - 23 : [ 'whiteBG' ], + 16 : [ 'blackBG' ], + 17 : [ 'blueBG' ], + 18 : [ 'greenBG' ], + 19 : [ 'cyanBG' ], + 20 : [ 'redBG' ], + 21 : [ 'magentaBG' ], + 22 : [ 'yellowBG' ], + 23 : [ 'whiteBG' ], - 24 : [ 'blink', 'blackBG' ], - 25 : [ 'blink', 'blueBG' ], - 26 : [ 'blink', 'greenBG' ], - 27 : [ 'blink', 'cyanBG' ], - 28 : [ 'blink', 'redBG' ], - 29 : [ 'blink', 'magentaBG' ], - 30 : [ 'blink', 'yellowBG' ], - 31 : [ 'blink', 'whiteBG' ], + 24 : [ 'blink', 'blackBG' ], + 25 : [ 'blink', 'blueBG' ], + 26 : [ 'blink', 'greenBG' ], + 27 : [ 'blink', 'cyanBG' ], + 28 : [ 'blink', 'redBG' ], + 29 : [ 'blink', 'magentaBG' ], + 30 : [ 'blink', 'yellowBG' ], + 31 : [ 'blink', 'whiteBG' ], }[cc] || 'normal'); } function renegadeToAnsi(s, client) { if(-1 == s.indexOf('|')) { - return s; // no pipe codes present + return s; // no pipe codes present } - var result = ''; - var re = /\|([A-Z\d]{2}|\|)/g; + var result = ''; + var re = /\|([A-Z\d]{2}|\|)/g; var m; var lastIndex = 0; while((m = re.exec(s))) { @@ -144,10 +144,10 @@ function renegadeToAnsi(s, client) { continue; } - // convert to number + // convert to number val = parseInt(val, 10); if(isNaN(val)) { - val = getPredefinedMCIValue(client, m[1]) || ('|' + m[1]); // value itself or literal + val = getPredefinedMCIValue(client, m[1]) || ('|' + m[1]); // value itself or literal } if(_.isString(val)) { @@ -164,27 +164,27 @@ function renegadeToAnsi(s, client) { } // -// Converts various control codes popular in BBS packages -// to ANSI escape sequences. Additionaly supports ENiGMA style -// MCI codes. +// Converts various control codes popular in BBS packages +// to ANSI escape sequences. Additionaly supports ENiGMA style +// MCI codes. // -// Supported control code formats: -// * Renegade : |## -// * PCBoard : @X## where the first number/char is FG color, and second is BG -// * WildCat! : @##@ the same as PCBoard without the X prefix, but with a @ suffix -// * WWIV : ^# +// Supported control code formats: +// * Renegade : |## +// * PCBoard : @X## where the first number/char is FG color, and second is BG +// * WildCat! : @##@ the same as PCBoard without the X prefix, but with a @ suffix +// * WWIV : ^# // -// TODO: Add Synchronet and Celerity format support +// TODO: Add Synchronet and Celerity format support // -// Resources: -// * http://wiki.synchro.net/custom:colors +// Resources: +// * http://wiki.synchro.net/custom:colors // function controlCodesToAnsi(s, client) { - const RE = /(\|([A-Z0-9]{2})|\|)|(@X([0-9A-F]{2}))|(@([0-9A-F]{2})@)|(\x03[0-9]|\x03)/g; // eslint-disable-line no-control-regex + const RE = /(\|([A-Z0-9]{2})|\|)|(@X([0-9A-F]{2}))|(@([0-9A-F]{2})@)|(\x03[0-9]|\x03)/g; // eslint-disable-line no-control-regex let m; - let result = ''; - let lastIndex = 0; + let result = ''; + let lastIndex = 0; let v; let fg; let bg; @@ -192,11 +192,11 @@ function controlCodesToAnsi(s, client) { while((m = RE.exec(s))) { switch(m[0].charAt(0)) { case '|' : - // Renegade or ENiGMA MCI + // Renegade or ENiGMA MCI v = parseInt(m[2], 10); if(isNaN(v)) { - v = getPredefinedMCIValue(client, m[2]) || m[0]; // value itself or literal + v = getPredefinedMCIValue(client, m[2]) || m[0]; // value itself or literal } if(_.isString(v)) { @@ -208,52 +208,52 @@ function controlCodesToAnsi(s, client) { break; case '@' : - // PCBoard @X## or Wildcat! @##@ + // PCBoard @X## or Wildcat! @##@ if('@' === m[0].substr(-1)) { - // Wildcat! + // Wildcat! v = m[6]; } else { v = m[4]; } fg = { - 0 : [ 'reset', 'black' ], - 1 : [ 'reset', 'blue' ], - 2 : [ 'reset', 'green' ], - 3 : [ 'reset', 'cyan' ], - 4 : [ 'reset', 'red' ], - 5 : [ 'reset', 'magenta' ], - 6 : [ 'reset', 'yellow' ], - 7 : [ 'reset', 'white' ], + 0 : [ 'reset', 'black' ], + 1 : [ 'reset', 'blue' ], + 2 : [ 'reset', 'green' ], + 3 : [ 'reset', 'cyan' ], + 4 : [ 'reset', 'red' ], + 5 : [ 'reset', 'magenta' ], + 6 : [ 'reset', 'yellow' ], + 7 : [ 'reset', 'white' ], - 8 : [ 'blink', 'black' ], - 9 : [ 'blink', 'blue' ], - A : [ 'blink', 'green' ], - B : [ 'blink', 'cyan' ], - C : [ 'blink', 'red' ], - D : [ 'blink', 'magenta' ], - E : [ 'blink', 'yellow' ], - F : [ 'blink', 'white' ], + 8 : [ 'blink', 'black' ], + 9 : [ 'blink', 'blue' ], + A : [ 'blink', 'green' ], + B : [ 'blink', 'cyan' ], + C : [ 'blink', 'red' ], + D : [ 'blink', 'magenta' ], + E : [ 'blink', 'yellow' ], + F : [ 'blink', 'white' ], }[v.charAt(0)] || ['normal']; bg = { - 0 : [ 'blackBG' ], - 1 : [ 'blueBG' ], - 2 : [ 'greenBG' ], - 3 : [ 'cyanBG' ], - 4 : [ 'redBG' ], - 5 : [ 'magentaBG' ], - 6 : [ 'yellowBG' ], - 7 : [ 'whiteBG' ], + 0 : [ 'blackBG' ], + 1 : [ 'blueBG' ], + 2 : [ 'greenBG' ], + 3 : [ 'cyanBG' ], + 4 : [ 'redBG' ], + 5 : [ 'magentaBG' ], + 6 : [ 'yellowBG' ], + 7 : [ 'whiteBG' ], - 8 : [ 'bold', 'blackBG' ], - 9 : [ 'bold', 'blueBG' ], - A : [ 'bold', 'greenBG' ], - B : [ 'bold', 'cyanBG' ], - C : [ 'bold', 'redBG' ], - D : [ 'bold', 'magentaBG' ], - E : [ 'bold', 'yellowBG' ], - F : [ 'bold', 'whiteBG' ], + 8 : [ 'bold', 'blackBG' ], + 9 : [ 'bold', 'blueBG' ], + A : [ 'bold', 'greenBG' ], + B : [ 'bold', 'cyanBG' ], + C : [ 'bold', 'redBG' ], + D : [ 'bold', 'magentaBG' ], + E : [ 'bold', 'yellowBG' ], + F : [ 'bold', 'whiteBG' ], }[v.charAt(1)] || [ 'normal' ]; v = ansi.sgr(fg.concat(bg)); @@ -267,16 +267,16 @@ function controlCodesToAnsi(s, client) { v += m[0]; } else { v = ansi.sgr({ - 0 : [ 'reset', 'black' ], - 1 : [ 'bold', 'cyan' ], - 2 : [ 'bold', 'yellow' ], - 3 : [ 'reset', 'magenta' ], - 4 : [ 'bold', 'white', 'blueBG' ], - 5 : [ 'reset', 'green' ], - 6 : [ 'bold', 'blink', 'red' ], - 7 : [ 'bold', 'blue' ], - 8 : [ 'reset', 'blue' ], - 9 : [ 'reset', 'cyan' ], + 0 : [ 'reset', 'black' ], + 1 : [ 'bold', 'cyan' ], + 2 : [ 'bold', 'yellow' ], + 3 : [ 'reset', 'magenta' ], + 4 : [ 'bold', 'white', 'blueBG' ], + 5 : [ 'reset', 'green' ], + 6 : [ 'bold', 'blink', 'red' ], + 7 : [ 'bold', 'blue' ], + 8 : [ 'reset', 'blue' ], + 9 : [ 'reset', 'cyan' ], }[v] || 'normal'); } diff --git a/core/combatnet.js b/core/combatnet.js index d6616449..1b53a153 100644 --- a/core/combatnet.js +++ b/core/combatnet.js @@ -1,29 +1,29 @@ /* jslint node: true */ 'use strict'; -// enigma-bbs -const MenuModule = require('../core/menu_module.js').MenuModule; -const resetScreen = require('../core/ansi_term.js').resetScreen; +// enigma-bbs +const MenuModule = require('../core/menu_module.js').MenuModule; +const resetScreen = require('../core/ansi_term.js').resetScreen; -// deps -const async = require('async'); -const _ = require('lodash'); +// deps +const async = require('async'); +const _ = require('lodash'); const RLogin = require('rlogin'); exports.moduleInfo = { - name : 'CombatNet', - desc : 'CombatNet Access Module', - author : 'Dave Stephens', + name : 'CombatNet', + desc : 'CombatNet Access Module', + author : 'Dave Stephens', }; exports.getModule = class CombatNetModule extends MenuModule { constructor(options) { super(options); - // establish defaults - this.config = options.menuConfig.config; - this.config.host = this.config.host || 'bbs.combatnet.us'; - this.config.rloginPort = this.config.rloginPort || 4513; + // establish defaults + this.config = options.menuConfig.config; + this.config.host = this.config.host || 'bbs.combatnet.us'; + this.config.rloginPort = this.config.rloginPort || 4513; } initSequence() { @@ -51,7 +51,7 @@ exports.getModule = class CombatNetModule extends MenuModule { }; const rlogin = new RLogin( - { 'clientUsername' : self.config.password, + { 'clientUsername' : self.config.password, 'serverUsername' : `${self.config.bbsTag}${self.client.user.username}`, 'host' : self.config.host, 'port' : self.config.rloginPort, @@ -79,7 +79,7 @@ exports.getModule = class CombatNetModule extends MenuModule { } rlogin.on('connect', - /* The 'connect' event handler will be supplied with one argument, + /* The 'connect' event handler will be supplied with one argument, a boolean indicating whether or not the connection was established. */ function(state) { @@ -101,7 +101,7 @@ exports.getModule = class CombatNetModule extends MenuModule { // connect... rlogin.connect(); - // note: no explicit callback() until we're finished! + // note: no explicit callback() until we're finished! } ], err => { @@ -109,7 +109,7 @@ exports.getModule = class CombatNetModule extends MenuModule { self.client.log.warn( { error : err.message }, 'CombatNet error'); } - // if the client is still here, go to previous + // if the client is still here, go to previous self.prevMenu(); } ); diff --git a/core/conf_area_util.js b/core/conf_area_util.js index 7c4bf5bb..1c0b65c4 100644 --- a/core/conf_area_util.js +++ b/core/conf_area_util.js @@ -1,15 +1,15 @@ /* jslint node: true */ 'use strict'; -// deps -const _ = require('lodash'); +// deps +const _ = require('lodash'); -exports.sortAreasOrConfs = sortAreasOrConfs; +exports.sortAreasOrConfs = sortAreasOrConfs; // -// Method for sorting message, file, etc. areas and confs -// If the sort key is present and is a number, sort in numerical order; -// Otherwise, use a locale comparison on the sort key or name as a fallback +// Method for sorting message, file, etc. areas and confs +// If the sort key is present and is a number, sort in numerical order; +// Otherwise, use a locale comparison on the sort key or name as a fallback // function sortAreasOrConfs(areasOrConfs, type) { let entryA; @@ -24,7 +24,7 @@ function sortAreasOrConfs(areasOrConfs, type) { } else { const keyA = entryA.sort ? entryA.sort.toString() : entryA.name; const keyB = entryB.sort ? entryB.sort.toString() : entryB.name; - return keyA.localeCompare(keyB, { sensitivity : false, numeric : true } ); // "natural" compare + return keyA.localeCompare(keyB, { sensitivity : false, numeric : true } ); // "natural" compare } }); } \ No newline at end of file diff --git a/core/config.js b/core/config.js index 3a1359d7..f85ffe2a 100644 --- a/core/config.js +++ b/core/config.js @@ -1,17 +1,17 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Errors = require('./enig_error.js').Errors; +// ENiGMA½ +const Errors = require('./enig_error.js').Errors; -// deps -const paths = require('path'); -const async = require('async'); -const _ = require('lodash'); -const assert = require('assert'); +// deps +const paths = require('path'); +const async = require('async'); +const _ = require('lodash'); +const assert = require('assert'); -exports.init = init; -exports.getDefaultPath = getDefaultPath; +exports.init = init; +exports.getDefaultPath = getDefaultPath; let currentConfiguration = {}; @@ -31,7 +31,7 @@ function hasMessageConferenceAndArea(config) { let result = false; _.forEach(nonInternalConfs, confTag => { if(_.has(config.messageConferences[confTag], 'areas') && - Object.keys(config.messageConferences[confTag].areas) > 0) + Object.keys(config.messageConferences[confTag].areas) > 0) { result = true; return false; // stop iteration @@ -48,9 +48,9 @@ function mergeValidateAndFinalize(config, cb) { const mergedConfig = _.mergeWith( getDefaultConfig(), config, (conf1, conf2) => { - // Arrays should always concat + // Arrays should always concat if(_.isArray(conf1)) { - // :TODO: look for collisions & override dupes + // :TODO: look for collisions & override dupes return conf1.concat(conf2); } } @@ -60,16 +60,16 @@ function mergeValidateAndFinalize(config, cb) { }, function validate(mergedConfig, callback) { // - // Various sections must now exist in config + // Various sections must now exist in config // - // :TODO: Logic is broken here: + // :TODO: Logic is broken here: if(hasMessageConferenceAndArea(mergedConfig)) { return callback(Errors.MissingConfig('Please create at least one message conference and area!')); } return callback(null, mergedConfig); }, function setIt(mergedConfig, callback) { - // :TODO: .config property is to be deprecated once conversions are done + // :TODO: .config property is to be deprecated once conversions are done exports.config = currentConfiguration = mergedConfig; exports.get = () => currentConfiguration; @@ -101,8 +101,8 @@ function init(configPath, options, cb) { const ConfigCache = require('./config_cache.js'); const getConfigOptions = { - filePath : configPath, - noWatch : options.noWatch, + filePath : configPath, + noWatch : options.noWatch, }; if(!options.noWatch) { getConfigOptions.callback = changed; @@ -117,138 +117,138 @@ function init(configPath, options, cb) { } function getDefaultPath() { - // e.g. /enigma-bbs-install-path/config/ + // e.g. /enigma-bbs-install-path/config/ return './config/'; } function getDefaultConfig() { return { general : { - boardName : 'Another Fine ENiGMA½ BBS', + boardName : 'Another Fine ENiGMA½ BBS', - closedSystem : false, // is the system closed to new users? + closedSystem : false, // is the system closed to new users? - loginAttempts : 3, + loginAttempts : 3, - menuFile : 'menu.hjson', // Override to use something else, e.g. demo.hjson. Can be a full path (defaults to ./config) - promptFile : 'prompt.hjson', // Override to use soemthing else, e.g. myprompt.hjson. Can be a full path (defaults to ./config) + menuFile : 'menu.hjson', // Override to use something else, e.g. demo.hjson. Can be a full path (defaults to ./config) + promptFile : 'prompt.hjson', // Override to use soemthing else, e.g. myprompt.hjson. Can be a full path (defaults to ./config) }, - // :TODO: see notes below about 'theme' section - move this! + // :TODO: see notes below about 'theme' section - move this! preLoginTheme : 'luciano_blocktronics', users : { - usernameMin : 2, - usernameMax : 16, // Note that FidoNet wants 36 max - usernamePattern : '^[A-Za-z0-9~!@#$%^&*()\\-\\_+ ]+$', + usernameMin : 2, + usernameMax : 16, // Note that FidoNet wants 36 max + usernamePattern : '^[A-Za-z0-9~!@#$%^&*()\\-\\_+ ]+$', - passwordMin : 6, - passwordMax : 128, - badPassFile : paths.join(__dirname, '../misc/10_million_password_list_top_10000.txt'), // https://github.com/danielmiessler/SecLists + passwordMin : 6, + passwordMax : 128, + badPassFile : paths.join(__dirname, '../misc/10_million_password_list_top_10000.txt'), // https://github.com/danielmiessler/SecLists - realNameMax : 32, - locationMax : 32, - affilsMax : 32, - emailMax : 255, - webMax : 255, + realNameMax : 32, + locationMax : 32, + affilsMax : 32, + emailMax : 255, + webMax : 255, - requireActivation : false, // require SysOp activation? false = auto-activate + requireActivation : false, // require SysOp activation? false = auto-activate - groups : [ 'users', 'sysops' ], // built in groups - defaultGroups : [ 'users' ], // default groups new users belong to + groups : [ 'users', 'sysops' ], // built in groups + defaultGroups : [ 'users' ], // default groups new users belong to - newUserNames : [ 'new', 'apply' ], // Names reserved for applying + newUserNames : [ 'new', 'apply' ], // Names reserved for applying - badUserNames : [ + badUserNames : [ 'sysop', 'admin', 'administrator', 'root', 'all', 'areamgr', 'filemgr', 'filefix', 'areafix', 'allfix' ], }, - // :TODO: better name for "defaults"... which is redundant here! + // :TODO: better name for "defaults"... which is redundant here! /* - Concept - "theme" : { - "default" : "defaultThemeName", // or "*" - "preLogin" : "*", - "passwordChar" : "*", - ... - } - */ + Concept + "theme" : { + "default" : "defaultThemeName", // or "*" + "preLogin" : "*", + "passwordChar" : "*", + ... + } + */ defaults : { - theme : 'luciano_blocktronics', - passwordChar : '*', // TODO: move to user ? - dateFormat : { - short : 'MM/DD/YYYY', - long : 'ddd, MMMM Do, YYYY', + theme : 'luciano_blocktronics', + passwordChar : '*', // TODO: move to user ? + dateFormat : { + short : 'MM/DD/YYYY', + long : 'ddd, MMMM Do, YYYY', }, timeFormat : { - short : 'h:mm a', + short : 'h:mm a', }, dateTimeFormat : { - short : 'MM/DD/YYYY h:mm a', - long : 'ddd, MMMM Do, YYYY, h:mm a', + short : 'MM/DD/YYYY h:mm a', + long : 'ddd, MMMM Do, YYYY, h:mm a', } }, menus : { - cls : true, // Clear screen before each menu by default? + cls : true, // Clear screen before each menu by default? }, - paths : { - config : paths.join(__dirname, './../config/'), - mods : paths.join(__dirname, './../mods/'), - loginServers : paths.join(__dirname, './servers/login/'), - contentServers : paths.join(__dirname, './servers/content/'), + paths : { + config : paths.join(__dirname, './../config/'), + mods : paths.join(__dirname, './../mods/'), + loginServers : paths.join(__dirname, './servers/login/'), + contentServers : paths.join(__dirname, './servers/content/'), - scannerTossers : paths.join(__dirname, './scanner_tossers/'), - mailers : paths.join(__dirname, './mailers/') , + scannerTossers : paths.join(__dirname, './scanner_tossers/'), + mailers : paths.join(__dirname, './mailers/') , - art : paths.join(__dirname, './../art/general/'), - themes : paths.join(__dirname, './../art/themes/'), - logs : paths.join(__dirname, './../logs/'), // :TODO: set up based on system, e.g. /var/logs/enigmabbs or such - db : paths.join(__dirname, './../db/'), - modsDb : paths.join(__dirname, './../db/mods/'), - dropFiles : paths.join(__dirname, './../dropfiles/'), // + "/node/ - misc : paths.join(__dirname, './../misc/'), + art : paths.join(__dirname, './../art/general/'), + themes : paths.join(__dirname, './../art/themes/'), + logs : paths.join(__dirname, './../logs/'), // :TODO: set up based on system, e.g. /var/logs/enigmabbs or such + db : paths.join(__dirname, './../db/'), + modsDb : paths.join(__dirname, './../db/mods/'), + dropFiles : paths.join(__dirname, './../dropfiles/'), // + "/node/ + misc : paths.join(__dirname, './../misc/'), }, loginServers : { telnet : { - port : 8888, - enabled : true, - firstMenu : 'telnetConnected', + port : 8888, + enabled : true, + firstMenu : 'telnetConnected', }, ssh : { - port : 8889, - enabled : false, // default to false as PK/pass in config.hjson are required + port : 8889, + enabled : false, // default to false as PK/pass in config.hjson are required // - // Private key in PEM format + // Private key in PEM format // - // Generating your PK: - // > openssl genrsa -des3 -out ./config/ssh_private_key.pem 2048 + // Generating your PK: + // > openssl genrsa -des3 -out ./config/ssh_private_key.pem 2048 // - // Then, set servers.ssh.privateKeyPass to the password you use above - // in your config.hjson + // Then, set servers.ssh.privateKeyPass to the password you use above + // in your config.hjson // - privateKeyPem : paths.join(__dirname, './../config/ssh_private_key.pem'), - firstMenu : 'sshConnected', - firstMenuNewUser : 'sshConnectedNewUser', + privateKeyPem : paths.join(__dirname, './../config/ssh_private_key.pem'), + firstMenu : 'sshConnected', + firstMenuNewUser : 'sshConnectedNewUser', }, webSocket : { ws : { - // non-secure ws:// - enabled : false, - port : 8810, + // non-secure ws:// + enabled : false, + port : 8810, }, wss : { - // secure ws:// - // must provide valid certPem and keyPem - enabled : false, - port : 8811, - certPem : paths.join(__dirname, './../config/https_cert.pem'), - keyPem : paths.join(__dirname, './../config/https_cert_key.pem'), + // secure ws:// + // must provide valid certPem and keyPem + enabled : false, + port : 8811, + certPem : paths.join(__dirname, './../config/https_cert.pem'), + keyPem : paths.join(__dirname, './../config/https_cert_key.pem'), }, }, }, @@ -261,200 +261,200 @@ function getDefaultConfig() { resetPassword : { // - // The following templates have these variables available to them: + // The following templates have these variables available to them: // - // * %BOARDNAME% : Name of BBS - // * %USERNAME% : Username of whom to reset password - // * %TOKEN% : Reset token - // * %RESET_URL% : In case of email, the link to follow for reset. In case of landing page, - // URL to POST submit reset form. + // * %BOARDNAME% : Name of BBS + // * %USERNAME% : Username of whom to reset password + // * %TOKEN% : Reset token + // * %RESET_URL% : In case of email, the link to follow for reset. In case of landing page, + // URL to POST submit reset form. - // templates for pw reset *email* - resetPassEmailText : paths.join(__dirname, '../misc/reset_password_email.template.txt'), // plain text version - resetPassEmailHtml : paths.join(__dirname, '../misc/reset_password_email.template.html'), // HTML version + // templates for pw reset *email* + resetPassEmailText : paths.join(__dirname, '../misc/reset_password_email.template.txt'), // plain text version + resetPassEmailHtml : paths.join(__dirname, '../misc/reset_password_email.template.html'), // HTML version - // tempalte for pw reset *landing page* + // tempalte for pw reset *landing page* // - resetPageTemplate : paths.join(__dirname, './../www/reset_password.template.html'), + resetPageTemplate : paths.join(__dirname, './../www/reset_password.template.html'), }, http : { enabled : false, - port : 8080, + port : 8080, }, https : { - enabled : false, - port : 8443, - certPem : paths.join(__dirname, './../config/https_cert.pem'), - keyPem : paths.join(__dirname, './../config/https_cert_key.pem'), + enabled : false, + port : 8443, + certPem : paths.join(__dirname, './../config/https_cert.pem'), + keyPem : paths.join(__dirname, './../config/https_cert_key.pem'), } } }, infoExtractUtils : { Exiftool2Desc : { - cmd : `${__dirname}/../util/exiftool2desc.js`, // ensure chmod +x + cmd : `${__dirname}/../util/exiftool2desc.js`, // ensure chmod +x }, Exiftool : { - cmd : 'exiftool', - args : [ + cmd : 'exiftool', + args : [ '-charset', 'utf8', '{filePath}', - // exclude the following: + // exclude the following: '--directory', '--filepermissions', '--exiftoolversion', '--filename', '--filesize', '--filemodifydate', '--fileaccessdate', '--fileinodechangedate', '--createdate', '--modifydate', '--metadatadate', '--xmptoolkit' ] }, XDMS2Desc : { - // http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html - cmd : 'xdms', - args : [ 'd', '{filePath}' ] + // http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html + cmd : 'xdms', + args : [ 'd', '{filePath}' ] }, XDMS2LongDesc : { - // http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html - cmd : 'xdms', - args : [ 'f', '{filePath}' ] + // http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html + cmd : 'xdms', + args : [ 'f', '{filePath}' ] } }, fileTypes : { // - // File types explicitly known to the system. Here we can configure - // information extraction, archive treatment, etc. + // File types explicitly known to the system. Here we can configure + // information extraction, archive treatment, etc. // - // MIME types can be found in mime-db: https://github.com/jshttp/mime-db + // MIME types can be found in mime-db: https://github.com/jshttp/mime-db // - // Resources for signature/magic bytes: - // * http://www.garykessler.net/library/file_sigs.html + // Resources for signature/magic bytes: + // * http://www.garykessler.net/library/file_sigs.html // // - // :TODO: text/x-ansi -> SAUCE extraction for .ans uploads - // :TODO: textual : bool -- if text, we can view. - // :TODO: asText : { cmd, args[] } -> viewable text + // :TODO: text/x-ansi -> SAUCE extraction for .ans uploads + // :TODO: textual : bool -- if text, we can view. + // :TODO: asText : { cmd, args[] } -> viewable text // - // Audio + // Audio // 'audio/mpeg' : { - desc : 'MP3 Audio', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', + desc : 'MP3 Audio', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', }, 'application/pdf' : { - desc : 'Adobe PDF', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', + desc : 'Adobe PDF', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', }, // - // Video + // Video // 'video/mp4' : { - desc : 'MPEG Video', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', + desc : 'MPEG Video', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', }, 'video/x-matroska ' : { - desc : 'Matroska Video', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', + desc : 'Matroska Video', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', }, 'video/x-msvideo' : { - desc : 'Audio Video Interleave', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', + desc : 'Audio Video Interleave', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', }, // - // Images + // Images // - 'image/jpeg' : { - desc : 'JPEG Image', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', + 'image/jpeg' : { + desc : 'JPEG Image', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', }, - 'image/png' : { - desc : 'Portable Network Graphic Image', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', + 'image/png' : { + desc : 'Portable Network Graphic Image', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', }, 'image/gif' : { - desc : 'Graphics Interchange Format Image', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', + desc : 'Graphics Interchange Format Image', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', }, 'image/webp' : { - desc : 'WebP Image', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', + desc : 'WebP Image', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', }, // - // Archives + // Archives // 'application/zip' : { - desc : 'ZIP Archive', - sig : '504b0304', - offset : 0, - archiveHandler : '7Zip', + desc : 'ZIP Archive', + sig : '504b0304', + offset : 0, + archiveHandler : '7Zip', }, /* - 'application/x-cbr' : { - desc : 'Comic Book Archive', - sig : '504b0304', - }, - */ + 'application/x-cbr' : { + desc : 'Comic Book Archive', + sig : '504b0304', + }, + */ 'application/x-arj' : { - desc : 'ARJ Archive', - sig : '60ea', - offset : 0, - archiveHandler : 'Arj', + desc : 'ARJ Archive', + sig : '60ea', + offset : 0, + archiveHandler : 'Arj', }, 'application/x-rar-compressed' : { - desc : 'RAR Archive', - sig : '526172211a0700', - offset : 0, - archiveHandler : 'Rar', + desc : 'RAR Archive', + sig : '526172211a0700', + offset : 0, + archiveHandler : 'Rar', }, 'application/gzip' : { - desc : 'Gzip Archive', - sig : '1f8b', - offset : 0, - archiveHandler : 'TarGz', + desc : 'Gzip Archive', + sig : '1f8b', + offset : 0, + archiveHandler : 'TarGz', }, - // :TODO: application/x-bzip + // :TODO: application/x-bzip 'application/x-bzip2' : { - desc : 'BZip2 Archive', - sig : '425a68', - offset : 0, - archiveHandler : '7Zip', + desc : 'BZip2 Archive', + sig : '425a68', + offset : 0, + archiveHandler : '7Zip', }, 'application/x-lzh-compressed' : { - desc : 'LHArc Archive', - sig : '2d6c68', - offset : 2, - archiveHandler : 'Lha', + desc : 'LHArc Archive', + sig : '2d6c68', + offset : 2, + archiveHandler : 'Lha', }, 'application/x-lzx' : { - desc : 'LZX Archive', - sig : '4c5a5800', - offset : 0, - archiveHandler : 'Lzx', + desc : 'LZX Archive', + sig : '4c5a5800', + offset : 0, + archiveHandler : 'Lzx', }, 'application/x-7z-compressed' : { - desc : '7-Zip Archive', - sig : '377abcaf271c', - offset : 0, - archiveHandler : '7Zip', + desc : '7-Zip Archive', + sig : '377abcaf271c', + offset : 0, + archiveHandler : '7Zip', }, // - // Generics that need further mapping + // Generics that need further mapping // 'application/octet-stream' : [ { - desc : 'Amiga DISKMASHER', - sig : '444d5321', // DMS! - ext : '.dms', - shortDescUtil : 'XDMS2Desc', - longDescUtil : 'XDMS2LongDesc', + desc : 'Amiga DISKMASHER', + sig : '444d5321', // DMS! + ext : '.dms', + shortDescUtil : 'XDMS2Desc', + longDescUtil : 'XDMS2LongDesc', } ] }, @@ -462,119 +462,119 @@ function getDefaultConfig() { archives : { archivers : { '7Zip' : { - compress : { - cmd : '7za', - args : [ 'a', '-tzip', '{archivePath}', '{fileList}' ], + compress : { + cmd : '7za', + args : [ 'a', '-tzip', '{archivePath}', '{fileList}' ], }, - decompress : { - cmd : '7za', - args : [ 'e', '-o{extractPath}', '{archivePath}' ] // :TODO: should be 'x'? + decompress : { + cmd : '7za', + args : [ 'e', '-o{extractPath}', '{archivePath}' ] // :TODO: should be 'x'? }, - list : { - cmd : '7za', - args : [ 'l', '{archivePath}' ], - entryMatch : '^[0-9]{4}-[0-9]{2}-[0-9]{2}\\s[0-9]{2}:[0-9]{2}:[0-9]{2}\\s[A-Za-z\\.]{5}\\s+([0-9]+)\\s+[0-9]+\\s+([^\\r\\n]+)$', + list : { + cmd : '7za', + args : [ 'l', '{archivePath}' ], + entryMatch : '^[0-9]{4}-[0-9]{2}-[0-9]{2}\\s[0-9]{2}:[0-9]{2}:[0-9]{2}\\s[A-Za-z\\.]{5}\\s+([0-9]+)\\s+[0-9]+\\s+([^\\r\\n]+)$', }, - extract : { - cmd : '7za', - args : [ 'e', '-o{extractPath}', '{archivePath}', '{fileList}' ], + extract : { + cmd : '7za', + args : [ 'e', '-o{extractPath}', '{archivePath}', '{fileList}' ], }, }, Lha : { // - // 'lha' command can be obtained from: - // * apt-get: lhasa + // 'lha' command can be obtained from: + // * apt-get: lhasa // - // (compress not currently supported) + // (compress not currently supported) // - decompress : { - cmd : 'lha', - args : [ '-efw={extractPath}', '{archivePath}' ], + decompress : { + cmd : 'lha', + args : [ '-efw={extractPath}', '{archivePath}' ], }, - list : { - cmd : 'lha', - args : [ '-l', '{archivePath}' ], - entryMatch : '^[\\[a-z\\]]+(?:\\s+[0-9]+\\s+[0-9]+|\\s+)([0-9]+)\\s+[0-9]{2}\\.[0-9]\\%\\s+[A-Za-z]{3}\\s+[0-9]{1,2}\\s+[0-9]{4}\\s+([^\\r\\n]+)$', + list : { + cmd : 'lha', + args : [ '-l', '{archivePath}' ], + entryMatch : '^[\\[a-z\\]]+(?:\\s+[0-9]+\\s+[0-9]+|\\s+)([0-9]+)\\s+[0-9]{2}\\.[0-9]\\%\\s+[A-Za-z]{3}\\s+[0-9]{1,2}\\s+[0-9]{4}\\s+([^\\r\\n]+)$', }, - extract : { - cmd : 'lha', - args : [ '-efw={extractPath}', '{archivePath}', '{fileList}' ] + extract : { + cmd : 'lha', + args : [ '-efw={extractPath}', '{archivePath}', '{fileList}' ] } }, Lzx : { // - // 'unlzx' command can be obtained from: - // * Debian based: https://launchpad.net/~rzr/+archive/ubuntu/ppa/+build/2486127 (amd64/x86_64) - // * RedHat: https://fedora.pkgs.org/28/rpm-sphere/unlzx-1.1-4.1.x86_64.rpm.html - // * Source: http://xavprods.free.fr/lzx/ + // 'unlzx' command can be obtained from: + // * Debian based: https://launchpad.net/~rzr/+archive/ubuntu/ppa/+build/2486127 (amd64/x86_64) + // * RedHat: https://fedora.pkgs.org/28/rpm-sphere/unlzx-1.1-4.1.x86_64.rpm.html + // * Source: http://xavprods.free.fr/lzx/ // - decompress : { - cmd : 'unlzx', - // unzlx doesn't have a output dir option, but we'll cwd to the temp output dir first - args : [ '-x', '{archivePath}' ], + decompress : { + cmd : 'unlzx', + // unzlx doesn't have a output dir option, but we'll cwd to the temp output dir first + args : [ '-x', '{archivePath}' ], }, - list : { - cmd : 'unlzx', - args : [ '-v', '{archivePath}' ], - entryMatch : '^\\s+([0-9]+)\\s+[^\\s]+\\s+[0-9]{2}:[0-9]{2}:[0-9]{2}\\s+[0-9]{1,2}-[a-z]{3}-[0-9]{4}\\s+[a-z\\-]+\\s+\\"([^"]+)\\"$', + list : { + cmd : 'unlzx', + args : [ '-v', '{archivePath}' ], + entryMatch : '^\\s+([0-9]+)\\s+[^\\s]+\\s+[0-9]{2}:[0-9]{2}:[0-9]{2}\\s+[0-9]{1,2}-[a-z]{3}-[0-9]{4}\\s+[a-z\\-]+\\s+\\"([^"]+)\\"$', } }, Arj : { // - // 'arj' command can be obtained from: - // * apt-get: arj + // 'arj' command can be obtained from: + // * apt-get: arj // - decompress : { - cmd : 'arj', - args : [ 'x', '{archivePath}', '{extractPath}' ], + decompress : { + cmd : 'arj', + args : [ 'x', '{archivePath}', '{extractPath}' ], }, - list : { - cmd : 'arj', - args : [ 'l', '{archivePath}' ], - entryMatch : '^([^\\s]+)\\s+([0-9]+)\\s+[0-9]+\\s[0-9\\.]+\\s+[0-9]{2}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\:[0-9]{2}\\s+(?:[^\\r\\n]+)$', - entryGroupOrder : { // defaults to { byteSize : 1, fileName : 2 } - fileName : 1, - byteSize : 2, + list : { + cmd : 'arj', + args : [ 'l', '{archivePath}' ], + entryMatch : '^([^\\s]+)\\s+([0-9]+)\\s+[0-9]+\\s[0-9\\.]+\\s+[0-9]{2}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\:[0-9]{2}\\s+(?:[^\\r\\n]+)$', + entryGroupOrder : { // defaults to { byteSize : 1, fileName : 2 } + fileName : 1, + byteSize : 2, } }, - extract : { - cmd : 'arj', - args : [ 'e', '{archivePath}', '{extractPath}', '{fileList}' ], + extract : { + cmd : 'arj', + args : [ 'e', '{archivePath}', '{extractPath}', '{fileList}' ], } }, Rar : { - decompress : { - cmd : 'unrar', - args : [ 'x', '{archivePath}', '{extractPath}' ], + decompress : { + cmd : 'unrar', + args : [ 'x', '{archivePath}', '{extractPath}' ], }, - list : { - cmd : 'unrar', - args : [ 'l', '{archivePath}' ], - entryMatch : '^\\s+[\\.A-Z]+\\s+([\\d]+)\\s{2}[0-9]{2}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\s{2}([^\\r\\n]+)$', + list : { + cmd : 'unrar', + args : [ 'l', '{archivePath}' ], + entryMatch : '^\\s+[\\.A-Z]+\\s+([\\d]+)\\s{2}[0-9]{2}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\s{2}([^\\r\\n]+)$', }, - extract : { - cmd : 'unrar', - args : [ 'e', '{archivePath}', '{extractPath}', '{fileList}' ], + extract : { + cmd : 'unrar', + args : [ 'e', '{archivePath}', '{extractPath}', '{fileList}' ], } }, TarGz : { - decompress : { - cmd : 'tar', - args : [ '-xf', '{archivePath}', '-C', '{extractPath}', '--strip-components=1' ], + decompress : { + cmd : 'tar', + args : [ '-xf', '{archivePath}', '-C', '{extractPath}', '--strip-components=1' ], }, - list : { - cmd : 'tar', - args : [ '-tvf', '{archivePath}' ], - entryMatch : '^[drwx\\-]{10}\\s[A-Za-z0-9\\/]+\\s+([0-9]+)\\s[0-9]{4}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\s([^\\r\\n]+)$', + list : { + cmd : 'tar', + args : [ '-tvf', '{archivePath}' ], + entryMatch : '^[drwx\\-]{10}\\s[A-Za-z0-9\\/]+\\s+([0-9]+)\\s[0-9]{4}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\s([^\\r\\n]+)$', }, - extract : { - cmd : 'tar', - args : [ '-xvf', '{archivePath}', '-C', '{extractPath}', '{fileList}' ], + extract : { + cmd : 'tar', + args : [ '-xvf', '{archivePath}', '-C', '{extractPath}', '{fileList}' ], } } }, @@ -582,89 +582,89 @@ function getDefaultConfig() { fileTransferProtocols : { // - // See http://www.synchro.net/docs/sexyz.txt for information on SEXYZ + // See http://www.synchro.net/docs/sexyz.txt for information on SEXYZ // zmodem8kSexyz : { - name : 'ZModem 8k (SEXYZ)', - type : 'external', - sort : 1, - external : { - // :TODO: Look into shipping sexyz binaries or at least hosting them somewhere for common systems - sendCmd : 'sexyz', - sendArgs : [ '-telnet', '-8', 'sz', '@{fileListPath}' ], - recvCmd : 'sexyz', - recvArgs : [ '-telnet', '-8', 'rz', '{uploadDir}' ], - recvArgsNonBatch : [ '-telnet', '-8', 'rz', '{fileName}' ], + name : 'ZModem 8k (SEXYZ)', + type : 'external', + sort : 1, + external : { + // :TODO: Look into shipping sexyz binaries or at least hosting them somewhere for common systems + sendCmd : 'sexyz', + sendArgs : [ '-telnet', '-8', 'sz', '@{fileListPath}' ], + recvCmd : 'sexyz', + recvArgs : [ '-telnet', '-8', 'rz', '{uploadDir}' ], + recvArgsNonBatch : [ '-telnet', '-8', 'rz', '{fileName}' ], } }, xmodemSexyz : { - name : 'XModem (SEXYZ)', - type : 'external', - sort : 3, - external : { - sendCmd : 'sexyz', - sendArgs : [ '-telnet', 'sX', '@{fileListPath}' ], - recvCmd : 'sexyz', - recvArgsNonBatch : [ '-telnet', 'rC', '{fileName}' ] + name : 'XModem (SEXYZ)', + type : 'external', + sort : 3, + external : { + sendCmd : 'sexyz', + sendArgs : [ '-telnet', 'sX', '@{fileListPath}' ], + recvCmd : 'sexyz', + recvArgsNonBatch : [ '-telnet', 'rC', '{fileName}' ] } }, ymodemSexyz : { - name : 'YModem (SEXYZ)', - type : 'external', - sort : 4, - external : { - sendCmd : 'sexyz', - sendArgs : [ '-telnet', 'sY', '@{fileListPath}' ], - recvCmd : 'sexyz', - recvArgs : [ '-telnet', 'ry', '{uploadDir}' ], + name : 'YModem (SEXYZ)', + type : 'external', + sort : 4, + external : { + sendCmd : 'sexyz', + sendArgs : [ '-telnet', 'sY', '@{fileListPath}' ], + recvCmd : 'sexyz', + recvArgs : [ '-telnet', 'ry', '{uploadDir}' ], } }, zmodem8kSz : { - name : 'ZModem 8k', - type : 'external', - sort : 2, - external : { - sendCmd : 'sz', // Avail on Debian/Ubuntu based systems as the package "lrzsz" - sendArgs : [ - // :TODO: try -q + name : 'ZModem 8k', + type : 'external', + sort : 2, + external : { + sendCmd : 'sz', // Avail on Debian/Ubuntu based systems as the package "lrzsz" + sendArgs : [ + // :TODO: try -q '--zmodem', '--try-8k', '--binary', '--restricted', '{filePaths}' ], - recvCmd : 'rz', // Avail on Debian/Ubuntu based systems as the package "lrzsz" - recvArgs : [ - '--zmodem', '--binary', '--restricted', '--keep-uppercase', // dumps to CWD which is set to {uploadDir} + recvCmd : 'rz', // Avail on Debian/Ubuntu based systems as the package "lrzsz" + recvArgs : [ + '--zmodem', '--binary', '--restricted', '--keep-uppercase', // dumps to CWD which is set to {uploadDir} ], - // :TODO: can we not just use --escape ? - escapeTelnet : true, // set to true to escape Telnet codes such as IAC + // :TODO: can we not just use --escape ? + escapeTelnet : true, // set to true to escape Telnet codes such as IAC } } }, messageAreaDefaults : { // - // The following can be override per-area as well + // The following can be override per-area as well // - maxMessages : 1024, // 0 = unlimited - maxAgeDays : 0, // 0 = unlimited + maxMessages : 1024, // 0 = unlimited + maxAgeDays : 0, // 0 = unlimited }, messageConferences : { system_internal : { - name : 'System Internal', - desc : 'Built in conference for private messages, bulletins, etc.', + name : 'System Internal', + desc : 'Built in conference for private messages, bulletins, etc.', areas : { private_mail : { - name : 'Private Mail', - desc : 'Private user to user mail/email', - maxExternalSentAgeDays : 30, // max external "outbox" item age + name : 'Private Mail', + desc : 'Private user to user mail/email', + maxExternalSentAgeDays : 30, // max external "outbox" item age }, local_bulletin : { - name : 'System Bulletins', - desc : 'Bulletin messages for all users', + name : 'System Bulletins', + desc : 'Bulletin messages for all users', } } } @@ -673,99 +673,99 @@ 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. - //outboundNetMail : paths.join(__dirname, './../mail/ftn_netmail_out/'), - // set 'retain' to a valid path to keep good pkt files + 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 }, // - // Packet and (ArcMail) bundle target sizes are just that: targets. - // Actual sizes may be slightly larger when we must place a full - // PKT contents *somewhere* + // Packet and (ArcMail) bundle target sizes are just that: targets. + // Actual sizes may be slightly larger when we must place a full + // PKT contents *somewhere* // - packetTargetByteSize : 512000, // 512k, before placing messages in a new pkt - bundleTargetByteSize : 2048000, // 2M, before creating another archive - packetMsgEncoding : 'utf8', // default packet encoding. Override per node if desired. - packetAnsiMsgEncoding : 'cp437', // packet encoding for *ANSI ART* messages + packetTargetByteSize : 512000, // 512k, before placing messages in a new pkt + bundleTargetByteSize : 2048000, // 2M, before creating another archive + packetMsgEncoding : 'utf8', // default packet encoding. Override per node if desired. + packetAnsiMsgEncoding : 'cp437', // packet encoding for *ANSI ART* messages tic : { - secureInOnly : true, // only bring in from secure inbound (|secInbound| path, password protected) - uploadBy : 'ENiGMA TIC', // default upload by username (override @ network) - allowReplace : false, // use "Replaces" TIC field - descPriority : 'diz', // May be diz=.DIZ/etc., or tic=from TIC Ldesc + secureInOnly : true, // only bring in from secure inbound (|secInbound| path, password protected) + uploadBy : 'ENiGMA TIC', // default upload by username (override @ network) + allowReplace : false, // use "Replaces" TIC field + descPriority : 'diz', // May be diz=.DIZ/etc., or tic=from TIC Ldesc } } }, fileBase: { - // areas with an explicit |storageDir| will be stored relative to |areaStoragePrefix|: - areaStoragePrefix : paths.join(__dirname, './../file_base/'), + // areas with an explicit |storageDir| will be stored relative to |areaStoragePrefix|: + areaStoragePrefix : paths.join(__dirname, './../file_base/'), - maxDescFileByteSize : 471859, // ~1/4 MB - maxDescLongFileByteSize : 524288, // 1/2 MB + maxDescFileByteSize : 471859, // ~1/4 MB + maxDescLongFileByteSize : 524288, // 1/2 MB fileNamePatterns: { - // These are NOT case sensitive - // FILE_ID.DIZ - https://en.wikipedia.org/wiki/FILE_ID.DIZ - // Some groups include a FILE_ID.ANS. We try to use that over FILE_ID.DIZ if available. - desc : [ - '^[^/\]*FILE_ID\.ANS$', '^[^/\]*FILE_ID\.DIZ$', '^[^/\]*DESC\.SDI$', '^[^/\]*DESCRIPT\.ION$', '^[^/\]*FILE\.DES$', '^[^/\]*FILE\.SDI$', '^[^/\]*DISK\.ID$' // eslint-disable-line no-useless-escape + // These are NOT case sensitive + // FILE_ID.DIZ - https://en.wikipedia.org/wiki/FILE_ID.DIZ + // Some groups include a FILE_ID.ANS. We try to use that over FILE_ID.DIZ if available. + desc : [ + '^[^/\]*FILE_ID\.ANS$', '^[^/\]*FILE_ID\.DIZ$', '^[^/\]*DESC\.SDI$', '^[^/\]*DESCRIPT\.ION$', '^[^/\]*FILE\.DES$', '^[^/\]*FILE\.SDI$', '^[^/\]*DISK\.ID$' // eslint-disable-line no-useless-escape ], - // common README filename - https://en.wikipedia.org/wiki/README - descLong : [ - '^[^/\]*\.NFO$', '^[^/\]*README\.1ST$', '^[^/\]*README\.NOW$', '^[^/\]*README\.TXT$', '^[^/\]*READ\.ME$', '^[^/\]*README$', '^[^/\]*README\.md$' // eslint-disable-line no-useless-escape + // common README filename - https://en.wikipedia.org/wiki/README + descLong : [ + '^[^/\]*\.NFO$', '^[^/\]*README\.1ST$', '^[^/\]*README\.NOW$', '^[^/\]*README\.TXT$', '^[^/\]*READ\.ME$', '^[^/\]*README$', '^[^/\]*README\.md$' // eslint-disable-line no-useless-escape ], }, yearEstPatterns: [ // - // Patterns should produce the year in the first submatch. - // The extracted year may be YY or YYYY + // Patterns should produce the year in the first submatch. + // The extracted year may be YY or YYYY // - '\\b((?:[1-2][0-9][0-9]{2}))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]\\b', // yyyy-mm-dd, yyyy/mm/dd, ... - '\\b[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[1-2][0-9][0-9]{2}))\\b', // mm/dd/yyyy, mm.dd.yyyy, ... - '\\b((?:[1789][0-9]))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]\\b', // yy-mm-dd, yy-mm-dd, ... - '\\b[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[1789][0-9]))\\b', // mm-dd-yy, mm/dd/yy, ... - //'\\b((?:[1-2][0-9][0-9]{2}))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]|[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[0-9]{2})?[0-9]{2})\\b', // yyyy-mm-dd, m/d/yyyy, mm-dd-yyyy, etc. - //"\\b('[1789][0-9])\\b", // eslint-disable-line quotes + '\\b((?:[1-2][0-9][0-9]{2}))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]\\b', // yyyy-mm-dd, yyyy/mm/dd, ... + '\\b[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[1-2][0-9][0-9]{2}))\\b', // mm/dd/yyyy, mm.dd.yyyy, ... + '\\b((?:[1789][0-9]))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]\\b', // yy-mm-dd, yy-mm-dd, ... + '\\b[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[1789][0-9]))\\b', // mm-dd-yy, mm/dd/yy, ... + //'\\b((?:[1-2][0-9][0-9]{2}))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]|[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[0-9]{2})?[0-9]{2})\\b', // yyyy-mm-dd, m/d/yyyy, mm-dd-yyyy, etc. + //"\\b('[1789][0-9])\\b", // eslint-disable-line quotes '\\b[0-3]?[0-9][\\-\\/\\.](?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|january|february|march|april|may|june|july|august|september|october|november|december)[\\-\\/\\.]((?:[0-9]{2})?[0-9]{2})\\b', - '\\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|january|february|march|april|may|june|july|august|september|october|november|december),?\\s[0-9]+(?:st|nd|rd|th)?,?\\s((?:[0-9]{2})?[0-9]{2})\\b', // November 29th, 1997 - '\\(((?:19|20)[0-9]{2})\\)', // (19xx) or (20xx) -- with parens -- do this before 19xx 20xx such that this has priority - '\\b((?:19|20)[0-9]{2})\\b', // simple 19xx or 20xx with word boundaries - '\\b\'([17-9][0-9])\\b', // '95, '17, ... - // :TODO: DD/MMM/YY, DD/MMMM/YY, DD/MMM/YYYY, etc. + '\\b(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|january|february|march|april|may|june|july|august|september|october|november|december),?\\s[0-9]+(?:st|nd|rd|th)?,?\\s((?:[0-9]{2})?[0-9]{2})\\b', // November 29th, 1997 + '\\(((?:19|20)[0-9]{2})\\)', // (19xx) or (20xx) -- with parens -- do this before 19xx 20xx such that this has priority + '\\b((?:19|20)[0-9]{2})\\b', // simple 19xx or 20xx with word boundaries + '\\b\'([17-9][0-9])\\b', // '95, '17, ... + // :TODO: DD/MMM/YY, DD/MMMM/YY, DD/MMM/YYYY, etc. ], web : { - path : '/f/', - routePath : '/f/[a-zA-Z0-9]+$', - expireMinutes : 1440, // 1 day + path : '/f/', + routePath : '/f/[a-zA-Z0-9]+$', + expireMinutes : 1440, // 1 day }, // - // File area storage location tag/value pairs. - // Non-absolute paths are relative to |areaStoragePrefix|. + // File area storage location tag/value pairs. + // Non-absolute paths are relative to |areaStoragePrefix|. // storageTags : { - sys_msg_attach : 'sys_msg_attach', - sys_temp_download : 'sys_temp_download', + sys_msg_attach : 'sys_msg_attach', + sys_temp_download : 'sys_temp_download', }, areas: { system_message_attachment : { - name : 'System Message Attachments', - desc : 'File attachments to messages', - storageTags : [ 'sys_msg_attach' ], + name : 'System Message Attachments', + desc : 'File attachments to messages', + storageTags : [ 'sys_msg_attach' ], }, system_temporary_download : { - name : 'System Temporary Downloads', - desc : 'Temporary downloadables', - storageTags : [ 'sys_temp_download' ], + name : 'System Temporary Downloads', + desc : 'Temporary downloadables', + storageTags : [ 'sys_temp_download' ], } } }, @@ -774,63 +774,63 @@ function getDefaultConfig() { events : { trimMessageAreas : { - // may optionally use [or ]@watch:/path/to/file - schedule : 'every 24 hours', + // may optionally use [or ]@watch:/path/to/file + schedule : 'every 24 hours', - // action: - // - @method:path/to/module.js:theMethodName - // (path is relative to engima base dir) + // action: + // - @method:path/to/module.js:theMethodName + // (path is relative to engima base dir) // - // - @execute:/path/to/something/executable.sh + // - @execute:/path/to/something/executable.sh // - action : '@method:core/message_area.js:trimMessageAreasScheduledEvent', + action : '@method:core/message_area.js:trimMessageAreasScheduledEvent', }, updateFileAreaStats : { - schedule : 'every 1 hours', - action : '@method:core/file_base_area.js:updateAreaStatsScheduledEvent', + schedule : 'every 1 hours', + action : '@method:core/file_base_area.js:updateAreaStatsScheduledEvent', }, forgotPasswordMaintenance : { - schedule : 'every 24 hours', - action : '@method:core/web_password_reset.js:performMaintenanceTask', - args : [ '24 hours' ] // items older than this will be removed + schedule : 'every 24 hours', + action : '@method:core/web_password_reset.js:performMaintenanceTask', + args : [ '24 hours' ] // items older than this will be removed }, // - // Enable the following entry in your config.hjson to periodically create/update - // DESCRIPT.ION files for your file base + // Enable the following entry in your config.hjson to periodically create/update + // DESCRIPT.ION files for your file base // /* - updateDescriptIonFiles : { - schedule : 'on the last day of the week', - action : '@method:core/file_base_list_export.js:updateFileBaseDescFilesScheduledEvent', - } - */ + updateDescriptIonFiles : { + schedule : 'on the last day of the week', + action : '@method:core/file_base_list_export.js:updateFileBaseDescFilesScheduledEvent', + } + */ } }, misc : { - preAuthIdleLogoutSeconds : 60 * 3, // 3m - idleLogoutSeconds : 60 * 6, // 6m + preAuthIdleLogoutSeconds : 60 * 3, // 3m + idleLogoutSeconds : 60 * 6, // 6m }, logging : { - level : 'debug', + level : 'debug', - rotatingFile : { // set to 'disabled' or false to disable - type : 'rotating-file', - fileName : 'enigma-bbs.log', - period : '1d', - count : 3, - level : 'debug', + rotatingFile : { // set to 'disabled' or false to disable + type : 'rotating-file', + fileName : 'enigma-bbs.log', + period : '1d', + count : 3, + level : 'debug', } - // :TODO: syslog - https://github.com/mcavage/node-bunyan-syslog + // :TODO: syslog - https://github.com/mcavage/node-bunyan-syslog }, debug : { - assertsEnabled : false, + assertsEnabled : false, } }; } diff --git a/core/config_cache.js b/core/config_cache.js index 15143efc..e8e06f14 100644 --- a/core/config_cache.js +++ b/core/config_cache.js @@ -1,16 +1,16 @@ /* jslint node: true */ 'use strict'; -// deps -const paths = require('path'); -const fs = require('graceful-fs'); -const hjson = require('hjson'); -const sane = require('sane'); +// deps +const paths = require('path'); +const fs = require('graceful-fs'); +const hjson = require('hjson'); +const sane = require('sane'); module.exports = new class ConfigCache { constructor() { - this.cache = new Map(); // path->parsed config + this.cache = new Map(); // path->parsed config } getConfigWithOptions(options, cb) { diff --git a/core/config_util.js b/core/config_util.js index 1c61162c..bda0e1bd 100644 --- a/core/config_util.js +++ b/core/config_util.js @@ -1,19 +1,19 @@ /* jslint node: true */ 'use strict'; -const Config = require('./config.js').get; -const ConfigCache = require('./config_cache.js'); -const Events = require('./events.js'); +const Config = require('./config.js').get; +const ConfigCache = require('./config_cache.js'); +const Events = require('./events.js'); -// deps -const paths = require('path'); -const async = require('async'); +// deps +const paths = require('path'); +const async = require('async'); -exports.init = init; -exports.getFullConfig = getFullConfig; +exports.init = init; +exports.getFullConfig = getFullConfig; function getConfigPath(filePath) { - // |filePath| is assumed to be in the config path if it's only a file name + // |filePath| is assumed to be in the config path if it's only a file name if('.' === paths.dirname(filePath)) { filePath = paths.join(Config().paths.config, filePath); } @@ -21,7 +21,7 @@ function getConfigPath(filePath) { } function init(cb) { - // pre-cache menu.hjson and prompt.hjson + establish events + // pre-cache menu.hjson and prompt.hjson + establish events const changed = ( { fileName, fileRoot } ) => { const reCachedPath = paths.join(fileRoot, fileName); if(reCachedPath === getConfigPath(Config().general.menuFile)) { diff --git a/core/connect.js b/core/connect.js index 86cf3833..44277813 100644 --- a/core/connect.js +++ b/core/connect.js @@ -1,21 +1,21 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const ansi = require('./ansi_term.js'); +// ENiGMA½ +const ansi = require('./ansi_term.js'); const Events = require('./events.js'); -// deps -const async = require('async'); +// deps +const async = require('async'); -exports.connectEntry = connectEntry; +exports.connectEntry = connectEntry; function ansiDiscoverHomePosition(client, cb) { // - // We want to find the home position. ANSI-BBS and most terminals - // utilize 1,1 as home. However, some terminals such as ConnectBot - // think of home as 0,0. If this is the case, we need to offset - // our positioning to accomodate for such. + // We want to find the home position. ANSI-BBS and most terminals + // utilize 1,1 as home. However, some terminals such as ConnectBot + // think of home as 0,0. If this is the case, we need to offset + // our positioning to accomodate for such. // const done = function(err) { client.removeListener('cursor position report', cprListener); @@ -28,7 +28,7 @@ function ansiDiscoverHomePosition(client, cb) { const w = pos[1]; // - // We expect either 0,0, or 1,1. Anything else will be filed as bad data + // We expect either 0,0, or 1,1. Anything else will be filed as bad data // if(h > 1 || w > 1) { client.log.warn( { height : h, width : w }, 'Ignoring ANSI home position CPR due to unexpected values'); @@ -37,7 +37,7 @@ function ansiDiscoverHomePosition(client, cb) { if(0 === h & 0 === w) { // - // Store a CPR offset in the client. All CPR's from this point on will offset by this amount + // Store a CPR offset in the client. All CPR's from this point on will offset by this amount // client.log.info('Setting CPR offset to 1'); client.cprOffset = 1; @@ -50,9 +50,9 @@ function ansiDiscoverHomePosition(client, cb) { const giveUpTimer = setTimeout( () => { return done(new Error('Giving up on home position CPR')); - }, 3000); // 3s + }, 3000); // 3s - client.term.write(`${ansi.goHome()}${ansi.queryPos()}`); // go home, query pos + client.term.write(`${ansi.goHome()}${ansi.queryPos()}`); // go home, query pos } function ansiQueryTermSizeIfNeeded(client, cb) { @@ -68,7 +68,7 @@ function ansiQueryTermSizeIfNeeded(client, cb) { const cprListener = function(pos) { // - // If we've already found out, disregard + // If we've already found out, disregard // if(client.term.termHeight > 0 || client.term.termWidth > 0) { return done(null); @@ -78,8 +78,8 @@ function ansiQueryTermSizeIfNeeded(client, cb) { const w = pos[1]; // - // Netrunner for example gives us 1x1 here. Not really useful. Ignore - // values that seem obviously bad. + // Netrunner for example gives us 1x1 here. Not really useful. Ignore + // values that seem obviously bad. // if(h < 10 || w < 10) { client.log.warn( @@ -88,14 +88,14 @@ function ansiQueryTermSizeIfNeeded(client, cb) { return done(new Error('Term size <= 10 considered invalid')); } - client.term.termHeight = h; - client.term.termWidth = w; + client.term.termHeight = h; + client.term.termWidth = w; client.log.debug( { - termWidth : client.term.termWidth, - termHeight : client.term.termHeight, - source : 'ANSI CPR' + termWidth : client.term.termWidth, + termHeight : client.term.termHeight, + source : 'ANSI CPR' }, 'Window size updated' ); @@ -105,23 +105,23 @@ function ansiQueryTermSizeIfNeeded(client, cb) { client.once('cursor position report', cprListener); - // give up after 2s + // give up after 2s const giveUpTimer = setTimeout( () => { return done(new Error('No term size established by CPR within timeout')); }, 2000); - // Start the process: Query for CPR + // Start the process: Query for CPR client.term.rawWrite(ansi.queryScreenSize()); } function prepareTerminal(term) { term.rawWrite(ansi.normal()); //term.rawWrite(ansi.disableVT100LineWrapping()); - // :TODO: set xterm stuff -- see x84/others + // :TODO: set xterm stuff -- see x84/others } function displayBanner(term) { - // note: intentional formatting: + // note: intentional formatting: term.pipeWrite(` |06Connected to |02EN|10i|02GMA|10½ |06BBS version |12|VN |06Copyright (c) 2014-2018 Bryan Ashby |14- |12http://l33t.codes/ @@ -141,26 +141,26 @@ function connectEntry(client, nextMenu) { }, function discoverHomePosition(callback) { ansiDiscoverHomePosition(client, () => { - // :TODO: If CPR for home fully fails, we should bail out on the connection with an error, e.g. ANSI support required - return callback(null); // we try to continue anyway + // :TODO: If CPR for home fully fails, we should bail out on the connection with an error, e.g. ANSI support required + return callback(null); // we try to continue anyway }); }, function queryTermSizeByNonStandardAnsi(callback) { ansiQueryTermSizeIfNeeded(client, err => { if(err) { // - // Check again; We may have got via NAWS/similar before CPR completed. + // Check again; We may have got via NAWS/similar before CPR completed. // if(0 === term.termHeight || 0 === term.termWidth) { // - // We still don't have something good for term height/width. - // Default to DOS size 80x25. + // We still don't have something good for term height/width. + // Default to DOS size 80x25. // - // :TODO: Netrunner is currenting hitting this and it feels wrong. Why is NAWS/ENV/CPR all failing??? + // :TODO: Netrunner is currenting hitting this and it feels wrong. Why is NAWS/ENV/CPR all failing??? client.log.warn( { reason : err.message }, 'Failed to negotiate term size; Defaulting to 80x25!'); - term.termHeight = 25; - term.termWidth = 80; + term.termHeight = 25; + term.termWidth = 80; } } @@ -172,7 +172,7 @@ function connectEntry(client, nextMenu) { prepareTerminal(term); // - // Always show an ENiGMA½ banner + // Always show an ENiGMA½ banner // displayBanner(term); diff --git a/core/crc.js b/core/crc.js index d7974c66..f90ac961 100644 --- a/core/crc.js +++ b/core/crc.js @@ -52,8 +52,8 @@ exports.CRC32 = class CRC32 { } update_4(input) { - const len = input.length - 3; - let i = 0; + const len = input.length - 3; + let i = 0; for(i = 0; i < len;) { this.crc = (this.crc >>> 8) ^ CRC32_TABLE[ (this.crc ^ input[i++]) & 0xff ]; @@ -67,8 +67,8 @@ exports.CRC32 = class CRC32 { } update_8(input) { - const len = input.length - 7; - let i = 0; + const len = input.length - 7; + let i = 0; for(i = 0; i < len;) { this.crc = (this.crc >>> 8) ^ CRC32_TABLE[ (this.crc ^ input[i++]) & 0xff ]; diff --git a/core/database.js b/core/database.js index 3ce2e031..ba48b404 100644 --- a/core/database.js +++ b/core/database.js @@ -1,28 +1,28 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const conf = require('./config.js'); +// ENiGMA½ +const conf = require('./config.js'); -// deps -const sqlite3 = require('sqlite3'); -const sqlite3Trans = require('sqlite3-trans'); -const paths = require('path'); -const async = require('async'); -const _ = require('lodash'); -const assert = require('assert'); -const moment = require('moment'); +// deps +const sqlite3 = require('sqlite3'); +const sqlite3Trans = require('sqlite3-trans'); +const paths = require('path'); +const async = require('async'); +const _ = require('lodash'); +const assert = require('assert'); +const moment = require('moment'); -// database handles +// database handles const dbs = {}; -exports.getTransactionDatabase = getTransactionDatabase; -exports.getModDatabasePath = getModDatabasePath; -exports.getISOTimestampString = getISOTimestampString; -exports.sanatizeString = sanatizeString; -exports.initializeDatabases = initializeDatabases; +exports.getTransactionDatabase = getTransactionDatabase; +exports.getModDatabasePath = getModDatabasePath; +exports.getISOTimestampString = getISOTimestampString; +exports.sanatizeString = sanatizeString; +exports.initializeDatabases = initializeDatabases; -exports.dbs = dbs; +exports.dbs = dbs; function getTransactionDatabase(db) { return sqlite3Trans.wrap(db); @@ -34,9 +34,9 @@ function getDatabasePath(name) { function getModDatabasePath(moduleInfo, suffix) { // - // Mods that use a database are stored in Config.paths.modsDb (e.g. enigma-bbs/db/mods) - // We expect that moduleInfo defines packageName which will be the base of the modules - // filename. An optional suffix may be supplied as well. + // Mods that use a database are stored in Config.paths.modsDb (e.g. enigma-bbs/db/mods) + // We expect that moduleInfo defines packageName which will be the base of the modules + // filename. An optional suffix may be supplied as well. // const HOST_RE = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/; @@ -61,14 +61,14 @@ function getISOTimestampString(ts) { } function sanatizeString(s) { - return s.replace(/[\0\x08\x09\x1a\n\r"'\\%]/g, c => { // eslint-disable-line no-control-regex + return s.replace(/[\0\x08\x09\x1a\n\r"'\\%]/g, c => { // eslint-disable-line no-control-regex switch (c) { - case '\0' : return '\\0'; - case '\x08' : return '\\b'; - case '\x09' : return '\\t'; - case '\x1a' : return '\\z'; - case '\n' : return '\\n'; - case '\r' : return '\\r'; + case '\0' : return '\\0'; + case '\x08' : return '\\b'; + case '\x09' : return '\\t'; + case '\x1a' : return '\\z'; + case '\n' : return '\\n'; + case '\r' : return '\\r'; case '"' : case '\'' : @@ -107,35 +107,35 @@ const DB_INIT_TABLE = { system : (cb) => { enableForeignKeys(dbs.system); - // Various stat/event logging - see stat_log.js + // Various stat/event logging - see stat_log.js dbs.system.run( `CREATE TABLE IF NOT EXISTS system_stat ( - stat_name VARCHAR PRIMARY KEY NOT NULL, - stat_value VARCHAR NOT NULL - );` + stat_name VARCHAR PRIMARY KEY NOT NULL, + stat_value VARCHAR NOT NULL + );` ); dbs.system.run( `CREATE TABLE IF NOT EXISTS system_event_log ( - id INTEGER PRIMARY KEY, - timestamp DATETIME NOT NULL, - log_name VARCHAR NOT NULL, - log_value VARCHAR NOT NULL, + id INTEGER PRIMARY KEY, + timestamp DATETIME NOT NULL, + log_name VARCHAR NOT NULL, + log_value VARCHAR NOT NULL, - UNIQUE(timestamp, log_name) - );` + UNIQUE(timestamp, log_name) + );` ); dbs.system.run( `CREATE TABLE IF NOT EXISTS user_event_log ( - id INTEGER PRIMARY KEY, - timestamp DATETIME NOT NULL, - user_id INTEGER NOT NULL, - log_name VARCHAR NOT NULL, - log_value VARCHAR NOT NULL, + id INTEGER PRIMARY KEY, + timestamp DATETIME NOT NULL, + user_id INTEGER NOT NULL, + log_name VARCHAR NOT NULL, + log_value VARCHAR NOT NULL, - UNIQUE(timestamp, user_id, log_name) - );` + UNIQUE(timestamp, user_id, log_name) + );` ); return cb(null); @@ -146,38 +146,38 @@ const DB_INIT_TABLE = { dbs.user.run( `CREATE TABLE IF NOT EXISTS user ( - id INTEGER PRIMARY KEY, - user_name VARCHAR NOT NULL, - UNIQUE(user_name) - );` + id INTEGER PRIMARY KEY, + user_name VARCHAR NOT NULL, + UNIQUE(user_name) + );` ); - // :TODO: create FK on delete/etc. + // :TODO: create FK on delete/etc. dbs.user.run( `CREATE TABLE IF NOT EXISTS user_property ( - user_id INTEGER NOT NULL, - prop_name VARCHAR NOT NULL, - prop_value VARCHAR, - UNIQUE(user_id, prop_name), - FOREIGN KEY(user_id) REFERENCES user(id) ON DELETE CASCADE - );` + user_id INTEGER NOT NULL, + prop_name VARCHAR NOT NULL, + prop_value VARCHAR, + UNIQUE(user_id, prop_name), + FOREIGN KEY(user_id) REFERENCES user(id) ON DELETE CASCADE + );` ); dbs.user.run( `CREATE TABLE IF NOT EXISTS user_group_member ( - group_name VARCHAR NOT NULL, - user_id INTEGER NOT NULL, - UNIQUE(group_name, user_id) - );` + group_name VARCHAR NOT NULL, + user_id INTEGER NOT NULL, + UNIQUE(group_name, user_id) + );` ); dbs.user.run( - `CREATE TABLE IF NOT EXISTS user_login_history ( - user_id INTEGER NOT NULL, - user_name VARCHAR NOT NULL, - timestamp DATETIME NOT NULL - );` + `CREATE TABLE IF NOT EXISTS user_login_history ( + user_id INTEGER NOT NULL, + user_name VARCHAR NOT NULL, + timestamp DATETIME NOT NULL + );` ); return cb(null); @@ -188,104 +188,104 @@ const DB_INIT_TABLE = { dbs.message.run( `CREATE TABLE IF NOT EXISTS message ( - message_id INTEGER PRIMARY KEY, - area_tag VARCHAR NOT NULL, - message_uuid VARCHAR(36) NOT NULL, - reply_to_message_id INTEGER, - to_user_name VARCHAR NOT NULL, - from_user_name VARCHAR NOT NULL, - subject, /* FTS @ message_fts */ - message, /* FTS @ message_fts */ - modified_timestamp DATETIME NOT NULL, - view_count INTEGER NOT NULL DEFAULT 0, - UNIQUE(message_uuid) - );` + message_id INTEGER PRIMARY KEY, + area_tag VARCHAR NOT NULL, + message_uuid VARCHAR(36) NOT NULL, + reply_to_message_id INTEGER, + to_user_name VARCHAR NOT NULL, + from_user_name VARCHAR NOT NULL, + subject, /* FTS @ message_fts */ + message, /* FTS @ message_fts */ + modified_timestamp DATETIME NOT NULL, + view_count INTEGER NOT NULL DEFAULT 0, + UNIQUE(message_uuid) + );` ); dbs.message.run( `CREATE INDEX IF NOT EXISTS message_by_area_tag_index - ON message (area_tag);` + ON message (area_tag);` ); dbs.message.run( `CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts4 ( - content="message", - subject, - message - );` + content="message", + subject, + message + );` ); dbs.message.run( `CREATE TRIGGER IF NOT EXISTS message_before_update BEFORE UPDATE ON message BEGIN - DELETE FROM message_fts WHERE docid=old.rowid; - END;` + DELETE FROM message_fts WHERE docid=old.rowid; + END;` ); dbs.message.run( `CREATE TRIGGER IF NOT EXISTS message_before_delete BEFORE DELETE ON message BEGIN - DELETE FROM message_fts WHERE docid=old.rowid; - END;` + DELETE FROM message_fts WHERE docid=old.rowid; + END;` ); dbs.message.run( `CREATE TRIGGER IF NOT EXISTS message_after_update AFTER UPDATE ON message BEGIN - INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message); - END;` + INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message); + END;` ); dbs.message.run( `CREATE TRIGGER IF NOT EXISTS message_after_insert AFTER INSERT ON message BEGIN - INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message); - END;` + INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message); + END;` ); dbs.message.run( `CREATE TABLE IF NOT EXISTS message_meta ( - message_id INTEGER NOT NULL, - meta_category INTEGER NOT NULL, - meta_name VARCHAR NOT NULL, - meta_value VARCHAR NOT NULL, - UNIQUE(message_id, meta_category, meta_name, meta_value), - FOREIGN KEY(message_id) REFERENCES message(message_id) ON DELETE CASCADE - );` + message_id INTEGER NOT NULL, + meta_category INTEGER NOT NULL, + meta_name VARCHAR NOT NULL, + meta_value VARCHAR NOT NULL, + UNIQUE(message_id, meta_category, meta_name, meta_value), + FOREIGN KEY(message_id) REFERENCES message(message_id) ON DELETE CASCADE + );` ); - // :TODO: need SQL to ensure cleaned up if delete from message? + // :TODO: need SQL to ensure cleaned up if delete from message? /* - dbs.message.run( - `CREATE TABLE IF NOT EXISTS hash_tag ( - hash_tag_id INTEGER PRIMARY KEY, - hash_tag_name VARCHAR NOT NULL, - UNIQUE(hash_tag_name) - );` - ); + dbs.message.run( + `CREATE TABLE IF NOT EXISTS hash_tag ( + hash_tag_id INTEGER PRIMARY KEY, + hash_tag_name VARCHAR NOT NULL, + UNIQUE(hash_tag_name) + );` + ); - // :TODO: need SQL to ensure cleaned up if delete from message? - dbs.message.run( - `CREATE TABLE IF NOT EXISTS message_hash_tag ( - hash_tag_id INTEGER NOT NULL, - message_id INTEGER NOT NULL, - );` - ); - */ + // :TODO: need SQL to ensure cleaned up if delete from message? + dbs.message.run( + `CREATE TABLE IF NOT EXISTS message_hash_tag ( + hash_tag_id INTEGER NOT NULL, + message_id INTEGER NOT NULL, + );` + ); + */ dbs.message.run( `CREATE TABLE IF NOT EXISTS user_message_area_last_read ( - user_id INTEGER NOT NULL, - area_tag VARCHAR NOT NULL, - message_id INTEGER NOT NULL, - UNIQUE(user_id, area_tag) - );` + user_id INTEGER NOT NULL, + area_tag VARCHAR NOT NULL, + message_id INTEGER NOT NULL, + UNIQUE(user_id, area_tag) + );` ); dbs.message.run( `CREATE TABLE IF NOT EXISTS message_area_last_scan ( - scan_toss VARCHAR NOT NULL, - area_tag VARCHAR NOT NULL, - message_id INTEGER NOT NULL, - UNIQUE(scan_toss, area_tag) - );` + scan_toss VARCHAR NOT NULL, + area_tag VARCHAR NOT NULL, + message_id INTEGER NOT NULL, + UNIQUE(scan_toss, area_tag) + );` ); return cb(null); @@ -295,114 +295,114 @@ const DB_INIT_TABLE = { enableForeignKeys(dbs.file); dbs.file.run( - // :TODO: should any of this be unique -- file_sha256 unless dupes are allowed on the system + // :TODO: should any of this be unique -- file_sha256 unless dupes are allowed on the system `CREATE TABLE IF NOT EXISTS file ( - file_id INTEGER PRIMARY KEY, - area_tag VARCHAR NOT NULL, - file_sha256 VARCHAR NOT NULL, - file_name, /* FTS @ file_fts */ - storage_tag VARCHAR NOT NULL, - desc, /* FTS @ file_fts */ - desc_long, /* FTS @ file_fts */ - upload_timestamp DATETIME NOT NULL - );` + file_id INTEGER PRIMARY KEY, + area_tag VARCHAR NOT NULL, + file_sha256 VARCHAR NOT NULL, + file_name, /* FTS @ file_fts */ + storage_tag VARCHAR NOT NULL, + desc, /* FTS @ file_fts */ + desc_long, /* FTS @ file_fts */ + upload_timestamp DATETIME NOT NULL + );` ); dbs.file.run( `CREATE INDEX IF NOT EXISTS file_by_area_tag_index - ON file (area_tag);` + ON file (area_tag);` ); dbs.file.run( `CREATE INDEX IF NOT EXISTS file_by_sha256_index - ON file (file_sha256);` + ON file (file_sha256);` ); dbs.file.run( `CREATE VIRTUAL TABLE IF NOT EXISTS file_fts USING fts4 ( - content="file", - file_name, - desc, - desc_long - );` + content="file", + file_name, + desc, + desc_long + );` ); dbs.file.run( `CREATE TRIGGER IF NOT EXISTS file_before_update BEFORE UPDATE ON file BEGIN - DELETE FROM file_fts WHERE docid=old.rowid; - END;` + DELETE FROM file_fts WHERE docid=old.rowid; + END;` ); dbs.file.run( `CREATE TRIGGER IF NOT EXISTS file_before_delete BEFORE DELETE ON file BEGIN - DELETE FROM file_fts WHERE docid=old.rowid; - END;` + DELETE FROM file_fts WHERE docid=old.rowid; + END;` ); dbs.file.run( `CREATE TRIGGER IF NOT EXISTS file_after_update AFTER UPDATE ON file BEGIN - INSERT INTO file_fts(docid, file_name, desc, desc_long) VALUES(new.rowid, new.file_name, new.desc, new.desc_long); - END;` + INSERT INTO file_fts(docid, file_name, desc, desc_long) VALUES(new.rowid, new.file_name, new.desc, new.desc_long); + END;` ); dbs.file.run( `CREATE TRIGGER IF NOT EXISTS file_after_insert AFTER INSERT ON file BEGIN - INSERT INTO file_fts(docid, file_name, desc, desc_long) VALUES(new.rowid, new.file_name, new.desc, new.desc_long); - END;` + INSERT INTO file_fts(docid, file_name, desc, desc_long) VALUES(new.rowid, new.file_name, new.desc, new.desc_long); + END;` ); dbs.file.run( `CREATE TABLE IF NOT EXISTS file_meta ( - file_id INTEGER NOT NULL, - meta_name VARCHAR NOT NULL, - meta_value VARCHAR NOT NULL, - UNIQUE(file_id, meta_name, meta_value), - FOREIGN KEY(file_id) REFERENCES file(file_id) ON DELETE CASCADE - );` + file_id INTEGER NOT NULL, + meta_name VARCHAR NOT NULL, + meta_value VARCHAR NOT NULL, + UNIQUE(file_id, meta_name, meta_value), + FOREIGN KEY(file_id) REFERENCES file(file_id) ON DELETE CASCADE + );` ); dbs.file.run( `CREATE TABLE IF NOT EXISTS hash_tag ( - hash_tag_id INTEGER PRIMARY KEY, - hash_tag VARCHAR NOT NULL, - - UNIQUE(hash_tag) - );` + hash_tag_id INTEGER PRIMARY KEY, + hash_tag VARCHAR NOT NULL, + + UNIQUE(hash_tag) + );` ); dbs.file.run( `CREATE TABLE IF NOT EXISTS file_hash_tag ( - hash_tag_id INTEGER NOT NULL, - file_id INTEGER NOT NULL, - - UNIQUE(hash_tag_id, file_id) - );` + hash_tag_id INTEGER NOT NULL, + file_id INTEGER NOT NULL, + + UNIQUE(hash_tag_id, file_id) + );` ); dbs.file.run( `CREATE TABLE IF NOT EXISTS file_user_rating ( - file_id INTEGER NOT NULL, - user_id INTEGER NOT NULL, - rating INTEGER NOT NULL, + file_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + rating INTEGER NOT NULL, - UNIQUE(file_id, user_id) - );` + UNIQUE(file_id, user_id) + );` ); dbs.file.run( `CREATE TABLE IF NOT EXISTS file_web_serve ( - hash_id VARCHAR NOT NULL PRIMARY KEY, - expire_timestamp DATETIME NOT NULL - );` + hash_id VARCHAR NOT NULL PRIMARY KEY, + expire_timestamp DATETIME NOT NULL + );` ); dbs.file.run( `CREATE TABLE IF NOT EXISTS file_web_serve_batch ( - hash_id VARCHAR NOT NULL, - file_id INTEGER NOT NULL, + hash_id VARCHAR NOT NULL, + file_id INTEGER NOT NULL, - UNIQUE(hash_id, file_id) - );` + UNIQUE(hash_id, file_id) + );` ); return cb(null); diff --git a/core/descript_ion_file.js b/core/descript_ion_file.js index 1ead544f..a5d68e1d 100644 --- a/core/descript_ion_file.js +++ b/core/descript_ion_file.js @@ -1,10 +1,10 @@ /* jslint node: true */ 'use strict'; -// deps -const fs = require('graceful-fs'); -const iconv = require('iconv-lite'); -const async = require('async'); +// deps +const fs = require('graceful-fs'); +const iconv = require('iconv-lite'); +const async = require('async'); module.exports = class DescriptIonFile { constructor() { @@ -30,34 +30,34 @@ module.exports = class DescriptIonFile { const descIonFile = new DescriptIonFile(); - // DESCRIPT.ION entries are terminated with a CR and/or LF + // DESCRIPT.ION entries are terminated with a CR and/or LF const lines = iconv.decode(descData, 'cp437').split(/\r?\n/g); async.each(lines, (entryData, nextLine) => { // - // We allow quoted (long) filenames or non-quoted filenames. - // FILENAMEDESC<0x04> + // We allow quoted (long) filenames or non-quoted filenames. + // FILENAMEDESC<0x04> // - const parts = entryData.match(/^(?:(?:"([^"]+)" )|(?:([^ ]+) ))([^\x04]+)\x04(.)[^\r\n]*$/); // eslint-disable-line no-control-regex + const parts = entryData.match(/^(?:(?:"([^"]+)" )|(?:([^ ]+) ))([^\x04]+)\x04(.)[^\r\n]*$/); // eslint-disable-line no-control-regex if(!parts) { return nextLine(null); } - const fileName = parts[1] || parts[2]; + const fileName = parts[1] || parts[2]; // - // Un-escape CR/LF's - // - escapped \r and/or \n - // - BBBS style @n - See https://www.bbbs.net/sysop.html + // Un-escape CR/LF's + // - escapped \r and/or \n + // - BBBS style @n - See https://www.bbbs.net/sysop.html // - const desc = parts[3].replace(/\\r\\n|\\n|[^@]@n/g, '\r\n'); + const desc = parts[3].replace(/\\r\\n|\\n|[^@]@n/g, '\r\n'); descIonFile.entries.set( fileName, { - desc : desc, - programId : parts[4], - programData : parts[5], + desc : desc, + programId : parts[4], + programData : parts[5], } ); diff --git a/core/door.js b/core/door.js index 06a10f60..ffd57b48 100644 --- a/core/door.js +++ b/core/door.js @@ -2,36 +2,36 @@ 'use strict'; -const stringFormat = require('./string_format.js'); +const stringFormat = require('./string_format.js'); -const events = require('events'); -const _ = require('lodash'); -const pty = require('node-pty'); -const decode = require('iconv-lite').decode; -const createServer = require('net').createServer; +const events = require('events'); +const _ = require('lodash'); +const pty = require('node-pty'); +const decode = require('iconv-lite').decode; +const createServer = require('net').createServer; -exports.Door = Door; +exports.Door = Door; function Door(client, exeInfo) { events.EventEmitter.call(this); - const self = this; - this.client = client; - this.exeInfo = exeInfo; - this.exeInfo.encoding = (this.exeInfo.encoding || 'cp437').toLowerCase(); - let restored = false; + const self = this; + this.client = client; + this.exeInfo = exeInfo; + this.exeInfo.encoding = (this.exeInfo.encoding || 'cp437').toLowerCase(); + let restored = false; // - // Members of exeInfo: - // cmd - // args[] - // env{} - // cwd - // io - // encoding - // dropFile - // node - // inhSocket + // Members of exeInfo: + // cmd + // args[] + // env{} + // cwd + // io + // encoding + // dropFile + // node + // inhSocket // this.doorDataHandler = function(data) { @@ -52,7 +52,7 @@ function Door(client, exeInfo) { sockServer.getConnections( (err, count) => { - // We expect only one connection from our DOOR/emulator/etc. + // We expect only one connection from our DOOR/emulator/etc. if(!err && count <= 1) { self.client.term.output.pipe(conn); @@ -94,25 +94,25 @@ Door.prototype.run = function() { return self.doorExited(); } - // Expand arg strings, e.g. {dropFile} -> DOOR32.SYS - // :TODO: Use .map() here - let args = _.clone(self.exeInfo.args); // we need a copy so the original is not modified + // Expand arg strings, e.g. {dropFile} -> DOOR32.SYS + // :TODO: Use .map() here + let args = _.clone(self.exeInfo.args); // we need a copy so the original is not modified for(let i = 0; i < args.length; ++i) { args[i] = stringFormat(self.exeInfo.args[i], { - dropFile : self.exeInfo.dropFile, - node : self.exeInfo.node.toString(), - srvPort : sockServer ? sockServer.address().port.toString() : '-1', - userId : self.client.user.userId.toString(), + dropFile : self.exeInfo.dropFile, + node : self.exeInfo.node.toString(), + srvPort : sockServer ? sockServer.address().port.toString() : '-1', + userId : self.client.user.userId.toString(), }); } const door = pty.spawn(self.exeInfo.cmd, args, { - cols : self.client.term.termWidth, - rows : self.client.term.termHeight, - // :TODO: cwd - env : self.exeInfo.env, - encoding : null, // we want to handle all encoding ourself + cols : self.client.term.termWidth, + rows : self.client.term.termHeight, + // :TODO: cwd + env : self.exeInfo.env, + encoding : null, // we want to handle all encoding ourself }); if('stdio' === self.exeInfo.io) { @@ -136,7 +136,7 @@ Door.prototype.run = function() { sockServer.close(); } - // we may not get a close + // we may not get a close if('stdio' === self.exeInfo.io) { self.restoreIo(door); } diff --git a/core/door_party.js b/core/door_party.js index 3c5b29a7..63247f69 100644 --- a/core/door_party.js +++ b/core/door_party.js @@ -1,30 +1,30 @@ /* jslint node: true */ 'use strict'; -// enigma-bbs -const MenuModule = require('../core/menu_module.js').MenuModule; -const resetScreen = require('../core/ansi_term.js').resetScreen; +// enigma-bbs +const MenuModule = require('../core/menu_module.js').MenuModule; +const resetScreen = require('../core/ansi_term.js').resetScreen; -// deps -const async = require('async'); -const _ = require('lodash'); -const SSHClient = require('ssh2').Client; +// deps +const async = require('async'); +const _ = require('lodash'); +const SSHClient = require('ssh2').Client; exports.moduleInfo = { - name : 'DoorParty', - desc : 'DoorParty Access Module', - author : 'NuSkooler', + name : 'DoorParty', + desc : 'DoorParty Access Module', + author : 'NuSkooler', }; exports.getModule = class DoorPartyModule extends MenuModule { constructor(options) { super(options); - // establish defaults - this.config = options.menuConfig.config; - this.config.host = this.config.host || 'dp.throwbackbbs.com'; - this.config.sshPort = this.config.sshPort || 2022; - this.config.rloginPort = this.config.rloginPort || 513; + // establish defaults + this.config = options.menuConfig.config; + this.config.host = this.config.host || 'dp.throwbackbbs.com'; + this.config.sshPort = this.config.sshPort || 2022; + this.config.rloginPort = this.config.rloginPort || 513; } initSequence() { @@ -61,32 +61,32 @@ exports.getModule = class DoorPartyModule extends MenuModule { }; sshClient.on('ready', () => { - // track client termination so we can clean up early + // track client termination so we can clean up early self.client.once('end', () => { self.client.log.info('Connection ended. Terminating DoorParty connection'); clientTerminated = true; sshClient.end(); }); - // establish tunnel for rlogin + // establish tunnel for rlogin sshClient.forwardOut('127.0.0.1', self.config.sshPort, self.config.host, self.config.rloginPort, (err, stream) => { if(err) { return callback(new Error('Failed to establish tunnel')); } // - // Send rlogin - // DoorParty wants the "server username" portion to be in the format of [BBS_TAG]USERNAME, e.g. - // [XA]nuskooler + // Send rlogin + // DoorParty wants the "server username" portion to be in the format of [BBS_TAG]USERNAME, e.g. + // [XA]nuskooler // const rlogin = `\x00${self.client.user.username}\x00[${self.config.bbsTag}]${self.client.user.username}\x00${self.client.term.termType}\x00`; stream.write(rlogin); - pipedStream = stream; // :TODO: this is hacky... + pipedStream = stream; // :TODO: this is hacky... self.client.term.output.pipe(stream); stream.on('data', d => { - // :TODO: we should just pipe this... + // :TODO: we should just pipe this... self.client.term.rawWrite(d); }); @@ -107,13 +107,13 @@ exports.getModule = class DoorPartyModule extends MenuModule { }); sshClient.connect( { - host : self.config.host, - port : self.config.sshPort, - username : self.config.username, - password : self.config.password, + host : self.config.host, + port : self.config.sshPort, + username : self.config.username, + password : self.config.password, }); - // note: no explicit callback() until we're finished! + // note: no explicit callback() until we're finished! } ], err => { @@ -121,7 +121,7 @@ exports.getModule = class DoorPartyModule extends MenuModule { self.client.log.warn( { error : err.message }, 'DoorParty error'); } - // if the client is stil here, go to previous + // if the client is stil here, go to previous if(!clientTerminated) { self.prevMenu(); } diff --git a/core/download_queue.js b/core/download_queue.js index d8617f75..0d1e3847 100644 --- a/core/download_queue.js +++ b/core/download_queue.js @@ -1,14 +1,14 @@ /* jslint node: true */ 'use strict'; -const FileEntry = require('./file_entry.js'); +const FileEntry = require('./file_entry.js'); -// deps -const { partition } = require('lodash'); +// deps +const { partition } = require('lodash'); module.exports = class DownloadQueue { constructor(client) { - this.client = client; + this.client = client; if(!Array.isArray(this.client.user.downloadQueue)) { if(this.client.user.properties.dl_queue) { @@ -37,12 +37,12 @@ module.exports = class DownloadQueue { add(fileEntry, systemFile=false) { this.client.user.downloadQueue.push({ - fileId : fileEntry.fileId, - areaTag : fileEntry.areaTag, - fileName : fileEntry.fileName, - path : fileEntry.filePath, - byteSize : fileEntry.meta.byte_size || 0, - systemFile : systemFile, + fileId : fileEntry.fileId, + areaTag : fileEntry.areaTag, + fileName : fileEntry.fileName, + path : fileEntry.filePath, + byteSize : fileEntry.meta.byte_size || 0, + systemFile : systemFile, }); } diff --git a/core/dropfile.js b/core/dropfile.js index 7a49cca4..76977eaf 100644 --- a/core/dropfile.js +++ b/core/dropfile.js @@ -1,31 +1,31 @@ /* jslint node: true */ 'use strict'; -var Config = require('./config.js').get; -const StatLog = require('./stat_log.js'); +var Config = require('./config.js').get; +const StatLog = require('./stat_log.js'); -var fs = require('graceful-fs'); -var paths = require('path'); -var _ = require('lodash'); -var moment = require('moment'); -var iconv = require('iconv-lite'); +var fs = require('graceful-fs'); +var paths = require('path'); +var _ = require('lodash'); +var moment = require('moment'); +var iconv = require('iconv-lite'); -exports.DropFile = DropFile; +exports.DropFile = DropFile; // -// Resources -// * http://goldfndr.home.mindspring.com/dropfile/ -// * https://en.wikipedia.org/wiki/Talk%3ADropfile -// * http://thoughtproject.com/libraries/bbs/Sysop/Doors/DropFiles/index.htm -// * http://thebbs.org/bbsfaq/ch06.02.htm +// Resources +// * http://goldfndr.home.mindspring.com/dropfile/ +// * https://en.wikipedia.org/wiki/Talk%3ADropfile +// * http://thoughtproject.com/libraries/bbs/Sysop/Doors/DropFiles/index.htm +// * http://thebbs.org/bbsfaq/ch06.02.htm -// http://lord.lordlegacy.com/dosemu/ +// http://lord.lordlegacy.com/dosemu/ function DropFile(client, fileType) { - var self = this; - this.client = client; - this.fileType = (fileType || 'DORINFO').toUpperCase(); + var self = this; + this.client = client; + this.fileType = (fileType || 'DORINFO').toUpperCase(); Object.defineProperty(this, 'fullPath', { get : function() { @@ -36,20 +36,20 @@ function DropFile(client, fileType) { Object.defineProperty(this, 'fileName', { get : function() { return { - DOOR : 'DOOR.SYS', // GAP BBS, many others - DOOR32 : 'DOOR32.SYS', // EleBBS / Mystic, Syncronet, Maximus, Telegard, AdeptXBBS, ... - CALLINFO : 'CALLINFO.BBS', // Citadel? - DORINFO : self.getDoorInfoFileName(), // RBBS, RemoteAccess, QBBS, ... - CHAIN : 'CHAIN.TXT', // WWIV - CURRUSER : 'CURRUSER.BBS', // RyBBS - SFDOORS : 'SFDOORS.DAT', // Spitfire - PCBOARD : 'PCBOARD.SYS', // PCBoard - TRIBBS : 'TRIBBS.SYS', // TriBBS - USERINFO : 'USERINFO.DAT', // Wildcat! 3.0+ - JUMPER : 'JUMPER.DAT', // 2AM BBS - SXDOOR : // System/X, dESiRE - 'SXDOOR.' + _.pad(self.client.node.toString(), 3, '0'), - INFO : 'INFO.BBS', // Phoenix BBS + DOOR : 'DOOR.SYS', // GAP BBS, many others + DOOR32 : 'DOOR32.SYS', // EleBBS / Mystic, Syncronet, Maximus, Telegard, AdeptXBBS, ... + CALLINFO : 'CALLINFO.BBS', // Citadel? + DORINFO : self.getDoorInfoFileName(), // RBBS, RemoteAccess, QBBS, ... + CHAIN : 'CHAIN.TXT', // WWIV + CURRUSER : 'CURRUSER.BBS', // RyBBS + SFDOORS : 'SFDOORS.DAT', // Spitfire + PCBOARD : 'PCBOARD.SYS', // PCBoard + TRIBBS : 'TRIBBS.SYS', // TriBBS + USERINFO : 'USERINFO.DAT', // Wildcat! 3.0+ + JUMPER : 'JUMPER.DAT', // 2AM BBS + SXDOOR : // System/X, dESiRE + 'SXDOOR.' + _.pad(self.client.node.toString(), 3, '0'), + INFO : 'INFO.BBS', // Phoenix BBS }[self.fileType]; } }); @@ -57,9 +57,9 @@ function DropFile(client, fileType) { Object.defineProperty(this, 'dropFileContents', { get : function() { return { - DOOR : self.getDoorSysBuffer(), - DOOR32 : self.getDoor32Buffer(), - DORINFO : self.getDoorInfoDefBuffer(), + DOOR : self.getDoorSysBuffer(), + DOOR32 : self.getDoor32Buffer(), + DORINFO : self.getDoorInfoDefBuffer(), }[self.fileType]; } }); @@ -78,124 +78,124 @@ function DropFile(client, fileType) { }; this.getDoorSysBuffer = function() { - var up = self.client.user.properties; - var now = moment(); - var secLevel = self.client.user.getLegacySecurityLevel().toString(); + var up = self.client.user.properties; + var now = moment(); + var secLevel = self.client.user.getLegacySecurityLevel().toString(); - // :TODO: fix time remaining - // :TODO: fix default protocol -- user prop: transfer_protocol + // :TODO: fix time remaining + // :TODO: fix default protocol -- user prop: transfer_protocol return iconv.encode( [ - 'COM1:', // "Comm Port - COM0: = LOCAL MODE" - '57600', // "Baud Rate - 300 to 38400" (Note: set as 57600 instead!) - '8', // "Parity - 7 or 8" - self.client.node.toString(), // "Node Number - 1 to 99" - '57600', // "DTE Rate. Actual BPS rate to use. (kg)" - 'Y', // "Screen Display - Y=On N=Off (Default to Y)" - 'Y', // "Printer Toggle - Y=On N=Off (Default to Y)" - 'Y', // "Page Bell - Y=On N=Off (Default to Y)" - 'Y', // "Caller Alarm - Y=On N=Off (Default to Y)" - up.real_name || self.client.user.username, // "User Full Name" - up.location || 'Anywhere', // "Calling From" - '123-456-7890', // "Home Phone" - '123-456-7890', // "Work/Data Phone" - 'NOPE', // "Password" (Note: this is never given out or even stored plaintext) - secLevel, // "Security Level" - up.login_count.toString(), // "Total Times On" - now.format('MM/DD/YY'), // "Last Date Called" - '15360', // "Seconds Remaining THIS call (for those that particular)" - '256', // "Minutes Remaining THIS call" - 'GR', // "Graphics Mode - GR=Graph, NG=Non-Graph, 7E=7,E Caller" - self.client.term.termHeight.toString(), // "Page Length" - 'N', // "User Mode - Y = Expert, N = Novice" - '1,2,3,4,5,6,7', // "Conferences/Forums Registered In (ABCDEFG)" - '1', // "Conference Exited To DOOR From (G)" - '01/01/99', // "User Expiration Date (mm/dd/yy)" - self.client.user.userId.toString(), // "User File's Record Number" - 'Z', // "Default Protocol - X, C, Y, G, I, N, Etc." - // :TODO: fix up, down, etc. form user properties - '0', // "Total Uploads" - '0', // "Total Downloads" - '0', // "Daily Download "K" Total" - '999999', // "Daily Download Max. "K" Limit" - moment(up.birthdate).format('MM/DD/YY'), // "Caller's Birthdate" - 'X:\\MAIN\\', // "Path to the MAIN directory (where User File is)" - 'X:\\GEN\\', // "Path to the GEN directory" - StatLog.getSystemStat('sysop_username'), // "Sysop's Name (name BBS refers to Sysop as)" - self.client.user.username, // "Alias name" - '00:05', // "Event time (hh:mm)" (note: wat?) - 'Y', // "If its an error correcting connection (Y/N)" - 'Y', // "ANSI supported & caller using NG mode (Y/N)" - 'Y', // "Use Record Locking (Y/N)" - '7', // "BBS Default Color (Standard IBM color code, ie, 1-15)" - // :TODO: fix minutes here also: - '256', // "Time Credits In Minutes (positive/negative)" - '07/07/90', // "Last New Files Scan Date (mm/dd/yy)" - // :TODO: fix last vs now times: - now.format('hh:mm'), // "Time of This Call" - now.format('hh:mm'), // "Time of Last Call (hh:mm)" - '9999', // "Maximum daily files available" - // :TODO: fix these stats: - '0', // "Files d/led so far today" - '0', // "Total "K" Bytes Uploaded" - '0', // "Total "K" Bytes Downloaded" - up.user_comment || 'None', // "User Comment" - '0', // "Total Doors Opened" - '0', // "Total Messages Left" + 'COM1:', // "Comm Port - COM0: = LOCAL MODE" + '57600', // "Baud Rate - 300 to 38400" (Note: set as 57600 instead!) + '8', // "Parity - 7 or 8" + self.client.node.toString(), // "Node Number - 1 to 99" + '57600', // "DTE Rate. Actual BPS rate to use. (kg)" + 'Y', // "Screen Display - Y=On N=Off (Default to Y)" + 'Y', // "Printer Toggle - Y=On N=Off (Default to Y)" + 'Y', // "Page Bell - Y=On N=Off (Default to Y)" + 'Y', // "Caller Alarm - Y=On N=Off (Default to Y)" + up.real_name || self.client.user.username, // "User Full Name" + up.location || 'Anywhere', // "Calling From" + '123-456-7890', // "Home Phone" + '123-456-7890', // "Work/Data Phone" + 'NOPE', // "Password" (Note: this is never given out or even stored plaintext) + secLevel, // "Security Level" + up.login_count.toString(), // "Total Times On" + now.format('MM/DD/YY'), // "Last Date Called" + '15360', // "Seconds Remaining THIS call (for those that particular)" + '256', // "Minutes Remaining THIS call" + 'GR', // "Graphics Mode - GR=Graph, NG=Non-Graph, 7E=7,E Caller" + self.client.term.termHeight.toString(), // "Page Length" + 'N', // "User Mode - Y = Expert, N = Novice" + '1,2,3,4,5,6,7', // "Conferences/Forums Registered In (ABCDEFG)" + '1', // "Conference Exited To DOOR From (G)" + '01/01/99', // "User Expiration Date (mm/dd/yy)" + self.client.user.userId.toString(), // "User File's Record Number" + 'Z', // "Default Protocol - X, C, Y, G, I, N, Etc." + // :TODO: fix up, down, etc. form user properties + '0', // "Total Uploads" + '0', // "Total Downloads" + '0', // "Daily Download "K" Total" + '999999', // "Daily Download Max. "K" Limit" + moment(up.birthdate).format('MM/DD/YY'), // "Caller's Birthdate" + 'X:\\MAIN\\', // "Path to the MAIN directory (where User File is)" + 'X:\\GEN\\', // "Path to the GEN directory" + StatLog.getSystemStat('sysop_username'), // "Sysop's Name (name BBS refers to Sysop as)" + self.client.user.username, // "Alias name" + '00:05', // "Event time (hh:mm)" (note: wat?) + 'Y', // "If its an error correcting connection (Y/N)" + 'Y', // "ANSI supported & caller using NG mode (Y/N)" + 'Y', // "Use Record Locking (Y/N)" + '7', // "BBS Default Color (Standard IBM color code, ie, 1-15)" + // :TODO: fix minutes here also: + '256', // "Time Credits In Minutes (positive/negative)" + '07/07/90', // "Last New Files Scan Date (mm/dd/yy)" + // :TODO: fix last vs now times: + now.format('hh:mm'), // "Time of This Call" + now.format('hh:mm'), // "Time of Last Call (hh:mm)" + '9999', // "Maximum daily files available" + // :TODO: fix these stats: + '0', // "Files d/led so far today" + '0', // "Total "K" Bytes Uploaded" + '0', // "Total "K" Bytes Downloaded" + up.user_comment || 'None', // "User Comment" + '0', // "Total Doors Opened" + '0', // "Total Messages Left" ].join('\r\n') + '\r\n', 'cp437'); }; this.getDoor32Buffer = function() { // - // Resources: - // * http://wiki.bbses.info/index.php/DOOR32.SYS + // Resources: + // * http://wiki.bbses.info/index.php/DOOR32.SYS // - // :TODO: local/serial/telnet need to be configurable -- which also changes socket handle! + // :TODO: local/serial/telnet need to be configurable -- which also changes socket handle! return iconv.encode([ - '2', // :TODO: This needs to be configurable! - // :TODO: Completely broken right now -- This need to be configurable & come from temp socket server most likely - '-1', // self.client.output._handle.fd.toString(), // :TODO: ALWAYS -1 on Windows! + '2', // :TODO: This needs to be configurable! + // :TODO: Completely broken right now -- This need to be configurable & come from temp socket server most likely + '-1', // self.client.output._handle.fd.toString(), // :TODO: ALWAYS -1 on Windows! '57600', Config().general.boardName, self.client.user.userId.toString(), self.client.user.properties.real_name || self.client.user.username, self.client.user.username, self.client.user.getLegacySecurityLevel().toString(), - '546', // :TODO: Minutes left! - '1', // ANSI + '546', // :TODO: Minutes left! + '1', // ANSI self.client.node.toString(), ].join('\r\n') + '\r\n', 'cp437'); }; this.getDoorInfoDefBuffer = function() { - // :TODO: fix time remaining + // :TODO: fix time remaining // - // Resources: - // * http://goldfndr.home.mindspring.com/dropfile/dorinfo.htm + // Resources: + // * http://goldfndr.home.mindspring.com/dropfile/dorinfo.htm // - // Note that usernames are just used for first/last names here + // Note that usernames are just used for first/last names here // - var opUn = /[^\s]*/.exec(StatLog.getSystemStat('sysop_username'))[0]; - var un = /[^\s]*/.exec(self.client.user.username)[0]; - var secLevel = self.client.user.getLegacySecurityLevel().toString(); + var opUn = /[^\s]*/.exec(StatLog.getSystemStat('sysop_username'))[0]; + var un = /[^\s]*/.exec(self.client.user.username)[0]; + var secLevel = self.client.user.getLegacySecurityLevel().toString(); return iconv.encode( [ - Config().general.boardName, // "The name of the system." - opUn, // "The sysop's name up to the first space." - opUn, // "The sysop's name following the first space." - 'COM1', // "The serial port the modem is connected to, or 0 if logged in on console." - '57600', // "The current port (DTE) rate." - '0', // "The number "0"" - un, // "The current user's name, up to the first space." - un, // "The current user's name, following the first space." - self.client.user.properties.location || '', // "Where the user lives, or a blank line if unknown." - '1', // "The number "0" if TTY, or "1" if ANSI." - secLevel, // "The number 5 for problem users, 30 for regular users, 80 for Aides, and 100 for Sysops." - '546', // "The number of minutes left in the current user's account, limited to 546 to keep from overflowing other software." - '-1' // "The number "-1" if using an external serial driver or "0" if using internal serial routines." + Config().general.boardName, // "The name of the system." + opUn, // "The sysop's name up to the first space." + opUn, // "The sysop's name following the first space." + 'COM1', // "The serial port the modem is connected to, or 0 if logged in on console." + '57600', // "The current port (DTE) rate." + '0', // "The number "0"" + un, // "The current user's name, up to the first space." + un, // "The current user's name, following the first space." + self.client.user.properties.location || '', // "Where the user lives, or a blank line if unknown." + '1', // "The number "0" if TTY, or "1" if ANSI." + secLevel, // "The number 5 for problem users, 30 for regular users, 80 for Aides, and 100 for Sysops." + '546', // "The number of minutes left in the current user's account, limited to 546 to keep from overflowing other software." + '-1' // "The number "-1" if using an external serial driver or "0" if using internal serial routines." ].join('\r\n') + '\r\n', 'cp437'); }; diff --git a/core/edit_text_view.js b/core/edit_text_view.js index b1b89726..05868e92 100644 --- a/core/edit_text_view.js +++ b/core/edit_text_view.js @@ -1,21 +1,21 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const TextView = require('./text_view.js').TextView; -const miscUtil = require('./misc_util.js'); -const strUtil = require('./string_util.js'); +// ENiGMA½ +const TextView = require('./text_view.js').TextView; +const miscUtil = require('./misc_util.js'); +const strUtil = require('./string_util.js'); -// deps -const _ = require('lodash'); +// deps +const _ = require('lodash'); -exports.EditTextView = EditTextView; +exports.EditTextView = EditTextView; function EditTextView(options) { - options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true); - options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true); - options.cursorStyle = miscUtil.valueWithDefault(options.cursorStyle, 'steady block'); - options.resizable = false; + options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true); + options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true); + options.cursorStyle = miscUtil.valueWithDefault(options.cursorStyle, 'steady block'); + options.resizable = false; TextView.call(this, options); @@ -47,9 +47,9 @@ EditTextView.prototype.onKeyPress = function(ch, key) { return EditTextView.super_.prototype.onKeyPress.call(this, ch, key); } else if(this.isKeyMapped('clearLine', key.name)) { - this.text = ''; - this.cursorPos.col = 0; - this.setFocus(true); // resetting focus will redraw & adjust cursor + this.text = ''; + this.cursorPos.col = 0; + this.setFocus(true); // resetting focus will redraw & adjust cursor return EditTextView.super_.prototype.onKeyPress.call(this, ch, key); } @@ -62,7 +62,7 @@ EditTextView.prototype.onKeyPress = function(ch, key) { this.text += ch; if(this.text.length > this.dimens.width) { - // no shortcuts - redraw the view + // no shortcuts - redraw the view this.redraw(); } else { this.cursorPos.col += 1; @@ -82,9 +82,9 @@ EditTextView.prototype.onKeyPress = function(ch, key) { }; EditTextView.prototype.setText = function(text) { - // draw & set |text| + // draw & set |text| EditTextView.super_.prototype.setText.call(this, text); - // adjust local cursor tracking + // adjust local cursor tracking this.cursorPos = { row : 0, col : text.length }; }; diff --git a/core/email.js b/core/email.js index af195da5..1de3b034 100644 --- a/core/email.js +++ b/core/email.js @@ -1,16 +1,16 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('./config.js').get; -const Errors = require('./enig_error.js').Errors; -const Log = require('./logger.js').log; +// ENiGMA½ +const Config = require('./config.js').get; +const Errors = require('./enig_error.js').Errors; +const Log = require('./logger.js').log; -// deps -const _ = require('lodash'); -const nodeMailer = require('nodemailer'); +// deps +const _ = require('lodash'); +const nodeMailer = require('nodemailer'); -exports.sendMail = sendMail; +exports.sendMail = sendMail; function sendMail(message, cb) { const config = Config(); @@ -21,7 +21,7 @@ function sendMail(message, cb) { message.from = message.from || config.email.defaultFrom; const transportOptions = Object.assign( {}, config.email.transport, { - logger : Log, + logger : Log, }); const transport = nodeMailer.createTransport(transportOptions); diff --git a/core/enig_error.js b/core/enig_error.js index 879a18ab..63c90cb9 100644 --- a/core/enig_error.js +++ b/core/enig_error.js @@ -5,11 +5,11 @@ class EnigError extends Error { constructor(message, code, reason, reasonCode) { super(message); - this.name = this.constructor.name; - this.message = message; - this.code = code; - this.reason = reason; - this.reasonCode = reasonCode; + this.name = this.constructor.name; + this.message = message; + this.code = code; + this.reason = reason; + this.reasonCode = reasonCode; if(this.reason) { this.message += `: ${this.reason}`; @@ -23,24 +23,24 @@ class EnigError extends Error { } } -exports.EnigError = EnigError; +exports.EnigError = EnigError; exports.Errors = { - General : (reason, reasonCode) => new EnigError('An error occurred', -33000, reason, reasonCode), - MenuStack : (reason, reasonCode) => new EnigError('Menu stack error', -33001, reason, reasonCode), - DoesNotExist : (reason, reasonCode) => new EnigError('Object does not exist', -33002, reason, reasonCode), - AccessDenied : (reason, reasonCode) => new EnigError('Access denied', -32003, reason, reasonCode), - Invalid : (reason, reasonCode) => new EnigError('Invalid', -32004, reason, reasonCode), - ExternalProcess : (reason, reasonCode) => new EnigError('External process error', -32005, reason, reasonCode), - MissingConfig : (reason, reasonCode) => new EnigError('Missing configuration', -32006, reason, reasonCode), - UnexpectedState : (reason, reasonCode) => new EnigError('Unexpected state', -32007, reason, reasonCode), - MissingParam : (reason, reasonCode) => new EnigError('Missing paramater(s)', -32008, reason, reasonCode), + General : (reason, reasonCode) => new EnigError('An error occurred', -33000, reason, reasonCode), + MenuStack : (reason, reasonCode) => new EnigError('Menu stack error', -33001, reason, reasonCode), + DoesNotExist : (reason, reasonCode) => new EnigError('Object does not exist', -33002, reason, reasonCode), + AccessDenied : (reason, reasonCode) => new EnigError('Access denied', -32003, reason, reasonCode), + Invalid : (reason, reasonCode) => new EnigError('Invalid', -32004, reason, reasonCode), + ExternalProcess : (reason, reasonCode) => new EnigError('External process error', -32005, reason, reasonCode), + MissingConfig : (reason, reasonCode) => new EnigError('Missing configuration', -32006, reason, reasonCode), + UnexpectedState : (reason, reasonCode) => new EnigError('Unexpected state', -32007, reason, reasonCode), + MissingParam : (reason, reasonCode) => new EnigError('Missing paramater(s)', -32008, reason, reasonCode), }; exports.ErrorReasons = { - AlreadyThere : 'ALREADYTHERE', - InvalidNextMenu : 'BADNEXT', - NoPreviousMenu : 'NOPREV', - NoConditionMatch : 'NOCONDMATCH', - NotEnabled : 'NOTENABLED', + AlreadyThere : 'ALREADYTHERE', + InvalidNextMenu : 'BADNEXT', + NoPreviousMenu : 'NOPREV', + NoConditionMatch : 'NOCONDMATCH', + NotEnabled : 'NOTENABLED', }; \ No newline at end of file diff --git a/core/enigma_assert.js b/core/enigma_assert.js index 0d1d5176..34f9beed 100644 --- a/core/enigma_assert.js +++ b/core/enigma_assert.js @@ -1,12 +1,12 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('./config.js').get; -const Log = require('./logger.js').log; +// ENiGMA½ +const Config = require('./config.js').get; +const Log = require('./logger.js').log; -// deps -const assert = require('assert'); +// deps +const assert = require('assert'); module.exports = function(condition, message) { if(Config().debug.assertsEnabled) { diff --git a/core/erc_client.js b/core/erc_client.js index 79a47240..018c9372 100644 --- a/core/erc_client.js +++ b/core/erc_client.js @@ -1,55 +1,55 @@ /* jslint node: true */ 'use strict'; -const MenuModule = require('./menu_module.js').MenuModule; -const stringFormat = require('./string_format.js'); +const MenuModule = require('./menu_module.js').MenuModule; +const stringFormat = require('./string_format.js'); -// deps -const async = require('async'); -const _ = require('lodash'); -const net = require('net'); +// deps +const async = require('async'); +const _ = require('lodash'); +const net = require('net'); /* - Expected configuration block example: + Expected configuration block example: - config: { - host: 192.168.1.171 - port: 5001 - bbsTag: SOME_TAG - } + config: { + host: 192.168.1.171 + port: 5001 + bbsTag: SOME_TAG + } */ -exports.getModule = ErcClientModule; +exports.getModule = ErcClientModule; exports.moduleInfo = { - name : 'ENiGMA Relay Chat Client', - desc : 'Chat with other ENiGMA BBSes', - author : 'Andrew Pamment', + name : 'ENiGMA Relay Chat Client', + desc : 'Chat with other ENiGMA BBSes', + author : 'Andrew Pamment', }; var MciViewIds = { ChatDisplay : 1, - InputArea : 3, + InputArea : 3, }; -// :TODO: needs converted to ES6 MenuModule subclass +// :TODO: needs converted to ES6 MenuModule subclass function ErcClientModule(options) { MenuModule.prototype.ctorShim.call(this, options); - const self = this; - this.config = options.menuConfig.config; + const self = this; + this.config = options.menuConfig.config; - this.chatEntryFormat = this.config.chatEntryFormat || '[{bbsTag}] {userName}: {message}'; - this.systemEntryFormat = this.config.systemEntryFormat || '[*SYSTEM*] {message}'; + this.chatEntryFormat = this.config.chatEntryFormat || '[{bbsTag}] {userName}: {message}'; + this.systemEntryFormat = this.config.systemEntryFormat || '[*SYSTEM*] {message}'; this.finishedLoading = function() { async.waterfall( [ function validateConfig(callback) { if(_.isString(self.config.host) && - _.isNumber(self.config.port) && - _.isString(self.config.bbsTag)) + _.isNumber(self.config.port) && + _.isString(self.config.bbsTag)) { return callback(null); } else { @@ -58,8 +58,8 @@ function ErcClientModule(options) { }, function connectToServer(callback) { const connectOpts = { - port : self.config.port, - host : self.config.host, + port : self.config.port, + host : self.config.host, }; const chatMessageView = self.viewControllers.menu.getView(MciViewIds.ChatDisplay); @@ -69,7 +69,7 @@ function ErcClientModule(options) { self.viewControllers.menu.switchFocus(MciViewIds.InputArea); - // :TODO: Track actual client->enig connection for optional prevMenu @ final CB + // :TODO: Track actual client->enig connection for optional prevMenu @ final CB self.chatConnection = net.createConnection(connectOpts.port, connectOpts.host); self.chatConnection.on('data', data => { @@ -87,10 +87,10 @@ function ErcClientModule(options) { let text; try { if(data.userName) { - // user message + // user message text = stringFormat(self.chatEntryFormat, data); } else { - // system message + // system message text = stringFormat(self.systemEntryFormat, data); } } catch(e) { @@ -99,7 +99,7 @@ function ErcClientModule(options) { chatMessageView.addText(text); - if(chatMessageView.getLineCount() > 30) { // :TODO: should probably be ChatDisplay.height? + if(chatMessageView.getLineCount() > 30) { // :TODO: should probably be ChatDisplay.height? chatMessageView.deleteLine(0); chatMessageView.scrollDown(); } @@ -130,8 +130,8 @@ function ErcClientModule(options) { }; this.scrollHandler = function(keyName) { - const inputAreaView = self.viewControllers.menu.getView(MciViewIds.InputArea); - const chatDisplayView = self.viewControllers.menu.getView(MciViewIds.ChatDisplay); + const inputAreaView = self.viewControllers.menu.getView(MciViewIds.InputArea); + const chatDisplayView = self.viewControllers.menu.getView(MciViewIds.ChatDisplay); if('up arrow' === keyName) { chatDisplayView.scrollUp(); @@ -147,7 +147,7 @@ function ErcClientModule(options) { this.menuMethods = { inputAreaSubmit : function(formData, extraArgs, cb) { const inputAreaView = self.viewControllers.menu.getView(MciViewIds.InputArea); - const inputData = inputAreaView.getData(); + const inputData = inputAreaView.getData(); if('/quit' === inputData.toLowerCase()) { self.chatConnection.end(); diff --git a/core/event_scheduler.js b/core/event_scheduler.js index 1465d42b..22c2d58d 100644 --- a/core/event_scheduler.js +++ b/core/event_scheduler.js @@ -1,37 +1,37 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const PluginModule = require('./plugin_module.js').PluginModule; -const Config = require('./config.js').get; -const Log = require('./logger.js').log; +// ENiGMA½ +const PluginModule = require('./plugin_module.js').PluginModule; +const Config = require('./config.js').get; +const Log = require('./logger.js').log; -const _ = require('lodash'); -const later = require('later'); -const path = require('path'); -const pty = require('node-pty'); -const sane = require('sane'); -const moment = require('moment'); -const paths = require('path'); -const fse = require('fs-extra'); +const _ = require('lodash'); +const later = require('later'); +const path = require('path'); +const pty = require('node-pty'); +const sane = require('sane'); +const moment = require('moment'); +const paths = require('path'); +const fse = require('fs-extra'); -exports.getModule = EventSchedulerModule; -exports.EventSchedulerModule = EventSchedulerModule; // allow for loadAndStart +exports.getModule = EventSchedulerModule; +exports.EventSchedulerModule = EventSchedulerModule; // allow for loadAndStart exports.moduleInfo = { - name : 'Event Scheduler', - desc : 'Support for scheduling arbritary events', - author : 'NuSkooler', + name : 'Event Scheduler', + desc : 'Support for scheduling arbritary events', + author : 'NuSkooler', }; -const SCHEDULE_REGEXP = /(?:^|or )?(@watch:)([^\0]+)?$/; -const ACTION_REGEXP = /@(method|execute):([^\0]+)?$/; +const SCHEDULE_REGEXP = /(?:^|or )?(@watch:)([^\0]+)?$/; +const ACTION_REGEXP = /@(method|execute):([^\0]+)?$/; class ScheduledEvent { constructor(events, name) { - this.name = name; - this.schedule = this.parseScheduleString(events[name].schedule); - this.action = this.parseActionSpec(events[name].action); + this.name = name; + this.schedule = this.parseScheduleString(events[name].schedule); + this.action = this.parseActionSpec(events[name].action); if(this.action) { this.action.args = events[name].args || []; } @@ -72,7 +72,7 @@ class ScheduledEvent { } } - // return undefined if we couldn't parse out anything useful + // return undefined if we couldn't parse out anything useful if(!_.isEmpty(schedule)) { return schedule; } @@ -86,21 +86,21 @@ class ScheduledEvent { if(m[2].indexOf(':') > -1) { const parts = m[2].split(':'); return { - type : m[1], - location : parts[0], - what : parts[1], + type : m[1], + location : parts[0], + what : parts[1], }; } else { return { - type : m[1], - what : m[2], + type : m[1], + what : m[2], }; } } } else { return { - type : 'execute', - what : actionSpec, + type : 'execute', + what : actionSpec, }; } } @@ -110,7 +110,7 @@ class ScheduledEvent { Log.info( { eventName : this.name, action : this.action, reason : reason }, 'Executing scheduled event action...'); if('method' === this.action.type) { - const modulePath = path.join(__dirname, '../', this.action.location); // enigma-bbs base + supplied location (path/file.js') + const modulePath = path.join(__dirname, '../', this.action.location); // enigma-bbs base + supplied location (path/file.js') try { const methodModule = require(modulePath); methodModule[this.action.what](this.action.args, err => { @@ -131,11 +131,11 @@ class ScheduledEvent { } } else if('execute' === this.action.type) { const opts = { - // :TODO: cwd - name : this.name, - cols : 80, - rows : 24, - env : process.env, + // :TODO: cwd + name : this.name, + cols : 80, + rows : 24, + env : process.env, }; const proc = pty.spawn(this.action.what, this.action.args, opts); @@ -165,7 +165,7 @@ function EventSchedulerModule(options) { this.performAction = function(schedEvent, reason) { if(self.runningActions.has(schedEvent.name)) { - return; // already running + return; // already running } self.runningActions.add(schedEvent.name); @@ -176,13 +176,13 @@ function EventSchedulerModule(options) { }; } -// convienence static method for direct load + start +// convienence static method for direct load + start EventSchedulerModule.loadAndStart = function(cb) { const loadModuleEx = require('./module_util.js').loadModuleEx; const loadOpts = { - name : path.basename(__filename, '.js'), - path : __dirname, + name : path.basename(__filename, '.js'), + path : __dirname, }; loadModuleEx(loadOpts, (err, mod) => { @@ -199,7 +199,7 @@ EventSchedulerModule.loadAndStart = function(cb) { EventSchedulerModule.prototype.startup = function(cb) { - this.eventTimers = []; + this.eventTimers = []; const self = this; if(this.moduleConfig && _.has(this.moduleConfig, 'events')) { @@ -215,10 +215,10 @@ EventSchedulerModule.prototype.startup = function(cb) { Log.debug( { - eventName : schedEvent.name, - schedule : this.moduleConfig.events[schedEvent.name].schedule, - action : schedEvent.action, - next : schedEvent.schedule.sched ? moment(later.schedule(schedEvent.schedule.sched).next(1)).format('ddd, MMM Do, YYYY @ h:m:ss a') : 'N/A', + eventName : schedEvent.name, + schedule : this.moduleConfig.events[schedEvent.name].schedule, + action : schedEvent.action, + next : schedEvent.schedule.sched ? moment(later.schedule(schedEvent.schedule.sched).next(1)).format('ddd, MMM Do, YYYY @ h:m:ss a') : 'N/A', }, 'Scheduled event loaded' ); @@ -237,7 +237,7 @@ EventSchedulerModule.prototype.startup = function(cb) { } ); - // :TODO: should track watched files & stop watching @ shutdown? + // :TODO: should track watched files & stop watching @ shutdown? [ 'change', 'add', 'delete' ].forEach(event => { watcher.on(event, (fileName, fileRoot) => { diff --git a/core/events.js b/core/events.js index aa75345f..6284597e 100644 --- a/core/events.js +++ b/core/events.js @@ -1,20 +1,20 @@ /* jslint node: true */ 'use strict'; -const paths = require('path'); -const events = require('events'); -const Log = require('./logger.js').log; -const SystemEvents = require('./system_events.js'); +const paths = require('path'); +const events = require('events'); +const Log = require('./logger.js').log; +const SystemEvents = require('./system_events.js'); -// deps -const _ = require('lodash'); -const async = require('async'); -const glob = require('glob'); +// deps +const _ = require('lodash'); +const async = require('async'); +const glob = require('glob'); module.exports = new class Events extends events.EventEmitter { constructor() { super(); - this.setMaxListeners(32); // :TODO: play with this... + this.setMaxListeners(32); // :TODO: play with this... } getSystemEvents() { @@ -60,7 +60,7 @@ module.exports = new class Events extends events.EventEmitter { const mod = require(fullModulePath); if(_.isFunction(mod.registerEvents)) { - // :TODO: ... or just systemInit() / systemShutdown() & mods could call Events.on() / Events.removeListener() ? + // :TODO: ... or just systemInit() / systemShutdown() & mods could call Events.on() / Events.removeListener() ? mod.registerEvents(this); } } catch(e) { diff --git a/core/exodus.js b/core/exodus.js index b20f18a1..e25b4973 100644 --- a/core/exodus.js +++ b/core/exodus.js @@ -1,83 +1,83 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const resetScreen = require('./ansi_term.js').resetScreen; -const Config = require('./config.js').get; -const Errors = require('./enig_error.js').Errors; -const Log = require('./logger.js').log; -const getEnigmaUserAgent = require('./misc_util.js').getEnigmaUserAgent; +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const resetScreen = require('./ansi_term.js').resetScreen; +const Config = require('./config.js').get; +const Errors = require('./enig_error.js').Errors; +const Log = require('./logger.js').log; +const getEnigmaUserAgent = require('./misc_util.js').getEnigmaUserAgent; -// deps -const async = require('async'); -const _ = require('lodash'); -const joinPath = require('path').join; -const crypto = require('crypto'); -const moment = require('moment'); -const https = require('https'); -const querystring = require('querystring'); -const fs = require('fs'); -const SSHClient = require('ssh2').Client; +// deps +const async = require('async'); +const _ = require('lodash'); +const joinPath = require('path').join; +const crypto = require('crypto'); +const moment = require('moment'); +const https = require('https'); +const querystring = require('querystring'); +const fs = require('fs'); +const SSHClient = require('ssh2').Client; /* - Configuration block: + Configuration block: - someDoor: { - module: exodus - config: { - // defaults - ticketHost: oddnetwork.org - ticketPort: 1984 - ticketPath: /exodus - rejectUnauthorized: false // set to true to allow untrusted CA's (dangerous!) - sshHost: oddnetwork.org - sshPort: 22 - sshUser: exodus - sshKeyPem: /path/to/enigma-bbs/misc/exodus.id_rsa + someDoor: { + module: exodus + config: { + // defaults + ticketHost: oddnetwork.org + ticketPort: 1984 + ticketPath: /exodus + rejectUnauthorized: false // set to true to allow untrusted CA's (dangerous!) + sshHost: oddnetwork.org + sshPort: 22 + sshUser: exodus + sshKeyPem: /path/to/enigma-bbs/misc/exodus.id_rsa - // optional - caPem: /path/to/cacerts.pem // see https://curl.haxx.se/docs/caextract.html + // optional + caPem: /path/to/cacerts.pem // see https://curl.haxx.se/docs/caextract.html - // required - board: XXXX - key: XXXX - door: some_door - } - } + // required + board: XXXX + key: XXXX + door: some_door + } + } */ exports.moduleInfo = { - name : 'Exodus', - desc : 'Exodus Door Server Access Module - https://oddnetwork.org/exodus/', - author : 'NuSkooler', + name : 'Exodus', + desc : 'Exodus Door Server Access Module - https://oddnetwork.org/exodus/', + author : 'NuSkooler', }; exports.getModule = class ExodusModule extends MenuModule { constructor(options) { super(options); - this.config = options.menuConfig.config || {}; - this.config.ticketHost = this.config.ticketHost || 'oddnetwork.org'; - this.config.ticketPort = this.config.ticketPort || 1984, - this.config.ticketPath = this.config.ticketPath || '/exodus'; - this.config.rejectUnauthorized = _.get(this.config, 'rejectUnauthorized', true); - this.config.sshHost = this.config.sshHost || this.config.ticketHost; - this.config.sshPort = this.config.sshPort || 22; - this.config.sshUser = this.config.sshUser || 'exodus_server'; - this.config.sshKeyPem = this.config.sshKeyPem || joinPath(Config().paths.misc, 'exodus.id_rsa'); + this.config = options.menuConfig.config || {}; + this.config.ticketHost = this.config.ticketHost || 'oddnetwork.org'; + this.config.ticketPort = this.config.ticketPort || 1984, + this.config.ticketPath = this.config.ticketPath || '/exodus'; + this.config.rejectUnauthorized = _.get(this.config, 'rejectUnauthorized', true); + this.config.sshHost = this.config.sshHost || this.config.ticketHost; + this.config.sshPort = this.config.sshPort || 22; + this.config.sshUser = this.config.sshUser || 'exodus_server'; + this.config.sshKeyPem = this.config.sshKeyPem || joinPath(Config().paths.misc, 'exodus.id_rsa'); } initSequence() { - const self = this; - let clientTerminated = false; + const self = this; + let clientTerminated = false; async.waterfall( [ function validateConfig(callback) { - // very basic validation on optionals + // very basic validation on optionals async.each( [ 'board', 'key', 'door' ], (key, next) => { return _.isString(self.config[key]) ? next(null) : next(Errors.MissingConfig(`Config requires "${key}"!`)); }, callback); @@ -92,27 +92,27 @@ exports.getModule = class ExodusModule extends MenuModule { }); }, function getTicket(certAuthorities, callback) { - const now = moment.utc().unix(); - const sha256 = crypto.createHash('sha256').update(`${self.config.key}${now}`).digest('hex'); - const token = `${sha256}|${now}`; + const now = moment.utc().unix(); + const sha256 = crypto.createHash('sha256').update(`${self.config.key}${now}`).digest('hex'); + const token = `${sha256}|${now}`; - const postData = querystring.stringify({ - token : token, - board : self.config.board, - user : self.client.user.username, - door : self.config.door, + const postData = querystring.stringify({ + token : token, + board : self.config.board, + user : self.client.user.username, + door : self.config.door, }); const reqOptions = { - hostname : self.config.ticketHost, - port : self.config.ticketPort, - path : self.config.ticketPath, - rejectUnauthorized : self.config.rejectUnauthorized, - method : 'POST', - headers : { - 'Content-Type' : 'application/x-www-form-urlencoded', - 'Content-Length' : postData.length, - 'User-Agent' : getEnigmaUserAgent(), + hostname : self.config.ticketHost, + port : self.config.ticketPort, + path : self.config.ticketPath, + rejectUnauthorized : self.config.rejectUnauthorized, + method : 'POST', + headers : { + 'Content-Type' : 'application/x-www-form-urlencoded', + 'Content-Length' : postData.length, + 'User-Agent' : getEnigmaUserAgent(), } }; @@ -165,11 +165,11 @@ exports.getModule = class ExodusModule extends MenuModule { const sshClient = new SSHClient(); const window = { - rows : self.client.term.termHeight, - cols : self.client.term.termWidth, - width : 0, - height : 0, - term : 'vt100', // Want to pass |self.client.term.termClient| here, but we end up getting hung up on :( + rows : self.client.term.termHeight, + cols : self.client.term.termWidth, + width : 0, + height : 0, + term : 'vt100', // Want to pass |self.client.term.termClient| here, but we end up getting hung up on :( }; const options = { @@ -186,7 +186,7 @@ exports.getModule = class ExodusModule extends MenuModule { }); sshClient.shell(window, options, (err, stream) => { - pipedStream = stream; // :TODO: ewwwwwwwww hack + pipedStream = stream; // :TODO: ewwwwwwwww hack self.client.term.output.pipe(stream); stream.on('data', d => { @@ -210,10 +210,10 @@ exports.getModule = class ExodusModule extends MenuModule { }); sshClient.connect({ - host : self.config.sshHost, - port : self.config.sshPort, - username : self.config.sshUser, - privateKey : privateKey, + host : self.config.sshHost, + port : self.config.sshPort, + username : self.config.sshUser, + privateKey : privateKey, }); } ], diff --git a/core/file_area_filter_edit.js b/core/file_area_filter_edit.js index 3cdeb68b..06c0fd6e 100644 --- a/core/file_area_filter_edit.js +++ b/core/file_area_filter_edit.js @@ -1,36 +1,36 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const getSortedAvailableFileAreas = require('./file_base_area.js').getSortedAvailableFileAreas; -const FileBaseFilters = require('./file_base_filter.js'); -const stringFormat = require('./string_format.js'); +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const getSortedAvailableFileAreas = require('./file_base_area.js').getSortedAvailableFileAreas; +const FileBaseFilters = require('./file_base_filter.js'); +const stringFormat = require('./string_format.js'); -// deps -const async = require('async'); +// deps +const async = require('async'); exports.moduleInfo = { - name : 'File Area Filter Editor', - desc : 'Module for adding, deleting, and modifying file base filters', - author : 'NuSkooler', + name : 'File Area Filter Editor', + desc : 'Module for adding, deleting, and modifying file base filters', + author : 'NuSkooler', }; const MciViewIds = { editor : { - searchTerms : 1, - tags : 2, - area : 3, - sort : 4, - order : 5, - filterName : 6, - navMenu : 7, + searchTerms : 1, + tags : 2, + area : 3, + sort : 4, + order : 5, + filterName : 6, + navMenu : 7, - // :TODO: use the customs new standard thing - filter obj can have active/selected, etc. - selectedFilterInfo : 10, // { ...filter object ... } - activeFilterInfo : 11, // { ...filter object ... } - error : 12, // validation errors + // :TODO: use the customs new standard thing - filter obj can have active/selected, etc. + selectedFilterInfo : 10, // { ...filter object ... } + activeFilterInfo : 11, // { ...filter object ... } + error : 12, // validation errors } }; @@ -38,11 +38,11 @@ exports.getModule = class FileAreaFilterEdit extends MenuModule { constructor(options) { super(options); - this.filtersArray = new FileBaseFilters(this.client).toArray(); // ordered, such that we can index into them - this.currentFilterIndex = 0; // into |filtersArray| + this.filtersArray = new FileBaseFilters(this.client).toArray(); // ordered, such that we can index into them + this.currentFilterIndex = 0; // into |filtersArray| // - // Lexical sort + keep currently active filter (if any) as the first item in |filtersArray| + // Lexical sort + keep currently active filter (if any) as the first item in |filtersArray| // const activeFilter = FileBaseFilters.getActiveFilter(this.client); this.filtersArray.sort( (filterA, filterB) => { @@ -87,41 +87,41 @@ exports.getModule = class FileAreaFilterEdit extends MenuModule { return cb(null); }, newFilter : (formData, extraArgs, cb) => { - this.currentFilterIndex = this.filtersArray.length; // next avail slot + this.currentFilterIndex = this.filtersArray.length; // next avail slot this.clearForm(MciViewIds.editor.searchTerms); return cb(null); }, deleteFilter : (formData, extraArgs, cb) => { - const selectedFilter = this.filtersArray[this.currentFilterIndex]; - const filterUuid = selectedFilter.uuid; + const selectedFilter = this.filtersArray[this.currentFilterIndex]; + const filterUuid = selectedFilter.uuid; - // cannot delete built-in/system filters + // cannot delete built-in/system filters if(true === selectedFilter.system) { this.showError('Cannot delete built in filters!'); return cb(null); } - this.filtersArray.splice(this.currentFilterIndex, 1); // remove selected entry + this.filtersArray.splice(this.currentFilterIndex, 1); // remove selected entry - // remove from stored properties + // remove from stored properties const filters = new FileBaseFilters(this.client); filters.remove(filterUuid); filters.persist( () => { // - // If the item was also the active filter, we need to make a new one active + // If the item was also the active filter, we need to make a new one active // if(filterUuid === this.client.user.properties.file_base_filter_active_uuid) { const newActive = this.filtersArray[this.currentFilterIndex]; if(newActive) { filters.setActive(newActive.uuid); } else { - // nothing to set active to + // nothing to set active to this.client.user.removeProperty('file_base_filter_active_uuid'); } } - // update UI + // update UI this.updateActiveLabel(); if(this.filtersArray.length > 0) { @@ -140,7 +140,7 @@ exports.getModule = class FileAreaFilterEdit extends MenuModule { if(errorView) { if(err) { errorView.setText(err.message); - err.view.clearText(); // clear out the invalid data + err.view.clearText(); // clear out the invalid data } else { errorView.clearText(); } @@ -168,8 +168,8 @@ exports.getModule = class FileAreaFilterEdit extends MenuModule { return cb(err); } - const self = this; - const vc = self.addViewController( 'editor', new ViewController( { client : this.client } ) ); + const self = this; + const vc = self.addViewController( 'editor', new ViewController( { client : this.client } ) ); async.series( [ @@ -241,7 +241,7 @@ exports.getModule = class FileAreaFilterEdit extends MenuModule { getSelectedAreaTag(index) { if(0 === index) { - return ''; // -ALL- + return ''; // -ALL- } const area = this.availAreas[index]; if(!area) { @@ -258,7 +258,7 @@ exports.getModule = class FileAreaFilterEdit extends MenuModule { let index; const filter = this.getCurrentFilter(); if(filter) { - // special treatment: areaTag saved as blank ("") if -ALL- + // special treatment: areaTag saved as blank ("") if -ALL- index = (filter.areaTag && this.availAreas.findIndex(area => filter.areaTag === area.areaTag)) || 0; } else { index = 0; @@ -293,31 +293,31 @@ exports.getModule = class FileAreaFilterEdit extends MenuModule { } setFilterValuesFromFormData(filter, formData) { - filter.name = formData.value.name; - filter.areaTag = this.getSelectedAreaTag(formData.value.areaIndex); - filter.terms = formData.value.searchTerms; - filter.tags = formData.value.tags; - filter.order = this.getOrderBy(formData.value.orderByIndex); - filter.sort = this.getSortBy(formData.value.sortByIndex); + filter.name = formData.value.name; + filter.areaTag = this.getSelectedAreaTag(formData.value.areaIndex); + filter.terms = formData.value.searchTerms; + filter.tags = formData.value.tags; + filter.order = this.getOrderBy(formData.value.orderByIndex); + filter.sort = this.getSortBy(formData.value.sortByIndex); } saveCurrentFilter(formData, cb) { - const filters = new FileBaseFilters(this.client); - const selectedFilter = this.filtersArray[this.currentFilterIndex]; + const filters = new FileBaseFilters(this.client); + const selectedFilter = this.filtersArray[this.currentFilterIndex]; if(selectedFilter) { - // *update* currently selected filter + // *update* currently selected filter this.setFilterValuesFromFormData(selectedFilter, formData); filters.replace(selectedFilter.uuid, selectedFilter); } else { - // add a new entry; note that UUID will be generated + // add a new entry; note that UUID will be generated const newFilter = {}; this.setFilterValuesFromFormData(newFilter, formData); - // set current to what we just saved + // set current to what we just saved newFilter.uuid = filters.add(newFilter); - // add to our array (at current index position) + // add to our array (at current index position) this.filtersArray[this.currentFilterIndex] = newFilter; } @@ -327,9 +327,9 @@ exports.getModule = class FileAreaFilterEdit extends MenuModule { loadDataForFilter(filterIndex) { const filter = this.filtersArray[filterIndex]; if(filter) { - this.setText(MciViewIds.editor.searchTerms, filter.terms); - this.setText(MciViewIds.editor.tags, filter.tags); - this.setText(MciViewIds.editor.filterName, filter.name); + this.setText(MciViewIds.editor.searchTerms, filter.terms); + this.setText(MciViewIds.editor.tags, filter.tags); + this.setText(MciViewIds.editor.filterName, filter.name); this.setAreaIndexFromCurrentFilter(); this.setSortByFromCurrentFilter(); diff --git a/core/file_area_list.js b/core/file_area_list.js index 8b992f94..f3172d4d 100644 --- a/core/file_area_list.js +++ b/core/file_area_list.js @@ -1,71 +1,71 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const ansi = require('./ansi_term.js'); -const theme = require('./theme.js'); -const FileEntry = require('./file_entry.js'); -const stringFormat = require('./string_format.js'); -const FileArea = require('./file_base_area.js'); -const Errors = require('./enig_error.js').Errors; -const ErrNotEnabled = require('./enig_error.js').ErrorReasons.NotEnabled; -const ArchiveUtil = require('./archive_util.js'); -const Config = require('./config.js').get; -const DownloadQueue = require('./download_queue.js'); -const FileAreaWeb = require('./file_area_web.js'); -const FileBaseFilters = require('./file_base_filter.js'); -const resolveMimeType = require('./mime_util.js').resolveMimeType; -const isAnsi = require('./string_util.js').isAnsi; -const controlCodesToAnsi = require('./color_codes.js').controlCodesToAnsi; +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const ansi = require('./ansi_term.js'); +const theme = require('./theme.js'); +const FileEntry = require('./file_entry.js'); +const stringFormat = require('./string_format.js'); +const FileArea = require('./file_base_area.js'); +const Errors = require('./enig_error.js').Errors; +const ErrNotEnabled = require('./enig_error.js').ErrorReasons.NotEnabled; +const ArchiveUtil = require('./archive_util.js'); +const Config = require('./config.js').get; +const DownloadQueue = require('./download_queue.js'); +const FileAreaWeb = require('./file_area_web.js'); +const FileBaseFilters = require('./file_base_filter.js'); +const resolveMimeType = require('./mime_util.js').resolveMimeType; +const isAnsi = require('./string_util.js').isAnsi; +const controlCodesToAnsi = require('./color_codes.js').controlCodesToAnsi; -// deps -const async = require('async'); -const _ = require('lodash'); -const moment = require('moment'); -const paths = require('path'); +// deps +const async = require('async'); +const _ = require('lodash'); +const moment = require('moment'); +const paths = require('path'); exports.moduleInfo = { - name : 'File Area List', - desc : 'Lists contents of file an file area', - author : 'NuSkooler', + name : 'File Area List', + desc : 'Lists contents of file an file area', + author : 'NuSkooler', }; const FormIds = { - browse : 0, - details : 1, - detailsGeneral : 2, - detailsNfo : 3, - detailsFileList : 4, + browse : 0, + details : 1, + detailsGeneral : 2, + detailsNfo : 3, + detailsFileList : 4, }; const MciViewIds = { - browse : { - desc : 1, - navMenu : 2, + browse : { + desc : 1, + navMenu : 2, - customRangeStart : 10, // 10+ = customs + customRangeStart : 10, // 10+ = customs }, - details : { - navMenu : 1, - infoXyTop : 2, // %XY starting position for info area - infoXyBottom : 3, + details : { + navMenu : 1, + infoXyTop : 2, // %XY starting position for info area + infoXyBottom : 3, - customRangeStart : 10, // 10+ = customs + customRangeStart : 10, // 10+ = customs }, detailsGeneral : { - customRangeStart : 10, // 10+ = customs + customRangeStart : 10, // 10+ = customs }, detailsNfo : { - nfo : 1, + nfo : 1, - customRangeStart : 10, // 10+ = customs + customRangeStart : 10, // 10+ = customs }, detailsFileList : { - fileList : 1, + fileList : 1, - customRangeStart : 10, // 10+ = customs + customRangeStart : 10, // 10+ = customs }, }; @@ -74,12 +74,12 @@ exports.getModule = class FileAreaList extends MenuModule { constructor(options) { super(options); - this.filterCriteria = _.get(options, 'extraArgs.filterCriteria'); - this.fileList = _.get(options, 'extraArgs.fileList'); - this.lastFileNextExit = _.get(options, 'extraArgs.lastFileNextExit', true); + this.filterCriteria = _.get(options, 'extraArgs.filterCriteria'); + this.fileList = _.get(options, 'extraArgs.fileList'); + this.lastFileNextExit = _.get(options, 'extraArgs.lastFileNextExit', true); if(this.fileList) { - // we'll need to adjust position as well! + // we'll need to adjust position as well! this.fileListPosition = 0; } @@ -102,7 +102,7 @@ exports.getModule = class FileAreaList extends MenuModule { if(this.fileListPosition + 1 < this.fileList.length) { this.fileListPosition += 1; - return this.displayBrowsePage(true, cb); // true=clerarScreen + return this.displayBrowsePage(true, cb); // true=clerarScreen } if(this.lastFileNextExit) { @@ -115,7 +115,7 @@ exports.getModule = class FileAreaList extends MenuModule { if(this.fileListPosition > 0) { --this.fileListPosition; - return this.displayBrowsePage(true, cb); // true=clearScreen + return this.displayBrowsePage(true, cb); // true=clearScreen } return cb(null); @@ -132,7 +132,7 @@ exports.getModule = class FileAreaList extends MenuModule { } }); - return this.displayBrowsePage(true, cb); // true=clearScreen + return this.displayBrowsePage(true, cb); // true=clearScreen }, toggleQueue : (formData, extraArgs, cb) => { this.dlQueue.toggle(this.currentFileEntry); @@ -158,15 +158,15 @@ exports.getModule = class FileAreaList extends MenuModule { getSaveState() { return { - fileList : this.fileList, - fileListPosition : this.fileListPosition, + fileList : this.fileList, + fileListPosition : this.fileListPosition, }; } restoreSavedState(savedState) { if(savedState) { - this.fileList = savedState.fileList; - this.fileListPosition = savedState.fileListPosition; + this.fileList = savedState.fileList; + this.fileListPosition = savedState.fileListPosition; } } @@ -215,35 +215,35 @@ exports.getModule = class FileAreaList extends MenuModule { } populateCurrentEntryInfo(cb) { - const config = this.menuConfig.config; - const currEntry = this.currentFileEntry; + const config = this.menuConfig.config; + const currEntry = this.currentFileEntry; const uploadTimestampFormat = config.browseUploadTimestampFormat || config.uploadTimestampFormat || 'YYYY-MMM-DD'; - const area = FileArea.getFileAreaByTag(currEntry.areaTag); - const hashTagsSep = config.hashTagsSep || ', '; - const isQueuedIndicator = config.isQueuedIndicator || 'Y'; - const isNotQueuedIndicator = config.isNotQueuedIndicator || 'N'; + const area = FileArea.getFileAreaByTag(currEntry.areaTag); + const hashTagsSep = config.hashTagsSep || ', '; + const isQueuedIndicator = config.isQueuedIndicator || 'Y'; + const isNotQueuedIndicator = config.isNotQueuedIndicator || 'N'; const entryInfo = currEntry.entryInfo = { - fileId : currEntry.fileId, - areaTag : currEntry.areaTag, - areaName : _.get(area, 'name') || 'N/A', - areaDesc : _.get(area, 'desc') || 'N/A', - fileSha256 : currEntry.fileSha256, - fileName : currEntry.fileName, - desc : currEntry.desc || '', - descLong : currEntry.descLong || '', - userRating : currEntry.userRating, - uploadTimestamp : moment(currEntry.uploadTimestamp).format(uploadTimestampFormat), - hashTags : Array.from(currEntry.hashTags).join(hashTagsSep), - isQueued : this.dlQueue.isQueued(currEntry) ? isQueuedIndicator : isNotQueuedIndicator, - webDlLink : '', // :TODO: fetch web any existing web d/l link - webDlExpire : '', // :TODO: fetch web d/l link expire time + fileId : currEntry.fileId, + areaTag : currEntry.areaTag, + areaName : _.get(area, 'name') || 'N/A', + areaDesc : _.get(area, 'desc') || 'N/A', + fileSha256 : currEntry.fileSha256, + fileName : currEntry.fileName, + desc : currEntry.desc || '', + descLong : currEntry.descLong || '', + userRating : currEntry.userRating, + uploadTimestamp : moment(currEntry.uploadTimestamp).format(uploadTimestampFormat), + hashTags : Array.from(currEntry.hashTags).join(hashTagsSep), + isQueued : this.dlQueue.isQueued(currEntry) ? isQueuedIndicator : isNotQueuedIndicator, + webDlLink : '', // :TODO: fetch web any existing web d/l link + webDlExpire : '', // :TODO: fetch web d/l link expire time }; // - // We need the entry object to contain meta keys even if they are empty as - // consumers may very likely attempt to use them + // We need the entry object to contain meta keys even if they are empty as + // consumers may very likely attempt to use them // const metaValues = FileEntry.WellKnownMetaValues; metaValues.forEach(name => { @@ -258,7 +258,7 @@ exports.getModule = class FileAreaList extends MenuModule { let fileType = _.get(Config(), [ 'fileTypes', mimeType ] ); if(Array.isArray(fileType)) { - // further refine by extention + // further refine by extention fileType = fileType.find(ft => paths.extname(currEntry.fileName) === ft.ext); } desc = fileType && fileType.desc; @@ -268,31 +268,31 @@ exports.getModule = class FileAreaList extends MenuModule { entryInfo.archiveTypeDesc = 'N/A'; } - entryInfo.uploadByUsername = entryInfo.uploadByUsername || 'N/A'; // may be imported - entryInfo.hashTags = entryInfo.hashTags || '(none)'; + entryInfo.uploadByUsername = entryInfo.uploadByUsername || 'N/A'; // may be imported + entryInfo.hashTags = entryInfo.hashTags || '(none)'; - // create a rating string, e.g. "**---" - const userRatingTicked = config.userRatingTicked || '*'; - const userRatingUnticked = config.userRatingUnticked || ''; - entryInfo.userRating = ~~Math.round(entryInfo.userRating) || 0; // be safe! - entryInfo.userRatingString = userRatingTicked.repeat(entryInfo.userRating); + // create a rating string, e.g. "**---" + const userRatingTicked = config.userRatingTicked || '*'; + const userRatingUnticked = config.userRatingUnticked || ''; + entryInfo.userRating = ~~Math.round(entryInfo.userRating) || 0; // be safe! + entryInfo.userRatingString = userRatingTicked.repeat(entryInfo.userRating); if(entryInfo.userRating < 5) { entryInfo.userRatingString += userRatingUnticked.repeat( (5 - entryInfo.userRating) ); } FileAreaWeb.getExistingTempDownloadServeItem(this.client, this.currentFileEntry, (err, serveItem) => { if(err) { - entryInfo.webDlExpire = ''; + entryInfo.webDlExpire = ''; if(ErrNotEnabled === err.reasonCode) { - entryInfo.webDlExpire = config.webDlLinkNoWebserver || 'Web server is not enabled'; + entryInfo.webDlExpire = config.webDlLinkNoWebserver || 'Web server is not enabled'; } else { - entryInfo.webDlLink = config.webDlLinkNeedsGenerated || 'Not yet generated'; + entryInfo.webDlLink = config.webDlLinkNeedsGenerated || 'Not yet generated'; } } else { const webDlExpireTimeFormat = config.webDlExpireTimeFormat || 'YYYY-MMM-DD @ h:mm'; - entryInfo.webDlLink = ansi.vtxHyperlink(this.client, serveItem.url) + serveItem.url; - entryInfo.webDlExpire = moment(serveItem.expireTimestamp).format(webDlExpireTimeFormat); + entryInfo.webDlLink = ansi.vtxHyperlink(this.client, serveItem.url) + serveItem.url; + entryInfo.webDlExpire = moment(serveItem.expireTimestamp).format(webDlExpireTimeFormat); } return cb(null); @@ -304,8 +304,8 @@ exports.getModule = class FileAreaList extends MenuModule { } displayArtAndPrepViewController(name, options, cb) { - const self = this; - const config = this.menuConfig.config; + const self = this; + const config = this.menuConfig.config; async.waterfall( [ @@ -326,8 +326,8 @@ exports.getModule = class FileAreaList extends MenuModule { function prepeareViewController(artData, callback) { if(_.isUndefined(self.viewControllers[name])) { const vcOpts = { - client : self.client, - formId : FormIds[name], + client : self.client, + formId : FormIds[name], }; if(!_.isUndefined(options.noInput)) { @@ -339,8 +339,8 @@ exports.getModule = class FileAreaList extends MenuModule { if('details' === name) { try { self.detailsInfoArea = { - top : artData.mciMap.XY2.position, - bottom : artData.mciMap.XY3.position, + top : artData.mciMap.XY2.position, + bottom : artData.mciMap.XY3.position, }; } catch(e) { return callback(Errors.DoesNotExist('Missing XY2 and XY3 position indicators!')); @@ -348,9 +348,9 @@ exports.getModule = class FileAreaList extends MenuModule { } const loadOpts = { - callingMenu : self, - mciMap : artData.mciMap, - formId : FormIds[name], + callingMenu : self, + mciMap : artData.mciMap, + formId : FormIds[name], }; return vc.loadFromMenuConfig(loadOpts, callback); @@ -368,7 +368,7 @@ exports.getModule = class FileAreaList extends MenuModule { } displayBrowsePage(clearScreen, cb) { - const self = this; + const self = this; async.series( [ @@ -376,7 +376,7 @@ exports.getModule = class FileAreaList extends MenuModule { if(self.fileList) { return callback(null); } - return self.loadFileIds(false, callback); // false=do not force + return self.loadFileIds(false, callback); // false=do not force }, function checkEmptyResults(callback) { if(0 === self.fileList.length) { @@ -403,21 +403,21 @@ exports.getModule = class FileAreaList extends MenuModule { const descView = self.viewControllers.browse.getView(MciViewIds.browse.desc); if(descView) { // - // For descriptions we want to support as many color code systems - // as we can for coverage of what is found in the while (e.g. Renegade - // pipes, PCB @X##, etc.) + // For descriptions we want to support as many color code systems + // as we can for coverage of what is found in the while (e.g. Renegade + // pipes, PCB @X##, etc.) // - // MLTEV doesn't support all of this, so convert. If we produced ANSI - // esc sequences, we'll proceed with specialization, else just treat - // it as text. + // MLTEV doesn't support all of this, so convert. If we produced ANSI + // esc sequences, we'll proceed with specialization, else just treat + // it as text. // const desc = controlCodesToAnsi(self.currentFileEntry.desc); if(desc.length != self.currentFileEntry.desc.length || isAnsi(desc)) { descView.setAnsi( desc, { - prepped : false, - forceLineTerm : true + prepped : false, + forceLineTerm : true }, () => { return callback(null); @@ -447,7 +447,7 @@ exports.getModule = class FileAreaList extends MenuModule { } displayDetailsPage(cb) { - const self = this; + const self = this; async.series( [ @@ -467,9 +467,9 @@ exports.getModule = class FileAreaList extends MenuModule { navMenu.on('index update', index => { const sectionName = { - 0 : 'general', - 1 : 'nfo', - 2 : 'fileList', + 0 : 'general', + 1 : 'nfo', + 2 : 'fileList', }[index]; if(sectionName) { @@ -524,8 +524,8 @@ exports.getModule = class FileAreaList extends MenuModule { const webDlExpireTimeFormat = self.menuConfig.config.webDlExpireTimeFormat || 'YYYY-MMM-DD @ h:mm'; - self.currentFileEntry.entryInfo.webDlLink = ansi.vtxHyperlink(self.client, url) + url; - self.currentFileEntry.entryInfo.webDlExpire = expireTime.format(webDlExpireTimeFormat); + self.currentFileEntry.entryInfo.webDlLink = ansi.vtxHyperlink(self.client, url) + url; + self.currentFileEntry.entryInfo.webDlExpire = expireTime.format(webDlExpireTimeFormat); return callback(null); } @@ -547,8 +547,8 @@ exports.getModule = class FileAreaList extends MenuModule { } updateQueueIndicator() { - const isQueuedIndicator = this.menuConfig.config.isQueuedIndicator || 'Y'; - const isNotQueuedIndicator = this.menuConfig.config.isNotQueuedIndicator || 'N'; + const isQueuedIndicator = this.menuConfig.config.isQueuedIndicator || 'Y'; + const isNotQueuedIndicator = this.menuConfig.config.isNotQueuedIndicator || 'N'; this.currentFileEntry.entryInfo.isQueued = stringFormat( this.dlQueue.isQueued(this.currentFileEntry) ? @@ -565,7 +565,7 @@ exports.getModule = class FileAreaList extends MenuModule { } cacheArchiveEntries(cb) { - // check cache + // check cache if(this.currentFileEntry.archiveEntries) { return cb(null, 'cache'); } @@ -575,8 +575,8 @@ exports.getModule = class FileAreaList extends MenuModule { return cb(Errors.Invalid('Invalid area tag')); } - const filePath = this.currentFileEntry.filePath; - const archiveUtil = ArchiveUtil.getInstance(); + const filePath = this.currentFileEntry.filePath; + const archiveUtil = ArchiveUtil.getInstance(); archiveUtil.listEntries(filePath, this.currentFileEntry.entryInfo.archiveType, (err, entries) => { if(err) { @@ -594,14 +594,14 @@ exports.getModule = class FileAreaList extends MenuModule { if(this.currentFileEntry.entryInfo.archiveType) { this.cacheArchiveEntries( (err, cacheStatus) => { if(err) { - // :TODO: Handle me!!! - fileListView.setItems( [ 'Failed getting file listing' ] ); // :TODO: make this not suck + // :TODO: Handle me!!! + fileListView.setItems( [ 'Failed getting file listing' ] ); // :TODO: make this not suck return; } if('re-cached' === cacheStatus) { - const fileListEntryFormat = this.menuConfig.config.fileListEntryFormat || '{fileName} {fileSize}'; // :TODO: use byteSize here? - const focusFileListEntryFormat = this.menuConfig.config.focusFileListEntryFormat || fileListEntryFormat; + const fileListEntryFormat = this.menuConfig.config.fileListEntryFormat || '{fileName} {fileSize}'; // :TODO: use byteSize here? + const focusFileListEntryFormat = this.menuConfig.config.focusFileListEntryFormat || fileListEntryFormat; fileListView.setItems( this.currentFileEntry.archiveEntries.map( entry => stringFormat(fileListEntryFormat, entry) ) ); fileListView.setFocusItems( this.currentFileEntry.archiveEntries.map( entry => stringFormat(focusFileListEntryFormat, entry) ) ); @@ -615,8 +615,8 @@ exports.getModule = class FileAreaList extends MenuModule { } displayDetailsSection(sectionName, clearArea, cb) { - const self = this; - const name = `details${_.upperFirst(sectionName)}`; + const self = this; + const name = `details${_.upperFirst(sectionName)}`; async.series( [ @@ -637,8 +637,8 @@ exports.getModule = class FileAreaList extends MenuModule { if(clearArea) { self.client.term.rawWrite(ansi.reset()); - let pos = self.detailsInfoArea.top[0]; - const bottom = self.detailsInfoArea.bottom[0]; + let pos = self.detailsInfoArea.top[0]; + const bottom = self.detailsInfoArea.bottom[0]; while(pos++ <= bottom) { self.client.term.rawWrite(ansi.eraseLine() + ansi.down()); @@ -664,8 +664,8 @@ exports.getModule = class FileAreaList extends MenuModule { nfoView.setAnsi( self.currentFileEntry.entryInfo.descLong, { - prepped : false, - forceLineTerm : true, + prepped : false, + forceLineTerm : true, }, () => { return callback(null); @@ -701,7 +701,7 @@ exports.getModule = class FileAreaList extends MenuModule { loadFileIds(force, cb) { if(force || (_.isUndefined(this.fileList) || _.isUndefined(this.fileListPosition))) { - this.fileListPosition = 0; + this.fileListPosition = 0; const filterCriteria = Object.assign({}, this.filterCriteria); if(!filterCriteria.areaTag) { diff --git a/core/file_area_web.js b/core/file_area_web.js index 88c108f6..a1d12989 100644 --- a/core/file_area_web.js +++ b/core/file_area_web.js @@ -1,29 +1,29 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('./config.js').get; -const FileDb = require('./database.js').dbs.file; -const getISOTimestampString = require('./database.js').getISOTimestampString; -const FileEntry = require('./file_entry.js'); -const getServer = require('./listening_server.js').getServer; -const Errors = require('./enig_error.js').Errors; -const ErrNotEnabled = require('./enig_error.js').ErrorReasons.NotEnabled; -const StatLog = require('./stat_log.js'); -const User = require('./user.js'); -const Log = require('./logger.js').log; -const getConnectionByUserId = require('./client_connections.js').getConnectionByUserId; -const webServerPackageName = require('./servers/content/web.js').moduleInfo.packageName; -const Events = require('./events.js'); +// ENiGMA½ +const Config = require('./config.js').get; +const FileDb = require('./database.js').dbs.file; +const getISOTimestampString = require('./database.js').getISOTimestampString; +const FileEntry = require('./file_entry.js'); +const getServer = require('./listening_server.js').getServer; +const Errors = require('./enig_error.js').Errors; +const ErrNotEnabled = require('./enig_error.js').ErrorReasons.NotEnabled; +const StatLog = require('./stat_log.js'); +const User = require('./user.js'); +const Log = require('./logger.js').log; +const getConnectionByUserId = require('./client_connections.js').getConnectionByUserId; +const webServerPackageName = require('./servers/content/web.js').moduleInfo.packageName; +const Events = require('./events.js'); -// deps -const hashids = require('hashids'); -const moment = require('moment'); -const paths = require('path'); -const async = require('async'); -const fs = require('graceful-fs'); -const mimeTypes = require('mime-types'); -const yazl = require('yazl'); +// deps +const hashids = require('hashids'); +const moment = require('moment'); +const paths = require('path'); +const async = require('async'); +const fs = require('graceful-fs'); +const mimeTypes = require('mime-types'); +const yazl = require('yazl'); function notEnabledError() { return Errors.General('Web server is not enabled', ErrNotEnabled); @@ -31,8 +31,8 @@ function notEnabledError() { class FileAreaWebAccess { constructor() { - this.hashids = new hashids(Config().general.boardName); - this.expireTimers = {}; // hashId->timer + this.hashids = new hashids(Config().general.boardName); + this.expireTimers = {}; // hashId->timer } startup(cb) { @@ -51,13 +51,13 @@ class FileAreaWebAccess { if(self.isEnabled()) { const routeAdded = self.webServer.instance.addRoute({ - method : 'GET', - path : Config().fileBase.web.routePath, - handler : self.routeWebRequest.bind(self), + method : 'GET', + path : Config().fileBase.web.routePath, + handler : self.routeWebRequest.bind(self), }); return callback(routeAdded ? null : Errors.General('Failed adding route')); } else { - return callback(null); // not enabled, but no error + return callback(null); // not enabled, but no error } } ], @@ -77,18 +77,18 @@ class FileAreaWebAccess { static getHashIdTypes() { return { - SingleFile : 0, - BatchArchive : 1, + SingleFile : 0, + BatchArchive : 1, }; } load(cb) { // - // Load entries, register expiration timers + // Load entries, register expiration timers // FileDb.each( `SELECT hash_id, expire_timestamp - FROM file_web_serve;`, + FROM file_web_serve;`, (err, row) => { if(row) { this.scheduleExpire(row.hash_id, moment(row.expire_timestamp)); @@ -102,11 +102,11 @@ class FileAreaWebAccess { removeEntry(hashId) { // - // Delete record from DB, and our timer + // Delete record from DB, and our timer // FileDb.run( `DELETE FROM file_web_serve - WHERE hash_id = ?;`, + WHERE hash_id = ?;`, [ hashId ] ); @@ -115,7 +115,7 @@ class FileAreaWebAccess { scheduleExpire(hashId, expireTime) { - // remove any previous entry for this hashId + // remove any previous entry for this hashId const previous = this.expireTimers[hashId]; if(previous) { clearTimeout(previous); @@ -138,8 +138,8 @@ class FileAreaWebAccess { loadServedHashId(hashId, cb) { FileDb.get( `SELECT expire_timestamp FROM - file_web_serve - WHERE hash_id = ?`, + file_web_serve + WHERE hash_id = ?`, [ hashId ], (err, result) => { if(err || !result) { @@ -148,16 +148,16 @@ class FileAreaWebAccess { const decoded = this.hashids.decode(hashId); - // decode() should provide an array of [ userId, hashIdType, id, ... ] + // decode() should provide an array of [ userId, hashIdType, id, ... ] if(!Array.isArray(decoded) || decoded.length < 3) { return cb(Errors.Invalid('Invalid or unknown hash ID')); } const servedItem = { - hashId : hashId, - userId : decoded[0], - hashIdType : decoded[1], - expireTimestamp : moment(result.expire_timestamp), + hashId : hashId, + userId : decoded[0], + hashIdType : decoded[1], + expireTimestamp : moment(result.expire_timestamp), }; if(FileAreaWebAccess.getHashIdTypes().SingleFile === servedItem.hashIdType) { @@ -209,10 +209,10 @@ class FileAreaWebAccess { } _addOrUpdateHashIdRecord(dbOrTrans, hashId, expireTime, cb) { - // add/update rec with hash id and (latest) timestamp + // add/update rec with hash id and (latest) timestamp dbOrTrans.run( `REPLACE INTO file_web_serve (hash_id, expire_timestamp) - VALUES (?, ?);`, + VALUES (?, ?);`, [ hashId, getISOTimestampString(expireTime) ], err => { if(err) { @@ -231,9 +231,9 @@ class FileAreaWebAccess { return cb(notEnabledError()); } - const hashId = this.getSingleFileHashId(client, fileEntry); - const url = this.buildSingleFileTempDownloadLink(client, fileEntry, hashId); - options.expireTime = options.expireTime || moment().add(2, 'days'); + const hashId = this.getSingleFileHashId(client, fileEntry); + const url = this.buildSingleFileTempDownloadLink(client, fileEntry, hashId); + options.expireTime = options.expireTime || moment().add(2, 'days'); this._addOrUpdateHashIdRecord(FileDb, hashId, options.expireTime, err => { return cb(err, url); @@ -245,10 +245,10 @@ class FileAreaWebAccess { return cb(notEnabledError()); } - const batchId = moment().utc().unix(); - const hashId = this.getBatchArchiveHashId(client, batchId); - const url = this.buildBatchArchiveTempDownloadLink(client, hashId); - options.expireTime = options.expireTime || moment().add(2, 'days'); + const batchId = moment().utc().unix(); + const hashId = this.getBatchArchiveHashId(client, batchId); + const url = this.buildBatchArchiveTempDownloadLink(client, hashId); + options.expireTime = options.expireTime || moment().add(2, 'days'); FileDb.beginTransaction( (err, trans) => { if(err) { @@ -265,7 +265,7 @@ class FileAreaWebAccess { async.eachSeries(fileEntries, (entry, nextEntry) => { trans.run( `INSERT INTO file_web_serve_batch (hash_id, file_id) - VALUES (?, ?);`, + VALUES (?, ?);`, [ hashId, entry.fileId ], err => { return nextEntry(err); @@ -332,19 +332,19 @@ class FileAreaWebAccess { } resp.on('close', () => { - // connection closed *before* the response was fully sent - // :TODO: Log and such + // connection closed *before* the response was fully sent + // :TODO: Log and such }); resp.on('finish', () => { - // transfer completed fully + // transfer completed fully this.updateDownloadStatsForUserIdAndSystem(servedItem.userId, stats.size, [ fileEntry ]); }); const headers = { - 'Content-Type' : mimeTypes.contentType(filePath) || mimeTypes.contentType('.bin'), - 'Content-Length' : stats.size, - 'Content-Disposition' : `attachment; filename="${fileEntry.fileName}"`, + 'Content-Type' : mimeTypes.contentType(filePath) || mimeTypes.contentType('.bin'), + 'Content-Length' : stats.size, + 'Content-Disposition' : `attachment; filename="${fileEntry.fileName}"`, }; const readStream = fs.createReadStream(filePath); @@ -358,10 +358,10 @@ class FileAreaWebAccess { Log.debug( { servedItem : servedItem }, 'Batch file web request'); // - // We are going to build an on-the-fly zip file stream of 1:n - // files in the batch. + // We are going to build an on-the-fly zip file stream of 1:n + // files in the batch. // - // First, collect all file IDs + // First, collect all file IDs // const self = this; @@ -370,8 +370,8 @@ class FileAreaWebAccess { function fetchFileIds(callback) { FileDb.all( `SELECT file_id - FROM file_web_serve_batch - WHERE hash_id = ?;`, + FROM file_web_serve_batch + WHERE hash_id = ?;`, [ servedItem.hashId ], (err, fileIdRows) => { if(err || !Array.isArray(fileIdRows) || 0 === fileIdRows.length) { @@ -408,10 +408,10 @@ class FileAreaWebAccess { filePaths.forEach(fp => { zipFile.addFile( - fp, // path to physical file - paths.basename(fp), // filename/path *stored in archive* + fp, // path to physical file + paths.basename(fp), // filename/path *stored in archive* { - compress : false, // :TODO: do this smartly - if ext is in set = false, else true via isArchive() or such... mimeDB has this for us. + compress : false, // :TODO: do this smartly - if ext is in set = false, else true via isArchive() or such... mimeDB has this for us. } ); }); @@ -422,21 +422,21 @@ class FileAreaWebAccess { } resp.on('close', () => { - // connection closed *before* the response was fully sent - // :TODO: Log and such + // connection closed *before* the response was fully sent + // :TODO: Log and such }); resp.on('finish', () => { - // transfer completed fully + // transfer completed fully self.updateDownloadStatsForUserIdAndSystem(servedItem.userId, finalZipSize, fileEntries); }); const batchFileName = `batch_${servedItem.hashId}.zip`; const headers = { - 'Content-Type' : mimeTypes.contentType(batchFileName) || mimeTypes.contentType('.bin'), - 'Content-Length' : finalZipSize, - 'Content-Disposition' : `attachment; filename="${batchFileName}"`, + 'Content-Type' : mimeTypes.contentType(batchFileName) || mimeTypes.contentType('.bin'), + 'Content-Length' : finalZipSize, + 'Content-Disposition' : `attachment; filename="${batchFileName}"`, }; resp.writeHead(200, headers); @@ -446,11 +446,11 @@ class FileAreaWebAccess { ], err => { if(err) { - // :TODO: Log me! + // :TODO: Log me! return this.fileNotFound(resp); } - // ...otherwise, we would have called resp() already. + // ...otherwise, we would have called resp() already. } ); } @@ -464,7 +464,7 @@ class FileAreaWebAccess { return callback(null, clientForUserId.user); } - // not online now - look 'em up + // not online now - look 'em up User.getUser(userId, (err, assocUser) => { return callback(err, assocUser); }); @@ -481,8 +481,8 @@ class FileAreaWebAccess { Events.emit( Events.getSystemEvents().UserDownload, { - user : user, - files : fileEntries, + user : user, + files : fileEntries, } ); return callback(null); diff --git a/core/file_base_area.js b/core/file_base_area.js index 511e11af..7d7ce6fb 100644 --- a/core/file_base_area.js +++ b/core/file_base_area.js @@ -1,57 +1,57 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('./config.js').get; -const Errors = require('./enig_error.js').Errors; -const sortAreasOrConfs = require('./conf_area_util.js').sortAreasOrConfs; -const FileEntry = require('./file_entry.js'); -const FileDb = require('./database.js').dbs.file; -const ArchiveUtil = require('./archive_util.js'); -const CRC32 = require('./crc.js').CRC32; -const Log = require('./logger.js').log; -const resolveMimeType = require('./mime_util.js').resolveMimeType; -const stringFormat = require('./string_format.js'); -const wordWrapText = require('./word_wrap.js').wordWrapText; -const StatLog = require('./stat_log.js'); +// ENiGMA½ +const Config = require('./config.js').get; +const Errors = require('./enig_error.js').Errors; +const sortAreasOrConfs = require('./conf_area_util.js').sortAreasOrConfs; +const FileEntry = require('./file_entry.js'); +const FileDb = require('./database.js').dbs.file; +const ArchiveUtil = require('./archive_util.js'); +const CRC32 = require('./crc.js').CRC32; +const Log = require('./logger.js').log; +const resolveMimeType = require('./mime_util.js').resolveMimeType; +const stringFormat = require('./string_format.js'); +const wordWrapText = require('./word_wrap.js').wordWrapText; +const StatLog = require('./stat_log.js'); -// deps -const _ = require('lodash'); -const async = require('async'); -const fs = require('graceful-fs'); -const crypto = require('crypto'); -const paths = require('path'); -const temptmp = require('temptmp').createTrackedSession('file_area'); -const iconv = require('iconv-lite'); -const execFile = require('child_process').execFile; -const moment = require('moment'); +// deps +const _ = require('lodash'); +const async = require('async'); +const fs = require('graceful-fs'); +const crypto = require('crypto'); +const paths = require('path'); +const temptmp = require('temptmp').createTrackedSession('file_area'); +const iconv = require('iconv-lite'); +const execFile = require('child_process').execFile; +const moment = require('moment'); -exports.startup = startup; -exports.isInternalArea = isInternalArea; -exports.getAvailableFileAreas = getAvailableFileAreas; -exports.getAvailableFileAreaTags = getAvailableFileAreaTags; -exports.getSortedAvailableFileAreas = getSortedAvailableFileAreas; -exports.isValidStorageTag = isValidStorageTag; -exports.getAreaStorageDirectoryByTag = getAreaStorageDirectoryByTag; -exports.getAreaDefaultStorageDirectory = getAreaDefaultStorageDirectory; -exports.getAreaStorageLocations = getAreaStorageLocations; -exports.getDefaultFileAreaTag = getDefaultFileAreaTag; -exports.getFileAreaByTag = getFileAreaByTag; -exports.getFileEntryPath = getFileEntryPath; -exports.changeFileAreaWithOptions = changeFileAreaWithOptions; -exports.scanFile = scanFile; -exports.scanFileAreaForChanges = scanFileAreaForChanges; -exports.getDescFromFileName = getDescFromFileName; -exports.getAreaStats = getAreaStats; -exports.cleanUpTempSessionItems = cleanUpTempSessionItems; +exports.startup = startup; +exports.isInternalArea = isInternalArea; +exports.getAvailableFileAreas = getAvailableFileAreas; +exports.getAvailableFileAreaTags = getAvailableFileAreaTags; +exports.getSortedAvailableFileAreas = getSortedAvailableFileAreas; +exports.isValidStorageTag = isValidStorageTag; +exports.getAreaStorageDirectoryByTag = getAreaStorageDirectoryByTag; +exports.getAreaDefaultStorageDirectory = getAreaDefaultStorageDirectory; +exports.getAreaStorageLocations = getAreaStorageLocations; +exports.getDefaultFileAreaTag = getDefaultFileAreaTag; +exports.getFileAreaByTag = getFileAreaByTag; +exports.getFileEntryPath = getFileEntryPath; +exports.changeFileAreaWithOptions = changeFileAreaWithOptions; +exports.scanFile = scanFile; +exports.scanFileAreaForChanges = scanFileAreaForChanges; +exports.getDescFromFileName = getDescFromFileName; +exports.getAreaStats = getAreaStats; +exports.cleanUpTempSessionItems = cleanUpTempSessionItems; -// for scheduler: -exports.updateAreaStatsScheduledEvent = updateAreaStatsScheduledEvent; +// for scheduler: +exports.updateAreaStatsScheduledEvent = updateAreaStatsScheduledEvent; -const WellKnownAreaTags = exports.WellKnownAreaTags = { - Invalid : '', - MessageAreaAttach : 'system_message_attachment', - TempDownloads : 'system_temporary_download', +const WellKnownAreaTags = exports.WellKnownAreaTags = { + Invalid : '', + MessageAreaAttach : 'system_message_attachment', + TempDownloads : 'system_temporary_download', }; function startup(cb) { @@ -65,7 +65,7 @@ function isInternalArea(areaTag) { function getAvailableFileAreas(client, options) { options = options || { }; - // perform ACS check per conf & omit internal if desired + // perform ACS check per conf & omit internal if desired const allAreas = _.map(Config().fileBase.areas, (areaInfo, areaTag) => Object.assign(areaInfo, { areaTag : areaTag } )); return _.omitBy(allAreas, areaInfo => { @@ -74,11 +74,11 @@ function getAvailableFileAreas(client, options) { } if(options.skipAcsCheck) { - return false; // no ACS checks (below) + return false; // no ACS checks (below) } if(options.writeAcs && !client.acs.hasFileAreaWrite(areaInfo)) { - return true; // omit + return true; // omit } return !client.acs.hasFileAreaRead(areaInfo); @@ -116,8 +116,8 @@ function getDefaultFileAreaTag(client, disableAcsCheck) { function getFileAreaByTag(areaTag) { const areaInfo = Config().fileBase.areas[areaTag]; if(areaInfo) { - areaInfo.areaTag = areaTag; // convienence! - areaInfo.storage = getAreaStorageLocations(areaInfo); + areaInfo.areaTag = areaTag; // convienence! + areaInfo.storage = getAreaStorageLocations(areaInfo); return areaInfo; } } @@ -183,8 +183,8 @@ function getAreaStorageLocations(areaInfo) { return _.compact(storageTags.map(storageTag => { if(avail[storageTag]) { return { - storageTag : storageTag, - dir : getAreaStorageDirectoryByTag(storageTag), + storageTag : storageTag, + dir : getAreaStorageDirectoryByTag(storageTag), }; } })); @@ -202,14 +202,14 @@ function getExistingFileEntriesBySha256(sha256, cb) { FileDb.each( `SELECT file_id, area_tag - FROM file - WHERE file_sha256=?;`, + FROM file + WHERE file_sha256=?;`, [ sha256 ], (err, fileRow) => { if(fileRow) { entries.push({ - fileId : fileRow.file_id, - areaTag : fileRow.area_tag, + fileId : fileRow.file_id, + areaTag : fileRow.area_tag, }); } }, @@ -219,10 +219,10 @@ function getExistingFileEntriesBySha256(sha256, cb) { ); } -// :TODO: This is bascially sliceAtEOF() from art.js .... DRY! +// :TODO: This is bascially sliceAtEOF() from art.js .... DRY! function sliceAtSauceMarker(data) { - let eof = data.length; - const stopPos = Math.max(data.length - (256), 0); // 256 = 2 * sizeof(SAUCE) + let eof = data.length; + const stopPos = Math.max(data.length - (256), 0); // 256 = 2 * sizeof(SAUCE) for(let i = eof - 1; i > stopPos; i--) { if(0x1a === data[i]) { @@ -234,8 +234,8 @@ function sliceAtSauceMarker(data) { } function attemptSetEstimatedReleaseDate(fileEntry) { - // :TODO: yearEstPatterns RegExp's should be cached - we can do this @ Config (re)load time - const patterns = Config().fileBase.yearEstPatterns.map( p => new RegExp(p, 'gmi')); + // :TODO: yearEstPatterns RegExp's should be cached - we can do this @ Config (re)load time + const patterns = Config().fileBase.yearEstPatterns.map( p => new RegExp(p, 'gmi')); function getMatch(input) { if(input) { @@ -250,10 +250,10 @@ function attemptSetEstimatedReleaseDate(fileEntry) { } // - // We attempt detection in short -> long order + // We attempt detection in short -> long order // - // Throw out anything that is current_year + 2 (we give some leway) - // with the assumption that must be wrong. + // Throw out anything that is current_year + 2 (we give some leway) + // with the assumption that must be wrong. // const maxYear = moment().add(2, 'year').year(); const match = getMatch(fileEntry.desc) || getMatch(fileEntry.descLong); @@ -279,7 +279,7 @@ function attemptSetEstimatedReleaseDate(fileEntry) { } } -// a simple log proxy for when we call from oputil.js +// a simple log proxy for when we call from oputil.js function logDebug(obj, msg) { if(Log) { Log.debug(obj, msg); @@ -290,8 +290,8 @@ function extractAndProcessDescFiles(fileEntry, filePath, archiveEntries, cb) { async.waterfall( [ function extractDescFiles(callback) { - // :TODO: would be nice if these RegExp's were cached - // :TODO: this is long winded... + // :TODO: would be nice if these RegExp's were cached + // :TODO: this is long winded... const config = Config(); const extractList = []; @@ -327,8 +327,8 @@ function extractAndProcessDescFiles(fileEntry, filePath, archiveEntries, cb) { } const descFiles = { - desc : shortDescFile ? paths.join(tempDir, shortDescFile.fileName) : null, - descLong : longDescFile ? paths.join(tempDir, longDescFile.fileName) : null, + desc : shortDescFile ? paths.join(tempDir, shortDescFile.fileName) : null, + descLong : longDescFile ? paths.join(tempDir, longDescFile.fileName) : null, }; return callback(null, descFiles); @@ -348,7 +348,7 @@ function extractAndProcessDescFiles(fileEntry, filePath, archiveEntries, cb) { return next(null); } - // skip entries that are too large + // skip entries that are too large const maxFileSizeKey = `max${_.upperFirst(descType)}FileByteSize`; if(config.fileBase[maxFileSizeKey] && stats.size > config.fileBase[maxFileSizeKey]) { logDebug( { byteSize : stats.size, maxByteSize : config.fileBase[maxFileSizeKey] }, `Skipping "${descType}"; Too large` ); @@ -361,18 +361,18 @@ function extractAndProcessDescFiles(fileEntry, filePath, archiveEntries, cb) { } // - // Assume FILE_ID.DIZ, NFO files, etc. are CP437. + // Assume FILE_ID.DIZ, NFO files, etc. are CP437. // - // :TODO: This isn't really always the case - how to handle this? We could do a quick detection... - fileEntry[descType] = iconv.decode(sliceAtSauceMarker(data, 0x1a), 'cp437'); - fileEntry[`${descType}Src`] = 'descFile'; + // :TODO: This isn't really always the case - how to handle this? We could do a quick detection... + fileEntry[descType] = iconv.decode(sliceAtSauceMarker(data, 0x1a), 'cp437'); + fileEntry[`${descType}Src`] = 'descFile'; return next(null); }); }); }, () => { - // cleanup but don't wait + // cleanup but don't wait temptmp.cleanup( paths => { - // note: don't use client logger here - may not be avail + // note: don't use client logger here - may not be avail logDebug( { paths : paths, sessionId : temptmp.sessionId }, 'Cleaned up temporary files' ); }); return callback(null); @@ -390,7 +390,7 @@ function extractAndProcessSingleArchiveEntry(fileEntry, filePath, archiveEntries async.waterfall( [ function extractToTemp(callback) { - // :TODO: we may want to skip this if the compressed file is too large... + // :TODO: we may want to skip this if the compressed file is too large... temptmp.mkdir( { prefix : 'enigextract-' }, (err, tempDir) => { if(err) { return callback(err); @@ -398,7 +398,7 @@ function extractAndProcessSingleArchiveEntry(fileEntry, filePath, archiveEntries const archiveUtil = ArchiveUtil.getInstance(); - // ensure we only extract one - there should only be one anyway -- we also just need the fileName + // ensure we only extract one - there should only be one anyway -- we also just need the fileName const extractList = archiveEntries.slice(0, 1).map(entry => entry.fileName); archiveUtil.extractTo(filePath, tempDir, fileEntry.meta.archive_type, extractList, err => { @@ -413,8 +413,8 @@ function extractAndProcessSingleArchiveEntry(fileEntry, filePath, archiveEntries function processSingleExtractedFile(extractedFile, callback) { populateFileEntryInfoFromFile(fileEntry, extractedFile, err => { if(!fileEntry.desc) { - fileEntry.desc = getDescFromFileName(filePath); - fileEntry.descSrc = 'fileName'; + fileEntry.desc = getDescFromFileName(filePath); + fileEntry.descSrc = 'fileName'; } return callback(err); }); @@ -427,8 +427,8 @@ function extractAndProcessSingleArchiveEntry(fileEntry, filePath, archiveEntries } function populateFileEntryWithArchive(fileEntry, filePath, stepInfo, iterator, cb) { - const archiveUtil = ArchiveUtil.getInstance(); - const archiveType = fileEntry.meta.archive_type; // we set this previous to populateFileEntryWithArchive() + const archiveUtil = ArchiveUtil.getInstance(); + const archiveType = fileEntry.meta.archive_type; // we set this previous to populateFileEntryWithArchive() async.waterfall( [ @@ -449,7 +449,7 @@ function populateFileEntryWithArchive(fileEntry, filePath, stepInfo, iterator, c } iterator(iterErr => { - return callback( iterErr, entries || [] ); // ignore original |err| here + return callback( iterErr, entries || [] ); // ignore original |err| here }); }); }); @@ -462,12 +462,12 @@ function populateFileEntryWithArchive(fileEntry, filePath, stepInfo, iterator, c }, function extractDescFromArchive(entries, callback) { // - // If we have a -single- entry in the archive, extract that file - // and try retrieving info in the non-archive manor. This should - // work for things like zipped up .pdf files. + // If we have a -single- entry in the archive, extract that file + // and try retrieving info in the non-archive manor. This should + // work for things like zipped up .pdf files. // - // Otherwise, try to find particular desc files such as FILE_ID.DIZ - // and README.1ST + // Otherwise, try to find particular desc files such as FILE_ID.DIZ + // and README.1ST // const archDescHandler = (1 === entries.length) ? extractAndProcessSingleArchiveEntry : extractAndProcessDescFiles; archDescHandler(fileEntry, filePath, entries, err => { @@ -494,7 +494,7 @@ function getInfoExtractUtilForDesc(mimeType, filePath, descType) { let fileType = _.get(config, [ 'fileTypes', mimeType ] ); if(Array.isArray(fileType)) { - // further refine by extention + // further refine by extention fileType = fileType.find(ft => paths.extname(filePath) === ft.ext); } @@ -542,17 +542,17 @@ function populateFileEntryInfoFromFile(fileEntry, filePath, cb) { const key = 'short' === descType ? 'desc' : 'descLong'; if('desc' === key) { // - // Word wrap short descriptions to FILE_ID.DIZ spec + // Word wrap short descriptions to FILE_ID.DIZ spec // - // "...no more than 45 characters long" + // "...no more than 45 characters long" // - // See http://www.textfiles.com/computers/fileid.txt + // See http://www.textfiles.com/computers/fileid.txt // stdout = (wordWrapText( stdout, { width : 45 } ).wrapped || []).join('\n'); } - fileEntry[key] = stdout; - fileEntry[`${key}Src`] = 'infoTool'; + fileEntry[key] = stdout; + fileEntry[`${key}Src`] = 'infoTool'; } } @@ -574,8 +574,8 @@ function populateFileEntryNonArchive(fileEntry, filePath, stepInfo, iterator, cb function getDescriptions(callback) { populateFileEntryInfoFromFile(fileEntry, filePath, err => { if(!fileEntry.desc) { - fileEntry.desc = getDescFromFileName(filePath); - fileEntry.descSrc = 'fileName'; + fileEntry.desc = getDescFromFileName(filePath); + fileEntry.descSrc = 'fileName'; } return callback(err); }); @@ -592,7 +592,7 @@ function populateFileEntryNonArchive(fileEntry, filePath, stepInfo, iterator, cb } function addNewFileEntry(fileEntry, filePath, cb) { - // :TODO: Use detectTypeWithBuf() once avail - we *just* read some file data + // :TODO: Use detectTypeWithBuf() once avail - we *just* read some file data async.series( [ @@ -611,26 +611,26 @@ const HASH_NAMES = [ 'sha1', 'sha256', 'md5', 'crc32' ]; function scanFile(filePath, options, iterator, cb) { if(3 === arguments.length && _.isFunction(iterator)) { - cb = iterator; - iterator = null; + cb = iterator; + iterator = null; } else if(2 === arguments.length && _.isFunction(options)) { - cb = options; - iterator = null; - options = {}; + cb = options; + iterator = null; + options = {}; } const fileEntry = new FileEntry({ - areaTag : options.areaTag, - meta : options.meta, - hashTags : options.hashTags, // Set() or Array - fileName : paths.basename(filePath), - storageTag : options.storageTag, - fileSha256 : options.sha256, // caller may know this already + areaTag : options.areaTag, + meta : options.meta, + hashTags : options.hashTags, // Set() or Array + fileName : paths.basename(filePath), + storageTag : options.storageTag, + fileSha256 : options.sha256, // caller may know this already }); const stepInfo = { - filePath : filePath, - fileName : paths.basename(filePath), + filePath : filePath, + fileName : paths.basename(filePath), }; const callIter = (next) => { @@ -638,8 +638,8 @@ function scanFile(filePath, options, iterator, cb) { }; const readErrorCallIter = (origError, next) => { - stepInfo.step = 'read_error'; - stepInfo.error = origError.message; + stepInfo.step = 'read_error'; + stepInfo.error = origError.message; callIter( () => { return next(origError); @@ -648,7 +648,7 @@ function scanFile(filePath, options, iterator, cb) { let lastCalcHashPercent; - // don't re-calc hashes for any we already have in |options| + // don't re-calc hashes for any we already have in |options| const hashesToCalc = HASH_NAMES.filter(hn => { if('sha256' === hn && fileEntry.fileSha256) { return false; @@ -669,8 +669,8 @@ function scanFile(filePath, options, iterator, cb) { return readErrorCallIter(err, callback); } - stepInfo.step = 'start'; - stepInfo.byteSize = fileEntry.meta.byte_size = stats.size; + stepInfo.step = 'start'; + stepInfo.byteSize = fileEntry.meta.byte_size = stats.size; return callIter(callback); }); @@ -694,9 +694,9 @@ function scanFile(filePath, options, iterator, cb) { }; // - // Note that we are not using fs.createReadStream() here: - // While convenient, it is quite a bit slower -- which adds - // up to many seconds in time for larger files. + // Note that we are not using fs.createReadStream() here: + // While convenient, it is quite a bit slower -- which adds + // up to many seconds in time for larger files. // const chunkSize = 1024 * 64; const buffer = new Buffer(chunkSize); @@ -714,7 +714,7 @@ function scanFile(filePath, options, iterator, cb) { } if(0 === bytesRead) { - // done - finalize + // done - finalize fileEntry.meta.byte_size = stepInfo.bytesProcessed; for(let i = 0; i < hashesToCalc.length; ++i) { @@ -733,11 +733,11 @@ function scanFile(filePath, options, iterator, cb) { return callIter(callback); } - stepInfo.bytesProcessed += bytesRead; - stepInfo.calcHashPercent = Math.round(((stepInfo.bytesProcessed / stepInfo.byteSize) * 100)); + stepInfo.bytesProcessed += bytesRead; + stepInfo.calcHashPercent = Math.round(((stepInfo.bytesProcessed / stepInfo.byteSize) * 100)); // - // Only send 'hash_update' step update if we have a noticable percentage change in progress + // Only send 'hash_update' step update if we have a noticable percentage change in progress // const data = bytesRead < chunkSize ? buffer.slice(0, bytesRead) : buffer; if(!iterator || stepInfo.calcHashPercent === lastCalcHashPercent) { @@ -745,7 +745,7 @@ function scanFile(filePath, options, iterator, cb) { return nextChunk(); } else { lastCalcHashPercent = stepInfo.calcHashPercent; - stepInfo.step = 'hash_update'; + stepInfo.step = 'hash_update'; callIter(err => { if(err) { @@ -767,7 +767,7 @@ function scanFile(filePath, options, iterator, cb) { archiveUtil.detectType(filePath, (err, archiveType) => { if(archiveType) { - // save this off + // save this off fileEntry.meta.archive_type = archiveType; populateFileEntryWithArchive(fileEntry, filePath, stepInfo, callIter, err => { @@ -776,7 +776,7 @@ function scanFile(filePath, options, iterator, cb) { if(err) { logDebug( { error : err.message }, 'Non-archive file entry population failed'); } - return callback(null); // ignore err + return callback(null); // ignore err }); } else { return callback(null); @@ -787,7 +787,7 @@ function scanFile(filePath, options, iterator, cb) { if(err) { logDebug( { error : err.message }, 'Non-archive file entry population failed'); } - return callback(null); // ignore err + return callback(null); // ignore err }); } }); @@ -816,12 +816,12 @@ function scanFile(filePath, options, iterator, cb) { function scanFileAreaForChanges(areaInfo, options, iterator, cb) { if(3 === arguments.length && _.isFunction(iterator)) { - cb = iterator; - iterator = null; + cb = iterator; + iterator = null; } else if(2 === arguments.length && _.isFunction(options)) { - cb = options; - iterator = null; - options = {}; + cb = options; + iterator = null; + options = {}; } const storageLocations = getAreaStorageLocations(areaInfo); @@ -842,8 +842,8 @@ function scanFileAreaForChanges(areaInfo, options, iterator, cb) { fs.stat(fullPath, (err, stats) => { if(err) { - // :TODO: Log me! - return nextFile(null); // always try next file + // :TODO: Log me! + return nextFile(null); // always try next file } if(!stats.isFile()) { @@ -853,18 +853,18 @@ function scanFileAreaForChanges(areaInfo, options, iterator, cb) { scanFile( fullPath, { - areaTag : areaInfo.areaTag, - storageTag : storageLoc.storageTag + areaTag : areaInfo.areaTag, + storageTag : storageLoc.storageTag }, iterator, (err, fileEntry, dupeEntries) => { if(err) { - // :TODO: Log me!!! - return nextFile(null); // try next anyway + // :TODO: Log me!!! + return nextFile(null); // try next anyway } if(dupeEntries.length > 0) { - // :TODO: Handle duplidates -- what to do here??? + // :TODO: Handle duplidates -- what to do here??? } else { if(Array.isArray(options.tags)) { options.tags.forEach(tag => { @@ -872,7 +872,7 @@ function scanFileAreaForChanges(areaInfo, options, iterator, cb) { }); } addNewFileEntry(fileEntry, fullPath, err => { - // pass along error; we failed to insert a record in our DB or something else bad + // pass along error; we failed to insert a record in our DB or something else bad return nextFile(err); }); } @@ -885,7 +885,7 @@ function scanFileAreaForChanges(areaInfo, options, iterator, cb) { }); }, function scanDbEntries(callback) { - // :TODO: Look @ db entries for area that were *not* processed above + // :TODO: Look @ db entries for area that were *not* processed above return callback(null); } ], @@ -900,7 +900,7 @@ function scanFileAreaForChanges(areaInfo, options, iterator, cb) { } function getDescFromFileName(fileName) { - // :TODO: this method could use some more logic to really be nice. + // :TODO: this method could use some more logic to really be nice. const ext = paths.extname(fileName); const name = paths.basename(fileName, ext); @@ -908,26 +908,26 @@ function getDescFromFileName(fileName) { } // -// Return an object of stats about an area(s) +// Return an object of stats about an area(s) // -// { +// { // -// totalFiles : , -// totalBytes : , -// areas : { -// : { -// files : , -// bytes : -// } -// } -// } +// totalFiles : , +// totalBytes : , +// areas : { +// : { +// files : , +// bytes : +// } +// } +// } // function getAreaStats(cb) { FileDb.all( `SELECT DISTINCT f.area_tag, COUNT(f.file_id) AS total_files, SUM(m.meta_value) AS total_byte_size - FROM file f, file_meta m - WHERE f.file_id = m.file_id AND m.meta_name='byte_size' - GROUP BY f.area_tag;`, + FROM file f, file_meta m + WHERE f.file_id = m.file_id AND m.meta_name='byte_size' + GROUP BY f.area_tag;`, (err, statRows) => { if(err) { return cb(err); @@ -946,8 +946,8 @@ function getAreaStats(cb) { stats.areas = stats.areas || {}; stats.areas[v.area_tag] = { - files : v.total_files, - bytes : v.total_byte_size, + files : v.total_files, + bytes : v.total_byte_size, }; return stats; }, {}) @@ -956,7 +956,7 @@ function getAreaStats(cb) { ); } -// method exposed for event scheduler +// method exposed for event scheduler function updateAreaStatsScheduledEvent(args, cb) { getAreaStats( (err, stats) => { if(!err) { @@ -968,13 +968,13 @@ function updateAreaStatsScheduledEvent(args, cb) { } function cleanUpTempSessionItems(cb) { - // find (old) temporary session items and nuke 'em + // find (old) temporary session items and nuke 'em const filter = { - areaTag : WellKnownAreaTags.TempDownloads, - metaPairs : [ + areaTag : WellKnownAreaTags.TempDownloads, + metaPairs : [ { - name : 'session_temp_dl', - value : 1 + name : 'session_temp_dl', + value : 1 } ] }; diff --git a/core/file_base_area_select.js b/core/file_base_area_select.js index bdca4e72..1b77eb21 100644 --- a/core/file_base_area_select.js +++ b/core/file_base_area_select.js @@ -1,22 +1,22 @@ /* jslint node: true */ 'use strict'; -// enigma-bbs -const MenuModule = require('./menu_module.js').MenuModule; -const { getSortedAvailableFileAreas } = require('./file_base_area.js'); -const StatLog = require('./stat_log.js'); +// enigma-bbs +const MenuModule = require('./menu_module.js').MenuModule; +const { getSortedAvailableFileAreas } = require('./file_base_area.js'); +const StatLog = require('./stat_log.js'); -// deps -const async = require('async'); +// deps +const async = require('async'); exports.moduleInfo = { - name : 'File Area Selector', - desc : 'Select from available file areas', - author : 'NuSkooler', + name : 'File Area Selector', + desc : 'Select from available file areas', + author : 'NuSkooler', }; const MciViewIds = { - areaList : 1, + areaList : 1, }; exports.getModule = class FileAreaSelectModule extends MenuModule { @@ -26,14 +26,14 @@ exports.getModule = class FileAreaSelectModule extends MenuModule { this.menuMethods = { selectArea : (formData, extraArgs, cb) => { const filterCriteria = { - areaTag : formData.value.areaTag, + areaTag : formData.value.areaTag, }; const menuOpts = { - extraArgs : { - filterCriteria : filterCriteria, + extraArgs : { + filterCriteria : filterCriteria, }, - menuFlags : [ 'popParent', 'mergeFlags' ], + menuFlags : [ 'popParent', 'mergeFlags' ], }; return this.gotoMenu(this.menuConfig.config.fileBaseListEntriesMenu || 'fileBaseListEntries', menuOpts, cb); @@ -54,12 +54,12 @@ exports.getModule = class FileAreaSelectModule extends MenuModule { function mergeAreaStats(callback) { const areaStats = StatLog.getSystemStat('file_base_area_stats') || { areas : {} }; - // we could use 'sort' alone, but area/conf sorting has some special properties; user can still override + // we could use 'sort' alone, but area/conf sorting has some special properties; user can still override const availAreas = getSortedAvailableFileAreas(self.client); availAreas.forEach(area => { const stats = areaStats.areas[area.areaTag]; area.totalFiles = stats ? stats.files : 0; - area.totalBytes = stats ? stats.bytes : 0; + area.totalBytes = stats ? stats.bytes : 0; }); return callback(null, availAreas); diff --git a/core/file_base_download_manager.js b/core/file_base_download_manager.js index fc7672d0..f590ebd3 100644 --- a/core/file_base_download_manager.js +++ b/core/file_base_download_manager.js @@ -1,37 +1,37 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const DownloadQueue = require('./download_queue.js'); -const theme = require('./theme.js'); -const ansi = require('./ansi_term.js'); -const Errors = require('./enig_error.js').Errors; -const stringFormat = require('./string_format.js'); -const FileAreaWeb = require('./file_area_web.js'); +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const DownloadQueue = require('./download_queue.js'); +const theme = require('./theme.js'); +const ansi = require('./ansi_term.js'); +const Errors = require('./enig_error.js').Errors; +const stringFormat = require('./string_format.js'); +const FileAreaWeb = require('./file_area_web.js'); -// deps -const async = require('async'); -const _ = require('lodash'); -const moment = require('moment'); +// deps +const async = require('async'); +const _ = require('lodash'); +const moment = require('moment'); exports.moduleInfo = { - name : 'File Base Download Queue Manager', - desc : 'Module for interacting with download queue/batch', - author : 'NuSkooler', + name : 'File Base Download Queue Manager', + desc : 'Module for interacting with download queue/batch', + author : 'NuSkooler', }; const FormIds = { - queueManager : 0, + queueManager : 0, }; const MciViewIds = { queueManager : { - queue : 1, - navMenu : 2, + queue : 1, + navMenu : 2, - customRangeStart : 10, + customRangeStart : 10, }, }; @@ -52,8 +52,8 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule { downloadAll : (formData, extraArgs, cb) => { const modOpts = { extraArgs : { - sendQueue : this.dlQueue.items, - direction : 'send', + sendQueue : this.dlQueue.items, + direction : 'send', } }; @@ -67,13 +67,13 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule { this.dlQueue.removeItems(selectedItem.fileId); - // :TODO: broken: does not redraw menu properly - needs fixed! + // :TODO: broken: does not redraw menu properly - needs fixed! return this.removeItemsFromDownloadQueueView(formData.value.queueItem, cb); }, clearQueue : (formData, extraArgs, cb) => { this.dlQueue.clear(); - // :TODO: broken: does not redraw menu properly - needs fixed! + // :TODO: broken: does not redraw menu properly - needs fixed! return this.removeItemsFromDownloadQueueView('all', cb); } }; @@ -82,11 +82,11 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule { initSequence() { if(0 === this.dlQueue.items.length) { if(this.sendFileIds) { - // we've finished everything up - just fall back + // we've finished everything up - just fall back return this.prevMenu(); } - // Simply an empty D/L queue: Present a specialized "empty queue" page + // Simply an empty D/L queue: Present a specialized "empty queue" page return this.gotoMenu(this.menuConfig.config.emptyQueueMenu || 'fileBaseDownloadManagerEmptyQueue'); } @@ -129,11 +129,11 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule { if(serveItem && serveItem.url) { const webDlExpireTimeFormat = this.menuConfig.config.webDlExpireTimeFormat || 'YYYY-MMM-DD @ h:mm'; - fileEntry.webDlLink = ansi.vtxHyperlink(this.client, serveItem.url) + serveItem.url; - fileEntry.webDlExpire = moment(serveItem.expireTimestamp).format(webDlExpireTimeFormat); + fileEntry.webDlLink = ansi.vtxHyperlink(this.client, serveItem.url) + serveItem.url; + fileEntry.webDlExpire = moment(serveItem.expireTimestamp).format(webDlExpireTimeFormat); } else { - fileEntry.webDlLink = ''; - fileEntry.webDlExpire = ''; + fileEntry.webDlLink = ''; + fileEntry.webDlExpire = ''; } this.updateCustomViewTextsWithFilter( @@ -150,8 +150,8 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule { return cb(Errors.DoesNotExist('Queue view does not exist')); } - const queueListFormat = this.menuConfig.config.queueListFormat || '{fileName} {byteSize}'; - const focusQueueListFormat = this.menuConfig.config.focusQueueListFormat || queueListFormat; + const queueListFormat = this.menuConfig.config.queueListFormat || '{fileName} {byteSize}'; + const focusQueueListFormat = this.menuConfig.config.focusQueueListFormat || queueListFormat; queueView.setItems(this.dlQueue.items.map( queueItem => stringFormat(queueListFormat, queueItem) ) ); queueView.setFocusItems(this.dlQueue.items.map( queueItem => stringFormat(focusQueueListFormat, queueItem) ) ); @@ -188,8 +188,8 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule { } displayArtAndPrepViewController(name, options, cb) { - const self = this; - const config = this.menuConfig.config; + const self = this; + const config = this.menuConfig.config; async.waterfall( [ @@ -210,8 +210,8 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule { function prepeareViewController(artData, callback) { if(_.isUndefined(self.viewControllers[name])) { const vcOpts = { - client : self.client, - formId : FormIds[name], + client : self.client, + formId : FormIds[name], }; if(!_.isUndefined(options.noInput)) { @@ -221,9 +221,9 @@ exports.getModule = class FileBaseDownloadQueueManager extends MenuModule { const vc = self.addViewController(name, new ViewController(vcOpts)); const loadOpts = { - callingMenu : self, - mciMap : artData.mciMap, - formId : FormIds[name], + callingMenu : self, + mciMap : artData.mciMap, + formId : FormIds[name], }; return vc.loadFromMenuConfig(loadOpts, callback); diff --git a/core/file_base_filter.js b/core/file_base_filter.js index 9a22051f..82a75986 100644 --- a/core/file_base_filter.js +++ b/core/file_base_filter.js @@ -1,13 +1,13 @@ /* jslint node: true */ 'use strict'; -// deps -const _ = require('lodash'); -const uuidV4 = require('uuid/v4'); +// deps +const _ = require('lodash'); +const uuidV4 = require('uuid/v4'); module.exports = class FileBaseFilters { constructor(client) { - this.client = client; + this.client = client; this.load(); } @@ -74,7 +74,7 @@ module.exports = class FileBaseFilters { try { this.filters = JSON.parse(filtersProperty); } catch(e) { - this.filters = FileBaseFilters.getBuiltInSystemFilters(); // something bad happened; reset everything back to defaults :( + this.filters = FileBaseFilters.getBuiltInSystemFilters(); // something bad happened; reset everything back to defaults :( defaulted = true; this.client.log.error( { error : e.message, property : filtersProperty }, 'Failed parsing file base filters property' ); } @@ -110,18 +110,18 @@ module.exports = class FileBaseFilters { } static getBuiltInSystemFilters() { - const U_LATEST = '7458b09d-40ab-4f9b-a0d7-0cf866646329'; + const U_LATEST = '7458b09d-40ab-4f9b-a0d7-0cf866646329'; const filters = { [ U_LATEST ] : { - name : 'By Date Added', - areaTag : '', // all - terms : '', // * - tags : '', // * - order : 'descending', - sort : 'upload_timestamp', - uuid : U_LATEST, - system : true, + name : 'By Date Added', + areaTag : '', // all + terms : '', // * + tags : '', // * + order : 'descending', + sort : 'upload_timestamp', + uuid : U_LATEST, + system : true, } }; diff --git a/core/file_base_list_export.js b/core/file_base_list_export.js index b66e04db..5c4991f3 100644 --- a/core/file_base_list_export.js +++ b/core/file_base_list_export.js @@ -1,46 +1,46 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const stringFormat = require('./string_format.js'); -const FileEntry = require('./file_entry.js'); -const FileArea = require('./file_base_area.js'); -const Config = require('./config.js').get; -const { Errors } = require('./enig_error.js'); +// ENiGMA½ +const stringFormat = require('./string_format.js'); +const FileEntry = require('./file_entry.js'); +const FileArea = require('./file_base_area.js'); +const Config = require('./config.js').get; +const { Errors } = require('./enig_error.js'); const { splitTextAtTerms, isAnsi, -} = require('./string_util.js'); -const AnsiPrep = require('./ansi_prep.js'); -const Log = require('./logger.js').log; +} = require('./string_util.js'); +const AnsiPrep = require('./ansi_prep.js'); +const Log = require('./logger.js').log; -// deps -const _ = require('lodash'); -const async = require('async'); -const fs = require('graceful-fs'); -const paths = require('path'); -const iconv = require('iconv-lite'); -const moment = require('moment'); +// deps +const _ = require('lodash'); +const async = require('async'); +const fs = require('graceful-fs'); +const paths = require('path'); +const iconv = require('iconv-lite'); +const moment = require('moment'); -exports.exportFileList = exportFileList; -exports.updateFileBaseDescFilesScheduledEvent = updateFileBaseDescFilesScheduledEvent; +exports.exportFileList = exportFileList; +exports.updateFileBaseDescFilesScheduledEvent = updateFileBaseDescFilesScheduledEvent; function exportFileList(filterCriteria, options, cb) { - options.templateEncoding = options.templateEncoding || 'utf8'; - options.entryTemplate = options.entryTemplate || 'descript_ion_export_entry_template.asc'; - options.tsFormat = options.tsFormat || 'YYYY-MM-DD'; - options.descWidth = options.descWidth || 45; // FILE_ID.DIZ spec - options.escapeDesc = _.get(options, 'escapeDesc', false); // escape \r and \n in desc? + options.templateEncoding = options.templateEncoding || 'utf8'; + options.entryTemplate = options.entryTemplate || 'descript_ion_export_entry_template.asc'; + options.tsFormat = options.tsFormat || 'YYYY-MM-DD'; + options.descWidth = options.descWidth || 45; // FILE_ID.DIZ spec + options.escapeDesc = _.get(options, 'escapeDesc', false); // escape \r and \n in desc? if(true === options.escapeDesc) { options.escapeDesc = '\\n'; } const state = { - total : 0, - current : 0, - step : 'preparing', - status : 'Preparing', + total : 0, + current : 0, + step : 'preparing', + status : 'Preparing', }; const updateProgress = _.isFunction(options.progress) ? @@ -50,7 +50,7 @@ function exportFileList(filterCriteria, options, cb) { progCb => { return progCb(null); } - ; + ; async.waterfall( [ @@ -61,8 +61,8 @@ function exportFileList(filterCriteria, options, cb) { } const templateFiles = [ - { name : options.headerTemplate, req : false }, - { name : options.entryTemplate, req : true } + { name : options.headerTemplate, req : false }, + { name : options.entryTemplate, req : true } ]; const config = Config(); @@ -80,19 +80,19 @@ function exportFileList(filterCriteria, options, cb) { return callback(Errors.General(err.message)); } - // decode + ensure DOS style CRLF + // decode + ensure DOS style CRLF templates = templates.map(tmp => iconv.decode(tmp, options.templateEncoding).replace(/\r?\n/g, '\r\n') ); - // Look for the first {fileDesc} (if any) in 'entry' template & find indentation requirements + // Look for the first {fileDesc} (if any) in 'entry' template & find indentation requirements let descIndent = 0; if(!options.escapeDesc) { splitTextAtTerms(templates[1]).some(line => { const pos = line.indexOf('{fileDesc}'); if(pos > -1) { descIndent = pos; - return true; // found it! + return true; // found it! } - return false; // keep looking + return false; // keep looking }); } @@ -101,8 +101,8 @@ function exportFileList(filterCriteria, options, cb) { }); }, function findFiles(headerTemplate, entryTemplate, descIndent, callback) { - state.step = 'gathering'; - state.status = 'Gathering files for supplied criteria'; + state.step = 'gathering'; + state.status = 'Gathering files for supplied criteria'; updateProgress(err => { if(err) { return callback(err); @@ -119,15 +119,15 @@ function exportFileList(filterCriteria, options, cb) { }, function buildListEntries(headerTemplate, entryTemplate, descIndent, fileIds, callback) { const formatObj = { - totalFileCount : fileIds.length, + totalFileCount : fileIds.length, }; - let current = 0; - let listBody = ''; - const totals = { fileCount : fileIds.length, bytes : 0 }; - state.total = fileIds.length; + let current = 0; + let listBody = ''; + const totals = { fileCount : fileIds.length, bytes : 0 }; + state.total = fileIds.length; - state.step = 'file'; + state.step = 'file'; async.eachSeries(fileIds, (fileId, nextFileId) => { const fileInfo = new FileEntry(); @@ -135,7 +135,7 @@ function exportFileList(filterCriteria, options, cb) { fileInfo.load(fileId, err => { if(err) { - return nextFileId(null); // failed, but try the next + return nextFileId(null); // failed, but try the next } totals.bytes += fileInfo.meta.byte_size; @@ -151,9 +151,9 @@ function exportFileList(filterCriteria, options, cb) { listBody += stringFormat(entryTemplate, formatObj); - state.current = current; - state.status = `Processing ${fileInfo.fileName}`; - state.fileInfo = formatObj; + state.current = current; + state.status = `Processing ${fileInfo.fileName}`; + state.fileInfo = formatObj; updateProgress(err => { return nextFileId(err); @@ -162,33 +162,33 @@ function exportFileList(filterCriteria, options, cb) { const area = FileArea.getFileAreaByTag(fileInfo.areaTag); - formatObj.fileId = fileId; - formatObj.areaName = _.get(area, 'name') || 'N/A'; - formatObj.areaDesc = _.get(area, 'desc') || 'N/A'; - formatObj.userRating = fileInfo.userRating || 0; - formatObj.fileName = fileInfo.fileName; - formatObj.fileSize = fileInfo.meta.byte_size; - formatObj.fileDesc = fileInfo.desc || ''; - formatObj.fileDescShort = formatObj.fileDesc.slice(0, options.descWidth); - formatObj.fileSha256 = fileInfo.fileSha256; - formatObj.fileCrc32 = fileInfo.meta.file_crc32; - formatObj.fileMd5 = fileInfo.meta.file_md5; - formatObj.fileSha1 = fileInfo.meta.file_sha1; - formatObj.uploadBy = fileInfo.meta.upload_by_username || 'N/A'; - formatObj.fileUploadTs = moment(fileInfo.uploadTimestamp).format(options.tsFormat); - formatObj.fileHashTags = fileInfo.hashTags.size > 0 ? Array.from(fileInfo.hashTags).join(', ') : 'N/A'; - formatObj.currentFile = current; - formatObj.progress = Math.floor( (current / fileIds.length) * 100 ); + formatObj.fileId = fileId; + formatObj.areaName = _.get(area, 'name') || 'N/A'; + formatObj.areaDesc = _.get(area, 'desc') || 'N/A'; + formatObj.userRating = fileInfo.userRating || 0; + formatObj.fileName = fileInfo.fileName; + formatObj.fileSize = fileInfo.meta.byte_size; + formatObj.fileDesc = fileInfo.desc || ''; + formatObj.fileDescShort = formatObj.fileDesc.slice(0, options.descWidth); + formatObj.fileSha256 = fileInfo.fileSha256; + formatObj.fileCrc32 = fileInfo.meta.file_crc32; + formatObj.fileMd5 = fileInfo.meta.file_md5; + formatObj.fileSha1 = fileInfo.meta.file_sha1; + formatObj.uploadBy = fileInfo.meta.upload_by_username || 'N/A'; + formatObj.fileUploadTs = moment(fileInfo.uploadTimestamp).format(options.tsFormat); + formatObj.fileHashTags = fileInfo.hashTags.size > 0 ? Array.from(fileInfo.hashTags).join(', ') : 'N/A'; + formatObj.currentFile = current; + formatObj.progress = Math.floor( (current / fileIds.length) * 100 ); if(isAnsi(fileInfo.desc)) { AnsiPrep( fileInfo.desc, { - cols : Math.min(options.descWidth, 79 - descIndent), - forceLineTerm : true, // ensure each line is term'd - asciiMode : true, // export to ASCII - fillLines : false, // don't fill up to |cols| - indent : descIndent, + cols : Math.min(options.descWidth, 79 - descIndent), + forceLineTerm : true, // ensure each line is term'd + asciiMode : true, // export to ASCII + fillLines : false, // don't fill up to |cols| + indent : descIndent, }, (err, desc) => { if(desc) { @@ -208,29 +208,29 @@ function exportFileList(filterCriteria, options, cb) { }); }, function buildHeader(listBody, headerTemplate, totals, callback) { - // header is built last such that we can have totals/etc. + // header is built last such that we can have totals/etc. let filterAreaName; let filterAreaDesc; if(filterCriteria.areaTag) { - const area = FileArea.getFileAreaByTag(filterCriteria.areaTag); - filterAreaName = _.get(area, 'name') || 'N/A'; - filterAreaDesc = _.get(area, 'desc') || 'N/A'; + const area = FileArea.getFileAreaByTag(filterCriteria.areaTag); + filterAreaName = _.get(area, 'name') || 'N/A'; + filterAreaDesc = _.get(area, 'desc') || 'N/A'; } else { - filterAreaName = '-ALL-'; - filterAreaDesc = 'All areas'; + filterAreaName = '-ALL-'; + filterAreaDesc = 'All areas'; } const headerFormatObj = { - nowTs : moment().format(options.tsFormat), - boardName : Config().general.boardName, - totalFileCount : totals.fileCount, - totalFileSize : totals.bytes, - filterAreaTag : filterCriteria.areaTag || '-ALL-', - filterAreaName : filterAreaName, - filterAreaDesc : filterAreaDesc, - filterTerms : filterCriteria.terms || '(none)', - filterHashTags : filterCriteria.tags || '(none)', + nowTs : moment().format(options.tsFormat), + boardName : Config().general.boardName, + totalFileCount : totals.fileCount, + totalFileSize : totals.bytes, + filterAreaTag : filterCriteria.areaTag || '-ALL-', + filterAreaName : filterAreaName, + filterAreaDesc : filterAreaDesc, + filterTerms : filterCriteria.terms || '(none)', + filterHashTags : filterCriteria.tags || '(none)', }; listBody = stringFormat(headerTemplate, headerFormatObj) + listBody; @@ -238,8 +238,8 @@ function exportFileList(filterCriteria, options, cb) { }, function done(listBody, callback) { delete state.fileInfo; - state.step = 'finished'; - state.status = 'Finished processing'; + state.step = 'finished'; + state.status = 'Finished processing'; updateProgress( () => { return callback(null, listBody); }); @@ -252,16 +252,16 @@ function exportFileList(filterCriteria, options, cb) { function updateFileBaseDescFilesScheduledEvent(args, cb) { // - // For each area, loop over storage locations and build - // DESCRIPT.ION file to store in the same directory. + // For each area, loop over storage locations and build + // DESCRIPT.ION file to store in the same directory. // - // Standard-ish 4DOS spec is as such: - // * Entry: [0x04]\r\n - // * Multi line descriptions are stored with *escaped* \r\n pairs - // * Default template uses 0x2c for as per https://stackoverflow.com/questions/1810398/descript-ion-file-spec + // Standard-ish 4DOS spec is as such: + // * Entry: [0x04]\r\n + // * Multi line descriptions are stored with *escaped* \r\n pairs + // * Default template uses 0x2c for as per https://stackoverflow.com/questions/1810398/descript-ion-file-spec // - const entryTemplate = args[0]; - const headerTemplate = args[1]; + const entryTemplate = args[0]; + const headerTemplate = args[1]; const areas = FileArea.getAvailableFileAreas(null, { skipAcsCheck : true }); async.each(areas, (area, nextArea) => { @@ -269,15 +269,15 @@ function updateFileBaseDescFilesScheduledEvent(args, cb) { async.each(storageLocations, (storageLoc, nextStorageLoc) => { const filterCriteria = { - areaTag : area.areaTag, - storageTag : storageLoc.storageTag, + areaTag : area.areaTag, + storageTag : storageLoc.storageTag, }; const exportOpts = { - headerTemplate : headerTemplate, - entryTemplate : entryTemplate, - escapeDesc : true, // escape CRLF's - maxDescLen : 4096, // DESCRIPT.ION: "The line length limit is 4096 bytes" + headerTemplate : headerTemplate, + entryTemplate : entryTemplate, + escapeDesc : true, // escape CRLF's + maxDescLen : 4096, // DESCRIPT.ION: "The line length limit is 4096 bytes" }; exportFileList(filterCriteria, exportOpts, (err, listBody) => { diff --git a/core/file_base_search.js b/core/file_base_search.js index 3e754b91..168ed39a 100644 --- a/core/file_base_search.js +++ b/core/file_base_search.js @@ -1,30 +1,30 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const getSortedAvailableFileAreas = require('./file_base_area.js').getSortedAvailableFileAreas; -const FileBaseFilters = require('./file_base_filter.js'); +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const getSortedAvailableFileAreas = require('./file_base_area.js').getSortedAvailableFileAreas; +const FileBaseFilters = require('./file_base_filter.js'); -// deps -const async = require('async'); +// deps +const async = require('async'); exports.moduleInfo = { - name : 'File Base Search', - desc : 'Module for quickly searching the file base', - author : 'NuSkooler', + name : 'File Base Search', + desc : 'Module for quickly searching the file base', + author : 'NuSkooler', }; const MciViewIds = { search : { - searchTerms : 1, - search : 2, - tags : 3, - area : 4, - orderBy : 5, - sort : 6, - advSearch : 7, + searchTerms : 1, + search : 2, + tags : 3, + area : 4, + orderBy : 5, + sort : 6, + advSearch : 7, } }; @@ -46,8 +46,8 @@ exports.getModule = class FileBaseSearch extends MenuModule { return cb(err); } - const self = this; - const vc = self.addViewController( 'search', new ViewController( { client : this.client } ) ); + const self = this; + const vc = self.addViewController( 'search', new ViewController( { client : this.client } ) ); async.series( [ @@ -74,7 +74,7 @@ exports.getModule = class FileBaseSearch extends MenuModule { getSelectedAreaTag(index) { if(0 === index) { - return ''; // -ALL- + return ''; // -ALL- } const area = this.availAreas[index]; if(!area) { @@ -92,16 +92,16 @@ exports.getModule = class FileBaseSearch extends MenuModule { } getFilterValuesFromFormData(formData, isAdvanced) { - const areaIndex = isAdvanced ? formData.value.areaIndex : 0; - const orderByIndex = isAdvanced ? formData.value.orderByIndex : 0; - const sortByIndex = isAdvanced ? formData.value.sortByIndex : 0; + const areaIndex = isAdvanced ? formData.value.areaIndex : 0; + const orderByIndex = isAdvanced ? formData.value.orderByIndex : 0; + const sortByIndex = isAdvanced ? formData.value.sortByIndex : 0; return { - areaTag : this.getSelectedAreaTag(areaIndex), - terms : formData.value.searchTerms, - tags : isAdvanced ? formData.value.tags : '', - order : this.getOrderBy(orderByIndex), - sort : this.getSortBy(sortByIndex), + areaTag : this.getSelectedAreaTag(areaIndex), + terms : formData.value.searchTerms, + tags : isAdvanced ? formData.value.tags : '', + order : this.getOrderBy(orderByIndex), + sort : this.getSortBy(sortByIndex), }; } @@ -109,10 +109,10 @@ exports.getModule = class FileBaseSearch extends MenuModule { const filterCriteria = this.getFilterValuesFromFormData(formData, isAdvanced); const menuOpts = { - extraArgs : { - filterCriteria : filterCriteria, + extraArgs : { + filterCriteria : filterCriteria, }, - menuFlags : [ 'popParent' ], + menuFlags : [ 'popParent' ], }; return this.gotoMenu(this.menuConfig.config.fileBaseListEntriesMenu || 'fileBaseListEntries', menuOpts, cb); diff --git a/core/file_base_user_list_export.js b/core/file_base_user_list_export.js index 8f691273..3c00d167 100644 --- a/core/file_base_user_list_export.js +++ b/core/file_base_user_list_export.js @@ -1,66 +1,66 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const { MenuModule } = require('./menu_module.js'); -const FileEntry = require('./file_entry.js'); -const FileArea = require('./file_base_area.js'); -const { renderSubstr } = require('./string_util.js'); -const { Errors } = require('./enig_error.js'); -const Events = require('./events.js'); -const Log = require('./logger.js').log; -const DownloadQueue = require('./download_queue.js'); -const { exportFileList } = require('./file_base_list_export.js'); +// ENiGMA½ +const { MenuModule } = require('./menu_module.js'); +const FileEntry = require('./file_entry.js'); +const FileArea = require('./file_base_area.js'); +const { renderSubstr } = require('./string_util.js'); +const { Errors } = require('./enig_error.js'); +const Events = require('./events.js'); +const Log = require('./logger.js').log; +const DownloadQueue = require('./download_queue.js'); +const { exportFileList } = require('./file_base_list_export.js'); -// deps -const _ = require('lodash'); -const async = require('async'); -const fs = require('graceful-fs'); -const fse = require('fs-extra'); -const paths = require('path'); -const moment = require('moment'); -const uuidv4 = require('uuid/v4'); -const yazl = require('yazl'); +// deps +const _ = require('lodash'); +const async = require('async'); +const fs = require('graceful-fs'); +const fse = require('fs-extra'); +const paths = require('path'); +const moment = require('moment'); +const uuidv4 = require('uuid/v4'); +const yazl = require('yazl'); /* - Module config block can contain the following: - templateEncoding - encoding of template files (utf8) - tsFormat - timestamp format (theme 'short') - descWidth - max desc width (45) - progBarChar - progress bar character (▒) - compressThreshold - threshold to kick in comrpession for lists (1.44 MiB) - templates - object containing: - header - filename of header template (misc/file_list_header.asc) - entry - filename of entry template (misc/file_list_entry.asc) + Module config block can contain the following: + templateEncoding - encoding of template files (utf8) + tsFormat - timestamp format (theme 'short') + descWidth - max desc width (45) + progBarChar - progress bar character (▒) + compressThreshold - threshold to kick in comrpession for lists (1.44 MiB) + templates - object containing: + header - filename of header template (misc/file_list_header.asc) + entry - filename of entry template (misc/file_list_entry.asc) - Header template variables: - nowTs, boardName, totalFileCount, totalFileSize, - filterAreaTag, filterAreaName, filterAreaDesc, - filterTerms, filterHashTags + Header template variables: + nowTs, boardName, totalFileCount, totalFileSize, + filterAreaTag, filterAreaName, filterAreaDesc, + filterTerms, filterHashTags - Entry template variables: - fileId, areaName, areaDesc, userRating, fileName, - fileSize, fileDesc, fileDescShort, fileSha256, fileCrc32, - fileMd5, fileSha1, uploadBy, fileUploadTs, fileHashTags, - currentFile, progress, + Entry template variables: + fileId, areaName, areaDesc, userRating, fileName, + fileSize, fileDesc, fileDescShort, fileSha256, fileCrc32, + fileMd5, fileSha1, uploadBy, fileUploadTs, fileHashTags, + currentFile, progress, */ exports.moduleInfo = { - name : 'File Base List Export', - desc : 'Exports file base listings for download', - author : 'NuSkooler', + name : 'File Base List Export', + desc : 'Exports file base listings for download', + author : 'NuSkooler', }; const FormIds = { - main : 0, + main : 0, }; const MciViewIds = { main : { - status : 1, - progressBar : 2, + status : 1, + progressBar : 2, - customRangeStart : 10, + customRangeStart : 10, } }; @@ -70,11 +70,11 @@ exports.getModule = class FileBaseListExport extends MenuModule { super(options); this.config = Object.assign({}, _.get(options, 'menuConfig.config'), options.extraArgs); - this.config.templateEncoding = this.config.templateEncoding || 'utf8'; - this.config.tsFormat = this.config.tsFormat || this.client.currentTheme.helpers.getDateTimeFormat('short'); - this.config.descWidth = this.config.descWidth || 45; // ie FILE_ID.DIZ - this.config.progBarChar = renderSubstr( (this.config.progBarChar || '▒'), 0, 1); - this.config.compressThreshold = this.config.compressThreshold || (1440000); // >= 1.44M by default :) + this.config.templateEncoding = this.config.templateEncoding || 'utf8'; + this.config.tsFormat = this.config.tsFormat || this.client.currentTheme.helpers.getDateTimeFormat('short'); + this.config.descWidth = this.config.descWidth || 45; // ie FILE_ID.DIZ + this.config.progBarChar = renderSubstr( (this.config.progBarChar || '▒'), 0, 1); + this.config.compressThreshold = this.config.compressThreshold || (1440000); // >= 1.44M by default :) } mciReady(mciData, cb) { @@ -154,7 +154,7 @@ exports.getModule = class FileBaseListExport extends MenuModule { async.waterfall( [ function buildList(callback) { - // this may take quite a while; temp disable of idle monitor + // this may take quite a while; temp disable of idle monitor self.client.stopIdleMonitor(); self.client.on('key press', keyPressHandler); @@ -165,12 +165,12 @@ exports.getModule = class FileBaseListExport extends MenuModule { } const opts = { - templateEncoding : self.config.templateEncoding, - headerTemplate : _.get(self.config, 'templates.header', 'file_list_header.asc'), - entryTemplate : _.get(self.config, 'templates.entry', 'file_list_entry.asc'), - tsFormat : self.config.tsFormat, - descWidth : self.config.descWidth, - progress : exportListProgress, + templateEncoding : self.config.templateEncoding, + headerTemplate : _.get(self.config, 'templates.header', 'file_list_header.asc'), + entryTemplate : _.get(self.config, 'templates.entry', 'file_list_entry.asc'), + tsFormat : self.config.tsFormat, + descWidth : self.config.descWidth, + progress : exportListProgress, }; exportFileList(filterCriteria, opts, (err, listBody) => { @@ -180,8 +180,8 @@ exports.getModule = class FileBaseListExport extends MenuModule { function persistList(listBody, callback) { updateStatus('Persisting list'); - const sysTempDownloadArea = FileArea.getFileAreaByTag(FileArea.WellKnownAreaTags.TempDownloads); - const sysTempDownloadDir = FileArea.getAreaDefaultStorageDirectory(sysTempDownloadArea); + const sysTempDownloadArea = FileArea.getFileAreaByTag(FileArea.WellKnownAreaTags.TempDownloads); + const sysTempDownloadDir = FileArea.getAreaDefaultStorageDirectory(sysTempDownloadArea); fse.mkdirs(sysTempDownloadDir, err => { if(err) { @@ -206,14 +206,14 @@ exports.getModule = class FileBaseListExport extends MenuModule { }, function persistFileEntry(outputFileName, fileSize, sysTempDownloadArea, callback) { const newEntry = new FileEntry({ - areaTag : sysTempDownloadArea.areaTag, - fileName : paths.basename(outputFileName), - storageTag : sysTempDownloadArea.storageTags[0], - meta : { - upload_by_username : self.client.user.username, - upload_by_user_id : self.client.user.userId, - byte_size : fileSize, - session_temp_dl : 1, // download is valid until session is over + areaTag : sysTempDownloadArea.areaTag, + fileName : paths.basename(outputFileName), + storageTag : sysTempDownloadArea.storageTags[0], + meta : { + upload_by_username : self.client.user.username, + upload_by_user_id : self.client.user.userId, + byte_size : fileSize, + session_temp_dl : 1, // download is valid until session is over } }); @@ -221,11 +221,11 @@ exports.getModule = class FileBaseListExport extends MenuModule { newEntry.persist(err => { if(!err) { - // queue it! + // queue it! const dlQueue = new DownloadQueue(self.client); - dlQueue.add(newEntry, true); // true=systemFile + dlQueue.add(newEntry, true); // true=systemFile - // clean up after ourselves when the session ends + // clean up after ourselves when the session ends const thisClientId = self.client.session.id; Events.once(Events.getSystemEvents().ClientDisconnected, evt => { if(thisClientId === _.get(evt, 'client.session.id')) { @@ -243,7 +243,7 @@ exports.getModule = class FileBaseListExport extends MenuModule { }); }, function done(callback) { - // re-enable idle monitor + // re-enable idle monitor self.client.startIdleMonitor(); updateStatus('Exported list has been added to your download queue'); @@ -264,7 +264,7 @@ exports.getModule = class FileBaseListExport extends MenuModule { } if(stats.size < this.config.compressThreshold) { - // small enough, keep orig + // small enough, keep orig return cb(null, filePath, stats.size); } @@ -276,13 +276,13 @@ exports.getModule = class FileBaseListExport extends MenuModule { const outZipFile = fs.createWriteStream(zipFilePath); zipFile.outputStream.pipe(outZipFile); zipFile.outputStream.on('finish', () => { - // delete the original + // delete the original fse.unlink(filePath, err => { if(err) { return cb(err); } - // finally stat the new output + // finally stat the new output fse.stat(zipFilePath, (err, stats) => { return cb(err, zipFilePath, stats ? stats.size : 0); }); diff --git a/core/file_base_web_download_manager.js b/core/file_base_web_download_manager.js index 69c87ec8..02bffa3e 100644 --- a/core/file_base_web_download_manager.js +++ b/core/file_base_web_download_manager.js @@ -1,39 +1,39 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const DownloadQueue = require('./download_queue.js'); -const theme = require('./theme.js'); -const ansi = require('./ansi_term.js'); -const Errors = require('./enig_error.js').Errors; -const stringFormat = require('./string_format.js'); -const FileAreaWeb = require('./file_area_web.js'); -const ErrNotEnabled = require('./enig_error.js').ErrorReasons.NotEnabled; -const Config = require('./config.js').get; +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const DownloadQueue = require('./download_queue.js'); +const theme = require('./theme.js'); +const ansi = require('./ansi_term.js'); +const Errors = require('./enig_error.js').Errors; +const stringFormat = require('./string_format.js'); +const FileAreaWeb = require('./file_area_web.js'); +const ErrNotEnabled = require('./enig_error.js').ErrorReasons.NotEnabled; +const Config = require('./config.js').get; -// deps -const async = require('async'); -const _ = require('lodash'); -const moment = require('moment'); +// deps +const async = require('async'); +const _ = require('lodash'); +const moment = require('moment'); exports.moduleInfo = { - name : 'File Base Download Web Queue Manager', - desc : 'Module for interacting with web backed download queue/batch', - author : 'NuSkooler', + name : 'File Base Download Web Queue Manager', + desc : 'Module for interacting with web backed download queue/batch', + author : 'NuSkooler', }; const FormIds = { - queueManager : 0 + queueManager : 0 }; const MciViewIds = { queueManager : { - queue : 1, - navMenu : 2, + queue : 1, + navMenu : 2, - customRangeStart : 10, + customRangeStart : 10, } }; @@ -53,13 +53,13 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { this.dlQueue.removeItems(selectedItem.fileId); - // :TODO: broken: does not redraw menu properly - needs fixed! + // :TODO: broken: does not redraw menu properly - needs fixed! return this.removeItemsFromDownloadQueueView(formData.value.queueItem, cb); }, clearQueue : (formData, extraArgs, cb) => { this.dlQueue.clear(); - // :TODO: broken: does not redraw menu properly - needs fixed! + // :TODO: broken: does not redraw menu properly - needs fixed! return this.removeItemsFromDownloadQueueView('all', cb); }, getBatchLink : (formData, extraArgs, cb) => { @@ -111,7 +111,7 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { this.updateCustomViewTextsWithFilter( 'queueManager', MciViewIds.queueManager.customRangeStart, fileEntry, - { filter : [ '{webDlLink}', '{webDlExpire}', '{fileName}' ] } // :TODO: Others.... + { filter : [ '{webDlLink}', '{webDlExpire}', '{fileName}' ] } // :TODO: Others.... ); } @@ -121,8 +121,8 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { return cb(Errors.DoesNotExist('Queue view does not exist')); } - const queueListFormat = this.menuConfig.config.queueListFormat || '{webDlLink}'; - const focusQueueListFormat = this.menuConfig.config.focusQueueListFormat || queueListFormat; + const queueListFormat = this.menuConfig.config.queueListFormat || '{webDlLink}'; + const focusQueueListFormat = this.menuConfig.config.focusQueueListFormat || queueListFormat; queueView.setItems(this.dlQueue.items.map( queueItem => stringFormat(queueListFormat, queueItem) ) ); queueView.setFocusItems(this.dlQueue.items.map( queueItem => stringFormat(focusQueueListFormat, queueItem) ) ); @@ -148,7 +148,7 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { expireTime : expireTime }, (err, webBatchDlLink) => { - // :TODO: handle not enabled -> display such + // :TODO: handle not enabled -> display such if(err) { return cb(err); } @@ -156,8 +156,8 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { const webDlExpireTimeFormat = this.menuConfig.config.webDlExpireTimeFormat || 'YYYY-MMM-DD @ h:mm'; const formatObj = { - webBatchDlLink : ansi.vtxHyperlink(this.client, webBatchDlLink) + webBatchDlLink, - webBatchDlExpire : expireTime.format(webDlExpireTimeFormat), + webBatchDlLink : ansi.vtxHyperlink(this.client, webBatchDlLink) + webBatchDlLink, + webBatchDlExpire : expireTime.format(webDlExpireTimeFormat), }; this.updateCustomViewTextsWithFilter( @@ -188,7 +188,7 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { FileAreaWeb.getExistingTempDownloadServeItem(self.client, fileEntry, (err, serveItem) => { if(err) { if(ErrNotEnabled === err.reasonCode) { - return nextFileEntry(err); // we should have caught this prior + return nextFileEntry(err); // we should have caught this prior } const expireTime = moment().add(config.fileBase.web.expireMinutes, 'minutes'); @@ -202,17 +202,17 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { return nextFileEntry(err); } - fileEntry.webDlLinkRaw = url; - fileEntry.webDlLink = ansi.vtxHyperlink(self.client, url) + url; - fileEntry.webDlExpire = expireTime.format(webDlExpireTimeFormat); + fileEntry.webDlLinkRaw = url; + fileEntry.webDlLink = ansi.vtxHyperlink(self.client, url) + url; + fileEntry.webDlExpire = expireTime.format(webDlExpireTimeFormat); return nextFileEntry(null); } ); } else { - fileEntry.webDlLinkRaw = serveItem.url; - fileEntry.webDlLink = ansi.vtxHyperlink(self.client, serveItem.url) + serveItem.url; - fileEntry.webDlExpire = moment(serveItem.expireTimestamp).format(webDlExpireTimeFormat); + fileEntry.webDlLinkRaw = serveItem.url; + fileEntry.webDlLink = ansi.vtxHyperlink(self.client, serveItem.url) + serveItem.url; + fileEntry.webDlExpire = moment(serveItem.expireTimestamp).format(webDlExpireTimeFormat); return nextFileEntry(null); } }); @@ -233,8 +233,8 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { } displayArtAndPrepViewController(name, options, cb) { - const self = this; - const config = this.menuConfig.config; + const self = this; + const config = this.menuConfig.config; async.waterfall( [ @@ -255,8 +255,8 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { function prepeareViewController(artData, callback) { if(_.isUndefined(self.viewControllers[name])) { const vcOpts = { - client : self.client, - formId : FormIds[name], + client : self.client, + formId : FormIds[name], }; if(!_.isUndefined(options.noInput)) { @@ -266,9 +266,9 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { const vc = self.addViewController(name, new ViewController(vcOpts)); const loadOpts = { - callingMenu : self, - mciMap : artData.mciMap, - formId : FormIds[name], + callingMenu : self, + mciMap : artData.mciMap, + formId : FormIds[name], }; return vc.loadFromMenuConfig(loadOpts, callback); diff --git a/core/file_entry.js b/core/file_entry.js index 75fafb29..0f519e10 100644 --- a/core/file_entry.js +++ b/core/file_entry.js @@ -1,57 +1,57 @@ /* jslint node: true */ 'use strict'; -const fileDb = require('./database.js').dbs.file; -const Errors = require('./enig_error.js').Errors; +const fileDb = require('./database.js').dbs.file; +const Errors = require('./enig_error.js').Errors; const { getISOTimestampString, sanatizeString -} = require('./database.js'); -const Config = require('./config.js').get; +} = require('./database.js'); +const Config = require('./config.js').get; -// deps -const async = require('async'); -const _ = require('lodash'); -const paths = require('path'); -const fse = require('fs-extra'); -const { unlink, readFile } = require('graceful-fs'); -const crypto = require('crypto'); -const moment = require('moment'); +// deps +const async = require('async'); +const _ = require('lodash'); +const paths = require('path'); +const fse = require('fs-extra'); +const { unlink, readFile } = require('graceful-fs'); +const crypto = require('crypto'); +const moment = require('moment'); -const FILE_TABLE_MEMBERS = [ +const FILE_TABLE_MEMBERS = [ 'file_id', 'area_tag', 'file_sha256', 'file_name', 'storage_tag', 'desc', 'desc_long', 'upload_timestamp' ]; const FILE_WELL_KNOWN_META = { - // name -> *read* converter, if any - upload_by_username : null, - upload_by_user_id : (u) => parseInt(u) || 0, - file_md5 : null, - file_sha1 : null, - file_crc32 : null, - est_release_year : (y) => parseInt(y) || new Date().getFullYear(), - dl_count : (d) => parseInt(d) || 0, - byte_size : (b) => parseInt(b) || 0, - archive_type : null, - short_file_name : null, // e.g. DOS 8.3 filename, avail in some scenarios such as TIC import - tic_origin : null, // TIC "Origin" - tic_desc : null, // TIC "Desc" - tic_ldesc : null, // TIC "Ldesc" joined by '\n' - session_temp_dl : (v) => parseInt(v) ? true : false, + // name -> *read* converter, if any + upload_by_username : null, + upload_by_user_id : (u) => parseInt(u) || 0, + file_md5 : null, + file_sha1 : null, + file_crc32 : null, + est_release_year : (y) => parseInt(y) || new Date().getFullYear(), + dl_count : (d) => parseInt(d) || 0, + byte_size : (b) => parseInt(b) || 0, + archive_type : null, + short_file_name : null, // e.g. DOS 8.3 filename, avail in some scenarios such as TIC import + tic_origin : null, // TIC "Origin" + tic_desc : null, // TIC "Desc" + tic_ldesc : null, // TIC "Ldesc" joined by '\n' + session_temp_dl : (v) => parseInt(v) ? true : false, }; module.exports = class FileEntry { constructor(options) { - options = options || {}; + options = options || {}; - this.fileId = options.fileId || 0; - this.areaTag = options.areaTag || ''; - this.meta = Object.assign( { dl_count : 0 }, options.meta); - this.hashTags = options.hashTags || new Set(); - this.fileName = options.fileName; - this.storageTag = options.storageTag; - this.fileSha256 = options.fileSha256; + this.fileId = options.fileId || 0; + this.areaTag = options.areaTag || ''; + this.meta = Object.assign( { dl_count : 0 }, options.meta); + this.hashTags = options.hashTags || new Set(); + this.fileName = options.fileName; + this.storageTag = options.storageTag; + this.fileSha256 = options.fileSha256; } static loadBasicEntry(fileId, dest, cb) { @@ -59,9 +59,9 @@ module.exports = class FileEntry { fileDb.get( `SELECT ${FILE_TABLE_MEMBERS.join(', ')} - FROM file - WHERE file_id=? - LIMIT 1;`, + FROM file + WHERE file_id=? + LIMIT 1;`, [ fileId ], (err, file) => { if(err) { @@ -72,7 +72,7 @@ module.exports = class FileEntry { return cb(Errors.DoesNotExist('No file is available by that ID')); } - // assign props from |file| + // assign props from |file| FILE_TABLE_MEMBERS.forEach(prop => { dest[_.camelCase(prop)] = file[prop]; }); @@ -149,7 +149,7 @@ module.exports = class FileEntry { if(isUpdate) { trans.run( `REPLACE INTO file (file_id, area_tag, file_sha256, file_name, storage_tag, desc, desc_long, upload_timestamp) - VALUES(?, ?, ?, ?, ?, ?, ?, ?);`, + VALUES(?, ?, ?, ?, ?, ?, ?, ?);`, [ self.fileId, self.areaTag, self.fileSha256, self.fileName, self.storageTag, self.desc, self.descLong, getISOTimestampString() ], err => { return callback(err, trans); @@ -158,9 +158,9 @@ module.exports = class FileEntry { } else { trans.run( `REPLACE INTO file (area_tag, file_sha256, file_name, storage_tag, desc, desc_long, upload_timestamp) - VALUES(?, ?, ?, ?, ?, ?, ?);`, + VALUES(?, ?, ?, ?, ?, ?, ?);`, [ self.areaTag, self.fileSha256, self.fileName, self.storageTag, self.desc, self.descLong, getISOTimestampString() ], - function inserted(err) { // use non-arrow func for 'this' scope / lastID + function inserted(err) { // use non-arrow func for 'this' scope / lastID if(!err) { self.fileId = this.lastID; } @@ -189,7 +189,7 @@ module.exports = class FileEntry { } ], (err, trans) => { - // :TODO: Log orig err + // :TODO: Log orig err if(trans) { trans[err ? 'rollback' : 'commit'](transErr => { return cb(transErr ? transErr : err); @@ -205,12 +205,12 @@ module.exports = class FileEntry { const config = Config(); const storageLocation = (storageTag && config.fileBase.storageTags[storageTag]); - // absolute paths as-is + // absolute paths as-is if(storageLocation && '/' === storageLocation.charAt(0)) { return storageLocation; } - // relative to |areaStoragePrefix| + // relative to |areaStoragePrefix| return paths.join(config.fileBase.areaStoragePrefix, storageLocation || ''); } @@ -222,9 +222,9 @@ module.exports = class FileEntry { static quickCheckExistsByPath(fullPath, cb) { fileDb.get( `SELECT COUNT() AS count - FROM file - WHERE file_name = ? - LIMIT 1;`, + FROM file + WHERE file_name = ? + LIMIT 1;`, [ paths.basename(fullPath) ], (err, rows) => { return err ? cb(err) : cb(null, rows.count > 0 ? true : false); @@ -235,7 +235,7 @@ module.exports = class FileEntry { static persistUserRating(fileId, userId, rating, cb) { return fileDb.run( `REPLACE INTO file_user_rating (file_id, user_id, rating) - VALUES (?, ?, ?);`, + VALUES (?, ?, ?);`, [ fileId, userId, rating ], cb ); @@ -249,7 +249,7 @@ module.exports = class FileEntry { return transOrDb.run( `REPLACE INTO file_meta (file_id, meta_name, meta_value) - VALUES (?, ?, ?);`, + VALUES (?, ?, ?);`, [ fileId, name, value ], cb ); @@ -259,8 +259,8 @@ module.exports = class FileEntry { incrementBy = incrementBy || 1; fileDb.run( `UPDATE file_meta - SET meta_value = meta_value + ? - WHERE file_id = ? AND meta_name = ?;`, + SET meta_value = meta_value + ? + WHERE file_id = ? AND meta_name = ?;`, [ incrementBy, fileId, name ], err => { if(cb) { @@ -273,8 +273,8 @@ module.exports = class FileEntry { loadMeta(cb) { fileDb.each( `SELECT meta_name, meta_value - FROM file_meta - WHERE file_id=?;`, + FROM file_meta + WHERE file_id=?;`, [ this.fileId ], (err, meta) => { if(meta) { @@ -297,18 +297,18 @@ module.exports = class FileEntry { transOrDb.serialize( () => { transOrDb.run( `INSERT OR IGNORE INTO hash_tag (hash_tag) - VALUES (?);`, + VALUES (?);`, [ hashTag ] ); transOrDb.run( `REPLACE INTO file_hash_tag (hash_tag_id, file_id) - VALUES ( - (SELECT hash_tag_id - FROM hash_tag - WHERE hash_tag = ?), - ? - );`, + VALUES ( + (SELECT hash_tag_id + FROM hash_tag + WHERE hash_tag = ?), + ? + );`, [ hashTag, fileId ], err => { return cb(err); @@ -320,12 +320,12 @@ module.exports = class FileEntry { loadHashTags(cb) { fileDb.each( `SELECT ht.hash_tag_id, ht.hash_tag - FROM hash_tag ht - WHERE ht.hash_tag_id IN ( - SELECT hash_tag_id - FROM file_hash_tag - WHERE file_id=? - );`, + FROM hash_tag ht + WHERE ht.hash_tag_id IN ( + SELECT hash_tag_id + FROM file_hash_tag + WHERE file_id=? + );`, [ this.fileId ], (err, hashTag) => { if(hashTag) { @@ -341,10 +341,10 @@ module.exports = class FileEntry { loadRating(cb) { fileDb.get( `SELECT AVG(fur.rating) AS avg_rating - FROM file_user_rating fur - INNER JOIN file f - ON f.file_id = fur.file_id - AND f.file_id = ?`, + FROM file_user_rating fur + INNER JOIN file f + ON f.file_id = fur.file_id + AND f.file_id = ?`, [ this.fileId ], (err, result) => { if(result) { @@ -370,12 +370,12 @@ module.exports = class FileEntry { } static findFileBySha(sha, cb) { - // full or partial SHA-256 + // full or partial SHA-256 fileDb.all( `SELECT file_id - FROM file - WHERE file_sha256 LIKE "${sha}%" - LIMIT 2;`, // limit 2 such that we can find if there are dupes + FROM file + WHERE file_sha256 LIKE "${sha}%" + LIMIT 2;`, // limit 2 such that we can find if there are dupes (err, fileIdRows) => { if(err) { return cb(err); @@ -398,14 +398,14 @@ module.exports = class FileEntry { } static findByFileNameWildcard(wc, cb) { - // convert any * -> % and ? -> _ for SQLite syntax - see https://www.sqlite.org/lang_expr.html + // convert any * -> % and ? -> _ for SQLite syntax - see https://www.sqlite.org/lang_expr.html wc = wc.replace(/\*/g, '%').replace(/\?/g, '_'); fileDb.all( `SELECT file_id - FROM file - WHERE file_name LIKE "${wc}" - `, + FROM file + WHERE file_name LIKE "${wc}" + `, (err, fileIdRows) => { if(err) { return cb(err); @@ -462,38 +462,38 @@ module.exports = class FileEntry { } if(filter.sort && filter.sort.length > 0) { - if(Object.keys(FILE_WELL_KNOWN_META).indexOf(filter.sort) > -1) { // sorting via a meta value? + if(Object.keys(FILE_WELL_KNOWN_META).indexOf(filter.sort) > -1) { // sorting via a meta value? sql = - `SELECT DISTINCT f.file_id - FROM file f, file_meta m`; + `SELECT DISTINCT f.file_id + FROM file f, file_meta m`; appendWhereClause(`f.file_id = m.file_id AND m.meta_name = "${filter.sort}"`); sqlOrderBy = `${getOrderByWithCast('m.meta_value')} ${sqlOrderDir}`; } else { - // additional special treatment for user ratings: we need to average them + // additional special treatment for user ratings: we need to average them if('user_rating' === filter.sort) { sql = - `SELECT DISTINCT f.file_id, - (SELECT IFNULL(AVG(rating), 0) rating - FROM file_user_rating - WHERE file_id = f.file_id) - AS avg_rating - FROM file f`; + `SELECT DISTINCT f.file_id, + (SELECT IFNULL(AVG(rating), 0) rating + FROM file_user_rating + WHERE file_id = f.file_id) + AS avg_rating + FROM file f`; sqlOrderBy = `ORDER BY avg_rating ${sqlOrderDir}`; } else { sql = - `SELECT DISTINCT f.file_id - FROM file f`; + `SELECT DISTINCT f.file_id + FROM file f`; sqlOrderBy = getOrderByWithCast(`f.${filter.sort}`) + ' ' + sqlOrderDir; } } } else { sql = - `SELECT DISTINCT f.file_id - FROM file f`; + `SELECT DISTINCT f.file_id + FROM file f`; sqlOrderBy = `${getOrderByWithCast('f.file_id')} ${sqlOrderDir}`; } @@ -511,22 +511,22 @@ module.exports = class FileEntry { filter.metaPairs.forEach(mp => { if(mp.wildcards) { - // convert any * -> % and ? -> _ for SQLite syntax - see https://www.sqlite.org/lang_expr.html + // convert any * -> % and ? -> _ for SQLite syntax - see https://www.sqlite.org/lang_expr.html mp.value = mp.value.replace(/\*/g, '%').replace(/\?/g, '_'); appendWhereClause( `f.file_id IN ( - SELECT file_id - FROM file_meta - WHERE meta_name = "${mp.name}" AND meta_value LIKE "${mp.value}" - )` + SELECT file_id + FROM file_meta + WHERE meta_name = "${mp.name}" AND meta_value LIKE "${mp.value}" + )` ); } else { appendWhereClause( `f.file_id IN ( - SELECT file_id - FROM file_meta - WHERE meta_name = "${mp.name}" AND meta_value = "${mp.value}" - )` + SELECT file_id + FROM file_meta + WHERE meta_name = "${mp.name}" AND meta_value = "${mp.value}" + )` ); } }); @@ -537,30 +537,30 @@ module.exports = class FileEntry { } if(filter.terms && filter.terms.length > 0) { - // note the ':' in MATCH expr., see https://www.sqlite.org/cvstrac/wiki?p=FullTextIndex + // note the ':' in MATCH expr., see https://www.sqlite.org/cvstrac/wiki?p=FullTextIndex appendWhereClause( `f.file_id IN ( - SELECT rowid - FROM file_fts - WHERE file_fts MATCH ":${sanatizeString(filter.terms)}" - )` + SELECT rowid + FROM file_fts + WHERE file_fts MATCH ":${sanatizeString(filter.terms)}" + )` ); } if(filter.tags && filter.tags.length > 0) { - // build list of quoted tags; filter.tags comes in as a space and/or comma separated values + // build list of quoted tags; filter.tags comes in as a space and/or comma separated values const tags = filter.tags.replace(/,/g, ' ').replace(/\s{2,}/g, ' ').split(' ').map( tag => `"${sanatizeString(tag)}"` ).join(','); appendWhereClause( `f.file_id IN ( - SELECT file_id - FROM file_hash_tag - WHERE hash_tag_id IN ( - SELECT hash_tag_id - FROM hash_tag - WHERE hash_tag IN (${tags}) - ) - )` + SELECT file_id + FROM file_hash_tag + WHERE hash_tag_id IN ( + SELECT hash_tag_id + FROM hash_tag + WHERE hash_tag IN (${tags}) + ) + )` ); } @@ -585,7 +585,7 @@ module.exports = class FileEntry { return cb(err); } if(!rows || 0 === rows.length) { - return cb(null, []); // no matches + return cb(null, []); // no matches } return cb(null, rows.map(r => r.file_id)); }); @@ -602,7 +602,7 @@ module.exports = class FileEntry { function removeFromDatabase(callback) { fileDb.run( `DELETE FROM file - WHERE file_id = ?;`, + WHERE file_id = ?;`, [ srcFileEntry.fileId ], err => { return callback(err); @@ -631,20 +631,20 @@ module.exports = class FileEntry { destFileName = srcFileEntry.fileName; } - const srcPath = srcFileEntry.filePath; - const dstDir = FileEntry.getAreaStorageDirectoryByTag(destStorageTag); + const srcPath = srcFileEntry.filePath; + const dstDir = FileEntry.getAreaStorageDirectoryByTag(destStorageTag); if(!dstDir) { return cb(Errors.Invalid('Invalid storage tag')); } - const dstPath = paths.join(dstDir, destFileName); + const dstPath = paths.join(dstDir, destFileName); async.series( [ function movePhysFile(callback) { if(srcPath === dstPath) { - return callback(null); // don't need to move file, but may change areas + return callback(null); // don't need to move file, but may change areas } fse.move(srcPath, dstPath, err => { @@ -654,8 +654,8 @@ module.exports = class FileEntry { function updateDatabase(callback) { fileDb.run( `UPDATE file - SET area_tag = ?, file_name = ?, storage_tag = ? - WHERE file_id = ?;`, + SET area_tag = ?, file_name = ?, storage_tag = ? + WHERE file_id = ?;`, [ destAreaTag, destFileName, destStorageTag, srcFileEntry.fileId ], err => { return callback(err); diff --git a/core/file_transfer.js b/core/file_transfer.js index e66a98f7..dbd19a8f 100644 --- a/core/file_transfer.js +++ b/core/file_transfer.js @@ -1,50 +1,50 @@ /* jslint node: true */ 'use strict'; -// enigma-bbs -const MenuModule = require('./menu_module.js').MenuModule; -const Config = require('./config.js').get; -const stringFormat = require('./string_format.js'); -const Errors = require('./enig_error.js').Errors; -const DownloadQueue = require('./download_queue.js'); -const StatLog = require('./stat_log.js'); -const FileEntry = require('./file_entry.js'); -const Log = require('./logger.js').log; -const Events = require('./events.js'); +// enigma-bbs +const MenuModule = require('./menu_module.js').MenuModule; +const Config = require('./config.js').get; +const stringFormat = require('./string_format.js'); +const Errors = require('./enig_error.js').Errors; +const DownloadQueue = require('./download_queue.js'); +const StatLog = require('./stat_log.js'); +const FileEntry = require('./file_entry.js'); +const Log = require('./logger.js').log; +const Events = require('./events.js'); -// deps -const async = require('async'); -const _ = require('lodash'); -const pty = require('node-pty'); -const temptmp = require('temptmp').createTrackedSession('transfer_file'); -const paths = require('path'); -const fs = require('graceful-fs'); -const fse = require('fs-extra'); +// deps +const async = require('async'); +const _ = require('lodash'); +const pty = require('node-pty'); +const temptmp = require('temptmp').createTrackedSession('transfer_file'); +const paths = require('path'); +const fs = require('graceful-fs'); +const fse = require('fs-extra'); -// some consts -const SYSTEM_EOL = require('os').EOL; -const TEMP_SUFFIX = 'enigtf-'; // temp CWD/etc. +// some consts +const SYSTEM_EOL = require('os').EOL; +const TEMP_SUFFIX = 'enigtf-'; // temp CWD/etc. /* - Notes - ----------------------------------------------------------------------------- + Notes + ----------------------------------------------------------------------------- - See core/config.js for external protocol configuration + See core/config.js for external protocol configuration - Resources - ----------------------------------------------------------------------------- + Resources + ----------------------------------------------------------------------------- - ZModem - * http://gallium.inria.fr/~doligez/zmodem/zmodem.txt - * https://github.com/protomouse/synchronet/blob/master/src/sbbs3/zmodem.c + ZModem + * http://gallium.inria.fr/~doligez/zmodem/zmodem.txt + * https://github.com/protomouse/synchronet/blob/master/src/sbbs3/zmodem.c */ exports.moduleInfo = { - name : 'Transfer file', - desc : 'Sends or receives a file(s)', - author : 'NuSkooler', + name : 'Transfer file', + desc : 'Sends or receives a file(s)', + author : 'NuSkooler', }; exports.getModule = class TransferFileModule extends MenuModule { @@ -54,7 +54,7 @@ exports.getModule = class TransferFileModule extends MenuModule { this.config = this.menuConfig.config || {}; // - // Most options can be set via extraArgs or config block + // Most options can be set via extraArgs or config block // const config = Config(); if(options.extraArgs) { @@ -99,11 +99,11 @@ exports.getModule = class TransferFileModule extends MenuModule { } } - this.protocolConfig = this.protocolConfig || config.fileTransferProtocols.zmodem8kSz; // try for *something* - this.direction = this.direction || 'send'; - this.sendQueue = this.sendQueue || []; + this.protocolConfig = this.protocolConfig || config.fileTransferProtocols.zmodem8kSz; // try for *something* + this.direction = this.direction || 'send'; + this.sendQueue = this.sendQueue || []; - // Ensure sendQueue is an array of objects that contain at least a 'path' member + // Ensure sendQueue is an array of objects that contain at least a 'path' member this.sendQueue = this.sendQueue.map(item => { if(_.isString(item)) { return { path : item }; @@ -128,8 +128,8 @@ exports.getModule = class TransferFileModule extends MenuModule { } sendFiles(cb) { - // assume *sending* can always batch - // :TODO: Look into this further + // assume *sending* can always batch + // :TODO: Look into this further const allFiles = this.sendQueue.map(f => f.path); this.executeExternalProtocolHandlerForSend(allFiles, err => { if(err) { @@ -149,64 +149,64 @@ exports.getModule = class TransferFileModule extends MenuModule { } /* - sendFiles(cb) { - // :TODO: built in/native protocol support + sendFiles(cb) { + // :TODO: built in/native protocol support - if(this.protocolConfig.external.supportsBatch) { - const allFiles = this.sendQueue.map(f => f.path); - this.executeExternalProtocolHandlerForSend(allFiles, err => { - if(err) { - this.client.log.warn( { files : allFiles, error : err.message }, 'Error sending file(s)' ); - } else { - const sentFiles = []; - this.sendQueue.forEach(f => { - f.sent = true; - sentFiles.push(f.path); + if(this.protocolConfig.external.supportsBatch) { + const allFiles = this.sendQueue.map(f => f.path); + this.executeExternalProtocolHandlerForSend(allFiles, err => { + if(err) { + this.client.log.warn( { files : allFiles, error : err.message }, 'Error sending file(s)' ); + } else { + const sentFiles = []; + this.sendQueue.forEach(f => { + f.sent = true; + sentFiles.push(f.path); - }); + }); - this.client.log.info( { sentFiles : sentFiles }, `Successfully sent ${sentFiles.length} file(s)` ); - } - return cb(err); - }); - } else { - // :TODO: we need to prompt between entries such that users can prepare their clients - async.eachSeries(this.sendQueue, (queueItem, next) => { - this.executeExternalProtocolHandlerForSend(queueItem.path, err => { - if(err) { - this.client.log.warn( { file : queueItem.path, error : err.message }, 'Error sending file' ); - } else { - queueItem.sent = true; + this.client.log.info( { sentFiles : sentFiles }, `Successfully sent ${sentFiles.length} file(s)` ); + } + return cb(err); + }); + } else { + // :TODO: we need to prompt between entries such that users can prepare their clients + async.eachSeries(this.sendQueue, (queueItem, next) => { + this.executeExternalProtocolHandlerForSend(queueItem.path, err => { + if(err) { + this.client.log.warn( { file : queueItem.path, error : err.message }, 'Error sending file' ); + } else { + queueItem.sent = true; - this.client.log.info( { sentFile : queueItem.path }, 'Successfully sent file' ); - } - return next(err); - }); - }, err => { - return cb(err); - }); - } - } - */ + this.client.log.info( { sentFile : queueItem.path }, 'Successfully sent file' ); + } + return next(err); + }); + }, err => { + return cb(err); + }); + } + } + */ moveFileWithCollisionHandling(src, dst, cb) { // - // Move |src| -> |dst| renaming to file(1).ext, file(2).ext, etc. - // in the case of collisions. + // Move |src| -> |dst| renaming to file(1).ext, file(2).ext, etc. + // in the case of collisions. // - const dstPath = paths.dirname(dst); - const dstFileExt = paths.extname(dst); - const dstFileSuffix = paths.basename(dst, dstFileExt); + const dstPath = paths.dirname(dst); + const dstFileExt = paths.extname(dst); + const dstFileSuffix = paths.basename(dst, dstFileExt); - let renameIndex = 0; - let movedOk = false; + let renameIndex = 0; + let movedOk = false; let tryDstPath; async.until( - () => movedOk, // until moved OK + () => movedOk, // until moved OK (cb) => { if(0 === renameIndex) { - // try originally supplied path first + // try originally supplied path first tryDstPath = dst; } else { tryDstPath = paths.join(dstPath, `${dstFileSuffix}(${renameIndex})${dstFileExt}`); @@ -216,7 +216,7 @@ exports.getModule = class TransferFileModule extends MenuModule { if(err) { if('EEXIST' === err.code) { renameIndex += 1; - return cb(null); // keep trying + return cb(null); // keep trying } return cb(err); @@ -242,8 +242,8 @@ exports.getModule = class TransferFileModule extends MenuModule { if(this.recvFileName) { // - // file name specified - we expect a single file in |this.recvDirectory| - // by the name of |this.recvFileName| + // file name specified - we expect a single file in |this.recvDirectory| + // by the name of |this.recvFileName| // const recvFullPath = paths.join(this.recvDirectory, this.recvFileName); fs.stat(recvFullPath, (err, stats) => { @@ -260,21 +260,21 @@ exports.getModule = class TransferFileModule extends MenuModule { }); } else { // - // Blind Upload (recv): files in |this.recvDirectory| should be named appropriately already + // Blind Upload (recv): files in |this.recvDirectory| should be named appropriately already // fs.readdir(this.recvDirectory, (err, files) => { if(err) { return cb(err); } - // stat each to grab files only + // stat each to grab files only async.each(files, (fileName, nextFile) => { const recvFullPath = paths.join(this.recvDirectory, fileName); fs.stat(recvFullPath, (err, stats) => { if(err) { this.client.log.warn('Failed to stat file', { path : recvFullPath } ); - return nextFile(null); // just try the next one + return nextFile(null); // just try the next one } if(stats.isFile()) { @@ -299,7 +299,7 @@ exports.getModule = class TransferFileModule extends MenuModule { } prepAndBuildSendArgs(filePaths, cb) { - const externalArgs = this.protocolConfig.external['sendArgs']; + const externalArgs = this.protocolConfig.external['sendArgs']; async.waterfall( [ @@ -311,7 +311,7 @@ exports.getModule = class TransferFileModule extends MenuModule { temptmp.open( { prefix : TEMP_SUFFIX, suffix : '.txt' }, (err, tempFileInfo) => { if(err) { - return callback(err); // failed to create it + return callback(err); // failed to create it } fs.write(tempFileInfo.fd, filePaths.join(SYSTEM_EOL)); @@ -321,16 +321,16 @@ exports.getModule = class TransferFileModule extends MenuModule { }); }, function createArgs(tempFileListPath, callback) { - // initial args: ignore {filePaths} as we must break that into it's own sep array items + // initial args: ignore {filePaths} as we must break that into it's own sep array items const args = externalArgs.map(arg => { return '{filePaths}' === arg ? arg : stringFormat(arg, { - fileListPath : tempFileListPath || '', + fileListPath : tempFileListPath || '', }); }); const filePathsPos = args.indexOf('{filePaths}'); if(filePathsPos > -1) { - // replace {filePaths} with 0:n individual entries in |args| + // replace {filePaths} with 0:n individual entries in |args| args.splice.apply( args, [ filePathsPos, 1 ].concat(filePaths) ); } @@ -344,19 +344,19 @@ exports.getModule = class TransferFileModule extends MenuModule { } prepAndBuildRecvArgs(cb) { - const argsKey = this.recvFileName ? 'recvArgsNonBatch' : 'recvArgs'; - const externalArgs = this.protocolConfig.external[argsKey]; - const args = externalArgs.map(arg => stringFormat(arg, { - uploadDir : this.recvDirectory, - fileName : this.recvFileName || '', + const argsKey = this.recvFileName ? 'recvArgsNonBatch' : 'recvArgs'; + const externalArgs = this.protocolConfig.external[argsKey]; + const args = externalArgs.map(arg => stringFormat(arg, { + uploadDir : this.recvDirectory, + fileName : this.recvFileName || '', })); return cb(null, args); } executeExternalProtocolHandler(args, cb) { - const external = this.protocolConfig.external; - const cmd = external[`${this.direction}Cmd`]; + const external = this.protocolConfig.external; + const cmd = external[`${this.direction}Cmd`]; this.client.log.debug( { cmd : cmd, args : args, tempDir : this.recvDirectory, direction : this.direction }, @@ -364,18 +364,18 @@ exports.getModule = class TransferFileModule extends MenuModule { ); const spawnOpts = { - cols : this.client.term.termWidth, - rows : this.client.term.termHeight, - cwd : this.recvDirectory, - encoding : null, // don't bork our data! + cols : this.client.term.termWidth, + rows : this.client.term.termHeight, + cwd : this.recvDirectory, + encoding : null, // don't bork our data! }; const externalProc = pty.spawn(cmd, args, spawnOpts); this.client.setTemporaryDirectDataHandler(data => { - // needed for things like sz/rz + // needed for things like sz/rz if(external.escapeTelnet) { - const tmp = data.toString('binary').replace(/\xff{2}/g, '\xff'); // de-escape + const tmp = data.toString('binary').replace(/\xff{2}/g, '\xff'); // de-escape externalProc.write(Buffer.from(tmp, 'binary')); } else { externalProc.write(data); @@ -383,9 +383,9 @@ exports.getModule = class TransferFileModule extends MenuModule { }); externalProc.on('data', data => { - // needed for things like sz/rz + // needed for things like sz/rz if(external.escapeTelnet) { - const tmp = data.toString('binary').replace(/\xff/g, '\xff\xff'); // escape + const tmp = data.toString('binary').replace(/\xff/g, '\xff\xff'); // escape this.client.term.rawWrite(Buffer.from(tmp, 'binary')); } else { this.client.term.rawWrite(data); @@ -443,9 +443,9 @@ exports.getModule = class TransferFileModule extends MenuModule { } updateSendStats(cb) { - let downloadBytes = 0; - let downloadCount = 0; - let fileIds = []; + let downloadBytes = 0; + let downloadCount = 0; + let fileIds = []; async.each(this.sendQueue, (queueItem, next) => { if(!queueItem.sent) { @@ -462,7 +462,7 @@ exports.getModule = class TransferFileModule extends MenuModule { return next(null); } - // we just have a path - figure it out + // we just have a path - figure it out fs.stat(queueItem.path, (err, stats) => { if(err) { this.client.log.warn( { error : err.message, path : queueItem.path }, 'File stat failed' ); @@ -474,7 +474,7 @@ exports.getModule = class TransferFileModule extends MenuModule { return next(null); }); }, () => { - // All stats/meta currently updated via fire & forget - if this is ever a issue, we can wait for callbacks + // All stats/meta currently updated via fire & forget - if this is ever a issue, we can wait for callbacks StatLog.incrementUserStat(this.client.user, 'dl_total_count', downloadCount); StatLog.incrementUserStat(this.client.user, 'dl_total_bytes', downloadBytes); StatLog.incrementSystemStat('dl_total_count', downloadCount); @@ -489,16 +489,16 @@ exports.getModule = class TransferFileModule extends MenuModule { } updateRecvStats(cb) { - let uploadBytes = 0; - let uploadCount = 0; + let uploadBytes = 0; + let uploadCount = 0; async.each(this.recvFilePaths, (filePath, next) => { - // we just have a path - figure it out + // we just have a path - figure it out fs.stat(filePath, (err, stats) => { if(err) { this.client.log.warn( { error : err.message, path : filePath }, 'File stat failed' ); } else { - uploadCount += 1; + uploadCount += 1; uploadBytes += stats.size; } @@ -517,7 +517,7 @@ exports.getModule = class TransferFileModule extends MenuModule { initSequence() { const self = this; - // :TODO: break this up to send|recv + // :TODO: break this up to send|recv async.series( [ @@ -545,16 +545,16 @@ exports.getModule = class TransferFileModule extends MenuModule { }); if(sentFileIds.length > 0) { - // remove items we sent from the D/L queue + // remove items we sent from the D/L queue const dlQueue = new DownloadQueue(self.client); const dlFileEntries = dlQueue.removeItems(sentFileIds); - // fire event for downloaded entries + // fire event for downloaded entries Events.emit( Events.getSystemEvents().UserDownload, { - user : self.client.user, - files : dlFileEntries + user : self.client.user, + files : dlFileEntries } ); diff --git a/core/file_transfer_protocol_select.js b/core/file_transfer_protocol_select.js index 1fe1944b..d8500dc5 100644 --- a/core/file_transfer_protocol_select.js +++ b/core/file_transfer_protocol_select.js @@ -1,24 +1,24 @@ /* jslint node: true */ 'use strict'; -// enigma-bbs -const MenuModule = require('./menu_module.js').MenuModule; -const Config = require('./config.js').get; -const stringFormat = require('./string_format.js'); -const ViewController = require('./view_controller.js').ViewController; +// enigma-bbs +const MenuModule = require('./menu_module.js').MenuModule; +const Config = require('./config.js').get; +const stringFormat = require('./string_format.js'); +const ViewController = require('./view_controller.js').ViewController; -// deps -const async = require('async'); -const _ = require('lodash'); +// deps +const async = require('async'); +const _ = require('lodash'); exports.moduleInfo = { - name : 'File transfer protocol selection', - desc : 'Select protocol / method for file transfer', - author : 'NuSkooler', + name : 'File transfer protocol selection', + desc : 'Select protocol / method for file transfer', + author : 'NuSkooler', }; const MciViewIds = { - protList : 1, + protList : 1, }; exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { @@ -36,7 +36,7 @@ exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { this.config.direction = this.config.direction || 'send'; - this.extraArgs = options.extraArgs; + this.extraArgs = options.extraArgs; if(_.has(options, 'lastMenuResult.sentFileIds')) { this.sentFileIds = options.lastMenuResult.sentFileIds; @@ -46,13 +46,13 @@ exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { this.recvFilePaths = options.lastMenuResult.recvFilePaths; } - this.fallbackOnly = options.lastMenuResult ? true : false; + this.fallbackOnly = options.lastMenuResult ? true : false; this.loadAvailProtocols(); this.menuMethods = { selectProtocol : (formData, extraArgs, cb) => { - const protocol = this.protocols[formData.value.protocol]; + const protocol = this.protocols[formData.value.protocol]; const finalExtraArgs = this.extraArgs || {}; Object.assign(finalExtraArgs, { protocol : protocol.protocol, direction : this.config.direction }, extraArgs ); @@ -81,7 +81,7 @@ exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { initSequence() { if(this.sentFileIds || this.recvFilePaths) { - // nothing to do here; move along (we're just falling through) + // nothing to do here; move along (we're just falling through) this.prevMenu(); } else { super.initSequence(); @@ -94,15 +94,15 @@ exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { return cb(err); } - const self = this; - const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); + const self = this; + const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); async.series( [ function loadFromConfig(callback) { const loadOpts = { - callingMenu : self, - mciMap : mciData.menu + callingMenu : self, + mciMap : mciData.menu }; return vc.loadFromMenuConfig(loadOpts, callback); @@ -110,8 +110,8 @@ exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { function populateList(callback) { const protListView = vc.getView(MciViewIds.protList); - const protListFormat = self.config.protListFormat || '{name}'; - const protListFocusFormat = self.config.protListFocusFormat || protListFormat; + const protListFormat = self.config.protListFormat || '{name}'; + const protListFocusFormat = self.config.protListFocusFormat || protListFormat; protListView.setItems(self.protocols.map(p => stringFormat(protListFormat, p) ) ); protListView.setFocusItems(self.protocols.map(p => stringFormat(protListFocusFormat, p) ) ); @@ -131,22 +131,22 @@ exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { loadAvailProtocols() { this.protocols = _.map(Config().fileTransferProtocols, (protInfo, protocol) => { return { - protocol : protocol, - name : protInfo.name, - hasBatch : _.has(protInfo, 'external.recvArgs'), - hasNonBatch : _.has(protInfo, 'external.recvArgsNonBatch'), - sort : protInfo.sort, + protocol : protocol, + name : protInfo.name, + hasBatch : _.has(protInfo, 'external.recvArgs'), + hasNonBatch : _.has(protInfo, 'external.recvArgsNonBatch'), + sort : protInfo.sort, }; }); - // Filter out batch vs non-batch only protocols - if(this.extraArgs.recvFileName) { // non-batch aka non-blind + // Filter out batch vs non-batch only protocols + if(this.extraArgs.recvFileName) { // non-batch aka non-blind this.protocols = this.protocols.filter( prot => prot.hasNonBatch ); } else { this.protocols = this.protocols.filter( prot => prot.hasBatch ); } - // natural sort taking explicit orders into consideration + // natural sort taking explicit orders into consideration this.protocols.sort( (a, b) => { if(_.isNumber(a.sort) && _.isNumber(b.sort)) { return a.sort - b.sort; diff --git a/core/file_util.js b/core/file_util.js index 428622da..64167771 100644 --- a/core/file_util.js +++ b/core/file_util.js @@ -1,28 +1,28 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const EnigAssert = require('./enigma_assert.js'); +// ENiGMA½ +const EnigAssert = require('./enigma_assert.js'); -// deps -const fse = require('fs-extra'); -const paths = require('path'); -const async = require('async'); +// deps +const fse = require('fs-extra'); +const paths = require('path'); +const async = require('async'); -exports.moveFileWithCollisionHandling = moveFileWithCollisionHandling; -exports.copyFileWithCollisionHandling = copyFileWithCollisionHandling; -exports.pathWithTerminatingSeparator = pathWithTerminatingSeparator; +exports.moveFileWithCollisionHandling = moveFileWithCollisionHandling; +exports.copyFileWithCollisionHandling = copyFileWithCollisionHandling; +exports.pathWithTerminatingSeparator = pathWithTerminatingSeparator; function moveOrCopyFileWithCollisionHandling(src, dst, operation, cb) { - operation = operation || 'copy'; - const dstPath = paths.dirname(dst); - const dstFileExt = paths.extname(dst); - const dstFileSuffix = paths.basename(dst, dstFileExt); + operation = operation || 'copy'; + const dstPath = paths.dirname(dst); + const dstFileExt = paths.extname(dst); + const dstFileSuffix = paths.basename(dst, dstFileExt); EnigAssert('move' === operation || 'copy' === operation); - let renameIndex = 0; - let opOk = false; + let renameIndex = 0; + let opOk = false; let tryDstPath; function tryOperation(src, dst, callback) { @@ -38,10 +38,10 @@ function moveOrCopyFileWithCollisionHandling(src, dst, operation, cb) { } async.until( - () => opOk, // until moved OK + () => opOk, // until moved OK (cb) => { if(0 === renameIndex) { - // try originally supplied path first + // try originally supplied path first tryDstPath = dst; } else { tryDstPath = paths.join(dstPath, `${dstFileSuffix}(${renameIndex})${dstFileExt}`); @@ -49,11 +49,11 @@ function moveOrCopyFileWithCollisionHandling(src, dst, operation, cb) { tryOperation(src, tryDstPath, err => { if(err) { - // for some reason fs-extra copy doesn't pass err.code - // :TODO: this is dangerous: submit a PR to fs-extra to set EEXIST + // for some reason fs-extra copy doesn't pass err.code + // :TODO: this is dangerous: submit a PR to fs-extra to set EEXIST if('EEXIST' === err.code || 'copy' === operation) { renameIndex += 1; - return cb(null); // keep trying + return cb(null); // keep trying } return cb(err); @@ -70,8 +70,8 @@ function moveOrCopyFileWithCollisionHandling(src, dst, operation, cb) { } // -// Move |src| -> |dst| renaming to file(1).ext, file(2).ext, etc. -// in the case of collisions. +// Move |src| -> |dst| renaming to file(1).ext, file(2).ext, etc. +// in the case of collisions. // function moveFileWithCollisionHandling(src, dst, cb) { return moveOrCopyFileWithCollisionHandling(src, dst, 'move', cb); diff --git a/core/fnv1a.js b/core/fnv1a.js index 53400a66..9acc8f27 100644 --- a/core/fnv1a.js +++ b/core/fnv1a.js @@ -1,9 +1,9 @@ /* jslint node: true */ 'use strict'; -let _ = require('lodash'); +let _ = require('lodash'); -// FNV-1a based on work here: https://github.com/wiedi/node-fnv +// FNV-1a based on work here: https://github.com/wiedi/node-fnv module.exports = class FNV1a { constructor(data) { this.hash = 0x811c9dc5; @@ -29,8 +29,8 @@ module.exports = class FNV1a { for(let b of data) { this.hash = this.hash ^ b; this.hash += - (this.hash << 24) + (this.hash << 8) + (this.hash << 7) + - (this.hash << 4) + (this.hash << 1); + (this.hash << 24) + (this.hash << 8) + (this.hash << 7) + + (this.hash << 4) + (this.hash << 1); } return this; diff --git a/core/fse.js b/core/fse.js index 6dea6a1b..7d3565ad 100644 --- a/core/fse.js +++ b/core/fse.js @@ -1,118 +1,118 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const ansi = require('./ansi_term.js'); -const theme = require('./theme.js'); -const Message = require('./message.js'); -const updateMessageAreaLastReadId = require('./message_area.js').updateMessageAreaLastReadId; -const getMessageAreaByTag = require('./message_area.js').getMessageAreaByTag; -const User = require('./user.js'); -const StatLog = require('./stat_log.js'); -const stringFormat = require('./string_format.js'); -const MessageAreaConfTempSwitcher = require('./mod_mixins.js').MessageAreaConfTempSwitcher; -const { isAnsi, cleanControlCodes, insert } = require('./string_util.js'); -const Config = require('./config.js').get; -const { getAddressedToInfo } = require('./mail_util.js'); +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const ansi = require('./ansi_term.js'); +const theme = require('./theme.js'); +const Message = require('./message.js'); +const updateMessageAreaLastReadId = require('./message_area.js').updateMessageAreaLastReadId; +const getMessageAreaByTag = require('./message_area.js').getMessageAreaByTag; +const User = require('./user.js'); +const StatLog = require('./stat_log.js'); +const stringFormat = require('./string_format.js'); +const MessageAreaConfTempSwitcher = require('./mod_mixins.js').MessageAreaConfTempSwitcher; +const { isAnsi, cleanControlCodes, insert } = require('./string_util.js'); +const Config = require('./config.js').get; +const { getAddressedToInfo } = require('./mail_util.js'); -// deps -const async = require('async'); -const assert = require('assert'); -const _ = require('lodash'); -const moment = require('moment'); +// deps +const async = require('async'); +const assert = require('assert'); +const _ = require('lodash'); +const moment = require('moment'); exports.moduleInfo = { - name : 'Full Screen Editor (FSE)', - desc : 'A full screen editor/viewer', - author : 'NuSkooler', + name : 'Full Screen Editor (FSE)', + desc : 'A full screen editor/viewer', + author : 'NuSkooler', }; const MciViewIds = { header : { - from : 1, - to : 2, - subject : 3, - errorMsg : 4, - modTimestamp : 5, - msgNum : 6, - msgTotal : 7, + from : 1, + to : 2, + subject : 3, + errorMsg : 4, + modTimestamp : 5, + msgNum : 6, + msgTotal : 7, - customRangeStart : 10, // 10+ = customs + customRangeStart : 10, // 10+ = customs }, body : { - message : 1, + message : 1, }, - // :TODO: quote builder MCIs - remove all magic #'s + // :TODO: quote builder MCIs - remove all magic #'s - // :TODO: consolidate all footer MCI's - remove all magic #'s + // :TODO: consolidate all footer MCI's - remove all magic #'s ViewModeFooter : { - MsgNum : 6, - MsgTotal : 7, - // :TODO: Just use custom ranges + MsgNum : 6, + MsgTotal : 7, + // :TODO: Just use custom ranges }, quoteBuilder : { - quotedMsg : 1, - // 2 NYI - quoteLines : 3, + quotedMsg : 1, + // 2 NYI + quoteLines : 3, } }; /* - Custom formatting: - header - fromUserName - toUserName + Custom formatting: + header + fromUserName + toUserName - fromRealName (may be fromUserName) NYI - toRealName (may be toUserName) NYI + fromRealName (may be fromUserName) NYI + toRealName (may be toUserName) NYI - fromRemoteUser (may be "N/A") - toRemoteUser (may be "N/A") - subject - modTimestamp - msgNum - msgTotal (in area) - messageId + fromRemoteUser (may be "N/A") + toRemoteUser (may be "N/A") + subject + modTimestamp + msgNum + msgTotal (in area) + messageId */ -// :TODO: convert code in this class to newer styles, conventions, etc. There is a lot of experimental stuff here that has better (DRY) alternatives +// :TODO: convert code in this class to newer styles, conventions, etc. There is a lot of experimental stuff here that has better (DRY) alternatives exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModule extends MessageAreaConfTempSwitcher(MenuModule) { constructor(options) { super(options); - const self = this; - const config = this.menuConfig.config; + const self = this; + const config = this.menuConfig.config; // - // menuConfig.config: - // editorType : email | area - // editorMode : view | edit | quote + // menuConfig.config: + // editorType : email | area + // editorMode : view | edit | quote // - // menuConfig.config or extraArgs - // messageAreaTag - // messageIndex / messageTotal - // toUserId + // menuConfig.config or extraArgs + // messageAreaTag + // messageIndex / messageTotal + // toUserId // - this.editorType = config.editorType; - this.editorMode = config.editorMode; + this.editorType = config.editorType; + this.editorMode = config.editorMode; if(config.messageAreaTag) { - // :TODO: swtich to this.config.messageAreaTag so we can follow Object.assign pattern for config/extraArgs - this.messageAreaTag = config.messageAreaTag; + // :TODO: swtich to this.config.messageAreaTag so we can follow Object.assign pattern for config/extraArgs + this.messageAreaTag = config.messageAreaTag; } - this.messageIndex = config.messageIndex || 0; - this.messageTotal = config.messageTotal || 0; - this.toUserId = config.toUserId || 0; + this.messageIndex = config.messageIndex || 0; + this.messageTotal = config.messageTotal || 0; + this.toUserId = config.toUserId || 0; - // extraArgs can override some config + // extraArgs can override some config if(_.isObject(options.extraArgs)) { if(options.extraArgs.messageAreaTag) { this.messageAreaTag = options.extraArgs.messageAreaTag; @@ -140,7 +140,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul this.menuMethods = { // - // Validation stuff + // Validation stuff // viewValidationListener : function(err, cb) { var errMsgView = self.viewControllers.header.getView(MciViewIds.header.errorMsg); @@ -150,7 +150,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul errMsgView.setText(err.message); if(MciViewIds.header.subject === err.view.getId()) { - // :TODO: for "area" mode, should probably just bail if this is emtpy (e.g. cancel) + // :TODO: for "area" mode, should probably just bail if this is emtpy (e.g. cancel) } } else { errMsgView.clearText(); @@ -201,19 +201,19 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul if(self.newQuoteBlock) { self.newQuoteBlock = false; - // :TODO: If replying to ANSI, add a blank sepration line here + // :TODO: If replying to ANSI, add a blank sepration line here quoteMsgView.addText(self.getQuoteByHeader()); } const quoteListView = self.viewControllers.quoteBuilder.getView(MciViewIds.quoteBuilder.quoteLines); - const quoteText = quoteListView.getItem(formData.value.quote); + const quoteText = quoteListView.getItem(formData.value.quote); quoteMsgView.addText(quoteText); // - // If this is *not* the last item, advance. Otherwise, do nothing as we - // don't want to jump back to the top and repeat already quoted lines + // If this is *not* the last item, advance. Otherwise, do nothing as we + // don't want to jump back to the top and repeat already quoted lines // if(quoteListView.getData() !== quoteListView.getCount() - 1) { @@ -229,18 +229,18 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul return cb(null); }, /* - replyDiscard : function(formData, extraArgs) { - // :TODO: need to prompt yes/no - // :TODO: @method for fallback would be better - self.prevMenu(); - }, - */ + replyDiscard : function(formData, extraArgs) { + // :TODO: need to prompt yes/no + // :TODO: @method for fallback would be better + self.prevMenu(); + }, + */ editModeMenuHelp : function(formData, extraArgs, cb) { self.viewControllers.footerEditorMenu.setFocus(false); return self.displayHelp(cb); }, /////////////////////////////////////////////////////////////////////// - // View Mode + // View Mode /////////////////////////////////////////////////////////////////////// viewModeMenuHelp : function(formData, extraArgs, cb) { self.viewControllers.footerView.setFocus(false); @@ -266,43 +266,43 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul } getFooterName() { - return 'footer' + _.upperFirst(this.footerMode); // e.g. 'footerEditor', 'footerEditorMenu', ... + return 'footer' + _.upperFirst(this.footerMode); // e.g. 'footerEditor', 'footerEditorMenu', ... } getFormId(name) { return { - header : 0, - body : 1, - footerEditor : 2, - footerEditorMenu : 3, - footerView : 4, - quoteBuilder : 5, + header : 0, + body : 1, + footerEditor : 2, + footerEditorMenu : 3, + footerView : 4, + quoteBuilder : 5, - help : 50, + help : 50, }[name]; } getHeaderFormatObj() { - const remoteUserNotAvail = this.menuConfig.config.remoteUserNotAvail || 'N/A'; - const localUserIdNotAvail = this.menuConfig.config.localUserIdNotAvail || 'N/A'; - const modTimestampFormat = this.menuConfig.config.modTimestampFormat || this.client.currentTheme.helpers.getDateTimeFormat(); + const remoteUserNotAvail = this.menuConfig.config.remoteUserNotAvail || 'N/A'; + const localUserIdNotAvail = this.menuConfig.config.localUserIdNotAvail || 'N/A'; + const modTimestampFormat = this.menuConfig.config.modTimestampFormat || this.client.currentTheme.helpers.getDateTimeFormat(); return { - // :TODO: ensure we show real names for form/to if they are enforced in the area - fromUserName : this.message.fromUserName, - toUserName : this.message.toUserName, - // :TODO: + // :TODO: ensure we show real names for form/to if they are enforced in the area + fromUserName : this.message.fromUserName, + toUserName : this.message.toUserName, + // :TODO: //fromRealName //toRealName - fromUserId : _.get(this.message, 'meta.System.local_from_user_id', localUserIdNotAvail), - toUserId : _.get(this.message, 'meta.System.local_to_user_id', localUserIdNotAvail), - fromRemoteUser : _.get(this.message, 'meta.System.remote_from_user', remoteUserNotAvail), - toRemoteUser : _.get(this.messgae, 'meta.System.remote_to_user', remoteUserNotAvail), - subject : this.message.subject, - modTimestamp : this.message.modTimestamp.format(modTimestampFormat), - msgNum : this.messageIndex + 1, - msgTotal : this.messageTotal, - messageId : this.message.messageId, + fromUserId : _.get(this.message, 'meta.System.local_from_user_id', localUserIdNotAvail), + toUserId : _.get(this.message, 'meta.System.local_to_user_id', localUserIdNotAvail), + fromRemoteUser : _.get(this.message, 'meta.System.remote_from_user', remoteUserNotAvail), + toRemoteUser : _.get(this.messgae, 'meta.System.remote_to_user', remoteUserNotAvail), + subject : this.message.subject, + modTimestamp : this.message.modTimestamp.format(modTimestampFormat), + msgNum : this.messageIndex + 1, + msgTotal : this.messageTotal, + messageId : this.message.messageId, }; } @@ -317,26 +317,26 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul const headerValues = this.viewControllers.header.getFormData().value; const msgOpts = { - areaTag : this.messageAreaTag, - toUserName : headerValues.to, - fromUserName : this.client.user.username, - subject : headerValues.subject, - // :TODO: don't hard code 1 here: - message : this.viewControllers.body.getView(MciViewIds.body.message).getData( { forceLineTerms : this.replyIsAnsi } ), + areaTag : this.messageAreaTag, + toUserName : headerValues.to, + fromUserName : this.client.user.username, + subject : headerValues.subject, + // :TODO: don't hard code 1 here: + message : this.viewControllers.body.getView(MciViewIds.body.message).getData( { forceLineTerms : this.replyIsAnsi } ), }; if(this.isReply()) { - msgOpts.replyToMsgId = this.replyToMessage.messageId; + msgOpts.replyToMsgId = this.replyToMessage.messageId; if(this.replyIsAnsi) { // - // Ensure first characters indicate ANSI for detection down - // the line (other boards/etc.). We also set explicit_encoding - // to packetAnsiMsgEncoding (generally cp437) as various boards - // really don't like ANSI messages in UTF-8 encoding (they should!) + // Ensure first characters indicate ANSI for detection down + // the line (other boards/etc.). We also set explicit_encoding + // to packetAnsiMsgEncoding (generally cp437) as various boards + // really don't like ANSI messages in UTF-8 encoding (they should!) // - msgOpts.meta = { System : { 'explicit_encoding' : _.get(Config(), 'scannerTossers.ftn_bso.packetAnsiMsgEncoding', 'cp437') } }; - msgOpts.message = `${ansi.reset()}${ansi.eraseData(2)}${ansi.goto(1,1)}\r\n${ansi.up()}${msgOpts.message}`; + msgOpts.meta = { System : { 'explicit_encoding' : _.get(Config(), 'scannerTossers.ftn_bso.packetAnsiMsgEncoding', 'cp437') } }; + msgOpts.message = `${ansi.reset()}${ansi.eraseData(2)}${ansi.goto(1,1)}\r\n${ansi.up()}${msgOpts.message}`; } } @@ -363,18 +363,18 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul this.initHeaderViewMode(); this.initFooterViewMode(); - const bodyMessageView = this.viewControllers.body.getView(MciViewIds.body.message); - let msg = this.message.message; + const bodyMessageView = this.viewControllers.body.getView(MciViewIds.body.message); + let msg = this.message.message; if(bodyMessageView && _.has(this, 'message.message')) { // - // We handle ANSI messages differently than standard messages -- this is required as - // we don't want to do things like word wrap ANSI, but instead, trust that it's formatted - // how the author wanted it + // We handle ANSI messages differently than standard messages -- this is required as + // we don't want to do things like word wrap ANSI, but instead, trust that it's formatted + // how the author wanted it // if(isAnsi(msg)) { // - // Find tearline - we want to color it differently. + // Find tearline - we want to color it differently. // const tearLinePos = this.message.getTearLinePosition(msg); @@ -383,10 +383,10 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul } bodyMessageView.setAnsi( - msg.replace(/\r?\n/g, '\r\n'), // messages are stored with CRLF -> LF + msg.replace(/\r?\n/g, '\r\n'), // messages are stored with CRLF -> LF { - prepped : false, - forceLineTerm : true, + prepped : false, + forceLineTerm : true, } ); } else { @@ -404,7 +404,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul [ function buildIfNecessary(callback) { if(self.isEditMode()) { - return self.buildMessage(callback); // creates initial self.message + return self.buildMessage(callback); // creates initial self.message } return callback(null); @@ -422,9 +422,9 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul } // - // If the message we're replying to is from a remote user - // don't try to look up the local user ID. Instead, mark the mail - // for export with the remote to address. + // If the message we're replying to is from a remote user + // don't try to look up the local user ID. Instead, mark the mail + // for export with the remote to address. // if(self.replyToMessage && self.replyToMessage.isFromRemoteUser()) { self.message.setRemoteToUser(self.replyToMessage.meta.System[Message.SystemMetaNames.RemoteFromUser]); @@ -433,9 +433,9 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul } // - // Detect if the user is attempting to send to a remote mail type that we support + // Detect if the user is attempting to send to a remote mail type that we support // - // :TODO: how to plug in support without tying to various types here? isSupportedExteranlType() or such + // :TODO: how to plug in support without tying to various types here? isSupportedExteranlType() or such const addressedToInfo = getAddressedToInfo(self.message.toUserName); if(addressedToInfo.name && Message.AddressFlavor.FTN === addressedToInfo.flavor) { self.message.setRemoteToUser(addressedToInfo.remote); @@ -444,7 +444,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul return callback(null); } - // we need to look it up + // we need to look it up User.getUserIdAndNameByLookup(self.message.toUserName, (err, toUserId) => { if(err) { return callback(err); @@ -466,7 +466,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul if(cb) { cb(null); } - return; // don't inc stats for private messages + return; // don't inc stats for private messages } return StatLog.incrementUserStat(this.client.user, 'post_count', 1, cb); @@ -479,9 +479,9 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul [ function moveToFooterPosition(callback) { // - // Calculate footer starting position + // Calculate footer starting position // - // row = (header height + body height) + // row = (header height + body height) // var footerRow = self.header.height + self.body.height; self.client.term.rawWrite(ansi.goto(footerRow, 1)); @@ -489,10 +489,10 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul }, function clearFooterArea(callback) { if(options.clear) { - // footer up to 3 rows in height + // footer up to 3 rows in height - // :TODO: We'd like to delete up to N rows, but this does not work - // in NetRunner: + // :TODO: We'd like to delete up to N rows, but this does not work + // in NetRunner: self.client.term.rawWrite(ansi.reset() + ansi.deleteLine(3)); self.client.term.rawWrite(ansi.reset() + ansi.eraseLine(2)); @@ -519,9 +519,9 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul } redrawScreen(cb) { - var comps = [ 'header', 'body' ]; - const self = this; - var art = self.menuConfig.config.art; + var comps = [ 'header', 'body' ]; + const self = this; + var art = self.menuConfig.config.art; self.client.term.rawWrite(ansi.resetScreen()); @@ -543,7 +543,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul }); }, function displayFooter(callback) { - // we have to treat the footer special + // we have to treat the footer special self.redrawFooter( { clear : false, footerName : self.getFooterName() }, function footerDisplayed(err) { callback(err); }); @@ -577,9 +577,9 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul if(_.isUndefined(this.viewControllers[footerName])) { var menuLoadOpts = { - callingMenu : this, - formId : formId, - mciMap : artData.mciMap + callingMenu : this, + formId : formId, + mciMap : artData.mciMap }; this.addViewController( @@ -597,8 +597,8 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul initSequence() { var mciData = { }; - const self = this; - var art = self.menuConfig.config.art; + const self = this; + var art = self.menuConfig.config.art; assert(_.isObject(art)); @@ -659,7 +659,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul [ function header(callback) { menuLoadOpts.formId = self.getFormId('header'); - menuLoadOpts.mciMap = mciData.header.mciMap; + menuLoadOpts.mciMap = mciData.header.mciMap; self.addViewController( 'header', @@ -669,8 +669,8 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul }); }, function body(callback) { - menuLoadOpts.formId = self.getFormId('body'); - menuLoadOpts.mciMap = mciData.body.mciMap; + menuLoadOpts.formId = self.getFormId('body'); + menuLoadOpts.mciMap = mciData.body.mciMap; self.addViewController( 'body', @@ -698,12 +698,12 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul from.acceptsFocus = false; //from.setText(self.client.user.username); - // :TODO: make this a method + // :TODO: make this a method var body = self.viewControllers.body.getView(MciViewIds.body.message); self.updateTextEditMode(body.getTextEditMode()); self.updateEditModePosition(body.getEditPosition()); - // :TODO: If view mode, set body to read only... which needs an impl... + // :TODO: If view mode, set body to read only... which needs an impl... callback(null); }, @@ -767,22 +767,22 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul mciReadyHandler(mciData, cb) { this.createInitialViews(mciData, err => { - // :TODO: Can probably be replaced with @systemMethod:validateUserNameExists when the framework is in - // place - if this is for existing usernames else validate spec + // :TODO: Can probably be replaced with @systemMethod:validateUserNameExists when the framework is in + // place - if this is for existing usernames else validate spec /* - self.viewControllers.header.on('leave', function headerViewLeave(view) { + self.viewControllers.header.on('leave', function headerViewLeave(view) { - if(2 === view.id) { // "to" field - self.validateToUserName(view.getData(), function result(err) { - if(err) { - // :TODO: display a error in a %TL area or such - view.clearText(); - self.viewControllers.headers.switchFocus(2); - } - }); - } - });*/ + if(2 === view.id) { // "to" field + self.validateToUserName(view.getData(), function result(err) { + if(err) { + // :TODO: display a error in a %TL area or such + view.clearText(); + self.viewControllers.headers.switchFocus(2); + } + }); + } + });*/ cb(err); }); @@ -793,7 +793,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul var posView = this.viewControllers.footerEditor.getView(1); if(posView) { this.client.term.rawWrite(ansi.savePos()); - // :TODO: Use new formatting techniques here, e.g. state.cursorPositionRow, cursorPositionCol and cursorPositionFormat + // :TODO: Use new formatting techniques here, e.g. state.cursorPositionRow, cursorPositionCol and cursorPositionFormat posView.setText(_.padStart(String(pos.row + 1), 2, '0') + ',' + _.padEnd(String(pos.col + 1), 2, '0')); this.client.term.rawWrite(ansi.restorePos()); } @@ -816,16 +816,16 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul } initHeaderViewMode() { - this.setHeaderText(MciViewIds.header.from, this.message.fromUserName); - this.setHeaderText(MciViewIds.header.to, this.message.toUserName); - this.setHeaderText(MciViewIds.header.subject, this.message.subject); - this.setHeaderText(MciViewIds.header.modTimestamp, moment(this.message.modTimestamp).format(this.client.currentTheme.helpers.getDateTimeFormat())); - this.setHeaderText(MciViewIds.header.msgNum, (this.messageIndex + 1).toString()); - this.setHeaderText(MciViewIds.header.msgTotal, this.messageTotal.toString()); + this.setHeaderText(MciViewIds.header.from, this.message.fromUserName); + this.setHeaderText(MciViewIds.header.to, this.message.toUserName); + this.setHeaderText(MciViewIds.header.subject, this.message.subject); + this.setHeaderText(MciViewIds.header.modTimestamp, moment(this.message.modTimestamp).format(this.client.currentTheme.helpers.getDateTimeFormat())); + this.setHeaderText(MciViewIds.header.msgNum, (this.messageIndex + 1).toString()); + this.setHeaderText(MciViewIds.header.msgTotal, this.messageTotal.toString()); this.updateCustomViewTextsWithFilter('header', MciViewIds.header.customRangeStart, this.getHeaderFormatObj()); - // if we changed conf/area we need to update any related standard MCI view + // if we changed conf/area we need to update any related standard MCI view this.refreshPredefinedMciViewsByCode('header', [ 'MA', 'MC', 'ML', 'CM' ] ); } @@ -835,15 +835,15 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul this.setHeaderText(MciViewIds.header.to, this.replyToMessage.fromUserName); // - // We want to prefix the subject with "RE: " only if it's not already - // that way -- avoid RE: RE: RE: RE: ... + // We want to prefix the subject with "RE: " only if it's not already + // that way -- avoid RE: RE: RE: RE: ... // let newSubj = this.replyToMessage.subject; if(false === /^RE:\s+/i.test(newSubj)) { newSubj = `RE: ${newSubj}`; } - this.setHeaderText(MciViewIds.header.subject, newSubj); + this.setHeaderText(MciViewIds.header.subject, newSubj); } initFooterViewMode() { @@ -869,7 +869,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul displayQuoteBuilder() { // - // Clear body area + // Clear body area // this.newQuoteBlock = true; const self = this; @@ -877,10 +877,10 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul async.waterfall( [ function clearAndDisplayArt(callback) { - // :TODO: NetRunner does NOT support delete line, so this does not work: + // :TODO: NetRunner does NOT support delete line, so this does not work: self.client.term.rawWrite( ansi.goto(self.header.height + 1, 1) + - ansi.deleteLine((self.client.term.termHeight - self.header.height) - 1)); + ansi.deleteLine((self.client.term.termHeight - self.header.height) - 1)); theme.displayThemeArt( { name : self.menuConfig.config.art.quote, client : self.client }, function displayed(err, artData) { callback(err, artData); @@ -891,9 +891,9 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul if(_.isUndefined(self.viewControllers.quoteBuilder)) { var menuLoadOpts = { - callingMenu : self, - formId : formId, - mciMap : artData.mciMap, + callingMenu : self, + formId : formId, + mciMap : artData.mciMap, }; self.addViewController( @@ -909,16 +909,16 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul }, function loadQuoteLines(callback) { const quoteView = self.viewControllers.quoteBuilder.getView(MciViewIds.quoteBuilder.quoteLines); - const bodyView = self.viewControllers.body.getView(MciViewIds.body.message); + const bodyView = self.viewControllers.body.getView(MciViewIds.body.message); self.replyToMessage.getQuoteLines( { - termWidth : self.client.term.termWidth, - termHeight : self.client.term.termHeight, - cols : quoteView.dimens.width, - startCol : quoteView.position.col, - ansiResetSgr : bodyView.styleSGR1, - ansiFocusPrefixSgr : quoteView.styleSGR2, + termWidth : self.client.term.termWidth, + termHeight : self.client.term.termHeight, + cols : quoteView.dimens.width, + startCol : quoteView.position.col, + ansiResetSgr : bodyView.styleSGR1, + ansiFocusPrefixSgr : quoteView.styleSGR2, }, (err, quoteLines, focusQuoteLines, replyIsAnsi) => { if(err) { @@ -959,16 +959,16 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul } /* - this.observeViewPosition = function() { - self.viewControllers.body.getView(MciViewIds.body.message).on('edit position', function positionUpdate(pos) { - console.log(pos.percent + ' / ' + pos.below) - }); - }; - */ + this.observeViewPosition = function() { + self.viewControllers.body.getView(MciViewIds.body.message).on('edit position', function positionUpdate(pos) { + console.log(pos.percent + ' / ' + pos.below) + }); + }; + */ switchToHeader() { this.viewControllers.body.setFocus(false); - this.viewControllers.header.switchFocus(2); // to + this.viewControllers.header.switchFocus(2); // to } switchToBody() { @@ -982,7 +982,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul this.viewControllers.header.setFocus(false); this.viewControllers.body.setFocus(false); - this.viewControllers[this.getFooterName()].switchFocus(1); // HM1 + this.viewControllers[this.getFooterName()].switchFocus(1); // HM1 } switchFromQuoteBuilderToBody() { @@ -991,7 +991,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul body.redraw(); this.viewControllers.body.switchFocus(1); - // :TODO: create method (DRY) + // :TODO: create method (DRY) this.updateTextEditMode(body.getTextEditMode()); this.updateEditModePosition(body.getEditPosition()); @@ -1000,11 +1000,11 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul } quoteBuilderFinalize() { - // :TODO: fix magic #'s - const quoteMsgView = this.viewControllers.quoteBuilder.getView(MciViewIds.quoteBuilder.quotedMsg); - const msgView = this.viewControllers.body.getView(MciViewIds.body.message); + // :TODO: fix magic #'s + const quoteMsgView = this.viewControllers.quoteBuilder.getView(MciViewIds.quoteBuilder.quotedMsg); + const msgView = this.viewControllers.body.getView(MciViewIds.body.message); - let quoteLines = quoteMsgView.getData().trim(); + let quoteLines = quoteMsgView.getData().trim(); if(quoteLines.length > 0) { if(this.replyIsAnsi) { @@ -1034,8 +1034,8 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul const dtFormat = this.menuConfig.config.quoteDateTimeFormat || this.client.currentTheme.helpers.getDateTimeFormat(); return stringFormat(quoteFormat, { - dateTime : moment(this.replyToMessage.modTimestamp).format(dtFormat), - userName : this.replyToMessage.fromUserName, + dateTime : moment(this.replyToMessage.modTimestamp).format(dtFormat), + userName : this.replyToMessage.fromUserName, }); } diff --git a/core/ftn_address.js b/core/ftn_address.js index 92c37557..6751adb8 100644 --- a/core/ftn_address.js +++ b/core/ftn_address.js @@ -1,7 +1,7 @@ /* jslint node: true */ 'use strict'; -const _ = require('lodash'); +const _ = require('lodash'); const FTN_ADDRESS_REGEXP = /^([0-9]+:)?([0-9]+)(\/[0-9]+)?(\.[0-9]+)?(@[a-z0-9\-.]+)?$/i; const FTN_PATTERN_REGEXP = /^([0-9*]+:)?([0-9*]+)(\/[0-9*]+)?(\.[0-9*]+)?(@[a-z0-9\-.*]+)?$/i; @@ -25,7 +25,7 @@ module.exports = class Address { } isValid() { - // FTN address is valid if we have at least a net/node + // FTN address is valid if we have at least a net/node return _.isNumber(this.net) && _.isNumber(this.node); } @@ -36,10 +36,10 @@ module.exports = class Address { return ( this.net === other.net && - this.node === other.node && - this.zone === other.zone && - this.point === other.point && - this.domain === other.domain + this.node === other.node && + this.zone === other.zone && + this.point === other.point && + this.domain === other.domain ); } @@ -95,36 +95,36 @@ module.exports = class Address { } /* - getMatchScore(pattern) { - let score = 0; - const addr = this.getMatchAddr(pattern); - if(addr) { - const PARTS = [ 'net', 'node', 'zone', 'point', 'domain' ]; - for(let i = 0; i < PARTS.length; ++i) { - const member = PARTS[i]; - if(this[member] === addr[member]) { - score += 2; - } else if('*' === addr[member]) { - score += 1; - } else { - break; - } - } - } + getMatchScore(pattern) { + let score = 0; + const addr = this.getMatchAddr(pattern); + if(addr) { + const PARTS = [ 'net', 'node', 'zone', 'point', 'domain' ]; + for(let i = 0; i < PARTS.length; ++i) { + const member = PARTS[i]; + if(this[member] === addr[member]) { + score += 2; + } else if('*' === addr[member]) { + score += 1; + } else { + break; + } + } + } - return score; - } - */ + return score; + } + */ isPatternMatch(pattern) { const addr = this.getMatchAddr(pattern); if(addr) { return ( ('*' === addr.net || this.net === addr.net) && - ('*' === addr.node || this.node === addr.node) && - ('*' === addr.zone || this.zone === addr.zone) && - ('*' === addr.point || this.point === addr.point) && - ('*' === addr.domain || this.domain === addr.domain) + ('*' === addr.node || this.node === addr.node) && + ('*' === addr.zone || this.zone === addr.zone) && + ('*' === addr.point || this.point === addr.point) && + ('*' === addr.domain || this.domain === addr.domain) ); } @@ -137,8 +137,8 @@ module.exports = class Address { if(m) { // start with a 2D let addr = { - net : parseInt(m[2]), - node : parseInt(m[3].substr(1)), + net : parseInt(m[2]), + node : parseInt(m[3].substr(1)), }; // 3D: Addition of zone if present @@ -165,14 +165,14 @@ module.exports = class Address { let addrStr = `${this.zone}:${this.net}`; - // allow for e.g. '4D' or 5 + // allow for e.g. '4D' or 5 const dim = parseInt(dimensions.toString()[0]); if(dim >= 3) { addrStr += `/${this.node}`; } - // missing & .0 are equiv for point + // missing & .0 are equiv for point if(dim >= 4 && this.point) { addrStr += `.${this.point}`; } diff --git a/core/ftn_mail_packet.js b/core/ftn_mail_packet.js index 6c49c1c6..cc0dde3e 100644 --- a/core/ftn_mail_packet.js +++ b/core/ftn_mail_packet.js @@ -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: + // ^ACHRS: // - // Where 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 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) ); diff --git a/core/ftn_util.js b/core/ftn_util.js index 0f65e127..e4637554 100644 --- a/core/ftn_util.js +++ b/core/ftn_util.js @@ -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: .@ -// 2606.agora-agn_tst@46:1/142 19609217 +// Synchronet: .@ +// 2606.agora-agn_tst@46:1/142 19609217 // -// Mystic: -// 46:3/102 46686263 +// Mystic: +// 46:3/102 46686263 // -// ENiGMA½: .@<5dFtnAddress> +// ENiGMA½: .@<5dFtnAddress> // -// 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 -// in which (; ; ) is used instead +// Note that we use a variant on the spec for +// in which (; ; ) 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: @YYYYMMDD.HHMMSS[.Precise][.Time Zone] - [Serial Number] - */ - const addrStr = new Address(address).toString('5D'); - const dateTime = moment().utc().format('YYYYMMDD.HHmmSS.SSSS.UTC'); - const version = getCleanEnigmaVersion(); + ^AVia: @YYYYMMDD.HHMMSS[.Precise][.Time Zone] + [Serial Number] + */ + 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" // - // ""INTL "" "" - // "...These addresses shall be given on the form :/" + // ""INTL "" "" + // "...These addresses shall be given on the form :/" // 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]; } \ No newline at end of file diff --git a/core/horizontal_menu_view.js b/core/horizontal_menu_view.js index eb45d993..b7d0aba3 100644 --- a/core/horizontal_menu_view.js +++ b/core/horizontal_menu_view.js @@ -1,21 +1,21 @@ /* jslint node: true */ 'use strict'; -const MenuView = require('./menu_view.js').MenuView; -const strUtil = require('./string_util.js'); -const formatString = require('./string_format'); -const { pipeToAnsi } = require('./color_codes.js'); -const { goto } = require('./ansi_term.js'); +const MenuView = require('./menu_view.js').MenuView; +const strUtil = require('./string_util.js'); +const formatString = require('./string_format'); +const { pipeToAnsi } = require('./color_codes.js'); +const { goto } = require('./ansi_term.js'); -const assert = require('assert'); -const _ = require('lodash'); +const assert = require('assert'); +const _ = require('lodash'); -exports.HorizontalMenuView = HorizontalMenuView; +exports.HorizontalMenuView = HorizontalMenuView; -// :TODO: Update this to allow scrolling if number of items cannot fit in width (similar to VerticalMenuView) +// :TODO: Update this to allow scrolling if number of items cannot fit in width (similar to VerticalMenuView) function HorizontalMenuView(options) { - options.cursor = options.cursor || 'hide'; + options.cursor = options.cursor || 'hide'; if(!_.isNumber(options.itemSpacing)) { options.itemSpacing = 1; @@ -23,7 +23,7 @@ function HorizontalMenuView(options) { MenuView.call(this, options); - this.dimens.height = 1; // always the case + this.dimens.height = 1; // always the case var self = this; @@ -33,8 +33,8 @@ function HorizontalMenuView(options) { this.performAutoScale = function() { if(self.autoScale.width) { - var spacer = self.getSpacer(); - var width = self.items.join(spacer).length + (spacer.length * 2); + var spacer = self.getSpacer(); + var width = self.items.join(spacer).length + (spacer.length * 2); assert(width <= self.client.term.termWidth - self.position.col); self.dimens.width = width; } @@ -44,8 +44,8 @@ function HorizontalMenuView(options) { this.cachePositions = function() { if(this.positionCacheExpired) { - var col = self.position.col; - var spacer = self.getSpacer(); + var col = self.position.col; + var spacer = self.getSpacer(); for(var i = 0; i < self.items.length; ++i) { self.items[i].col = col; @@ -90,7 +90,7 @@ require('util').inherits(HorizontalMenuView, MenuView); HorizontalMenuView.prototype.setHeight = function(height) { height = parseInt(height, 10); - assert(1 === height); // nothing else allowed here + assert(1 === height); // nothing else allowed here HorizontalMenuView.super_.prototype.setHeight(this, height); }; @@ -130,7 +130,7 @@ HorizontalMenuView.prototype.focusNext = function() { this.focusedItemIndex++; } - // :TODO: Optimize this in cases where we only need to redraw two items. Always the case now, somtimes + // :TODO: Optimize this in cases where we only need to redraw two items. Always the case now, somtimes this.redraw(); HorizontalMenuView.super_.prototype.focusNext.call(this); @@ -144,7 +144,7 @@ HorizontalMenuView.prototype.focusPrevious = function() { this.focusedItemIndex--; } - // :TODO: Optimize this in cases where we only need to redraw two items. Always the case now, somtimes + // :TODO: Optimize this in cases where we only need to redraw two items. Always the case now, somtimes this.redraw(); HorizontalMenuView.super_.prototype.focusPrevious.call(this); diff --git a/core/key_entry_view.js b/core/key_entry_view.js index 1d7ca905..0bab0ad5 100644 --- a/core/key_entry_view.js +++ b/core/key_entry_view.js @@ -1,12 +1,12 @@ /* jslint node: true */ 'use strict'; -const View = require('./view.js').View; -const valueWithDefault = require('./misc_util.js').valueWithDefault; -const isPrintable = require('./string_util.js').isPrintable; -const stylizeString = require('./string_util.js').stylizeString; +const View = require('./view.js').View; +const valueWithDefault = require('./misc_util.js').valueWithDefault; +const isPrintable = require('./string_util.js').isPrintable; +const stylizeString = require('./string_util.js').stylizeString; -const _ = require('lodash'); +const _ = require('lodash'); module.exports = class KeyEntryView extends View { constructor(options) { @@ -15,8 +15,8 @@ module.exports = class KeyEntryView extends View { super(options); - this.eatTabKey = options.eatTabKey || true; - this.caseInsensitive = options.caseInsensitive || true; + this.eatTabKey = options.eatTabKey || true; + this.caseInsensitive = options.caseInsensitive || true; if(Array.isArray(options.keys)) { if(this.caseInsensitive) { @@ -35,7 +35,7 @@ module.exports = class KeyEntryView extends View { } if(drawKey && isPrintable(drawKey) && (!this.keys || this.keys.indexOf(ch) > -1)) { - this.redraw(); // sets position + this.redraw(); // sets position this.client.term.write(stylizeString(ch, this.textStyle)); } @@ -46,7 +46,7 @@ module.exports = class KeyEntryView extends View { } this.emit('action', 'accept'); - // NOTE: we don't call super here. KeyEntryView is a special snowflake. + // NOTE: we don't call super here. KeyEntryView is a special snowflake. } setPropertyValue(propName, propValue) { @@ -73,5 +73,5 @@ module.exports = class KeyEntryView extends View { super.setPropertyValue(propName, propValue); } - getData() { return this.keyEntered; } + getData() { return this.keyEntered; } }; \ No newline at end of file diff --git a/core/last_callers.js b/core/last_callers.js index 88b0b716..52ec08f9 100644 --- a/core/last_callers.js +++ b/core/last_callers.js @@ -1,37 +1,37 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const StatLog = require('./stat_log.js'); -const User = require('./user.js'); -const stringFormat = require('./string_format.js'); +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const StatLog = require('./stat_log.js'); +const User = require('./user.js'); +const stringFormat = require('./string_format.js'); -// deps -const moment = require('moment'); -const async = require('async'); -const _ = require('lodash'); +// deps +const moment = require('moment'); +const async = require('async'); +const _ = require('lodash'); /* - Available listFormat object members: - userId - userName - location - affiliation - ts + Available listFormat object members: + userId + userName + location + affiliation + ts */ exports.moduleInfo = { - name : 'Last Callers', - desc : 'Last callers to the system', - author : 'NuSkooler', - packageName : 'codes.l33t.enigma.lastcallers' + name : 'Last Callers', + desc : 'Last callers to the system', + author : 'NuSkooler', + packageName : 'codes.l33t.enigma.lastcallers' }; const MciCodeIds = { - CallerList : 1, + CallerList : 1, }; exports.getModule = class LastCallersModule extends MenuModule { @@ -45,8 +45,8 @@ exports.getModule = class LastCallersModule extends MenuModule { return cb(err); } - const self = this; - const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); + const self = this; + const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); let loginHistory; let callersView; @@ -55,9 +55,9 @@ exports.getModule = class LastCallersModule extends MenuModule { [ function loadFromConfig(callback) { const loadOpts = { - callingMenu : self, - mciMap : mciData.menu, - noInput : true, + callingMenu : self, + mciMap : mciData.menu, + noInput : true, }; vc.loadFromMenuConfig(loadOpts, callback); @@ -65,18 +65,18 @@ exports.getModule = class LastCallersModule extends MenuModule { function fetchHistory(callback) { callersView = vc.getView(MciCodeIds.CallerList); - // fetch up + // fetch up StatLog.getSystemLogEntries('user_login_history', StatLog.Order.TimestampDesc, 200, (err, lh) => { loginHistory = lh; if(self.menuConfig.config.hideSysOpLogin) { const noOpLoginHistory = loginHistory.filter(lh => { - return false === User.isRootUserId(parseInt(lh.log_value)); // log_value=userId + return false === User.isRootUserId(parseInt(lh.log_value)); // log_value=userId }); // - // If we have enough items to display, or hideSysOpLogin is set to 'always', - // then set loginHistory to our filtered list. Else, we'll leave it be. + // If we have enough items to display, or hideSysOpLogin is set to 'always', + // then set loginHistory to our filtered list. Else, we'll leave it be. // if(noOpLoginHistory.length >= callersView.dimens.height || 'always' === self.menuConfig.config.hideSysOpLogin) { loginHistory = noOpLoginHistory; @@ -84,7 +84,7 @@ exports.getModule = class LastCallersModule extends MenuModule { } // - // Finally, we need to trim up the list to the needed size + // Finally, we need to trim up the list to the needed size // loginHistory = loginHistory.slice(0, callersView.dimens.height); @@ -93,7 +93,7 @@ exports.getModule = class LastCallersModule extends MenuModule { }, function getUserNamesAndProperties(callback) { const getPropOpts = { - names : [ 'location', 'affiliation' ] + names : [ 'location', 'affiliation' ] }; const dateTimeFormat = self.menuConfig.config.dateTimeFormat || 'ddd MMM DD'; @@ -102,7 +102,7 @@ exports.getModule = class LastCallersModule extends MenuModule { loginHistory, (item, next) => { item.userId = parseInt(item.log_value); - item.ts = moment(item.timestamp).format(dateTimeFormat); + item.ts = moment(item.timestamp).format(dateTimeFormat); User.getUserName(item.userId, (err, userName) => { if(err) { @@ -113,11 +113,11 @@ exports.getModule = class LastCallersModule extends MenuModule { User.loadProperties(item.userId, getPropOpts, (err, props) => { if(!err && props) { - item.location = props.location || 'N/A'; - item.affiliation = item.affils = (props.affiliation || 'N/A'); + item.location = props.location || 'N/A'; + item.affiliation = item.affils = (props.affiliation || 'N/A'); } else { - item.location = 'N/A'; - item.affiliation = item.affils = 'N/A'; + item.location = 'N/A'; + item.affiliation = item.affils = 'N/A'; } return next(null); }); diff --git a/core/listening_server.js b/core/listening_server.js index 3678ff3f..00cf0a86 100644 --- a/core/listening_server.js +++ b/core/listening_server.js @@ -1,17 +1,17 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const logger = require('./logger.js'); +// ENiGMA½ +const logger = require('./logger.js'); -// deps -const async = require('async'); +// deps +const async = require('async'); -const listeningServers = {}; // packageName -> info +const listeningServers = {}; // packageName -> info -exports.startup = startup; -exports.shutdown = shutdown; -exports.getServer = getServer; +exports.startup = startup; +exports.shutdown = shutdown; +exports.getServer = getServer; function startup(cb) { return startListening(cb); @@ -26,11 +26,11 @@ function getServer(packageName) { } function startListening(cb) { - const moduleUtil = require('./module_util.js'); // late load so we get Config + const moduleUtil = require('./module_util.js'); // late load so we get Config async.each( [ 'login', 'content' ], (category, next) => { moduleUtil.loadModulesForCategory(`${category}Servers`, (err, module) => { - // :TODO: use enig error here! + // :TODO: use enig error here! if(err) { if('EENIGMODDISABLED' === err.code) { logger.log.debug(err.message); @@ -48,8 +48,8 @@ function startListening(cb) { } listeningServers[module.moduleInfo.packageName] = { - instance : moduleInst, - info : module.moduleInfo, + instance : moduleInst, + info : module.moduleInfo, }; } catch(e) { diff --git a/core/logger.js b/core/logger.js index 8b95c821..9a4e8711 100644 --- a/core/logger.js +++ b/core/logger.js @@ -1,21 +1,21 @@ /* jslint node: true */ 'use strict'; -// deps -const bunyan = require('bunyan'); -const paths = require('path'); -const fs = require('graceful-fs'); -const _ = require('lodash'); +// deps +const bunyan = require('bunyan'); +const paths = require('path'); +const fs = require('graceful-fs'); +const _ = require('lodash'); module.exports = class Log { static init() { - const Config = require('./config.js').get(); - const logPath = Config.paths.logs; + const Config = require('./config.js').get(); + const logPath = Config.paths.logs; const err = this.checkLogPath(logPath); if(err) { - console.error(err.message); // eslint-disable-line no-console + console.error(err.message); // eslint-disable-line no-console return process.exit(); } @@ -26,18 +26,18 @@ module.exports = class Log { } const serializers = { - err : bunyan.stdSerializers.err, // handle 'err' fields with stack/etc. + err : bunyan.stdSerializers.err, // handle 'err' fields with stack/etc. }; - // try to remove sensitive info by default, e.g. 'password' fields + // try to remove sensitive info by default, e.g. 'password' fields [ 'formData', 'formValue' ].forEach(keyName => { serializers[keyName] = (fd) => Log.hideSensitive(fd); }); this.log = bunyan.createLogger({ - name : 'ENiGMA½ BBS', - streams : logStreams, - serializers : serializers, + name : 'ENiGMA½ BBS', + streams : logStreams, + serializers : serializers, }); } @@ -59,7 +59,7 @@ module.exports = class Log { static hideSensitive(obj) { try { // - // Use a regexp -- we don't know how nested fields we want to seek and destroy may be + // Use a regexp -- we don't know how nested fields we want to seek and destroy may be // return JSON.parse( JSON.stringify(obj).replace(/"(password|passwordConfirm|key|authCode)"\s?:\s?"([^"]+)"/, (match, valueName) => { @@ -67,7 +67,7 @@ module.exports = class Log { }) ); } catch(e) { - // be safe and return empty obj! + // be safe and return empty obj! return {}; } } diff --git a/core/login_server_module.js b/core/login_server_module.js index ef4e712e..02cd4e41 100644 --- a/core/login_server_module.js +++ b/core/login_server_module.js @@ -1,27 +1,27 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const conf = require('./config.js'); -const logger = require('./logger.js'); -const ServerModule = require('./server_module.js').ServerModule; -const clientConns = require('./client_connections.js'); +// ENiGMA½ +const conf = require('./config.js'); +const logger = require('./logger.js'); +const ServerModule = require('./server_module.js').ServerModule; +const clientConns = require('./client_connections.js'); -// deps -const _ = require('lodash'); +// deps +const _ = require('lodash'); module.exports = class LoginServerModule extends ServerModule { constructor() { super(); } - // :TODO: we need to max connections -- e.g. from config 'maxConnections' + // :TODO: we need to max connections -- e.g. from config 'maxConnections' prepareClient(client, cb) { const theme = require('./theme.js'); // - // Choose initial theme before we have user context + // Choose initial theme before we have user context // if('*' === conf.config.preLoginTheme) { client.user.properties.theme_id = theme.getRandomTheme() || ''; @@ -35,15 +35,15 @@ module.exports = class LoginServerModule extends ServerModule { handleNewClient(client, clientSock, modInfo) { // - // Start tracking the client. We'll assign it an ID which is - // just the index in our connections array. + // Start tracking the client. We'll assign it an ID which is + // just the index in our connections array. // if(_.isUndefined(client.session)) { client.session = {}; } - client.session.serverName = modInfo.name; - client.session.isSecure = _.isBoolean(client.isSecure) ? client.isSecure : (modInfo.isSecure || false); + client.session.serverName = modInfo.name; + client.session.isSecure = _.isBoolean(client.isSecure) ? client.isSecure : (modInfo.isSecure || false); clientConns.addNewClient(client, clientSock); @@ -51,7 +51,7 @@ module.exports = class LoginServerModule extends ServerModule { client.startIdleMonitor(); - // Go to module -- use default error handler + // Go to module -- use default error handler this.prepareClient(client, () => { require('./connect.js').connectEntry(client, readyOptions.firstMenu); }); @@ -77,7 +77,7 @@ module.exports = class LoginServerModule extends ServerModule { client.menuStack.goto('idleLogoff', err => { if(err) { - // likely just doesn't exist + // likely just doesn't exist client.term.write('\nIdle timeout expired. Goodbye!\n'); client.end(); } diff --git a/core/mail_packet.js b/core/mail_packet.js index 32b85a06..ce5b160a 100644 --- a/core/mail_packet.js +++ b/core/mail_packet.js @@ -1,16 +1,16 @@ /* jslint node: true */ 'use strict'; -var events = require('events'); -var assert = require('assert'); -var _ = require('lodash'); +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 } ) + // map of network name -> address obj ( { zone, net, node, point, domain } ) this.nodeAddresses = options.nodeAddresses || {}; } @@ -18,19 +18,19 @@ 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 + // options.packetPath | opts.packetBuffer: supplies a path-to-file + // or a buffer containing packet data // - // emits 'message' event per message read + // 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 + // options.messages[]: array of message(s) to create packets from // - // emits 'packet' event per packet constructed + // emits 'packet' event per packet constructed // assert(_.isArray(options.messages)); }; \ No newline at end of file diff --git a/core/mail_util.js b/core/mail_util.js index 6822e78e..6bd433d3 100644 --- a/core/mail_util.js +++ b/core/mail_util.js @@ -1,25 +1,25 @@ /* jslint node: true */ 'use strict'; -const Address = require('./ftn_address.js'); -const Message = require('./message.js'); +const Address = require('./ftn_address.js'); +const Message = require('./message.js'); -exports.getAddressedToInfo = getAddressedToInfo; +exports.getAddressedToInfo = getAddressedToInfo; const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; /* - Input Output - ---------------------------------------------------------------------------------------------------- - User { name : 'User', flavor : 'local' } - Some User { name : 'Some User', flavor : 'local' } - JoeUser @ 1:103/75 { name : 'JoeUser', flavor : 'ftn', remote : '1:103/75' } - Bob@1:103/705@fidonet.org { name : 'Bob', flavor : 'ftn', remote : '1:103/705@fidonet.org' } - 1:103/705@fidonet.org { flavor : 'ftn', remote : '1:103/705@fidonet.org' } - Jane <23:4/100> { name : 'Jane', flavor : 'ftn', remote : '23:4/100' } - 43:20/100.2 { flavor : 'ftn', remote : '43:20/100.2' } - foo@host.com { name : 'foo', flavor : 'email', remote : 'foo@host.com' } - Bar { name : 'Bar', flavor : 'email', remote : 'baz@foobar.com' } + Input Output + ---------------------------------------------------------------------------------------------------- + User { name : 'User', flavor : 'local' } + Some User { name : 'Some User', flavor : 'local' } + JoeUser @ 1:103/75 { name : 'JoeUser', flavor : 'ftn', remote : '1:103/75' } + Bob@1:103/705@fidonet.org { name : 'Bob', flavor : 'ftn', remote : '1:103/705@fidonet.org' } + 1:103/705@fidonet.org { flavor : 'ftn', remote : '1:103/705@fidonet.org' } + Jane <23:4/100> { name : 'Jane', flavor : 'ftn', remote : '23:4/100' } + 43:20/100.2 { flavor : 'ftn', remote : '43:20/100.2' } + foo@host.com { name : 'foo', flavor : 'email', remote : 'foo@host.com' } + Bar { name : 'Bar', flavor : 'email', remote : 'baz@foobar.com' } */ function getAddressedToInfo(input) { input = input.trim(); @@ -50,8 +50,8 @@ function getAddressedToInfo(input) { return { name : input, flavor : Message.AddressFlavor.Local }; } - const lessThanPos = input.indexOf('<'); - const greaterThanPos = input.indexOf('>'); + const lessThanPos = input.indexOf('<'); + const greaterThanPos = input.indexOf('>'); if(lessThanPos > 0 && greaterThanPos > lessThanPos) { const addr = input.slice(lessThanPos + 1, greaterThanPos); const m = addr.match(EMAIL_REGEX); @@ -67,7 +67,7 @@ function getAddressedToInfo(input) { return { name : input.slice(0, firstAtPos), flavor : Message.AddressFlavor.Email, remote : input }; } - let addr = Address.fromString(input); // 5D? + let addr = Address.fromString(input); // 5D? if(Address.isValidAddress(addr)) { return { flavor : Message.AddressFlavor.FTN, remote : addr.toString() } ; } diff --git a/core/mask_edit_text_view.js b/core/mask_edit_text_view.js index 417b7928..6cdf40b0 100644 --- a/core/mask_edit_text_view.js +++ b/core/mask_edit_text_view.js @@ -1,42 +1,42 @@ /* jslint node: true */ 'use strict'; -var TextView = require('./text_view.js').TextView; -var miscUtil = require('./misc_util.js'); -var strUtil = require('./string_util.js'); -var ansi = require('./ansi_term.js'); +var TextView = require('./text_view.js').TextView; +var miscUtil = require('./misc_util.js'); +var strUtil = require('./string_util.js'); +var ansi = require('./ansi_term.js'); -//var util = require('util'); -var assert = require('assert'); -var _ = require('lodash'); +//var util = require('util'); +var assert = require('assert'); +var _ = require('lodash'); -exports.MaskEditTextView = MaskEditTextView; +exports.MaskEditTextView = MaskEditTextView; -// ##/##/#### <--styleSGR2 if fillChar -// ^- styleSGR1 -// buildPattern -> [ RE, RE, '/', RE, RE, '/', RE, RE, RE, RE ] -// patternIndex -----^ +// ##/##/#### <--styleSGR2 if fillChar +// ^- styleSGR1 +// buildPattern -> [ RE, RE, '/', RE, RE, '/', RE, RE, RE, RE ] +// patternIndex -----^ -// styleSGR1: Literal's (non-focus) -// styleSGR2: Literals (focused) -// styleSGR3: fillChar +// styleSGR1: Literal's (non-focus) +// styleSGR2: Literals (focused) +// styleSGR3: fillChar // -// :TODO: -// * Hint, e.g. YYYY/MM/DD -// * Return values with literals in place +// :TODO: +// * Hint, e.g. YYYY/MM/DD +// * Return values with literals in place // function MaskEditTextView(options) { - options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true); - options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true); - options.cursorStyle = miscUtil.valueWithDefault(options.cursorStyle, 'steady block'); - options.resizable = false; + options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true); + options.acceptsInput = miscUtil.valueWithDefault(options.acceptsInput, true); + options.cursorStyle = miscUtil.valueWithDefault(options.cursorStyle, 'steady block'); + options.resizable = false; TextView.call(this, options); - this.cursorPos = { x : 0 }; - this.patternArrayPos = 0; + this.cursorPos = { x : 0 }; + this.patternArrayPos = 0; var self = this; @@ -52,7 +52,7 @@ function MaskEditTextView(options) { assert(textToDraw.length <= self.patternArray.length); - // draw out the text we have so far + // draw out the text we have so far var i = 0; var t = 0; while(i < self.patternArray.length) { @@ -72,11 +72,11 @@ function MaskEditTextView(options) { }; this.buildPattern = function() { - self.patternArray = []; - self.maxLength = 0; + self.patternArray = []; + self.maxLength = 0; for(var i = 0; i < self.maskPattern.length; i++) { - // :TODO: support escaped characters, e.g. \#. Also allow \\ for a '\' mark! + // :TODO: support escaped characters, e.g. \#. Also allow \\ for a '\' mark! if(self.maskPattern[i] in MaskEditTextView.maskPatternCharacterRegEx) { self.patternArray.push(MaskEditTextView.maskPatternCharacterRegEx[self.maskPattern[i]]); ++self.maxLength; @@ -97,16 +97,16 @@ function MaskEditTextView(options) { require('util').inherits(MaskEditTextView, TextView); MaskEditTextView.maskPatternCharacterRegEx = { - '#' : /[0-9]/, // Numeric - 'A' : /[a-zA-Z]/, // Alpha - '@' : /[0-9a-zA-Z]/, // Alphanumeric - '&' : /[\w\d\s]/, // Any "printable" 32-126, 128-255 + '#' : /[0-9]/, // Numeric + 'A' : /[a-zA-Z]/, // Alpha + '@' : /[0-9a-zA-Z]/, // Alphanumeric + '&' : /[\w\d\s]/, // Any "printable" 32-126, 128-255 }; MaskEditTextView.prototype.setText = function(text) { MaskEditTextView.super_.prototype.setText.call(this, text); - if(this.patternArray) { // :TODO: This is a hack - see TextView ctor note about setText() + if(this.patternArray) { // :TODO: This is a hack - see TextView ctor note about setText() this.patternArrayPos = this.patternArray.length; } }; @@ -143,9 +143,9 @@ MaskEditTextView.prototype.onKeyPress = function(ch, key) { return; } else if(this.isKeyMapped('clearLine', key.name)) { - this.text = ''; - this.patternArrayPos = 0; - this.setFocus(true); // redraw + adjust cursor + this.text = ''; + this.patternArrayPos = 0; + this.setFocus(true); // redraw + adjust cursor return; } @@ -163,7 +163,7 @@ MaskEditTextView.prototype.onKeyPress = function(ch, key) { this.patternArrayPos++; while(this.patternArrayPos < this.patternArray.length && - !_.isRegExp(this.patternArray[this.patternArrayPos])) + !_.isRegExp(this.patternArray[this.patternArrayPos])) { this.patternArrayPos++; } @@ -178,7 +178,7 @@ MaskEditTextView.prototype.onKeyPress = function(ch, key) { MaskEditTextView.prototype.setPropertyValue = function(propName, value) { switch(propName) { - case 'maskPattern' : this.setMaskPattern(value); break; + case 'maskPattern' : this.setMaskPattern(value); break; } MaskEditTextView.super_.prototype.setPropertyValue.call(this, propName, value); @@ -191,7 +191,7 @@ MaskEditTextView.prototype.getData = function() { return rawData; } - var data = ''; + var data = ''; assert(rawData.length <= this.patternArray.length); diff --git a/core/mci_view_factory.js b/core/mci_view_factory.js index 3bc333ae..86246d45 100644 --- a/core/mci_view_factory.js +++ b/core/mci_view_factory.js @@ -1,25 +1,25 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const TextView = require('./text_view.js').TextView; -const EditTextView = require('./edit_text_view.js').EditTextView; -const ButtonView = require('./button_view.js').ButtonView; -const VerticalMenuView = require('./vertical_menu_view.js').VerticalMenuView; -const HorizontalMenuView = require('./horizontal_menu_view.js').HorizontalMenuView; -const SpinnerMenuView = require('./spinner_menu_view.js').SpinnerMenuView; -const ToggleMenuView = require('./toggle_menu_view.js').ToggleMenuView; -const MaskEditTextView = require('./mask_edit_text_view.js').MaskEditTextView; -const KeyEntryView = require('./key_entry_view.js'); -const MultiLineEditTextView = require('./multi_line_edit_text_view.js').MultiLineEditTextView; -const getPredefinedMCIValue = require('./predefined_mci.js').getPredefinedMCIValue; -const ansi = require('./ansi_term.js'); +// ENiGMA½ +const TextView = require('./text_view.js').TextView; +const EditTextView = require('./edit_text_view.js').EditTextView; +const ButtonView = require('./button_view.js').ButtonView; +const VerticalMenuView = require('./vertical_menu_view.js').VerticalMenuView; +const HorizontalMenuView = require('./horizontal_menu_view.js').HorizontalMenuView; +const SpinnerMenuView = require('./spinner_menu_view.js').SpinnerMenuView; +const ToggleMenuView = require('./toggle_menu_view.js').ToggleMenuView; +const MaskEditTextView = require('./mask_edit_text_view.js').MaskEditTextView; +const KeyEntryView = require('./key_entry_view.js'); +const MultiLineEditTextView = require('./multi_line_edit_text_view.js').MultiLineEditTextView; +const getPredefinedMCIValue = require('./predefined_mci.js').getPredefinedMCIValue; +const ansi = require('./ansi_term.js'); -// deps -const assert = require('assert'); -const _ = require('lodash'); +// deps +const assert = require('assert'); +const _ = require('lodash'); -exports.MCIViewFactory = MCIViewFactory; +exports.MCIViewFactory = MCIViewFactory; function MCIViewFactory(client) { this.client = client; @@ -29,9 +29,9 @@ MCIViewFactory.UserViewCodes = [ 'TL', 'ET', 'ME', 'MT', 'PL', 'BT', 'VM', 'HM', 'SM', 'TM', 'KE', // - // XY is a special MCI code that allows finding positions - // and counts for key lookup, but does not explicitly - // represent a visible View on it's own + // XY is a special MCI code that allows finding positions + // and counts for key lookup, but does not explicitly + // represent a visible View on it's own // 'XY', ]; @@ -43,14 +43,14 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { var view; var options = { - client : this.client, - id : mci.id, - ansiSGR : mci.SGR, - ansiFocusSGR : mci.focusSGR, - position : { row : mci.position[0], col : mci.position[1] }, + client : this.client, + id : mci.id, + ansiSGR : mci.SGR, + ansiFocusSGR : mci.focusSGR, + position : { row : mci.position[0], col : mci.position[1] }, }; - // :TODO: These should use setPropertyValue()! + // :TODO: These should use setPropertyValue()! function setOption(pos, name) { if(mci.args.length > pos && mci.args[pos].length > 0) { options[name] = mci.args[pos]; @@ -73,44 +73,44 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { } // - // Note: Keep this in sync with UserViewCodes above! + // Note: Keep this in sync with UserViewCodes above! // switch(mci.code) { - // Text Label (Text View) + // Text Label (Text View) case 'TL' : - setOption(0, 'textStyle'); - setOption(1, 'justify'); + setOption(0, 'textStyle'); + setOption(1, 'justify'); setWidth(2); view = new TextView(options); break; - // Edit Text + // Edit Text case 'ET' : setWidth(0); - setOption(1, 'textStyle'); - setFocusOption(0, 'focusTextStyle'); + setOption(1, 'textStyle'); + setFocusOption(0, 'focusTextStyle'); view = new EditTextView(options); break; - // Masked Edit Text + // Masked Edit Text case 'ME' : - setOption(0, 'textStyle'); - setFocusOption(0, 'focusTextStyle'); + setOption(0, 'textStyle'); + setFocusOption(0, 'focusTextStyle'); view = new MaskEditTextView(options); break; - // Multi Line Edit Text + // Multi Line Edit Text case 'MT' : - // :TODO: apply params + // :TODO: apply params view = new MultiLineEditTextView(options); break; - // Pre-defined Label (Text View) - // :TODO: Currently no real point of PL -- @method replaces this pretty much... probably remove + // Pre-defined Label (Text View) + // :TODO: Currently no real point of PL -- @method replaces this pretty much... probably remove case 'PL' : if(mci.args.length > 0) { options.text = getPredefinedMCIValue(this.client, mci.args[0]); @@ -124,7 +124,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { } break; - // Button + // Button case 'BT' : if(mci.args.length > 0) { options.dimens = { width : parseInt(mci.args[0], 10) }; @@ -138,32 +138,32 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { view = new ButtonView(options); break; - // Vertial Menu + // Vertial Menu case 'VM' : - setOption(0, 'itemSpacing'); - setOption(1, 'justify'); - setOption(2, 'textStyle'); + setOption(0, 'itemSpacing'); + setOption(1, 'justify'); + setOption(2, 'textStyle'); - setFocusOption(0, 'focusTextStyle'); + setFocusOption(0, 'focusTextStyle'); view = new VerticalMenuView(options); break; - // Horizontal Menu + // Horizontal Menu case 'HM' : - setOption(0, 'itemSpacing'); - setOption(1, 'textStyle'); + setOption(0, 'itemSpacing'); + setOption(1, 'textStyle'); - setFocusOption(0, 'focusTextStyle'); + setFocusOption(0, 'focusTextStyle'); view = new HorizontalMenuView(options); break; case 'SM' : - setOption(0, 'textStyle'); - setOption(1, 'justify'); + setOption(0, 'textStyle'); + setOption(1, 'justify'); - setFocusOption(0, 'focusTextStyle'); + setFocusOption(0, 'focusTextStyle'); view = new SpinnerMenuView(options); break; @@ -177,7 +177,7 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { options.styleSG1 = ansi.getSGRFromGraphicRendition(styleSG1, true); } - setFocusOption(0, 'focusTextStyle'); + setFocusOption(0, 'focusTextStyle'); view = new ToggleMenuView(options); break; @@ -191,8 +191,8 @@ MCIViewFactory.prototype.createFromMCI = function(mci) { if(_.isString(options.text)) { setWidth(0); - setOption(1, 'textStyle'); - setOption(2, 'justify'); + setOption(1, 'textStyle'); + setOption(2, 'justify'); view = new TextView(options); } diff --git a/core/menu_module.js b/core/menu_module.js index 520d30e9..2a3bf23b 100644 --- a/core/menu_module.js +++ b/core/menu_module.js @@ -1,37 +1,37 @@ /* jslint node: true */ 'use strict'; -const PluginModule = require('./plugin_module.js').PluginModule; -const theme = require('./theme.js'); -const ansi = require('./ansi_term.js'); -const ViewController = require('./view_controller.js').ViewController; -const menuUtil = require('./menu_util.js'); -const Config = require('./config.js').get; -const stringFormat = require('../core/string_format.js'); -const MultiLineEditTextView = require('../core/multi_line_edit_text_view.js').MultiLineEditTextView; -const Errors = require('../core/enig_error.js').Errors; -const { getPredefinedMCIValue } = require('../core/predefined_mci.js'); +const PluginModule = require('./plugin_module.js').PluginModule; +const theme = require('./theme.js'); +const ansi = require('./ansi_term.js'); +const ViewController = require('./view_controller.js').ViewController; +const menuUtil = require('./menu_util.js'); +const Config = require('./config.js').get; +const stringFormat = require('../core/string_format.js'); +const MultiLineEditTextView = require('../core/multi_line_edit_text_view.js').MultiLineEditTextView; +const Errors = require('../core/enig_error.js').Errors; +const { getPredefinedMCIValue } = require('../core/predefined_mci.js'); -// deps -const async = require('async'); -const assert = require('assert'); -const _ = require('lodash'); +// deps +const async = require('async'); +const assert = require('assert'); +const _ = require('lodash'); exports.MenuModule = class MenuModule extends PluginModule { constructor(options) { super(options); - this.menuName = options.menuName; - this.menuConfig = options.menuConfig; - this.client = options.client; - this.menuConfig.options = options.menuConfig.options || {}; - this.menuMethods = {}; // methods called from @method's - this.menuConfig.config = this.menuConfig.config || {}; + this.menuName = options.menuName; + this.menuConfig = options.menuConfig; + this.client = options.client; + this.menuConfig.options = options.menuConfig.options || {}; + this.menuMethods = {}; // methods called from @method's + this.menuConfig.config = this.menuConfig.config || {}; this.cls = _.isBoolean(this.menuConfig.options.cls) ? this.menuConfig.options.cls : Config().menus.cls; - this.viewControllers = {}; + this.viewControllers = {}; } enter() { @@ -43,8 +43,8 @@ exports.MenuModule = class MenuModule extends PluginModule { } initSequence() { - const self = this; - const mciData = {}; + const self = this; + const mciData = {}; let pausePosition; async.series( @@ -67,13 +67,13 @@ exports.MenuModule = class MenuModule extends PluginModule { mciData.menu = artData.mciMap; } - return callback(null); // any errors are non-fatal + return callback(null); // any errors are non-fatal } ); }, function moveToPromptLocation(callback) { if(self.menuConfig.prompt) { - // :TODO: fetch and move cursor to prompt location, if supplied. See notes/etc. on placements + // :TODO: fetch and move cursor to prompt location, if supplied. See notes/etc. on placements } return callback(null); @@ -94,13 +94,13 @@ exports.MenuModule = class MenuModule extends PluginModule { if(artData) { mciData.prompt = artData.mciMap; } - return callback(err); // pass err here; prompts *must* have art + return callback(err); // pass err here; prompts *must* have art } ); }, function recordCursorPosition(callback) { if(!self.shouldPause()) { - return callback(null); // cursor position not needed + return callback(null); // cursor position not needed } self.client.once('cursor position report', pos => { @@ -138,7 +138,7 @@ exports.MenuModule = class MenuModule extends PluginModule { beforeArt(cb) { if(_.isNumber(this.menuConfig.options.baudRate)) { - // :TODO: some terminals not supporting cterm style emulated baud rate end up displaying a broken ESC sequence or a single "r" here + // :TODO: some terminals not supporting cterm style emulated baud rate end up displaying a broken ESC sequence or a single "r" here this.client.term.rawWrite(ansi.setEmulatedBaudRate(this.menuConfig.options.baudRate)); } @@ -150,30 +150,30 @@ exports.MenuModule = class MenuModule extends PluginModule { } mciReady(mciData, cb) { - // available for sub-classes + // available for sub-classes return cb(null); } finishedLoading() { - // nothing in base + // nothing in base } getSaveState() { - // nothing in base + // nothing in base } restoreSavedState(/*savedState*/) { - // nothing in base + // nothing in base } getMenuResult() { - // default to the formData that was provided @ a submit, if any + // default to the formData that was provided @ a submit, if any return this.submitFormData; } nextMenu(cb) { if(!this.haveNext()) { - return this.prevMenu(cb); // no next, go to prev + return this.prevMenu(cb); // no next, go to prev } return this.client.menuStack.next(cb); @@ -236,10 +236,10 @@ exports.MenuModule = class MenuModule extends PluginModule { standardMCIReadyHandler(mciData, cb) { // - // A quick rundown: - // * We may have mciData.menu, mciData.prompt, or both. - // * Prompt form is favored over menu form if both are present. - // * Standard/prefdefined MCI entries must load both (e.g. %BN is expected to resolve) + // A quick rundown: + // * We may have mciData.menu, mciData.prompt, or both. + // * Prompt form is favored over menu form if both are present. + // * Standard/prefdefined MCI entries must load both (e.g. %BN is expected to resolve) // const self = this; @@ -259,9 +259,9 @@ exports.MenuModule = class MenuModule extends PluginModule { } const menuLoadOpts = { - mciMap : mciData.menu, - callingMenu : self, - withoutForm : _.isObject(mciData.prompt), + mciMap : mciData.menu, + callingMenu : self, + withoutForm : _.isObject(mciData.prompt), }; self.viewControllers.menu.loadFromMenuConfig(menuLoadOpts, err => { @@ -274,8 +274,8 @@ exports.MenuModule = class MenuModule extends PluginModule { } const promptLoadOpts = { - callingMenu : self, - mciMap : mciData.prompt, + callingMenu : self, + mciMap : mciData.prompt, }; self.viewControllers.prompt.loadFromPromptConfig(promptLoadOpts, err => { @@ -314,16 +314,16 @@ exports.MenuModule = class MenuModule extends PluginModule { prepViewController(name, formId, mciMap, cb) { if(_.isUndefined(this.viewControllers[name])) { const vcOpts = { - client : this.client, - formId : formId, + client : this.client, + formId : formId, }; const vc = this.addViewController(name, new ViewController(vcOpts)); const loadOpts = { - callingMenu : this, - mciMap : mciMap, - formId : formId, + callingMenu : this, + mciMap : mciMap, + formId : formId, }; return vc.loadFromMenuConfig(loadOpts, err => { @@ -371,20 +371,20 @@ exports.MenuModule = class MenuModule extends PluginModule { } /* - :TODO: this needs quite a bit of work - but would be nice: promptForInput(..., (err, formData) => ... ) - promptForInput(formName, name, options, cb) { - if(!cb && _.isFunction(options)) { - cb = options; - options = {}; - } + :TODO: this needs quite a bit of work - but would be nice: promptForInput(..., (err, formData) => ... ) + promptForInput(formName, name, options, cb) { + if(!cb && _.isFunction(options)) { + cb = options; + options = {}; + } - options.viewController = this.viewControllers[formName]; + options.viewController = this.viewControllers[formName]; - this.optionalMoveToPosition(options.position); + this.optionalMoveToPosition(options.position); - return theme.displayThemedPrompt(name, this.client, options, cb); - } - */ + return theme.displayThemedPrompt(name, this.client, options, cb); + } + */ setViewText(formName, mciId, text, appendMultiLine) { const view = this.viewControllers[formName].getView(mciId); @@ -404,12 +404,12 @@ exports.MenuModule = class MenuModule extends PluginModule { let textView; let customMciId = startId; - const config = this.menuConfig.config; - const endId = options.endId || 99; // we'll fail to get a view before 99 + const config = this.menuConfig.config; + const endId = options.endId || 99; // we'll fail to get a view before 99 while(customMciId <= endId && (textView = this.viewControllers[formName].getView(customMciId)) ) { - const key = `${formName}InfoFormat${customMciId}`; // e.g. "mainInfoFormat10" - const format = config[key]; + const key = `${formName}InfoFormat${customMciId}`; // e.g. "mainInfoFormat10" + const format = config[key]; if(format && (!options.filter || options.filter.find(f => format.indexOf(f) > - 1))) { const text = stringFormat(format, fmtObj); diff --git a/core/menu_stack.js b/core/menu_stack.js index 90269bcb..5ab5a091 100644 --- a/core/menu_stack.js +++ b/core/menu_stack.js @@ -1,20 +1,20 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const loadMenu = require('./menu_util.js').loadMenu; -const Errors = require('./enig_error.js').Errors; +// ENiGMA½ +const loadMenu = require('./menu_util.js').loadMenu; +const Errors = require('./enig_error.js').Errors; -// deps -const _ = require('lodash'); -const assert = require('assert'); +// deps +const _ = require('lodash'); +const assert = require('assert'); -// :TODO: Stack is backwards.... top should be most recent! :) +// :TODO: Stack is backwards.... top should be most recent! :) module.exports = class MenuStack { constructor(client) { - this.client = client; - this.stack = []; + this.client = client; + this.stack = []; } push(moduleInfo) { @@ -52,8 +52,8 @@ module.exports = class MenuStack { const currentModuleInfo = this.top(); assert(currentModuleInfo, 'Empty menu stack!'); - const menuConfig = currentModuleInfo.instance.menuConfig; - const nextMenu = this.client.acs.getConditionalValue(menuConfig.next, 'next'); + const menuConfig = currentModuleInfo.instance.menuConfig; + const nextMenu = this.client.acs.getConditionalValue(menuConfig.next, 'next'); if(!nextMenu) { return cb(Array.isArray(menuConfig.next) ? Errors.MenuStack('No matching condition for "next"', 'NOCONDMATCH') : @@ -71,16 +71,16 @@ module.exports = class MenuStack { prev(cb) { const menuResult = this.top().instance.getMenuResult(); - // :TODO: leave() should really take a cb... - this.pop().instance.leave(); // leave & remove current + // :TODO: leave() should really take a cb... + this.pop().instance.leave(); // leave & remove current - const previousModuleInfo = this.pop(); // get previous + const previousModuleInfo = this.pop(); // get previous if(previousModuleInfo) { const opts = { - extraArgs : previousModuleInfo.extraArgs, - savedState : previousModuleInfo.savedState, - lastMenuResult : menuResult, + extraArgs : previousModuleInfo.extraArgs, + savedState : previousModuleInfo.savedState, + lastMenuResult : menuResult, }; return this.goto(previousModuleInfo.name, opts, cb); @@ -108,8 +108,8 @@ module.exports = class MenuStack { } const loadOpts = { - name : name, - client : self.client, + name : name, + client : self.client, }; if(currentModuleInfo && currentModuleInfo.menuFlags.includes('forwardArgs')) { @@ -117,19 +117,19 @@ module.exports = class MenuStack { } else { loadOpts.extraArgs = options.extraArgs || _.get(options, 'formData.value'); } - loadOpts.lastMenuResult = options.lastMenuResult; + loadOpts.lastMenuResult = options.lastMenuResult; loadMenu(loadOpts, (err, modInst) => { if(err) { - // :TODO: probably should just require a cb... + // :TODO: probably should just require a cb... const errCb = cb || self.client.defaultHandlerMissingMod(); errCb(err); } else { self.client.log.debug( { menuName : name }, 'Goto menu module'); // - // If menuFlags were supplied in menu.hjson, they should win over - // anything supplied in code. + // If menuFlags were supplied in menu.hjson, they should win over + // anything supplied in code. // let menuFlags; if(0 === modInst.menuConfig.options.menuFlags.length) { @@ -137,14 +137,14 @@ module.exports = class MenuStack { } else { menuFlags = modInst.menuConfig.options.menuFlags; - // in code we can ask to merge in + // in code we can ask to merge in if(Array.isArray(options.menuFlags) && options.menuFlags.includes('mergeFlags')) { menuFlags = _.uniq(menuFlags.concat(options.menuFlags)); } } if(currentModuleInfo) { - // save stack state + // save stack state currentModuleInfo.savedState = currentModuleInfo.instance.getSaveState(); currentModuleInfo.instance.leave(); @@ -154,18 +154,18 @@ module.exports = class MenuStack { } if(menuFlags.includes('popParent')) { - this.pop().instance.leave(); // leave & remove current + this.pop().instance.leave(); // leave & remove current } } self.push({ - name : name, - instance : modInst, - extraArgs : loadOpts.extraArgs, - menuFlags : menuFlags, + name : name, + instance : modInst, + extraArgs : loadOpts.extraArgs, + menuFlags : menuFlags, }); - // restore previous state if requested + // restore previous state if requested if(options.savedState) { modInst.restoreSavedState(options.savedState); } diff --git a/core/menu_util.js b/core/menu_util.js index 76205d3f..f362041c 100644 --- a/core/menu_util.js +++ b/core/menu_util.js @@ -1,22 +1,22 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -var moduleUtil = require('./module_util.js'); -var Log = require('./logger.js').log; -var Config = require('./config.js').get; -var asset = require('./asset.js'); -var MCIViewFactory = require('./mci_view_factory.js').MCIViewFactory; +// ENiGMA½ +var moduleUtil = require('./module_util.js'); +var Log = require('./logger.js').log; +var Config = require('./config.js').get; +var asset = require('./asset.js'); +var MCIViewFactory = require('./mci_view_factory.js').MCIViewFactory; -var paths = require('path'); -var async = require('async'); -var assert = require('assert'); -var _ = require('lodash'); +var paths = require('path'); +var async = require('async'); +var assert = require('assert'); +var _ = require('lodash'); -exports.loadMenu = loadMenu; -exports.getFormConfigByIDAndMap = getFormConfigByIDAndMap; -exports.handleAction = handleAction; -exports.handleNext = handleNext; +exports.loadMenu = loadMenu; +exports.getFormConfigByIDAndMap = getFormConfigByIDAndMap; +exports.handleAction = handleAction; +exports.handleNext = handleNext; function getMenuConfig(client, name, cb) { var menuConfig; @@ -70,20 +70,20 @@ function loadMenu(options, cb) { menuConfig.options.menuFlags = [ menuConfig.options.menuFlags ]; } - const modAsset = asset.getModuleAsset(menuConfig.module); - const modSupplied = null !== modAsset; + const modAsset = asset.getModuleAsset(menuConfig.module); + const modSupplied = null !== modAsset; const modLoadOpts = { - name : modSupplied ? modAsset.asset : 'standard_menu', - path : (!modSupplied || 'systemModule' === modAsset.type) ? __dirname : Config().paths.mods, - category : (!modSupplied || 'systemModule' === modAsset.type) ? null : 'mods', + name : modSupplied ? modAsset.asset : 'standard_menu', + path : (!modSupplied || 'systemModule' === modAsset.type) ? __dirname : Config().paths.mods, + category : (!modSupplied || 'systemModule' === modAsset.type) ? null : 'mods', }; moduleUtil.loadModuleEx(modLoadOpts, (err, mod) => { const modData = { - name : modLoadOpts.name, - config : menuConfig, - mod : mod, + name : modLoadOpts.name, + config : menuConfig, + mod : mod, }; return callback(err, modData); @@ -97,11 +97,11 @@ function loadMenu(options, cb) { let moduleInstance; try { moduleInstance = new modData.mod.getModule({ - menuName : options.name, - menuConfig : modData.config, - extraArgs : options.extraArgs, - client : options.client, - lastMenuResult : options.lastMenuResult, + menuName : options.name, + menuConfig : modData.config, + extraArgs : options.extraArgs, + client : options.client, + lastMenuResult : options.lastMenuResult, }); } catch(e) { return callback(e); @@ -137,7 +137,7 @@ function getFormConfigByIDAndMap(menuConfig, formId, mciMap, cb) { Log.trace( { mciKey : mciReqKey }, 'Looking for MCI configuration key'); // - // Exact, explicit match? + // Exact, explicit match? // if(_.isObject(formForId[mciReqKey])) { Log.trace( { mciKey : mciReqKey }, 'Using exact configuration key match'); @@ -146,7 +146,7 @@ function getFormConfigByIDAndMap(menuConfig, formId, mciMap, cb) { } // - // Generic match + // Generic match // if(_.has(formForId, 'mci') || _.has(formForId, 'submit')) { Log.trace('Using generic configuration'); @@ -156,7 +156,7 @@ function getFormConfigByIDAndMap(menuConfig, formId, mciMap, cb) { cb(new Error('No matching form configuration found for key \'' + mciReqKey + '\'')); } -// :TODO: Most of this should be moved elsewhere .... DRY... +// :TODO: Most of this should be moved elsewhere .... DRY... function callModuleMenuMethod(client, asset, path, formData, extraArgs, cb) { if('' === paths.extname(path)) { path += '.js'; @@ -194,8 +194,8 @@ function handleAction(client, formData, conf, cb) { conf.extraArgs, cb); } else if('systemMethod' === actionAsset.type) { - // :TODO: Need to pass optional args here -- conf.extraArgs and args between e.g. () - // :TODO: Probably better as system_method.js + // :TODO: Need to pass optional args here -- conf.extraArgs and args between e.g. () + // :TODO: Probably better as system_method.js return callModuleMenuMethod( client, actionAsset, @@ -204,7 +204,7 @@ function handleAction(client, formData, conf, cb) { conf.extraArgs, cb); } else { - // local to current module + // local to current module const currentModule = client.currentMenuModule; if(_.isFunction(currentModule.menuMethods[actionAsset.asset])) { return currentModule.menuMethods[actionAsset.asset](formData, conf.extraArgs, cb); @@ -221,28 +221,28 @@ function handleAction(client, formData, conf, cb) { } function handleNext(client, nextSpec, conf, cb) { - nextSpec = client.acs.getConditionalValue(nextSpec, 'next'); // handle any conditionals + nextSpec = client.acs.getConditionalValue(nextSpec, 'next'); // handle any conditionals const nextAsset = asset.getAssetWithShorthand(nextSpec, 'menu'); - // :TODO: getAssetWithShorthand() can return undefined - handle it! + // :TODO: getAssetWithShorthand() can return undefined - handle it! conf = conf || {}; const extraArgs = conf.extraArgs || {}; - // :TODO: DRY this with handleAction() + // :TODO: DRY this with handleAction() switch(nextAsset.type) { case 'method' : case 'systemMethod' : if(_.isString(nextAsset.location)) { return callModuleMenuMethod(client, nextAsset, paths.join(Config().paths.mods, nextAsset.location), {}, extraArgs, cb); } else if('systemMethod' === nextAsset.type) { - // :TODO: see other notes about system_menu_method.js here + // :TODO: see other notes about system_menu_method.js here return callModuleMenuMethod(client, nextAsset, paths.join(__dirname, 'system_menu_method.js'), {}, extraArgs, cb); } else { - // local to current module + // local to current module const currentModule = client.currentMenuModule; if(_.isFunction(currentModule.menuMethods[nextAsset.asset])) { - const formData = {}; // we don't have any + const formData = {}; // we don't have any return currentModule.menuMethods[nextAsset.asset]( formData, extraArgs, cb ); } diff --git a/core/menu_view.js b/core/menu_view.js index 78bf2c87..73e56a4a 100644 --- a/core/menu_view.js +++ b/core/menu_view.js @@ -1,17 +1,17 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const View = require('./view.js').View; -const miscUtil = require('./misc_util.js'); -const pipeToAnsi = require('./color_codes.js').pipeToAnsi; +// ENiGMA½ +const View = require('./view.js').View; +const miscUtil = require('./misc_util.js'); +const pipeToAnsi = require('./color_codes.js').pipeToAnsi; -// deps -const util = require('util'); -const assert = require('assert'); -const _ = require('lodash'); +// deps +const util = require('util'); +const assert = require('assert'); +const _ = require('lodash'); -exports.MenuView = MenuView; +exports.MenuView = MenuView; function MenuView(options) { options.acceptsFocus = miscUtil.valueWithDefault(options.acceptsFocus, true); @@ -38,14 +38,14 @@ function MenuView(options) { this.focusedItemIndex = options.focusedItemIndex || 0; this.focusedItemIndex = this.items.length >= this.focusedItemIndex ? this.focusedItemIndex : 0; - this.itemSpacing = _.isNumber(options.itemSpacing) ? options.itemSpacing : 0; + this.itemSpacing = _.isNumber(options.itemSpacing) ? options.itemSpacing : 0; - // :TODO: probably just replace this with owner draw / pipe codes / etc. more control, less specialization - this.focusPrefix = options.focusPrefix || ''; - this.focusSuffix = options.focusSuffix || ''; + // :TODO: probably just replace this with owner draw / pipe codes / etc. more control, less specialization + this.focusPrefix = options.focusPrefix || ''; + this.focusSuffix = options.focusSuffix || ''; - this.fillChar = miscUtil.valueWithDefault(options.fillChar, ' ').substr(0, 1); - this.justify = options.justify || 'none'; + this.fillChar = miscUtil.valueWithDefault(options.fillChar, ' ').substr(0, 1); + this.justify = options.justify || 'none'; this.hasFocusItems = function() { return !_.isUndefined(self.focusItems); @@ -74,15 +74,15 @@ MenuView.prototype.setItems = function(items) { this.renderCache = {}; // - // Items can be an array of strings or an array of objects. + // Items can be an array of strings or an array of objects. // - // In the case of objects, items are considered complex and - // may have one or more members that can later be formatted - // against. The default member is 'text'. The member 'data' - // may be overridden to provide a form value other than the - // item's index. + // In the case of objects, items are considered complex and + // may have one or more members that can later be formatted + // against. The default member is 'text'. The member 'data' + // may be overridden to provide a form value other than the + // item's index. // - // Items can be formatted with 'itemFormat' and 'focusItemFormat' + // Items can be formatted with 'itemFormat' and 'focusItemFormat' // let text; let stringItem; @@ -96,7 +96,7 @@ MenuView.prototype.setItems = function(items) { } text = this.disablePipe ? text : pipeToAnsi(text, this.client); - return Object.assign({ }, { text }, stringItem ? {} : item); // ensure we have a text member, plus any others + return Object.assign({ }, { text }, stringItem ? {} : item); // ensure we have a text member, plus any others }); if(this.complexItems) { @@ -122,7 +122,7 @@ MenuView.prototype.setSort = function(sort) { const key = true === sort ? 'text' : sort; if('text' !== sort && !this.complexItems) { - return; // need a valid sort key + return; // need a valid sort key } this.items.sort( (a, b) => { @@ -237,26 +237,26 @@ MenuView.prototype.setItemSpacing = function(itemSpacing) { itemSpacing = parseInt(itemSpacing); assert(_.isNumber(itemSpacing)); - this.itemSpacing = itemSpacing; - this.positionCacheExpired = true; + this.itemSpacing = itemSpacing; + this.positionCacheExpired = true; }; MenuView.prototype.setPropertyValue = function(propName, value) { switch(propName) { - case 'itemSpacing' : this.setItemSpacing(value); break; - case 'items' : this.setItems(value); break; - case 'focusItems' : this.setFocusItems(value); break; - case 'hotKeys' : this.setHotKeys(value); break; - case 'hotKeySubmit' : this.hotKeySubmit = value; break; - case 'justify' : this.justify = value; break; - case 'focusItemIndex' : this.focusedItemIndex = value; break; + case 'itemSpacing' : this.setItemSpacing(value); break; + case 'items' : this.setItems(value); break; + case 'focusItems' : this.setFocusItems(value); break; + case 'hotKeys' : this.setHotKeys(value); break; + case 'hotKeySubmit' : this.hotKeySubmit = value; break; + case 'justify' : this.justify = value; break; + case 'focusItemIndex' : this.focusedItemIndex = value; break; case 'itemFormat' : case 'focusItemFormat' : this[propName] = value; break; - case 'sort' : this.setSort(value); break; + case 'sort' : this.setSort(value); break; } MenuView.super_.prototype.setPropertyValue.call(this, propName, value); diff --git a/core/message.js b/core/message.js index 277f5781..53b82e73 100644 --- a/core/message.js +++ b/core/message.js @@ -1,96 +1,96 @@ /* jslint node: true */ 'use strict'; -const msgDb = require('./database.js').dbs.message; -const wordWrapText = require('./word_wrap.js').wordWrapText; -const ftnUtil = require('./ftn_util.js'); -const createNamedUUID = require('./uuid_util.js').createNamedUUID; -const Errors = require('./enig_error.js').Errors; -const ANSI = require('./ansi_term.js'); +const msgDb = require('./database.js').dbs.message; +const wordWrapText = require('./word_wrap.js').wordWrapText; +const ftnUtil = require('./ftn_util.js'); +const createNamedUUID = require('./uuid_util.js').createNamedUUID; +const Errors = require('./enig_error.js').Errors; +const ANSI = require('./ansi_term.js'); const { sanatizeString, - getISOTimestampString } = require('./database.js'); + getISOTimestampString } = require('./database.js'); const { isAnsi, isFormattedLine, splitTextAtTerms, renderSubstr -} = require('./string_util.js'); +} = require('./string_util.js'); -const ansiPrep = require('./ansi_prep.js'); +const ansiPrep = require('./ansi_prep.js'); -// deps -const uuidParse = require('uuid-parse'); -const async = require('async'); -const _ = require('lodash'); -const assert = require('assert'); -const moment = require('moment'); -const iconvEncode = require('iconv-lite').encode; +// deps +const uuidParse = require('uuid-parse'); +const async = require('async'); +const _ = require('lodash'); +const assert = require('assert'); +const moment = require('moment'); +const iconvEncode = require('iconv-lite').encode; -const ENIGMA_MESSAGE_UUID_NAMESPACE = uuidParse.parse('154506df-1df8-46b9-98f8-ebb5815baaf8'); +const ENIGMA_MESSAGE_UUID_NAMESPACE = uuidParse.parse('154506df-1df8-46b9-98f8-ebb5815baaf8'); const WELL_KNOWN_AREA_TAGS = { - Invalid : '', - Private : 'private_mail', - Bulletin : 'local_bulletin', + Invalid : '', + Private : 'private_mail', + Bulletin : 'local_bulletin', }; const SYSTEM_META_NAMES = { - LocalToUserID : 'local_to_user_id', - LocalFromUserID : 'local_from_user_id', - StateFlags0 : 'state_flags0', // See Message.StateFlags0 - ExplicitEncoding : 'explicit_encoding', // Explicitly set encoding when exporting/etc. - ExternalFlavor : 'external_flavor', // "Flavor" of message - imported from or to be exported to. See Message.AddressFlavor - RemoteToUser : 'remote_to_user', // Opaque value depends on external system, e.g. FTN address - RemoteFromUser : 'remote_from_user', // Opaque value depends on external system, e.g. FTN address + LocalToUserID : 'local_to_user_id', + LocalFromUserID : 'local_from_user_id', + StateFlags0 : 'state_flags0', // See Message.StateFlags0 + ExplicitEncoding : 'explicit_encoding', // Explicitly set encoding when exporting/etc. + ExternalFlavor : 'external_flavor', // "Flavor" of message - imported from or to be exported to. See Message.AddressFlavor + RemoteToUser : 'remote_to_user', // Opaque value depends on external system, e.g. FTN address + RemoteFromUser : 'remote_from_user', // Opaque value depends on external system, e.g. FTN address }; -// Types for Message.SystemMetaNames.ExternalFlavor meta +// Types for Message.SystemMetaNames.ExternalFlavor meta const ADDRESS_FLAVOR = { - Local : 'local', // local / non-remote addressing - FTN : 'ftn', // FTN style - Email : 'email', + Local : 'local', // local / non-remote addressing + FTN : 'ftn', // FTN style + Email : 'email', }; const STATE_FLAGS0 = { - None : 0x00000000, - Imported : 0x00000001, // imported from foreign system - Exported : 0x00000002, // exported to foreign system + None : 0x00000000, + Imported : 0x00000001, // imported from foreign system + Exported : 0x00000002, // exported to foreign system }; -// :TODO: these should really live elsewhere... +// :TODO: these should really live elsewhere... const FTN_PROPERTY_NAMES = { - // packet header oriented - FtnOrigNode : 'ftn_orig_node', - FtnDestNode : 'ftn_dest_node', - // :TODO: rename these to ftn_*_net vs network - ensure things won't break, may need mapping - FtnOrigNetwork : 'ftn_orig_network', - FtnDestNetwork : 'ftn_dest_network', - FtnAttrFlags : 'ftn_attr_flags', - FtnCost : 'ftn_cost', - FtnOrigZone : 'ftn_orig_zone', - FtnDestZone : 'ftn_dest_zone', - FtnOrigPoint : 'ftn_orig_point', - FtnDestPoint : 'ftn_dest_point', + // packet header oriented + FtnOrigNode : 'ftn_orig_node', + FtnDestNode : 'ftn_dest_node', + // :TODO: rename these to ftn_*_net vs network - ensure things won't break, may need mapping + FtnOrigNetwork : 'ftn_orig_network', + FtnDestNetwork : 'ftn_dest_network', + FtnAttrFlags : 'ftn_attr_flags', + FtnCost : 'ftn_cost', + FtnOrigZone : 'ftn_orig_zone', + FtnDestZone : 'ftn_dest_zone', + FtnOrigPoint : 'ftn_orig_point', + FtnDestPoint : 'ftn_dest_point', - // message header oriented - FtnMsgOrigNode : 'ftn_msg_orig_node', - FtnMsgDestNode : 'ftn_msg_dest_node', - FtnMsgOrigNet : 'ftn_msg_orig_net', - FtnMsgDestNet : 'ftn_msg_dest_net', + // message header oriented + FtnMsgOrigNode : 'ftn_msg_orig_node', + FtnMsgDestNode : 'ftn_msg_dest_node', + FtnMsgOrigNet : 'ftn_msg_orig_net', + FtnMsgDestNet : 'ftn_msg_dest_net', - FtnAttribute : 'ftn_attribute', + FtnAttribute : 'ftn_attribute', - FtnTearLine : 'ftn_tear_line', // http://ftsc.org/docs/fts-0004.001 - FtnOrigin : 'ftn_origin', // http://ftsc.org/docs/fts-0004.001 - FtnArea : 'ftn_area', // http://ftsc.org/docs/fts-0004.001 - FtnSeenBy : 'ftn_seen_by', // http://ftsc.org/docs/fts-0004.001 + FtnTearLine : 'ftn_tear_line', // http://ftsc.org/docs/fts-0004.001 + FtnOrigin : 'ftn_origin', // http://ftsc.org/docs/fts-0004.001 + FtnArea : 'ftn_area', // http://ftsc.org/docs/fts-0004.001 + FtnSeenBy : 'ftn_seen_by', // http://ftsc.org/docs/fts-0004.001 }; -// :TODO: this is a ugly hack due to bad variable names - clean it up & just _.camelCase(k)! +// :TODO: this is a ugly hack due to bad variable names - clean it up & just _.camelCase(k)! const MESSAGE_ROW_MAP = { - reply_to_message_id : 'replyToMsgId', - modified_timestamp : 'modTimestamp' + reply_to_message_id : 'replyToMsgId', + modified_timestamp : 'modTimestamp' }; module.exports = class Message { @@ -102,28 +102,28 @@ module.exports = class Message { } = { } ) { - this.messageId = messageId; - this.areaTag = areaTag; - this.uuid = uuid; - this.replyToMsgId = replyToMsgId; - this.toUserName = toUserName; - this.fromUserName = fromUserName; - this.subject = subject; - this.message = message; + this.messageId = messageId; + this.areaTag = areaTag; + this.uuid = uuid; + this.replyToMsgId = replyToMsgId; + this.toUserName = toUserName; + this.fromUserName = fromUserName; + this.subject = subject; + this.message = message; if(_.isDate(modTimestamp) || _.isString(modTimestamp)) { modTimestamp = moment(modTimestamp); } - this.modTimestamp = modTimestamp; + this.modTimestamp = modTimestamp; this.meta = {}; _.defaultsDeep(this.meta, { System : {} }, meta); - this.hashTags = hashTags; + this.hashTags = hashTags; } - isValid() { return true; } // :TODO: obviously useless; look into this or remove it + isValid() { return true; } // :TODO: obviously useless; look into this or remove it static isPrivateAreaTag(areaTag) { return areaTag.toLowerCase() === Message.WellKnownAreaTags.Private; @@ -187,10 +187,10 @@ module.exports = class Message { modTimestamp = moment(modTimestamp); } - areaTag = iconvEncode(areaTag.toUpperCase(), 'CP437'); - modTimestamp = iconvEncode(modTimestamp.format('DD MMM YY HH:mm:ss'), 'CP437'); - subject = iconvEncode(subject.toUpperCase().trim(), 'CP437'); - body = iconvEncode(body.replace(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g, '').trim(), 'CP437'); + areaTag = iconvEncode(areaTag.toUpperCase(), 'CP437'); + modTimestamp = iconvEncode(modTimestamp.format('DD MMM YY HH:mm:ss'), 'CP437'); + subject = iconvEncode(subject.toUpperCase().trim(), 'CP437'); + body = iconvEncode(body.replace(/\r\n|[\n\v\f\r\x85\u2028\u2029]/g, '').trim(), 'CP437'); return uuidParse.unparse(createNamedUUID(ENIGMA_MESSAGE_UUID_NAMESPACE, Buffer.concat( [ areaTag, modTimestamp, subject, body ] ))); } @@ -198,7 +198,7 @@ module.exports = class Message { static getMessageFromRow(row) { const msg = {}; _.each(row, (v, k) => { - // :TODO: see notes around MESSAGE_ROW_MAP -- clean this up so we can just _camelCase()! + // :TODO: see notes around MESSAGE_ROW_MAP -- clean this up so we can just _camelCase()! k = MESSAGE_ROW_MAP[k] || _.camelCase(k); msg[k] = v; }); @@ -206,38 +206,38 @@ module.exports = class Message { } /* - Find message IDs or UUIDs by filter. Available filters/options: + Find message IDs or UUIDs by filter. Available filters/options: - filter.uuids - use with resultType='id' - filter.ids - use with resultType='uuid' - filter.toUserName - filter.fromUserName - filter.replyToMesageId - filter.newerThanTimestamp - filter.newerThanMessageId - filter.areaTag - note if you want by conf, send in all areas for a conf - *filter.metaTuples - {category, name, value} + filter.uuids - use with resultType='id' + filter.ids - use with resultType='uuid' + filter.toUserName + filter.fromUserName + filter.replyToMesageId + filter.newerThanTimestamp + filter.newerThanMessageId + filter.areaTag - note if you want by conf, send in all areas for a conf + *filter.metaTuples - {category, name, value} - filter.terms - FTS search + filter.terms - FTS search - filter.sort = modTimestamp | messageId - filter.order = ascending | (descending) + filter.sort = modTimestamp | messageId + filter.order = ascending | (descending) - filter.limit - filter.resultType = (id) | uuid | count - filter.extraFields = [] + filter.limit + filter.resultType = (id) | uuid | count + filter.extraFields = [] - filter.privateTagUserId = - if set, only private messages belonging to are processed - - any other areaTag or confTag filters will be ignored - - if NOT present, private areas are skipped + filter.privateTagUserId = - if set, only private messages belonging to are processed + - any other areaTag or confTag filters will be ignored + - if NOT present, private areas are skipped - *=NYI - */ + *=NYI + */ static findMessages(filter, cb) { filter = filter || {}; - filter.resultType = filter.resultType || 'id'; - filter.extraFields = filter.extraFields || []; + filter.resultType = filter.resultType || 'id'; + filter.extraFields = filter.extraFields || []; if('messageList' === filter.resultType) { filter.extraFields = _.uniq(filter.extraFields.concat( @@ -254,13 +254,13 @@ module.exports = class Message { let sql; if('count' === filter.resultType) { sql = - `SELECT COUNT() AS count - FROM message m`; + `SELECT COUNT() AS count + FROM message m`; } else { sql = - `SELECT DISTINCT m.${field}${filter.extraFields.length > 0 ? ', ' + filter.extraFields.map(f => `m.${f}`).join(', ') : ''} - FROM message m`; + `SELECT DISTINCT m.${field}${filter.extraFields.length > 0 ? ', ' + filter.extraFields.map(f => `m.${f}`).join(', ') : ''} + FROM message m`; } const sqlOrderDir = 'ascending' === filter.order ? 'ASC' : 'DESC'; @@ -276,7 +276,7 @@ module.exports = class Message { sqlWhere += clause; } - // currently only avail sort + // currently only avail sort if('modTimestamp' === filter.sort) { sqlOrderBy = `ORDER BY m.modified_timestamp ${sqlOrderDir}`; } else { @@ -297,10 +297,10 @@ module.exports = class Message { appendWhereClause(`m.area_tag = "${Message.WellKnownAreaTags.Private}"`); appendWhereClause( `m.message_id IN ( - SELECT message_id - FROM message_meta - WHERE meta_category = "System" AND meta_name = "${Message.SystemMetaNames.LocalToUserID}" AND meta_value = ${filter.privateTagUserId} - )`); + SELECT message_id + FROM message_meta + WHERE meta_category = "System" AND meta_name = "${Message.SystemMetaNames.LocalToUserID}" AND meta_value = ${filter.privateTagUserId} + )`); } else { if(filter.areaTag && filter.areaTag.length > 0) { if(Array.isArray(filter.areaTag)) { @@ -315,7 +315,7 @@ module.exports = class Message { } } - // explicit exclude of Private + // explicit exclude of Private appendWhereClause(`m.area_tag != "${Message.WellKnownAreaTags.Private}"`); } @@ -338,13 +338,13 @@ module.exports = class Message { } if(filter.terms && filter.terms.length > 0) { - // note the ':' in MATCH expr., see https://www.sqlite.org/cvstrac/wiki?p=FullTextIndex + // note the ':' in MATCH expr., see https://www.sqlite.org/cvstrac/wiki?p=FullTextIndex appendWhereClause( `m.message_id IN ( - SELECT rowid - FROM message_fts - WHERE message_fts MATCH ":${sanatizeString(filter.terms)}" - )` + SELECT rowid + FROM message_fts + WHERE message_fts MATCH ":${sanatizeString(filter.terms)}" + )` ); } @@ -376,13 +376,13 @@ module.exports = class Message { } } - // :TODO: use findMessages, by uuid, limit=1 + // :TODO: use findMessages, by uuid, limit=1 static getMessageIdByUuid(uuid, cb) { msgDb.get( `SELECT message_id - FROM message - WHERE message_uuid = ? - LIMIT 1;`, + FROM message + WHERE message_uuid = ? + LIMIT 1;`, [ uuid ], (err, row) => { if(err) { @@ -398,27 +398,27 @@ module.exports = class Message { ); } - // :TODO: use findMessages + // :TODO: use findMessages static getMessageIdsByMetaValue(category, name, value, cb) { msgDb.all( `SELECT message_id - FROM message_meta - WHERE meta_category = ? AND meta_name = ? AND meta_value = ?;`, + FROM message_meta + WHERE meta_category = ? AND meta_name = ? AND meta_value = ?;`, [ category, name, value ], (err, rows) => { if(err) { return cb(err); } - return cb(null, rows.map(r => parseInt(r.message_id))); // return array of ID(s) + return cb(null, rows.map(r => parseInt(r.message_id))); // return array of ID(s) } ); } static getMetaValuesByMessageId(messageId, category, name, cb) { const sql = - `SELECT meta_value - FROM message_meta - WHERE message_id = ? AND meta_category = ? AND meta_name = ?;`; + `SELECT meta_value + FROM message_meta + WHERE message_id = ? AND meta_category = ? AND meta_name = ?;`; msgDb.all(sql, [ messageId, category, name ], (err, rows) => { if(err) { @@ -429,12 +429,12 @@ module.exports = class Message { return cb(Errors.DoesNotExist('No value for category/name')); } - // single values are returned without an array + // single values are returned without an array if(1 === rows.length) { return cb(null, rows[0].meta_value); } - return cb(null, rows.map(r => r.meta_value)); // map to array of values only + return cb(null, rows.map(r => r.meta_value)); // map to array of values only }); } @@ -460,23 +460,23 @@ module.exports = class Message { loadMeta(cb) { /* - Example of loaded this.meta: + Example of loaded this.meta: - meta: { - System: { - local_to_user_id: 1234, - }, - FtnProperty: { - ftn_seen_by: [ "1/102 103", "2/42 52 65" ] - } - } - */ + meta: { + System: { + local_to_user_id: 1234, + }, + FtnProperty: { + ftn_seen_by: [ "1/102 103", "2/42 52 65" ] + } + } + */ const sql = - `SELECT meta_category, meta_name, meta_value - FROM message_meta - WHERE message_id = ?;`; + `SELECT meta_category, meta_name, meta_value + FROM message_meta + WHERE message_id = ?;`; - const self = this; // :TODO: not required - arrow functions below: + const self = this; // :TODO: not required - arrow functions below: msgDb.each(sql, [ this.messageId ], (err, row) => { if(!(row.meta_category in self.meta)) { self.meta[row.meta_category] = { }; @@ -497,7 +497,7 @@ module.exports = class Message { }); } - // :TODO: this should only take a UUID... + // :TODO: this should only take a UUID... load(options, cb) { assert(_.isString(options.uuid)); @@ -508,10 +508,10 @@ module.exports = class Message { function loadMessage(callback) { msgDb.get( `SELECT message_id, area_tag, message_uuid, reply_to_message_id, to_user_name, from_user_name, subject, - message, modified_timestamp, view_count - FROM message - WHERE message_uuid=? - LIMIT 1;`, + message, modified_timestamp, view_count + FROM message + WHERE message_uuid=? + LIMIT 1;`, [ options.uuid ], (err, msgRow) => { if(err) { @@ -522,15 +522,15 @@ module.exports = class Message { return callback(Errors.DoesNotExist('Message (no longer) available')); } - self.messageId = msgRow.message_id; - self.areaTag = msgRow.area_tag; - self.messageUuid = msgRow.message_uuid; - self.replyToMsgId = msgRow.reply_to_message_id; - self.toUserName = msgRow.to_user_name; - self.fromUserName = msgRow.from_user_name; - self.subject = msgRow.subject; - self.message = msgRow.message; - self.modTimestamp = moment(msgRow.modified_timestamp); + self.messageId = msgRow.message_id; + self.areaTag = msgRow.area_tag; + self.messageUuid = msgRow.message_uuid; + self.replyToMsgId = msgRow.reply_to_message_id; + self.toUserName = msgRow.to_user_name; + self.fromUserName = msgRow.from_user_name; + self.subject = msgRow.subject; + self.message = msgRow.message; + self.modTimestamp = moment(msgRow.modified_timestamp); return callback(err); } @@ -542,7 +542,7 @@ module.exports = class Message { }); }, function loadHashTags(callback) { - // :TODO: + // :TODO: return callback(null); } ], @@ -560,7 +560,7 @@ module.exports = class Message { const metaStmt = transOrDb.prepare( `INSERT INTO message_meta (message_id, meta_category, meta_name, meta_value) - VALUES (?, ?, ?, ?);`); + VALUES (?, ?, ?, ?);`); if(!_.isArray(value)) { value = [ value ]; @@ -590,7 +590,7 @@ module.exports = class Message { return msgDb.beginTransaction(callback); }, function storeMessage(trans, callback) { - // generate a UUID for this message if required (general case) + // generate a UUID for this message if required (general case) const msgTimestamp = moment(); if(!self.uuid) { self.uuid = Message.createMessageUUID( @@ -603,9 +603,9 @@ module.exports = class Message { trans.run( `INSERT INTO message (area_tag, message_uuid, reply_to_message_id, to_user_name, from_user_name, subject, message, modified_timestamp) - VALUES (?, ?, ?, ?, ?, ?, ?, ?);`, + VALUES (?, ?, ?, ?, ?, ?, ?, ?);`, [ self.areaTag, self.uuid, self.replyToMsgId, self.toUserName, self.fromUserName, self.subject, self.message, getISOTimestampString(msgTimestamp) ], - function inserted(err) { // use non-arrow function for 'this' scope + function inserted(err) { // use non-arrow function for 'this' scope if(!err) { self.messageId = this.lastID; } @@ -619,17 +619,17 @@ module.exports = class Message { return callback(null, trans); } /* - Example of self.meta: + Example of self.meta: - meta: { - System: { - local_to_user_id: 1234, - }, - FtnProperty: { - ftn_seen_by: [ "1/102 103", "2/42 52 65" ] - } - } - */ + meta: { + System: { + local_to_user_id: 1234, + }, + FtnProperty: { + ftn_seen_by: [ "1/102 103", "2/42 52 65" ] + } + } + */ async.each(Object.keys(self.meta), (category, nextCat) => { async.each(Object.keys(self.meta[category]), (name, nextName) => { self.persistMetaValue(category, name, self.meta[category][name], trans, err => { @@ -644,7 +644,7 @@ module.exports = class Message { }); }, function storeHashTags(trans, callback) { - // :TODO: hash tag support + // :TODO: hash tag support return callback(null, trans); } ], @@ -660,7 +660,7 @@ module.exports = class Message { ); } - // :TODO: FTN stuff doesn't have any business here + // :TODO: FTN stuff doesn't have any business here getFTNQuotePrefix(source) { source = source || 'fromUserName'; @@ -677,32 +677,32 @@ module.exports = class Message { return cb(Errors.MissingParam()); } - options.startCol = options.startCol || 1; - options.includePrefix = _.get(options, 'includePrefix', true); - options.ansiResetSgr = options.ansiResetSgr || ANSI.getSGRFromGraphicRendition( { fg : 39, bg : 49 }, true); - options.ansiFocusPrefixSgr = options.ansiFocusPrefixSgr || ANSI.getSGRFromGraphicRendition( { intensity : 'bold', fg : 39, bg : 49 } ); - options.isAnsi = options.isAnsi || isAnsi(this.message); // :TODO: If this.isAnsi, use that setting + options.startCol = options.startCol || 1; + options.includePrefix = _.get(options, 'includePrefix', true); + options.ansiResetSgr = options.ansiResetSgr || ANSI.getSGRFromGraphicRendition( { fg : 39, bg : 49 }, true); + options.ansiFocusPrefixSgr = options.ansiFocusPrefixSgr || ANSI.getSGRFromGraphicRendition( { intensity : 'bold', fg : 39, bg : 49 } ); + options.isAnsi = options.isAnsi || isAnsi(this.message); // :TODO: If this.isAnsi, use that setting /* - Some long text that needs to be wrapped and quoted should look right after - doing so, don't ya think? yeah I think so + Some long text that needs to be wrapped and quoted should look right after + doing so, don't ya think? yeah I think so - Nu> Some long text that needs to be wrapped and quoted should look right - Nu> after doing so, don't ya think? yeah I think so + Nu> Some long text that needs to be wrapped and quoted should look right + Nu> after doing so, don't ya think? yeah I think so - Ot> Nu> Some long text that needs to be wrapped and quoted should look - Ot> Nu> right after doing so, don't ya think? yeah I think so + Ot> Nu> Some long text that needs to be wrapped and quoted should look + Ot> Nu> right after doing so, don't ya think? yeah I think so - */ + */ const quotePrefix = options.includePrefix ? this.getFTNQuotePrefix(options.prefixSource || 'fromUserName') : ''; function getWrapped(text, extraPrefix) { extraPrefix = extraPrefix ? ` ${extraPrefix}` : ''; const wrapOpts = { - width : options.cols - (quotePrefix.length + extraPrefix.length), - tabHandling : 'expand', - tabWidth : 4, + width : options.cols - (quotePrefix.length + extraPrefix.length), + tabHandling : 'expand', + tabWidth : 4, }; return wordWrapText(text, wrapOpts).wrapped.map( (w, i) => { @@ -711,7 +711,7 @@ module.exports = class Message { } function getFormattedLine(line) { - // for pre-formatted text, we just append a line truncated to fit + // for pre-formatted text, we just append a line truncated to fit let newLen; const total = line.length + quotePrefix.length; @@ -726,14 +726,14 @@ module.exports = class Message { if(options.isAnsi) { ansiPrep( - this.message.replace(/\r?\n/g, '\r\n'), // normalized LF -> CRLF + this.message.replace(/\r?\n/g, '\r\n'), // normalized LF -> CRLF { - termWidth : options.termWidth, - termHeight : options.termHeight, - cols : options.cols, - rows : 'auto', - startCol : options.startCol, - forceLineTerm : true, + termWidth : options.termWidth, + termHeight : options.termHeight, + cols : options.cols, + rows : 'auto', + startCol : options.startCol, + forceLineTerm : true, }, (err, prepped) => { prepped = prepped || this.message; @@ -741,20 +741,20 @@ module.exports = class Message { let lastSgr = ''; const split = splitTextAtTerms(prepped); - const quoteLines = []; - const focusQuoteLines = []; + const quoteLines = []; + const focusQuoteLines = []; // - // Do not include quote prefixes (e.g. XX> ) on ANSI replies (and therefor quote builder) - // as while this works in ENiGMA, other boards such as Mystic, WWIV, etc. will try to - // strip colors, colorize the lines, etc. If we exclude the prefixes, this seems to do - // the trick and allow them to leave them alone! + // Do not include quote prefixes (e.g. XX> ) on ANSI replies (and therefor quote builder) + // as while this works in ENiGMA, other boards such as Mystic, WWIV, etc. will try to + // strip colors, colorize the lines, etc. If we exclude the prefixes, this seems to do + // the trick and allow them to leave them alone! // split.forEach(l => { quoteLines.push(`${lastSgr}${l}`); focusQuoteLines.push(`${options.ansiFocusPrefixSgr}>${lastSgr}${renderSubstr(l, 1, l.length - 1)}`); - lastSgr = (l.match(/(?:\x1b\x5b)[?=;0-9]*m(?!.*(?:\x1b\x5b)[?=;0-9]*m)/) || [])[0] || ''; // eslint-disable-line no-control-regex + lastSgr = (l.match(/(?:\x1b\x5b)[?=;0-9]*m(?!.*(?:\x1b\x5b)[?=;0-9]*m)/) || [])[0] || ''; // eslint-disable-line no-control-regex }); quoteLines[quoteLines.length - 1] += options.ansiResetSgr; @@ -763,23 +763,23 @@ module.exports = class Message { } ); } else { - const QUOTE_RE = /^ ((?:[A-Za-z0-9]{2}> )+(?:[A-Za-z0-9]{2}>)*) */; - const quoted = []; - const input = _.trimEnd(this.message).replace(/\b/g, ''); + const QUOTE_RE = /^ ((?:[A-Za-z0-9]{2}> )+(?:[A-Za-z0-9]{2}>)*) */; + const quoted = []; + const input = _.trimEnd(this.message).replace(/\b/g, ''); - // find *last* tearline + // find *last* tearline let tearLinePos = this.getTearLinePosition(input); - tearLinePos = -1 === tearLinePos ? input.length : tearLinePos; // we just want the index or the entire string + tearLinePos = -1 === tearLinePos ? input.length : tearLinePos; // we just want the index or the entire string input.slice(0, tearLinePos).split(/\r\n\r\n|\n\n/).forEach(paragraph => { // - // For each paragraph, a state machine: - // - New line - line - // - New (pre)quoted line - quote_line - // - Continuation of new/quoted line + // For each paragraph, a state machine: + // - New line - line + // - New (pre)quoted line - quote_line + // - Continuation of new/quoted line // - // Also: - // - Detect pre-formatted lines & try to keep them as-is + // Also: + // - Detect pre-formatted lines & try to keep them as-is // let state; let buf = ''; @@ -787,18 +787,18 @@ module.exports = class Message { if(quoted.length > 0) { // - // Preserve paragraph seperation. + // Preserve paragraph seperation. // - // FSC-0032 states something about leaving blank lines fully blank - // (without a prefix) but it seems nicer (and more consistent with other systems) - // to put 'em in. + // FSC-0032 states something about leaving blank lines fully blank + // (without a prefix) but it seems nicer (and more consistent with other systems) + // to put 'em in. // quoted.push(quotePrefix); } paragraph.split(/\r?\n/).forEach(line => { if(0 === line.trim().length) { - // see blank line notes above + // see blank line notes above return quoted.push(quotePrefix); } @@ -839,8 +839,8 @@ module.exports = class Message { if(isFormattedLine(line)) { quoted.push(getFormattedLine(line)); } else { - state = quoteMatch ? 'quote_line' : 'line'; - buf = 'line' === state ? line : line.replace(/\s/, ''); // trim *first* leading space, if any + state = quoteMatch ? 'quote_line' : 'line'; + buf = 'line' === state ? line : line.replace(/\s/, ''); // trim *first* leading space, if any } break; } diff --git a/core/message_area.js b/core/message_area.js index 03cc914c..e5227ef0 100644 --- a/core/message_area.js +++ b/core/message_area.js @@ -1,45 +1,45 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const msgDb = require('./database.js').dbs.message; -const Config = require('./config.js').get; -const Message = require('./message.js'); -const Log = require('./logger.js').log; -const msgNetRecord = require('./msg_network.js').recordMessage; -const sortAreasOrConfs = require('./conf_area_util.js').sortAreasOrConfs; +// ENiGMA½ +const msgDb = require('./database.js').dbs.message; +const Config = require('./config.js').get; +const Message = require('./message.js'); +const Log = require('./logger.js').log; +const msgNetRecord = require('./msg_network.js').recordMessage; +const sortAreasOrConfs = require('./conf_area_util.js').sortAreasOrConfs; -// deps -const async = require('async'); -const _ = require('lodash'); -const assert = require('assert'); +// deps +const async = require('async'); +const _ = require('lodash'); +const assert = require('assert'); exports.getAvailableMessageConferences = getAvailableMessageConferences; -exports.getSortedAvailMessageConferences = getSortedAvailMessageConferences; +exports.getSortedAvailMessageConferences = getSortedAvailMessageConferences; exports.getAvailableMessageAreasByConfTag = getAvailableMessageAreasByConfTag; exports.getSortedAvailMessageAreasByConfTag = getSortedAvailMessageAreasByConfTag; exports.getDefaultMessageConferenceTag = getDefaultMessageConferenceTag; exports.getDefaultMessageAreaTagByConfTag = getDefaultMessageAreaTagByConfTag; -exports.getMessageConferenceByTag = getMessageConferenceByTag; -exports.getMessageAreaByTag = getMessageAreaByTag; -exports.changeMessageConference = changeMessageConference; -exports.changeMessageArea = changeMessageArea; -exports.tempChangeMessageConfAndArea = tempChangeMessageConfAndArea; -exports.getMessageListForArea = getMessageListForArea; -exports.getNewMessageCountInAreaForUser = getNewMessageCountInAreaForUser; -exports.getNewMessagesInAreaForUser = getNewMessagesInAreaForUser; -exports.getMessageIdNewerThanTimestampByArea = getMessageIdNewerThanTimestampByArea; -exports.getMessageAreaLastReadId = getMessageAreaLastReadId; -exports.updateMessageAreaLastReadId = updateMessageAreaLastReadId; -exports.persistMessage = persistMessage; -exports.trimMessageAreasScheduledEvent = trimMessageAreasScheduledEvent; +exports.getMessageConferenceByTag = getMessageConferenceByTag; +exports.getMessageAreaByTag = getMessageAreaByTag; +exports.changeMessageConference = changeMessageConference; +exports.changeMessageArea = changeMessageArea; +exports.tempChangeMessageConfAndArea = tempChangeMessageConfAndArea; +exports.getMessageListForArea = getMessageListForArea; +exports.getNewMessageCountInAreaForUser = getNewMessageCountInAreaForUser; +exports.getNewMessagesInAreaForUser = getNewMessagesInAreaForUser; +exports.getMessageIdNewerThanTimestampByArea = getMessageIdNewerThanTimestampByArea; +exports.getMessageAreaLastReadId = getMessageAreaLastReadId; +exports.updateMessageAreaLastReadId = updateMessageAreaLastReadId; +exports.persistMessage = persistMessage; +exports.trimMessageAreasScheduledEvent = trimMessageAreasScheduledEvent; function getAvailableMessageConferences(client, options) { options = options || { includeSystemInternal : false }; assert(client || true === options.noClient); - // perform ACS check per conf & omit system_internal if desired + // perform ACS check per conf & omit system_internal if desired return _.omitBy(Config().messageConferences, (conf, confTag) => { if(!options.includeSystemInternal && 'system_internal' === confTag) { return true; @@ -53,7 +53,7 @@ function getSortedAvailMessageConferences(client, options) { const confs = _.map(getAvailableMessageConferences(client, options), (v, k) => { return { confTag : k, - conf : v, + conf : v, }; }); @@ -73,10 +73,10 @@ function getAvailableMessageAreasByConfTag(confTag, options) { const areas = config.messageConferences[confTag].areas; if(!options.client || true === options.noAcsCheck) { - // everything - no ACS checks + // everything - no ACS checks return areas; } else { - // perform ACS check per area + // perform ACS check per area return _.omitBy(areas, area => { return !options.client.acs.hasMessageAreaRead(area); }); @@ -87,8 +87,8 @@ function getAvailableMessageAreasByConfTag(confTag, options) { function getSortedAvailMessageAreasByConfTag(confTag, options) { const areas = _.map(getAvailableMessageAreasByConfTag(confTag, options), (v, k) => { return { - areaTag : k, - area : v, + areaTag : k, + area : v, }; }); @@ -99,16 +99,16 @@ function getSortedAvailMessageAreasByConfTag(confTag, options) { function getDefaultMessageConferenceTag(client, disableAcsCheck) { // - // Find the first conference marked 'default'. If found, - // inspect |client| against *read* ACS using defaults if not - // specified. + // Find the first conference marked 'default'. If found, + // inspect |client| against *read* ACS using defaults if not + // specified. // - // If the above fails, just go down the list until we get one - // that passes. + // If the above fails, just go down the list until we get one + // that passes. // - // It's possible that we end up with nothing here! + // It's possible that we end up with nothing here! // - // Note that built in 'system_internal' is always ommited here + // Note that built in 'system_internal' is always ommited here // const config = Config(); let defaultConf = _.findKey(config.messageConferences, o => o.default); @@ -170,7 +170,7 @@ function getMessageConfTagByAreaTag(areaTag) { function getMessageAreaByTag(areaTag, optionalConfTag) { const confs = Config().messageConferences; - // :TODO: this could be cached + // :TODO: this could be cached if(_.isString(optionalConfTag)) { if(_.has(confs, [ optionalConfTag, 'areas', areaTag ])) { return confs[optionalConfTag].areas[areaTag]; @@ -204,8 +204,8 @@ function changeMessageConference(client, confTag, cb) { } }, function getDefaultAreaInConf(conf, callback) { - const areaTag = getDefaultMessageAreaTagByConfTag(client, confTag); - const area = getMessageAreaByTag(areaTag, confTag); + const areaTag = getDefaultMessageAreaTagByConfTag(client, confTag); + const area = getMessageAreaByTag(areaTag, confTag); if(area) { callback(null, conf, { areaTag : areaTag, area : area } ); @@ -222,8 +222,8 @@ function changeMessageConference(client, confTag, cb) { }, function changeConferenceAndArea(conf, areaInfo, callback) { const newProps = { - message_conf_tag : confTag, - message_area_tag : areaInfo.areaTag, + message_conf_tag : confTag, + message_area_tag : areaInfo.areaTag, }; client.user.persistProperties(newProps, err => { callback(err, conf, areaInfo); @@ -242,7 +242,7 @@ function changeMessageConference(client, confTag, cb) { } function changeMessageAreaWithOptions(client, areaTag, options, cb) { - options = options || {}; // :TODO: this is currently pointless... cb is required... + options = options || {}; // :TODO: this is currently pointless... cb is required... async.waterfall( [ @@ -284,14 +284,14 @@ function changeMessageAreaWithOptions(client, areaTag, options, cb) { } // -// Temporairly -- e.g. non-persisted -- change to an area and it's -// associated underlying conference. ACS is checked for both. +// Temporairly -- e.g. non-persisted -- change to an area and it's +// associated underlying conference. ACS is checked for both. // -// This is useful for example when doing a new scan +// This is useful for example when doing a new scan // function tempChangeMessageConfAndArea(client, areaTag) { - const area = getMessageAreaByTag(areaTag); - const confTag = getMessageConfTagByAreaTag(areaTag); + const area = getMessageAreaByTag(areaTag); + const confTag = getMessageConfTagByAreaTag(areaTag); if(!area || !confTag) { return false; @@ -303,7 +303,7 @@ function tempChangeMessageConfAndArea(client, areaTag) { return false; } - client.user.properties.message_conf_tag = confTag; + client.user.properties.message_conf_tag = confTag; client.user.properties.message_area_tag = areaTag; return true; @@ -319,8 +319,8 @@ function getNewMessageCountInAreaForUser(userId, areaTag, cb) { const filter = { areaTag, - newerThanMessageId : lastMessageId, - resultType : 'count', + newerThanMessageId : lastMessageId, + resultType : 'count', }; if(Message.isPrivateAreaTag(areaTag)) { @@ -339,10 +339,10 @@ function getNewMessagesInAreaForUser(userId, areaTag, cb) { const filter = { areaTag, - resultType : 'messageList', - newerThanMessageId : lastMessageId, - sort : 'messageId', - order : 'ascending', + resultType : 'messageList', + newerThanMessageId : lastMessageId, + sort : 'messageId', + order : 'ascending', }; if(Message.isPrivateAreaTag(areaTag)) { @@ -356,9 +356,9 @@ function getNewMessagesInAreaForUser(userId, areaTag, cb) { function getMessageListForArea(client, areaTag, cb) { const filter = { areaTag, - resultType : 'messageList', - sort : 'messageId', - order : 'ascending', + resultType : 'messageList', + sort : 'messageId', + order : 'ascending', }; if(Message.isPrivateAreaTag(areaTag)) { @@ -373,9 +373,9 @@ function getMessageIdNewerThanTimestampByArea(areaTag, newerThanTimestamp, cb) { { areaTag, newerThanTimestamp, - sort : 'modTimestamp', - order : 'ascending', - limit : 1, + sort : 'modTimestamp', + order : 'ascending', + limit : 1, }, (err, id) => { if(err) { @@ -388,9 +388,9 @@ function getMessageIdNewerThanTimestampByArea(areaTag, newerThanTimestamp, cb) { function getMessageAreaLastReadId(userId, areaTag, cb) { msgDb.get( - 'SELECT message_id ' + - 'FROM user_message_area_last_read ' + - 'WHERE user_id = ? AND area_tag = ?;', + 'SELECT message_id ' + + 'FROM user_message_area_last_read ' + + 'WHERE user_id = ? AND area_tag = ?;', [ userId, areaTag.toLowerCase() ], function complete(err, row) { cb(err, row ? row.message_id : 0); @@ -404,20 +404,20 @@ function updateMessageAreaLastReadId(userId, areaTag, messageId, allowOlder, cb) allowOlder = false; } - // :TODO: likely a better way to do this... + // :TODO: likely a better way to do this... async.waterfall( [ function getCurrent(callback) { getMessageAreaLastReadId(userId, areaTag, function result(err, lastId) { lastId = lastId || 0; - callback(null, lastId); // ignore errors as we default to 0 + callback(null, lastId); // ignore errors as we default to 0 }); }, function update(lastId, callback) { if(allowOlder || messageId > lastId) { msgDb.run( - 'REPLACE INTO user_message_area_last_read (user_id, area_tag, message_id) ' + - 'VALUES (?, ?, ?);', + 'REPLACE INTO user_message_area_last_read (user_id, area_tag, message_id) ' + + 'VALUES (?, ?, ?);', [ userId, areaTag, messageId ], function written(err) { callback(err, true); // true=didUpdate @@ -459,7 +459,7 @@ function persistMessage(message, cb) { ); } -// method exposed for event scheduler +// method exposed for event scheduler function trimMessageAreasScheduledEvent(args, cb) { function trimMessageAreaByMaxMessages(areaInfo, cb) { @@ -469,15 +469,15 @@ function trimMessageAreasScheduledEvent(args, cb) { msgDb.run( `DELETE FROM message - WHERE message_id IN( - SELECT message_id - FROM message - WHERE area_tag = ? - ORDER BY message_id DESC - LIMIT -1 OFFSET ${areaInfo.maxMessages} - );`, + WHERE message_id IN( + SELECT message_id + FROM message + WHERE area_tag = ? + ORDER BY message_id DESC + LIMIT -1 OFFSET ${areaInfo.maxMessages} + );`, [ areaInfo.areaTag.toLowerCase() ], - function result(err) { // no arrow func; need this + function result(err) { // no arrow func; need this if(err) { Log.error( { areaInfo : areaInfo, error : err.message, type : 'maxMessages' }, 'Error trimming message area'); } else { @@ -495,9 +495,9 @@ function trimMessageAreasScheduledEvent(args, cb) { msgDb.run( `DELETE FROM message - WHERE area_tag = ? AND modified_timestamp < date('now', '-${areaInfo.maxAgeDays} days');`, + WHERE area_tag = ? AND modified_timestamp < date('now', '-${areaInfo.maxAgeDays} days');`, [ areaInfo.areaTag ], - function result(err) { // no arrow func; need this + function result(err) { // no arrow func; need this if(err) { Log.warn( { areaInfo : areaInfo, error : err.message, type : 'maxAgeDays' }, 'Error trimming message area'); } else { @@ -514,17 +514,17 @@ function trimMessageAreasScheduledEvent(args, cb) { const areaTags = []; // - // We use SQL here vs API such that no-longer-used tags are picked up + // We use SQL here vs API such that no-longer-used tags are picked up // msgDb.each( `SELECT DISTINCT area_tag - FROM message;`, + FROM message;`, (err, row) => { if(err) { return callback(err); } - // We treat private mail special + // We treat private mail special if(!Message.isPrivateAreaTag(row.area_tag)) { areaTags.push(row.area_tag); } @@ -537,23 +537,23 @@ function trimMessageAreasScheduledEvent(args, cb) { function prepareAreaInfo(areaTags, callback) { let areaInfos = []; - // determine maxMessages & maxAgeDays per area + // determine maxMessages & maxAgeDays per area const config = Config(); areaTags.forEach(areaTag => { let maxMessages = config.messageAreaDefaults.maxMessages; - let maxAgeDays = config.messageAreaDefaults.maxAgeDays; + let maxAgeDays = config.messageAreaDefaults.maxAgeDays; - const area = getMessageAreaByTag(areaTag); // note: we don't know the conf here + const area = getMessageAreaByTag(areaTag); // note: we don't know the conf here if(area) { maxMessages = area.maxMessages || maxMessages; - maxAgeDays = area.maxAgeDays || maxAgeDays; + maxAgeDays = area.maxAgeDays || maxAgeDays; } areaInfos.push( { - areaTag : areaTag, - maxMessages : maxMessages, - maxAgeDays : maxAgeDays, + areaTag : areaTag, + maxMessages : maxMessages, + maxAgeDays : maxAgeDays, } ); }); @@ -578,13 +578,13 @@ function trimMessageAreasScheduledEvent(args, cb) { }, function trimExternalPrivateSentMail(callback) { // - // *External* (FTN, email, ...) outgoing is cleaned up *after export* - // if it is older than the configured |maxExternalSentAgeDays| days + // *External* (FTN, email, ...) outgoing is cleaned up *after export* + // if it is older than the configured |maxExternalSentAgeDays| days // - // Outgoing externally exported private mail is: - // - In the 'private_mail' area - // - Marked exported (state_flags0 exported bit set) - // - Marked with any external flavor (we don't mark local) + // Outgoing externally exported private mail is: + // - In the 'private_mail' area + // - Marked exported (state_flags0 exported bit set) + // - Marked with any external flavor (we don't mark local) // const maxExternalSentAgeDays = _.get( Config, @@ -594,18 +594,18 @@ function trimMessageAreasScheduledEvent(args, cb) { msgDb.run( `DELETE FROM message - WHERE message_id IN ( - SELECT m.message_id - FROM message m - JOIN message_meta mms - ON m.message_id = mms.message_id AND - (mms.meta_category='System' AND mms.meta_name='${Message.SystemMetaNames.StateFlags0}' AND (mms.meta_value & ${Message.StateFlags0.Exported} = ${Message.StateFlags0.Exported})) - JOIN message_meta mmf - ON m.message_id = mmf.message_id AND - (mmf.meta_category='System' AND mmf.meta_name='${Message.SystemMetaNames.ExternalFlavor}') - WHERE m.area_tag='${Message.WellKnownAreaTags.Private}' AND DATETIME('now') > DATETIME(m.modified_timestamp, '+${maxExternalSentAgeDays} days') - );`, - function results(err) { // no arrow func; need this + WHERE message_id IN ( + SELECT m.message_id + FROM message m + JOIN message_meta mms + ON m.message_id = mms.message_id AND + (mms.meta_category='System' AND mms.meta_name='${Message.SystemMetaNames.StateFlags0}' AND (mms.meta_value & ${Message.StateFlags0.Exported} = ${Message.StateFlags0.Exported})) + JOIN message_meta mmf + ON m.message_id = mmf.message_id AND + (mmf.meta_category='System' AND mmf.meta_name='${Message.SystemMetaNames.ExternalFlavor}') + WHERE m.area_tag='${Message.WellKnownAreaTags.Private}' AND DATETIME('now') > DATETIME(m.modified_timestamp, '+${maxExternalSentAgeDays} days') + );`, + function results(err) { // no arrow func; need this if(err) { Log.warn( { error : err.message }, 'Error trimming private externally sent messages'); } else { diff --git a/core/message_base_search.js b/core/message_base_search.js index 98f78552..9684b8f0 100644 --- a/core/message_base_search.js +++ b/core/message_base_search.js @@ -1,34 +1,34 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; const { getSortedAvailMessageConferences, getAvailableMessageAreasByConfTag, getSortedAvailMessageAreasByConfTag, -} = require('./message_area.js'); -const Errors = require('./enig_error.js').Errors; -const Message = require('./message.js'); +} = require('./message_area.js'); +const Errors = require('./enig_error.js').Errors; +const Message = require('./message.js'); -// deps -const _ = require('lodash'); +// deps +const _ = require('lodash'); exports.moduleInfo = { - name : 'Message Base Search', - desc : 'Module for quickly searching the message base', - author : 'NuSkooler', + name : 'Message Base Search', + desc : 'Module for quickly searching the message base', + author : 'NuSkooler', }; const MciViewIds = { search : { - searchTerms : 1, - search : 2, - conf : 3, - area : 4, - to : 5, - from : 6, - advSearch : 7, + searchTerms : 1, + search : 2, + conf : 3, + area : 4, + to : 5, + from : 6, + advSearch : 7, } }; @@ -54,8 +54,8 @@ exports.getModule = class MessageBaseSearch extends MenuModule { return cb(err); } - const confView = vc.getView(MciViewIds.search.conf); - const areaView = vc.getView(MciViewIds.search.area); + const confView = vc.getView(MciViewIds.search.conf); + const areaView = vc.getView(MciViewIds.search.area); if(!confView || !areaView) { return cb(Errors.DoesNotExist('Missing one or more required views')); @@ -65,7 +65,7 @@ exports.getModule = class MessageBaseSearch extends MenuModule { getSortedAvailMessageConferences(this.client).map(conf => Object.assign(conf, { text : conf.conf.name, data : conf.confTag } )) || [] ); - let availAreas = [ { text : '-ALL-', data : '' } ]; // note: will populate if conf changes from ALL + let availAreas = [ { text : '-ALL-', data : '' } ]; // note: will populate if conf changes from ALL confView.setItems(availConfs); areaView.setItems(availAreas); @@ -90,30 +90,30 @@ exports.getModule = class MessageBaseSearch extends MenuModule { } searchNow(formData, cb) { - const isAdvanced = formData.submitId === MciViewIds.search.advSearch; - const value = formData.value; + const isAdvanced = formData.submitId === MciViewIds.search.advSearch; + const value = formData.value; const filter = { - resultType : 'messageList', - sort : 'modTimestamp', - terms : value.searchTerms, - //extraFields : [ 'area_tag', 'message_uuid', 'reply_to_message_id', 'to_user_name', 'from_user_name', 'subject', 'modified_timestamp' ], - limit : 2048, // :TODO: best way to handle this? we should probably let the user know if some results are returned + resultType : 'messageList', + sort : 'modTimestamp', + terms : value.searchTerms, + //extraFields : [ 'area_tag', 'message_uuid', 'reply_to_message_id', 'to_user_name', 'from_user_name', 'subject', 'modified_timestamp' ], + limit : 2048, // :TODO: best way to handle this? we should probably let the user know if some results are returned }; if(isAdvanced) { - filter.toUserName = value.toUserName; - filter.fromUserName = value.fromUserName; + filter.toUserName = value.toUserName; + filter.fromUserName = value.fromUserName; if(value.confTag && !value.areaTag) { - // areaTag may be a string or array of strings - // getAvailableMessageAreasByConfTag() returns a obj - we only need tags + // areaTag may be a string or array of strings + // getAvailableMessageAreasByConfTag() returns a obj - we only need tags filter.areaTag = _.map( getAvailableMessageAreasByConfTag(value.confTag, { client : this.client } ), (area, areaTag) => areaTag ); } else if(value.areaTag) { - filter.areaTag = value.areaTag; // specific conf + area + filter.areaTag = value.areaTag; // specific conf + area } } @@ -133,9 +133,9 @@ exports.getModule = class MessageBaseSearch extends MenuModule { const menuOpts = { extraArgs : { messageList, - noUpdateLastReadId : true + noUpdateLastReadId : true }, - menuFlags : [ 'popParent' ], + menuFlags : [ 'popParent' ], }; return this.gotoMenu( diff --git a/core/mime_util.js b/core/mime_util.js index d6631077..857b967c 100644 --- a/core/mime_util.js +++ b/core/mime_util.js @@ -1,26 +1,26 @@ /* jslint node: true */ 'use strict'; -// deps -const _ = require('lodash'); +// deps +const _ = require('lodash'); -const mimeTypes = require('mime-types'); +const mimeTypes = require('mime-types'); -exports.startup = startup; -exports.resolveMimeType = resolveMimeType; +exports.startup = startup; +exports.resolveMimeType = resolveMimeType; function startup(cb) { // - // Add in types (not yet) supported by mime-db -- and therefor, mime-types + // Add in types (not yet) supported by mime-db -- and therefor, mime-types // const ADDITIONAL_EXT_MIMETYPES = { - ans : 'text/x-ansi', - gz : 'application/gzip', // not in mime-types 2.1.15 :( - lzx : 'application/x-lzx', // :TODO: submit to mime-types + ans : 'text/x-ansi', + gz : 'application/gzip', // not in mime-types 2.1.15 :( + lzx : 'application/x-lzx', // :TODO: submit to mime-types }; _.forEach(ADDITIONAL_EXT_MIMETYPES, (mimeType, ext) => { - // don't override any entries + // don't override any entries if(!_.isString(mimeTypes.types[ext])) { mimeTypes[ext] = mimeType; } @@ -35,8 +35,8 @@ function startup(cb) { function resolveMimeType(query) { if(mimeTypes.extensions[query]) { - return query; // alreaed a mime-type + return query; // alreaed a mime-type } - return mimeTypes.lookup(query) || undefined; // lookup() returns false; we want undefined + return mimeTypes.lookup(query) || undefined; // lookup() returns false; we want undefined } \ No newline at end of file diff --git a/core/misc_util.js b/core/misc_util.js index 3a75d065..633cc967 100644 --- a/core/misc_util.js +++ b/core/misc_util.js @@ -1,17 +1,17 @@ /* jslint node: true */ 'use strict'; -const paths = require('path'); +const paths = require('path'); -const os = require('os'); -const packageJson = require('../package.json'); +const os = require('os'); +const packageJson = require('../package.json'); -exports.isProduction = isProduction; -exports.isDevelopment = isDevelopment; -exports.valueWithDefault = valueWithDefault; -exports.resolvePath = resolvePath; -exports.getCleanEnigmaVersion = getCleanEnigmaVersion; -exports.getEnigmaUserAgent = getEnigmaUserAgent; +exports.isProduction = isProduction; +exports.isDevelopment = isDevelopment; +exports.valueWithDefault = valueWithDefault; +exports.resolvePath = resolvePath; +exports.getCleanEnigmaVersion = getCleanEnigmaVersion; +exports.getEnigmaUserAgent = getEnigmaUserAgent; function isProduction() { var env = process.env.NODE_ENV || 'dev'; @@ -42,11 +42,11 @@ function getCleanEnigmaVersion() { ; } -// See also ftn_util.js getTearLine() & getProductIdentifier() +// See also ftn_util.js getTearLine() & getProductIdentifier() function getEnigmaUserAgent() { - // can't have 1/2 or ½ in User-Agent according to RFC 1945 :( + // can't have 1/2 or ½ in User-Agent according to RFC 1945 :( const version = getCleanEnigmaVersion(); - const nodeVer = process.version.substr(1); // remove 'v' prefix + const nodeVer = process.version.substr(1); // remove 'v' prefix return `ENiGMA-BBS/${version} (${os.platform()}; ${os.arch()}; ${nodeVer})`; } \ No newline at end of file diff --git a/core/mod_mixins.js b/core/mod_mixins.js index fd9db771..f3d9d5ad 100644 --- a/core/mod_mixins.js +++ b/core/mod_mixins.js @@ -1,8 +1,8 @@ /* jslint node: true */ 'use strict'; -const messageArea = require('../core/message_area.js'); -const { get } = require('lodash'); +const messageArea = require('../core/message_area.js'); +const { get } = require('lodash'); exports.MessageAreaConfTempSwitcher = Sup => class extends Sup { @@ -10,13 +10,13 @@ exports.MessageAreaConfTempSwitcher = Sup => class extends Sup { tempMessageConfAndAreaSwitch(messageAreaTag, recordPrevious = true) { messageAreaTag = messageAreaTag || get(this, 'config.messageAreaTag', this.messageAreaTag); if(!messageAreaTag) { - return; // nothing to do! + return; // nothing to do! } if(recordPrevious) { this.prevMessageConfAndArea = { - confTag : this.client.user.properties.message_conf_tag, - areaTag : this.client.user.properties.message_area_tag, + confTag : this.client.user.properties.message_conf_tag, + areaTag : this.client.user.properties.message_area_tag, }; } diff --git a/core/module_util.js b/core/module_util.js index 26a1ec53..5a575f3e 100644 --- a/core/module_util.js +++ b/core/module_util.js @@ -1,21 +1,21 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('./config.js').get; +// ENiGMA½ +const Config = require('./config.js').get; -// deps -const fs = require('graceful-fs'); -const paths = require('path'); -const _ = require('lodash'); -const assert = require('assert'); -const async = require('async'); +// deps +const fs = require('graceful-fs'); +const paths = require('path'); +const _ = require('lodash'); +const assert = require('assert'); +const async = require('async'); -// exports -exports.loadModuleEx = loadModuleEx; -exports.loadModule = loadModule; -exports.loadModulesForCategory = loadModulesForCategory; -exports.getModulePaths = getModulePaths; +// exports +exports.loadModuleEx = loadModuleEx; +exports.loadModule = loadModule; +exports.loadModulesForCategory = loadModulesForCategory; +exports.getModulePaths = getModulePaths; function loadModuleEx(options, cb) { assert(_.isObject(options)); @@ -25,18 +25,18 @@ function loadModuleEx(options, cb) { const modConfig = _.isObject(Config[options.category]) ? Config[options.category][options.name] : null; if(_.isObject(modConfig) && false === modConfig.enabled) { - const err = new Error(`Module "${options.name}" is disabled`); - err.code = 'EENIGMODDISABLED'; + const err = new Error(`Module "${options.name}" is disabled`); + err.code = 'EENIGMODDISABLED'; return cb(err); } // - // Modules are allowed to live in /path/to//.js or - // simply in /path/to/.js. This allows for more advanced modules - // to have their own containing folder, package.json & dependencies, etc. + // Modules are allowed to live in /path/to//.js or + // simply in /path/to/.js. This allows for more advanced modules + // to have their own containing folder, package.json & dependencies, etc. // let mod; - let modPath = paths.join(options.path, `${options.name}.js`); // general case first + let modPath = paths.join(options.path, `${options.name}.js`); // general case first try { mod = require(modPath); } catch(e) { diff --git a/core/msg_area_list.js b/core/msg_area_list.js index 2d61044d..27cbcbb1 100644 --- a/core/msg_area_list.js +++ b/core/msg_area_list.js @@ -1,30 +1,30 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const messageArea = require('./message_area.js'); -const displayThemeArt = require('./theme.js').displayThemeArt; -const resetScreen = require('./ansi_term.js').resetScreen; -const stringFormat = require('./string_format.js'); -const Errors = require('./enig_error.js').Errors; +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const messageArea = require('./message_area.js'); +const displayThemeArt = require('./theme.js').displayThemeArt; +const resetScreen = require('./ansi_term.js').resetScreen; +const stringFormat = require('./string_format.js'); +const Errors = require('./enig_error.js').Errors; -// deps -const async = require('async'); -const _ = require('lodash'); +// deps +const async = require('async'); +const _ = require('lodash'); exports.moduleInfo = { - name : 'Message Area List', - desc : 'Module for listing / choosing message areas', - author : 'NuSkooler', + name : 'Message Area List', + desc : 'Module for listing / choosing message areas', + author : 'NuSkooler', }; /* - :TODO: + :TODO: - Obv/2 has the following: - CHANGE .ANS - Message base changing ansi + Obv/2 has the following: + CHANGE .ANS - Message base changing ansi |SN Current base name |SS Current base sponsor |NM Number of messages in current base @@ -35,9 +35,9 @@ exports.moduleInfo = { */ const MciViewIds = { - AreaList : 1, - SelAreaInfo1 : 2, - SelAreaInfo2 : 3, + AreaList : 1, + SelAreaInfo1 : 2, + SelAreaInfo2 : 3, }; exports.getModule = class MessageAreaListModule extends MenuModule { @@ -53,9 +53,9 @@ exports.getModule = class MessageAreaListModule extends MenuModule { this.menuMethods = { changeArea : function(formData, extraArgs, cb) { if(1 === formData.submitId) { - let area = self.messageAreas[formData.value.area]; - const areaTag = area.areaTag; - area = area.area; // what we want is actually embedded + let area = self.messageAreas[formData.value.area]; + const areaTag = area.areaTag; + area = area.area; // what we want is actually embedded messageArea.changeMessageArea(self.client, areaTag, err => { if(err) { @@ -65,14 +65,14 @@ exports.getModule = class MessageAreaListModule extends MenuModule { } else { if(_.isString(area.art)) { const dispOptions = { - client : self.client, - name : area.art, + client : self.client, + name : area.art, }; self.client.term.rawWrite(resetScreen()); displayThemeArt(dispOptions, () => { - // pause by default, unless explicitly told not to + // pause by default, unless explicitly told not to if(_.has(area, 'options.pause') && false === area.options.pause) { return self.prevMenuOnTimeout(1000, cb); } else { @@ -99,18 +99,18 @@ exports.getModule = class MessageAreaListModule extends MenuModule { }, timeout); } - // :TODO: these concepts have been replaced with the {someKey} style formatting - update me! + // :TODO: these concepts have been replaced with the {someKey} style formatting - update me! updateGeneralAreaInfoViews(areaIndex) { /* - const areaInfo = self.messageAreas[areaIndex]; + const areaInfo = self.messageAreas[areaIndex]; - [ MciViewIds.SelAreaInfo1, MciViewIds.SelAreaInfo2 ].forEach(mciId => { - const v = self.viewControllers.areaList.getView(mciId); - if(v) { - v.setFormatObject(areaInfo.area); - } - }); - */ + [ MciViewIds.SelAreaInfo1, MciViewIds.SelAreaInfo2 ].forEach(mciId => { + const v = self.viewControllers.areaList.getView(mciId); + if(v) { + v.setFormatObject(areaInfo.area); + } + }); + */ } mciReady(mciData, cb) { @@ -119,16 +119,16 @@ exports.getModule = class MessageAreaListModule extends MenuModule { return cb(err); } - const self = this; - const vc = self.viewControllers.areaList = new ViewController( { client : self.client } ); + const self = this; + const vc = self.viewControllers.areaList = new ViewController( { client : self.client } ); async.series( [ function loadFromConfig(callback) { const loadOpts = { - callingMenu : self, - mciMap : mciData.menu, - formId : 0, + callingMenu : self, + mciMap : mciData.menu, + formId : 0, }; vc.loadFromMenuConfig(loadOpts, function startingViewReady(err) { @@ -136,8 +136,8 @@ exports.getModule = class MessageAreaListModule extends MenuModule { }); }, function populateAreaListView(callback) { - const listFormat = self.menuConfig.config.listFormat || '{index} ) - {name}'; - const focusListFormat = self.menuConfig.config.focusListFormat || listFormat; + const listFormat = self.menuConfig.config.listFormat || '{index} ) - {name}'; + const focusListFormat = self.menuConfig.config.focusListFormat || listFormat; const areaListView = vc.getView(MciViewIds.AreaList); if(!areaListView) { diff --git a/core/msg_area_post_fse.js b/core/msg_area_post_fse.js index 8c2136c7..8e4d9f3f 100644 --- a/core/msg_area_post_fse.js +++ b/core/msg_area_post_fse.js @@ -1,16 +1,16 @@ /* jslint node: true */ 'use strict'; -const FullScreenEditorModule = require('./fse.js').FullScreenEditorModule; -const persistMessage = require('./message_area.js').persistMessage; +const FullScreenEditorModule = require('./fse.js').FullScreenEditorModule; +const persistMessage = require('./message_area.js').persistMessage; -const _ = require('lodash'); -const async = require('async'); +const _ = require('lodash'); +const async = require('async'); exports.moduleInfo = { - name : 'Message Area Post', - desc : 'Module for posting a new message to an area', - author : 'NuSkooler', + name : 'Message Area Post', + desc : 'Module for posting a new message to an area', + author : 'NuSkooler', }; exports.getModule = class AreaPostFSEModule extends FullScreenEditorModule { @@ -19,7 +19,7 @@ exports.getModule = class AreaPostFSEModule extends FullScreenEditorModule { const self = this; - // we're posting, so always start with 'edit' mode + // we're posting, so always start with 'edit' mode this.editorMode = 'edit'; this.menuMethods.editModeMenuSave = function(formData, extraArgs, cb) { @@ -42,9 +42,9 @@ exports.getModule = class AreaPostFSEModule extends FullScreenEditorModule { ], function complete(err) { if(err) { - // :TODO:... sooooo now what? + // :TODO:... sooooo now what? } else { - // note: not logging 'from' here as it's part of client.log.xxxx() + // note: not logging 'from' here as it's part of client.log.xxxx() self.client.log.info( { to : msg.toUserName, subject : msg.subject, uuid : msg.uuid }, 'Message persisted' diff --git a/core/msg_area_reply_fse.js b/core/msg_area_reply_fse.js index 83cb99c7..11742865 100644 --- a/core/msg_area_reply_fse.js +++ b/core/msg_area_reply_fse.js @@ -1,14 +1,14 @@ /* jslint node: true */ 'use strict'; -var FullScreenEditorModule = require('./fse.js').FullScreenEditorModule; +var FullScreenEditorModule = require('./fse.js').FullScreenEditorModule; -exports.getModule = AreaReplyFSEModule; +exports.getModule = AreaReplyFSEModule; exports.moduleInfo = { - name : 'Message Area Reply', - desc : 'Module for replying to an area message', - author : 'NuSkooler', + name : 'Message Area Reply', + desc : 'Module for replying to an area message', + author : 'NuSkooler', }; function AreaReplyFSEModule(options) { diff --git a/core/msg_area_view_fse.js b/core/msg_area_view_fse.js index af0cbb78..1ca5617c 100644 --- a/core/msg_area_view_fse.js +++ b/core/msg_area_view_fse.js @@ -1,35 +1,35 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const FullScreenEditorModule = require('./fse.js').FullScreenEditorModule; -const Message = require('./message.js'); +// ENiGMA½ +const FullScreenEditorModule = require('./fse.js').FullScreenEditorModule; +const Message = require('./message.js'); -// deps -const _ = require('lodash'); +// deps +const _ = require('lodash'); exports.moduleInfo = { - name : 'Message Area View', - desc : 'Module for viewing an area message', - author : 'NuSkooler', + name : 'Message Area View', + desc : 'Module for viewing an area message', + author : 'NuSkooler', }; exports.getModule = class AreaViewFSEModule extends FullScreenEditorModule { constructor(options) { super(options); - this.editorType = 'area'; - this.editorMode = 'view'; + this.editorType = 'area'; + this.editorMode = 'view'; if(_.isObject(options.extraArgs)) { - this.messageList = options.extraArgs.messageList; - this.messageIndex = options.extraArgs.messageIndex; - this.lastMessageNextExit = options.extraArgs.lastMessageNextExit; + this.messageList = options.extraArgs.messageList; + this.messageIndex = options.extraArgs.messageIndex; + this.lastMessageNextExit = options.extraArgs.lastMessageNextExit; } - this.messageList = this.messageList || []; - this.messageIndex = this.messageIndex || 0; - this.messageTotal = this.messageList.length; + this.messageList = this.messageList || []; + this.messageIndex = this.messageIndex || 0; + this.messageTotal = this.messageList.length; if(this.messageList.length > 0) { this.messageAreaTag = this.messageList[this.messageIndex].areaTag; @@ -37,19 +37,19 @@ exports.getModule = class AreaViewFSEModule extends FullScreenEditorModule { const self = this; - // assign *additional* menuMethods + // assign *additional* menuMethods Object.assign(this.menuMethods, { nextMessage : (formData, extraArgs, cb) => { if(self.messageIndex + 1 < self.messageList.length) { self.messageIndex++; this.messageAreaTag = this.messageList[this.messageIndex].areaTag; - this.tempMessageConfAndAreaSwitch(this.messageAreaTag, false); // false=don't record prev; we want what we entered the module with + this.tempMessageConfAndAreaSwitch(this.messageAreaTag, false); // false=don't record prev; we want what we entered the module with return self.loadMessageByUuid(self.messageList[self.messageIndex].messageUuid, cb); } - // auto-exit if no more to go? + // auto-exit if no more to go? if(self.lastMessageNextExit) { self.lastMessageReached = true; return self.prevMenu(cb); @@ -63,7 +63,7 @@ exports.getModule = class AreaViewFSEModule extends FullScreenEditorModule { self.messageIndex--; this.messageAreaTag = this.messageList[this.messageIndex].areaTag; - this.tempMessageConfAndAreaSwitch(this.messageAreaTag, false); // false=don't record prev; we want what we entered the module with + this.tempMessageConfAndAreaSwitch(this.messageAreaTag, false); // false=don't record prev; we want what we entered the module with return self.loadMessageByUuid(self.messageList[self.messageIndex].messageUuid, cb); } @@ -72,18 +72,18 @@ exports.getModule = class AreaViewFSEModule extends FullScreenEditorModule { }, movementKeyPressed : (formData, extraArgs, cb) => { - const bodyView = self.viewControllers.body.getView(1); // :TODO: use const here vs magic # + const bodyView = self.viewControllers.body.getView(1); // :TODO: use const here vs magic # - // :TODO: Create methods for up/down vs using keyPressXXXXX + // :TODO: Create methods for up/down vs using keyPressXXXXX switch(formData.key.name) { - case 'down arrow' : bodyView.scrollDocumentUp(); break; - case 'up arrow' : bodyView.scrollDocumentDown(); break; - case 'page up' : bodyView.keyPressPageUp(); break; - case 'page down' : bodyView.keyPressPageDown(); break; + case 'down arrow' : bodyView.scrollDocumentUp(); break; + case 'up arrow' : bodyView.scrollDocumentDown(); break; + case 'page up' : bodyView.keyPressPageUp(); break; + case 'page down' : bodyView.keyPressPageDown(); break; } - // :TODO: need to stop down/page down if doing so would push the last - // visible page off the screen at all .... this should be handled by MLTEV though... + // :TODO: need to stop down/page down if doing so would push the last + // visible page off the screen at all .... this should be handled by MLTEV though... return cb(null); }, @@ -92,8 +92,8 @@ exports.getModule = class AreaViewFSEModule extends FullScreenEditorModule { if(_.isString(extraArgs.menu)) { const modOpts = { extraArgs : { - messageAreaTag : self.messageAreaTag, - replyToMessage : self.message, + messageAreaTag : self.messageAreaTag, + replyToMessage : self.message, } }; @@ -124,22 +124,22 @@ exports.getModule = class AreaViewFSEModule extends FullScreenEditorModule { getSaveState() { return { - messageList : this.messageList, - messageIndex : this.messageIndex, - messageTotal : this.messageList.length, + messageList : this.messageList, + messageIndex : this.messageIndex, + messageTotal : this.messageList.length, }; } restoreSavedState(savedState) { - this.messageList = savedState.messageList; - this.messageIndex = savedState.messageIndex; - this.messageTotal = savedState.messageTotal; + this.messageList = savedState.messageList; + this.messageIndex = savedState.messageIndex; + this.messageTotal = savedState.messageTotal; } getMenuResult() { return { - messageIndex : this.messageIndex, - lastMessageReached : this.lastMessageReached, + messageIndex : this.messageIndex, + lastMessageReached : this.lastMessageReached, }; } }; diff --git a/core/msg_conf_list.js b/core/msg_conf_list.js index 0876f89a..c20d06ca 100644 --- a/core/msg_conf_list.js +++ b/core/msg_conf_list.js @@ -1,29 +1,29 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const messageArea = require('./message_area.js'); -const displayThemeArt = require('./theme.js').displayThemeArt; -const resetScreen = require('./ansi_term.js').resetScreen; -const stringFormat = require('./string_format.js'); +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const messageArea = require('./message_area.js'); +const displayThemeArt = require('./theme.js').displayThemeArt; +const resetScreen = require('./ansi_term.js').resetScreen; +const stringFormat = require('./string_format.js'); -// deps -const async = require('async'); -const _ = require('lodash'); +// deps +const async = require('async'); +const _ = require('lodash'); exports.moduleInfo = { - name : 'Message Conference List', - desc : 'Module for listing / choosing message conferences', - author : 'NuSkooler', + name : 'Message Conference List', + desc : 'Module for listing / choosing message conferences', + author : 'NuSkooler', }; const MciViewIds = { - ConfList : 1, + ConfList : 1, - // :TODO: - // # areas in conf .... see Obv/2, iNiQ, ... + // :TODO: + // # areas in conf .... see Obv/2, iNiQ, ... // }; @@ -37,9 +37,9 @@ exports.getModule = class MessageConfListModule extends MenuModule { this.menuMethods = { changeConference : function(formData, extraArgs, cb) { if(1 === formData.submitId) { - let conf = self.messageConfs[formData.value.conf]; - const confTag = conf.confTag; - conf = conf.conf; // what we want is embedded + let conf = self.messageConfs[formData.value.conf]; + const confTag = conf.confTag; + conf = conf.conf; // what we want is embedded messageArea.changeMessageConference(self.client, confTag, err => { if(err) { @@ -51,14 +51,14 @@ exports.getModule = class MessageConfListModule extends MenuModule { } else { if(_.isString(conf.art)) { const dispOptions = { - client : self.client, - name : conf.art, + client : self.client, + name : conf.art, }; self.client.term.rawWrite(resetScreen()); displayThemeArt(dispOptions, () => { - // pause by default, unless explicitly told not to + // pause by default, unless explicitly told not to if(_.has(conf, 'options.pause') && false === conf.options.pause) { return self.prevMenuOnTimeout(1000, cb); } else { @@ -91,23 +91,23 @@ exports.getModule = class MessageConfListModule extends MenuModule { return cb(err); } - const self = this; - const vc = self.viewControllers.areaList = new ViewController( { client : self.client } ); + const self = this; + const vc = self.viewControllers.areaList = new ViewController( { client : self.client } ); async.series( [ function loadFromConfig(callback) { let loadOpts = { - callingMenu : self, - mciMap : mciData.menu, - formId : 0, + callingMenu : self, + mciMap : mciData.menu, + formId : 0, }; vc.loadFromMenuConfig(loadOpts, callback); }, function populateConfListView(callback) { - const listFormat = self.menuConfig.config.listFormat || '{index} ) - {name}'; - const focusListFormat = self.menuConfig.config.focusListFormat || listFormat; + const listFormat = self.menuConfig.config.listFormat || '{index} ) - {name}'; + const focusListFormat = self.menuConfig.config.focusListFormat || listFormat; const confListView = vc.getView(MciViewIds.ConfList); let i = 1; @@ -135,7 +135,7 @@ exports.getModule = class MessageConfListModule extends MenuModule { callback(null); }, function populateTextViews(callback) { - // :TODO: populate other avail MCI, e.g. current conf name + // :TODO: populate other avail MCI, e.g. current conf name callback(null); } ], diff --git a/core/msg_list.js b/core/msg_list.js index 5c8624ab..a4eb18aa 100644 --- a/core/msg_list.js +++ b/core/msg_list.js @@ -1,51 +1,51 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const messageArea = require('./message_area.js'); -const stringFormat = require('./string_format.js'); -const MessageAreaConfTempSwitcher = require('./mod_mixins.js').MessageAreaConfTempSwitcher; +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const messageArea = require('./message_area.js'); +const stringFormat = require('./string_format.js'); +const MessageAreaConfTempSwitcher = require('./mod_mixins.js').MessageAreaConfTempSwitcher; -// deps -const async = require('async'); -const _ = require('lodash'); -const moment = require('moment'); +// deps +const async = require('async'); +const _ = require('lodash'); +const moment = require('moment'); /* - Available listFormat/focusListFormat members (VM1): + Available listFormat/focusListFormat members (VM1): - msgNum : Message number - to : To username/handle - from : From username/handle - subj : Subject - ts : Message mod timestamp (format with config.dateTimeFormat) - newIndicator : New mark/indicator (config.newIndicator) + msgNum : Message number + to : To username/handle + from : From username/handle + subj : Subject + ts : Message mod timestamp (format with config.dateTimeFormat) + newIndicator : New mark/indicator (config.newIndicator) - MCI codes: + MCI codes: - VM1 : Message list - TL2 : Message info 1: { msgNumSelected, msgNumTotal } + VM1 : Message list + TL2 : Message info 1: { msgNumSelected, msgNumTotal } */ exports.moduleInfo = { - name : 'Message List', - desc : 'Module for listing/browsing available messages', - author : 'NuSkooler', + name : 'Message List', + desc : 'Module for listing/browsing available messages', + author : 'NuSkooler', }; const MciViewIds = { - msgList : 1, // VM1 - msgInfo1 : 2, // TL2 + msgList : 1, // VM1 + msgInfo1 : 2, // TL2 }; exports.getModule = class MessageListModule extends MessageAreaConfTempSwitcher(MenuModule) { constructor(options) { super(options); - // :TODO: consider this pattern in base MenuModule - clean up code all over - this.config = Object.assign({}, _.get(options, 'menuConfig.config'), options.extraArgs); + // :TODO: consider this pattern in base MenuModule - clean up code all over + this.config = Object.assign({}, _.get(options, 'menuConfig.config'), options.extraArgs); this.lastMessageReachedExit = _.get(options, 'lastMenuResult.lastMessageReached', false); @@ -55,11 +55,11 @@ exports.getModule = class MessageListModule extends MessageAreaConfTempSwitcher( this.initialFocusIndex = formData.value.message; const modOpts = { - extraArgs : { - messageAreaTag : this.getSelectedAreaTag(formData.value.message),// this.config.messageAreaTag, - messageList : this.config.messageList, - messageIndex : formData.value.message, - lastMessageNextExit : true, + extraArgs : { + messageAreaTag : this.getSelectedAreaTag(formData.value.message),// this.config.messageAreaTag, + messageList : this.config.messageList, + messageIndex : formData.value.message, + lastMessageNextExit : true, } }; @@ -68,8 +68,8 @@ exports.getModule = class MessageListModule extends MessageAreaConfTempSwitcher( } // - // Provide a serializer so we don't dump *huge* bits of information to the log - // due to the size of |messageList|. See https://github.com/trentm/node-bunyan/issues/189 + // Provide a serializer so we don't dump *huge* bits of information to the log + // due to the size of |messageList|. See https://github.com/trentm/node-bunyan/issues/189 // const self = this; modOpts.extraArgs.toJSON = function() { @@ -78,11 +78,11 @@ exports.getModule = class MessageListModule extends MessageAreaConfTempSwitcher( self.config.messageList.slice(0, 2).concat(self.config.messageList.slice(-2)); return { - // note |this| is scope of toJSON()! - messageAreaTag : this.messageAreaTag, - apprevMessageList : logMsgList, - messageCount : this.messageList.length, - messageIndex : this.messageIndex, + // note |this| is scope of toJSON()! + messageAreaTag : this.messageAreaTag, + apprevMessageList : logMsgList, + messageCount : this.messageList.length, + messageIndex : this.messageIndex, }; }; @@ -111,10 +111,10 @@ exports.getModule = class MessageListModule extends MessageAreaConfTempSwitcher( super.enter(); // - // Config can specify |messageAreaTag| else it comes from - // the user's current area. If |messageList| is supplied, - // each item is expected to contain |areaTag|, so we use that - // instead in those cases. + // Config can specify |messageAreaTag| else it comes from + // the user's current area. If |messageList| is supplied, + // each item is expected to contain |areaTag|, so we use that + // instead in those cases. // if(!Array.isArray(this.config.messageList)) { if(this.config.messageAreaTag) { @@ -136,23 +136,23 @@ exports.getModule = class MessageListModule extends MessageAreaConfTempSwitcher( return cb(err); } - const self = this; - const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); + const self = this; + const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); let configProvidedMessageList = false; async.series( [ function loadFromConfig(callback) { const loadOpts = { - callingMenu : self, - mciMap : mciData.menu + callingMenu : self, + mciMap : mciData.menu }; return vc.loadFromMenuConfig(loadOpts, callback); }, function fetchMessagesInArea(callback) { // - // Config can supply messages else we'll need to populate the list now + // Config can supply messages else we'll need to populate the list now // if(_.isArray(self.config.messageList)) { configProvidedMessageList = true; @@ -169,7 +169,7 @@ exports.getModule = class MessageListModule extends MessageAreaConfTempSwitcher( }); }, function getLastReadMesageId(callback) { - // messageList entries can contain |isNew| if they want to be considered new + // messageList entries can contain |isNew| if they want to be considered new if(configProvidedMessageList) { self.lastReadId = 0; return callback(null); @@ -177,33 +177,33 @@ exports.getModule = class MessageListModule extends MessageAreaConfTempSwitcher( messageArea.getMessageAreaLastReadId(self.client.user.userId, self.config.messageAreaTag, function lastRead(err, lastReadId) { self.lastReadId = lastReadId || 0; - return callback(null); // ignore any errors, e.g. missing value + return callback(null); // ignore any errors, e.g. missing value }); }, function updateMessageListObjects(callback) { - const dateTimeFormat = self.menuConfig.config.dateTimeFormat || self.client.currentTheme.helpers.getDateTimeFormat(); - const newIndicator = self.menuConfig.config.newIndicator || '*'; - const regIndicator = new Array(newIndicator.length + 1).join(' '); // fill with space to avoid draw issues + const dateTimeFormat = self.menuConfig.config.dateTimeFormat || self.client.currentTheme.helpers.getDateTimeFormat(); + const newIndicator = self.menuConfig.config.newIndicator || '*'; + const regIndicator = new Array(newIndicator.length + 1).join(' '); // fill with space to avoid draw issues let msgNum = 1; self.config.messageList.forEach( (listItem, index) => { - listItem.msgNum = msgNum++; - listItem.ts = moment(listItem.modTimestamp).format(dateTimeFormat); - const isNew = _.isBoolean(listItem.isNew) ? listItem.isNew : listItem.messageId > self.lastReadId; - listItem.newIndicator = isNew ? newIndicator : regIndicator; + listItem.msgNum = msgNum++; + listItem.ts = moment(listItem.modTimestamp).format(dateTimeFormat); + const isNew = _.isBoolean(listItem.isNew) ? listItem.isNew : listItem.messageId > self.lastReadId; + listItem.newIndicator = isNew ? newIndicator : regIndicator; if(_.isUndefined(self.initialFocusIndex) && listItem.messageId > self.lastReadId) { self.initialFocusIndex = index; } - listItem.text = `${listItem.msgNum} - ${listItem.subject} from ${listItem.fromUserName}`; // default text + listItem.text = `${listItem.msgNum} - ${listItem.subject} from ${listItem.fromUserName}`; // default text }); return callback(null); }, function populateList(callback) { - const msgListView = vc.getView(MciViewIds.msgList); - // :TODO: replace with standard custom info MCI - msgNumSelected, msgNumTotal, areaName, areaDesc, confName, confDesc, ... - const messageInfo1Format = self.menuConfig.config.messageInfo1Format || '{msgNumSelected} / {msgNumTotal}'; + const msgListView = vc.getView(MciViewIds.msgList); + // :TODO: replace with standard custom info MCI - msgNumSelected, msgNumTotal, areaName, areaDesc, confName, confDesc, ... + const messageInfo1Format = self.menuConfig.config.messageInfo1Format || '{msgNumSelected} / {msgNumTotal}'; msgListView.setItems(self.config.messageList); @@ -215,7 +215,7 @@ exports.getModule = class MessageListModule extends MessageAreaConfTempSwitcher( }); if(self.initialFocusIndex > 0) { - // note: causes redraw() + // note: causes redraw() msgListView.setFocusItemIndex(self.initialFocusIndex); } else { msgListView.redraw(); diff --git a/core/msg_network.js b/core/msg_network.js index 721ebba4..b26d5f1b 100644 --- a/core/msg_network.js +++ b/core/msg_network.js @@ -1,15 +1,15 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -let loadModulesForCategory = require('./module_util.js').loadModulesForCategory; +// ENiGMA½ +let loadModulesForCategory = require('./module_util.js').loadModulesForCategory; -// standard/deps -let async = require('async'); +// standard/deps +let async = require('async'); -exports.startup = startup; -exports.shutdown = shutdown; -exports.recordMessage = recordMessage; +exports.startup = startup; +exports.shutdown = shutdown; +exports.recordMessage = recordMessage; let msgNetworkModules = []; @@ -53,9 +53,9 @@ function shutdown(cb) { function recordMessage(message, cb) { // - // Give all message network modules (scanner/tossers) - // a chance to do something with |message|. Any or all can - // choose to ignore it. + // Give all message network modules (scanner/tossers) + // a chance to do something with |message|. Any or all can + // choose to ignore it. // async.each(msgNetworkModules, (modInst, next) => { modInst.record(message); diff --git a/core/msg_scan_toss_module.js b/core/msg_scan_toss_module.js index 002c2cc3..59c94be0 100644 --- a/core/msg_scan_toss_module.js +++ b/core/msg_scan_toss_module.js @@ -1,10 +1,10 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -var PluginModule = require('./plugin_module.js').PluginModule; +// ENiGMA½ +var PluginModule = require('./plugin_module.js').PluginModule; -exports.MessageScanTossModule = MessageScanTossModule; +exports.MessageScanTossModule = MessageScanTossModule; function MessageScanTossModule() { PluginModule.call(this); diff --git a/core/multi_line_edit_text_view.js b/core/multi_line_edit_text_view.js index 3a7f29d9..1d16e201 100644 --- a/core/multi_line_edit_text_view.js +++ b/core/multi_line_edit_text_view.js @@ -1,22 +1,22 @@ /* jslint node: true */ 'use strict'; -const View = require('./view.js').View; -const strUtil = require('./string_util.js'); -const ansi = require('./ansi_term.js'); -const wordWrapText = require('./word_wrap.js').wordWrapText; -const ansiPrep = require('./ansi_prep.js'); +const View = require('./view.js').View; +const strUtil = require('./string_util.js'); +const ansi = require('./ansi_term.js'); +const wordWrapText = require('./word_wrap.js').wordWrapText; +const ansiPrep = require('./ansi_prep.js'); -const assert = require('assert'); -const _ = require('lodash'); +const assert = require('assert'); +const _ = require('lodash'); -// :TODO: Determine CTRL-* keys for various things -// See http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt -// http://wiki.synchro.net/howto:editor:slyedit#edit_mode -// http://sublime-text-unofficial-documentation.readthedocs.org/en/latest/reference/keyboard_shortcuts_win.html +// :TODO: Determine CTRL-* keys for various things +// See http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt +// http://wiki.synchro.net/howto:editor:slyedit#edit_mode +// http://sublime-text-unofficial-documentation.readthedocs.org/en/latest/reference/keyboard_shortcuts_win.html /* Mystic - [^B] Reformat Paragraph [^O] Show this help file + [^B] Reformat Paragraph [^O] Show this help file [^I] Insert tab space [^Q] Enter quote mode [^K] Cut current line of text [^V] Toggle insert/overwrite [^U] Paste previously cut text [^Y] Delete current line @@ -29,58 +29,58 @@ const _ = require('lodash'); */ // -// Some other interesting implementations, resources, etc. +// Some other interesting implementations, resources, etc. // -// Editors - BBS -// * https://github.com/M-griffin/Enthral/blob/master/src/msg_fse.cpp +// Editors - BBS +// * https://github.com/M-griffin/Enthral/blob/master/src/msg_fse.cpp // // -// Editors - Other -// * http://joe-editor.sourceforge.net/ -// * http://www.jbox.dk/downloads/edit.c -// * https://github.com/dominictarr/hipster +// Editors - Other +// * http://joe-editor.sourceforge.net/ +// * http://www.jbox.dk/downloads/edit.c +// * https://github.com/dominictarr/hipster // -// Implementations - Word Wrap -// * https://github.com/protomouse/synchronet/blob/93b01c55b3102ebc3c4f4793c3a45b8c13d0dc2a/src/sbbs3/wordwrap.c +// Implementations - Word Wrap +// * https://github.com/protomouse/synchronet/blob/93b01c55b3102ebc3c4f4793c3a45b8c13d0dc2a/src/sbbs3/wordwrap.c // -// Misc notes -// * https://github.com/dominictarr/hipster/issues/15 (Deleting lines/etc.) +// Misc notes +// * https://github.com/dominictarr/hipster/issues/15 (Deleting lines/etc.) // -// Blessed -// insertLine: CSR(top, bottom) + CUP(y, 0) + IL(1) + CSR(0, height) -// deleteLine: CSR(top, bottom) + CUP(y, 0) + DL(1) + CSR(0, height) -// Quick Ansi -- update only what was changed: -// https://github.com/dominictarr/quickansi +// Blessed +// insertLine: CSR(top, bottom) + CUP(y, 0) + IL(1) + CSR(0, height) +// deleteLine: CSR(top, bottom) + CUP(y, 0) + DL(1) + CSR(0, height) +// Quick Ansi -- update only what was changed: +// https://github.com/dominictarr/quickansi // -// To-Do +// To-Do // -// * Index pos % for emit scroll events -// * Some of this should be async'd where there is lots of processing (e.g. word wrap) -// * Fix backspace when col=0 (e.g. bs to prev line) -// * Add word delete (CTRL+????) -// * +// * Index pos % for emit scroll events +// * Some of this should be async'd where there is lots of processing (e.g. word wrap) +// * Fix backspace when col=0 (e.g. bs to prev line) +// * Add word delete (CTRL+????) +// * const SPECIAL_KEY_MAP_DEFAULT = { - 'line feed' : [ 'return' ], - exit : [ 'esc' ], - backspace : [ 'backspace' ], - delete : [ 'delete' ], - tab : [ 'tab' ], - up : [ 'up arrow' ], - down : [ 'down arrow' ], - end : [ 'end' ], - home : [ 'home' ], - left : [ 'left arrow' ], - right : [ 'right arrow' ], - 'delete line' : [ 'ctrl + y' ], - 'page up' : [ 'page up' ], - 'page down' : [ 'page down' ], - insert : [ 'insert', 'ctrl + v' ], + 'line feed' : [ 'return' ], + exit : [ 'esc' ], + backspace : [ 'backspace' ], + delete : [ 'delete' ], + tab : [ 'tab' ], + up : [ 'up arrow' ], + down : [ 'down arrow' ], + end : [ 'end' ], + home : [ 'home' ], + left : [ 'left arrow' ], + right : [ 'right arrow' ], + 'delete line' : [ 'ctrl + y' ], + 'page up' : [ 'page up' ], + 'page down' : [ 'page down' ], + insert : [ 'insert', 'ctrl + v' ], }; -exports.MultiLineEditTextView = MultiLineEditTextView; +exports.MultiLineEditTextView = MultiLineEditTextView; function MultiLineEditTextView(options) { if(!_.isBoolean(options.acceptsFocus)) { @@ -100,18 +100,18 @@ function MultiLineEditTextView(options) { var self = this; // - // ANSI seems to want tabs to default to 8 characters. See the following: - // * http://www.ansi-bbs.org/ansi-bbs2/control_chars/ - // * http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt + // ANSI seems to want tabs to default to 8 characters. See the following: + // * http://www.ansi-bbs.org/ansi-bbs2/control_chars/ + // * http://www.bbsdocumentary.com/library/PROGRAMS/GRAPHICS/ANSI/bansi.txt // - // This seems overkill though, so let's default to 4 :) - // :TODO: what shoudl this really be? Maybe 8 is OK + // This seems overkill though, so let's default to 4 :) + // :TODO: what shoudl this really be? Maybe 8 is OK // - this.tabWidth = _.isNumber(options.tabWidth) ? options.tabWidth : 4; + this.tabWidth = _.isNumber(options.tabWidth) ? options.tabWidth : 4; - this.textLines = [ ]; - this.topVisibleIndex = 0; - this.mode = options.mode || 'edit'; // edit | preview | read-only + this.textLines = [ ]; + this.topVisibleIndex = 0; + this.mode = options.mode || 'edit'; // edit | preview | read-only if ('preview' === this.mode) { this.autoScroll = options.autoScroll || true; @@ -121,10 +121,10 @@ function MultiLineEditTextView(options) { this.tabSwitchesView = options.tabSwitchesView || false; } // - // cursorPos represents zero-based row, col positions - // within the editor itself + // cursorPos represents zero-based row, col positions + // within the editor itself // - this.cursorPos = { col : 0, row : 0 }; + this.cursorPos = { col : 0, row : 0 }; this.getSGRFor = function(sgrFor) { return { @@ -140,7 +140,7 @@ function MultiLineEditTextView(options) { return 'preview' === self.mode; }; - // :TODO: Most of the calls to this could be avoided via incrementRow(), decrementRow() that keeps track or such + // :TODO: Most of the calls to this could be avoided via incrementRow(), decrementRow() that keeps track or such this.getTextLinesIndex = function(row) { if(!_.isNumber(row)) { row = self.cursorPos.row; @@ -172,34 +172,34 @@ function MultiLineEditTextView(options) { this.redrawRows = function(startRow, endRow) { self.toggleTextCursor('hide'); - const startIndex = self.getTextLinesIndex(startRow); - const endIndex = Math.min(self.getTextLinesIndex(endRow), self.textLines.length); - const absPos = self.getAbsolutePosition(startRow, 0); + const startIndex = self.getTextLinesIndex(startRow); + const endIndex = Math.min(self.getTextLinesIndex(endRow), self.textLines.length); + const absPos = self.getAbsolutePosition(startRow, 0); for(let i = startIndex; i < endIndex; ++i) { //${self.getSGRFor('text')} self.client.term.write( `${ansi.goto(absPos.row++, absPos.col)}${self.getRenderText(i)}`, - false // convertLineFeeds + false // convertLineFeeds ); } self.toggleTextCursor('show'); - return absPos.row - self.position.row; // row we ended on + return absPos.row - self.position.row; // row we ended on }; this.eraseRows = function(startRow, endRow) { self.toggleTextCursor('hide'); - const absPos = self.getAbsolutePosition(startRow, 0); - const absPosEnd = self.getAbsolutePosition(endRow, 0); - const eraseFiller = ' '.repeat(self.dimens.width);//new Array(self.dimens.width).join(' '); + const absPos = self.getAbsolutePosition(startRow, 0); + const absPosEnd = self.getAbsolutePosition(endRow, 0); + const eraseFiller = ' '.repeat(self.dimens.width);//new Array(self.dimens.width).join(' '); while(absPos.row < absPosEnd.row) { self.client.term.write( `${ansi.goto(absPos.row++, absPos.col)}${eraseFiller}`, - false // convertLineFeeds + false // convertLineFeeds ); } @@ -213,16 +213,16 @@ function MultiLineEditTextView(options) { self.eraseRows(lastRow, self.dimens.height); /* - // :TOOD: create eraseRows(startRow, endRow) - if(lastRow < self.dimens.height) { - var absPos = self.getAbsolutePosition(lastRow, 0); - var empty = new Array(self.dimens.width).join(' '); - while(lastRow++ < self.dimens.height) { - self.client.term.write(ansi.goto(absPos.row++, absPos.col)); - self.client.term.write(empty); - } - } - */ + // :TOOD: create eraseRows(startRow, endRow) + if(lastRow < self.dimens.height) { + var absPos = self.getAbsolutePosition(lastRow, 0); + var empty = new Array(self.dimens.width).join(' '); + while(lastRow++ < self.dimens.height) { + self.client.term.write(ansi.goto(absPos.row++, absPos.col)); + self.client.term.write(empty); + } + } + */ }; this.getVisibleText = function(index) { @@ -262,12 +262,12 @@ function MultiLineEditTextView(options) { }; this.getRenderText = function(index) { - let text = self.getVisibleText(index); - const remain = self.dimens.width - text.length; + let text = self.getVisibleText(index); + const remain = self.dimens.width - text.length; if(remain > 0) { text += ' '.repeat(remain + 1); - // text += new Array(remain + 1).join(' '); + // text += new Array(remain + 1).join(' '); } return text; @@ -278,15 +278,15 @@ function MultiLineEditTextView(options) { if(startIndex === endIndex) { lines = [ self.textLines[startIndex] ]; } else { - lines = self.textLines.slice(startIndex, endIndex + 1); // "slice extracts up to but not including end." + lines = self.textLines.slice(startIndex, endIndex + 1); // "slice extracts up to but not including end." } return lines; }; this.getOutputText = function(startIndex, endIndex, eolMarker, options) { - const lines = self.getTextLines(startIndex, endIndex); - let text = ''; - const re = new RegExp('\\t{1,' + (self.tabWidth) + '}', 'g'); + const lines = self.getTextLines(startIndex, endIndex); + let text = ''; + const re = new RegExp('\\t{1,' + (self.tabWidth) + '}', 'g'); lines.forEach(line => { text += line.text.replace(re, '\t'); @@ -317,28 +317,28 @@ function MultiLineEditTextView(options) { }; /* - this.editTextAtPosition = function(editAction, text, index, col) { - switch(editAction) { - case 'insert' : - self.insertCharactersInText(text, index, col); - break; + this.editTextAtPosition = function(editAction, text, index, col) { + switch(editAction) { + case 'insert' : + self.insertCharactersInText(text, index, col); + break; - case 'deleteForward' : - break; + case 'deleteForward' : + break; - case 'deleteBack' : - break; + case 'deleteBack' : + break; - case 'replace' : - break; - } - }; - */ + case 'replace' : + break; + } + }; + */ this.updateTextWordWrap = function(index) { - const nextEolIndex = self.getNextEndOfLineIndex(index); - const wrapped = self.wordWrapSingleLine(self.getContiguousText(index, nextEolIndex), 'tabsIntact'); - const newLines = wrapped.wrapped.map(l => { return { text : l }; } ); + const nextEolIndex = self.getNextEndOfLineIndex(index); + const wrapped = self.wordWrapSingleLine(self.getContiguousText(index, nextEolIndex), 'tabsIntact'); + const newLines = wrapped.wrapped.map(l => { return { text : l }; } ); newLines[newLines.length - 1].eol = true; @@ -352,17 +352,17 @@ function MultiLineEditTextView(options) { this.removeCharactersFromText = function(index, col, operation, count) { if('delete' === operation) { self.textLines[index].text = - self.textLines[index].text.slice(0, col) + - self.textLines[index].text.slice(col + count); + self.textLines[index].text.slice(0, col) + + self.textLines[index].text.slice(col + count); self.updateTextWordWrap(index); self.redrawRows(self.cursorPos.row, self.dimens.height); self.moveClientCursorToCursorPos(); } else if ('backspace' === operation) { - // :TODO: method for splicing text + // :TODO: method for splicing text self.textLines[index].text = - self.textLines[index].text.slice(0, col - (count - 1)) + - self.textLines[index].text.slice(col + 1); + self.textLines[index].text.slice(0, col - (count - 1)) + + self.textLines[index].text.slice(col + 1); self.cursorPos.col -= (count - 1); @@ -372,14 +372,14 @@ function MultiLineEditTextView(options) { self.moveClientCursorToCursorPos(); } else if('delete line' === operation) { // - // Delete a visible line. Note that this is *not* the "physical" line, or - // 1:n entries up to eol! This is to keep consistency with home/end, and - // some other text editors such as nano. Sublime for example want to - // treat all of these things using the physical approach, but this seems - // a bit odd in this context. + // Delete a visible line. Note that this is *not* the "physical" line, or + // 1:n entries up to eol! This is to keep consistency with home/end, and + // some other text editors such as nano. Sublime for example want to + // treat all of these things using the physical approach, but this seems + // a bit odd in this context. // - var isLastLine = (index === self.textLines.length - 1); - var hadEol = self.textLines[index].eol; + var isLastLine = (index === self.textLines.length - 1); + var hadEol = self.textLines[index].eol; self.textLines.splice(index, 1); if(hadEol && self.textLines.length > index && !self.textLines[index].eol) { @@ -387,11 +387,11 @@ function MultiLineEditTextView(options) { } // - // Create a empty edit buffer if necessary - // :TODO: Make this a method + // Create a empty edit buffer if necessary + // :TODO: Make this a method if(self.textLines.length < 1) { self.textLines = [ { text : '', eol : true } ]; - isLastLine = false; // resetting + isLastLine = false; // resetting } self.cursorPos.col = 0; @@ -400,7 +400,7 @@ function MultiLineEditTextView(options) { self.eraseRows(lastRow, self.dimens.height); // - // If we just deleted the last line in the buffer, move up + // If we just deleted the last line in the buffer, move up // if(isLastLine) { self.cursorEndOfPreviousLine(); @@ -411,8 +411,8 @@ function MultiLineEditTextView(options) { }; this.insertCharactersInText = function(c, index, col) { - const prevTextLength = self.getTextLength(index); - let editingEol = self.cursorPos.col === prevTextLength; + const prevTextLength = self.getTextLength(index); + let editingEol = self.cursorPos.col === prevTextLength; self.textLines[index].text = [ self.textLines[index].text.slice(0, col), @@ -424,43 +424,43 @@ function MultiLineEditTextView(options) { if(self.getTextLength(index) > self.dimens.width) { // - // Update word wrapping and |cursorOffset| if the cursor - // was within the bounds of the wrapped text + // Update word wrapping and |cursorOffset| if the cursor + // was within the bounds of the wrapped text // let cursorOffset; - const lastCol = self.cursorPos.col - c.length; - const firstWrapRange = self.updateTextWordWrap(index); + const lastCol = self.cursorPos.col - c.length; + const firstWrapRange = self.updateTextWordWrap(index); if(lastCol >= firstWrapRange.start && lastCol <= firstWrapRange.end) { cursorOffset = self.cursorPos.col - firstWrapRange.start; - editingEol = true; //override + editingEol = true; //override } else { cursorOffset = firstWrapRange.end; } - // redraw from current row to end of visible area + // redraw from current row to end of visible area self.redrawRows(self.cursorPos.row, self.dimens.height); - // If we're editing mid, we're done here. Else, we need to - // move the cursor to the new editing position after a wrap + // If we're editing mid, we're done here. Else, we need to + // move the cursor to the new editing position after a wrap if(editingEol) { self.cursorBeginOfNextLine(); self.cursorPos.col += cursorOffset; self.client.term.rawWrite(ansi.right(cursorOffset)); } else { - // adjust cursor after drawing new rows + // adjust cursor after drawing new rows const absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col); self.client.term.rawWrite(ansi.goto(absPos.row, absPos.col)); } } else { // - // We must only redraw from col -> end of current visible line + // We must only redraw from col -> end of current visible line // const absPos = self.getAbsolutePosition(self.cursorPos.row, self.cursorPos.col); const renderText = self.getRenderText(index).slice(self.cursorPos.col - c.length); self.client.term.write( `${ansi.hideCursor()}${self.getSGRFor('text')}${renderText}${ansi.goto(absPos.row, absPos.col)}${ansi.showCursor()}`, - false // convertLineFeeds + false // convertLineFeeds ); } }; @@ -502,24 +502,24 @@ function MultiLineEditTextView(options) { return wordWrapText( line, { - width : self.dimens.width, - tabHandling : tabHandling, - tabWidth : self.tabWidth, - tabChar : '\t', + width : self.dimens.width, + tabHandling : tabHandling, + tabWidth : self.tabWidth, + tabChar : '\t', } ); }; this.setTextLines = function(lines, index, termWithEol) { if(0 === index && (0 === self.textLines.length || (self.textLines.length === 1 && '' === self.textLines[0].text) )) { - // quick path: just set the things + // quick path: just set the things self.textLines = lines.slice(0, -1).map(l => { return { text : l }; }).concat( { text : lines[lines.length - 1], eol : termWithEol } ); } else { - // insert somewhere in textLines... + // insert somewhere in textLines... if(index > self.textLines.length) { - // fill with empty + // fill with empty self.textLines.splice( self.textLines.length, 0, @@ -547,7 +547,7 @@ function MultiLineEditTextView(options) { let index = 0; text.forEach(line => { - self.setTextLines( [ line ], index, true); // true=termWithEol + self.setTextLines( [ line ], index, true); // true=termWithEol index += 1; }); @@ -565,12 +565,12 @@ function MultiLineEditTextView(options) { ansiPrep( ansi, { - termWidth : this.client.term.termWidth, - termHeight : this.client.term.termHeight, - cols : this.dimens.width, - rows : 'auto', - startCol : this.position.col, - forceLineTerm : options.forceLineTerm, + termWidth : this.client.term.termWidth, + termHeight : this.client.term.termHeight, + cols : this.dimens.width, + rows : 'auto', + startCol : this.position.col, + forceLineTerm : options.forceLineTerm, }, (err, preppedAnsi) => { return setLines(err ? ansi : preppedAnsi); @@ -580,31 +580,31 @@ function MultiLineEditTextView(options) { this.insertRawText = function(text, index, col) { // - // Perform the following on |text|: - // * Normalize various line feed formats -> \n - // * Remove some control characters (e.g. \b) - // * Word wrap lines such that they fit in the visible workspace. - // Each actual line will then take 1:n elements in textLines[]. - // * Each tab will be appropriately expanded and take 1:n \t - // characters. This allows us to know when we're in tab space - // when doing cursor movement/etc. + // Perform the following on |text|: + // * Normalize various line feed formats -> \n + // * Remove some control characters (e.g. \b) + // * Word wrap lines such that they fit in the visible workspace. + // Each actual line will then take 1:n elements in textLines[]. + // * Each tab will be appropriately expanded and take 1:n \t + // characters. This allows us to know when we're in tab space + // when doing cursor movement/etc. // // - // Try to handle any possible newline that can be fed to us. - // See http://stackoverflow.com/questions/5034781/js-regex-to-split-by-line + // Try to handle any possible newline that can be fed to us. + // See http://stackoverflow.com/questions/5034781/js-regex-to-split-by-line // - // :TODO: support index/col insertion point + // :TODO: support index/col insertion point if(_.isNumber(index)) { if(_.isNumber(col)) { // - // Modify text to have information from index - // before and and after column + // Modify text to have information from index + // before and and after column // - // :TODO: Need to clean this string (e.g. collapse tabs) + // :TODO: Need to clean this string (e.g. collapse tabs) text = self.textLines; - // :TODO: Remove original line @ index + // :TODO: Remove original line @ index } } else { index = self.textLines.length; @@ -616,7 +616,7 @@ function MultiLineEditTextView(options) { text.forEach(line => { wrapped = self.wordWrapSingleLine(line, 'expand').wrapped; - self.setTextLines(wrapped, index, true); // true=termWithEol + self.setTextLines(wrapped, index, true); // true=termWithEol index += wrapped.length; }); }; @@ -638,16 +638,16 @@ function MultiLineEditTextView(options) { var index = self.getTextLinesIndex(); // - // :TODO: stuff that needs to happen - // * Break up into smaller methods - // * Even in overtype mode, word wrapping must apply if past bounds - // * A lot of this can be used for backspacing also - // * See how Sublime treats tabs in *non* overtype mode... just overwrite them? + // :TODO: stuff that needs to happen + // * Break up into smaller methods + // * Even in overtype mode, word wrapping must apply if past bounds + // * A lot of this can be used for backspacing also + // * See how Sublime treats tabs in *non* overtype mode... just overwrite them? // // if(self.overtypeMode) { - // :TODO: special handing for insert over eol mark? + // :TODO: special handing for insert over eol mark? self.replaceCharacterInText(c, index, self.cursorPos.col); self.cursorPos.col++; self.client.term.write(c); @@ -754,7 +754,7 @@ function MultiLineEditTextView(options) { self.adjustCursorIfPastEndOfLine(true); } else { self.cursorPos.row = 0; - self.moveClientCursorToCursorPos(); // :TODO: ajust if eol, etc. + self.moveClientCursorToCursorPos(); // :TODO: ajust if eol, etc. } self.emitEditPosition(); @@ -773,13 +773,13 @@ function MultiLineEditTextView(options) { this.keyPressLineFeed = function() { // - // Break up text from cursor position, redraw, and update cursor - // position to start of next line + // Break up text from cursor position, redraw, and update cursor + // position to start of next line // - var index = self.getTextLinesIndex(); - var nextEolIndex = self.getNextEndOfLineIndex(index); - var text = self.getContiguousText(index, nextEolIndex); - const newLines = self.wordWrapSingleLine(text.slice(self.cursorPos.col), 'tabsIntact').wrapped; + var index = self.getTextLinesIndex(); + var nextEolIndex = self.getNextEndOfLineIndex(index); + var text = self.getContiguousText(index, nextEolIndex); + const newLines = self.wordWrapSingleLine(text.slice(self.cursorPos.col), 'tabsIntact').wrapped; newLines.unshift( { text : text.slice(0, self.cursorPos.col), eol : true } ); for(var i = 1; i < newLines.length; ++i) { @@ -791,7 +791,7 @@ function MultiLineEditTextView(options) { self.textLines, [ index, (nextEolIndex - index) + 1 ].concat(newLines)); - // redraw from current row to end of visible area + // redraw from current row to end of visible area self.redrawRows(self.cursorPos.row, self.dimens.height); self.cursorBeginOfNextLine(); @@ -812,8 +812,8 @@ function MultiLineEditTextView(options) { this.keyPressBackspace = function() { if(self.cursorPos.col >= 1) { // - // Don't want to delete character at cursor, but rather the character - // to the left of the cursor! + // Don't want to delete character at cursor, but rather the character + // to the left of the cursor! // self.cursorPos.col -= 1; @@ -842,12 +842,12 @@ function MultiLineEditTextView(options) { count); } else { // - // Delete character at end of line previous. - // * This may be a eol marker - // * Word wrapping will need re-applied + // Delete character at end of line previous. + // * This may be a eol marker + // * Word wrapping will need re-applied // - // :TODO: apply word wrapping such that text can be re-adjusted if it can now fit on prev - self.keyPressLeft(); // same as hitting left - jump to previous line + // :TODO: apply word wrapping such that text can be re-adjusted if it can now fit on prev + self.keyPressLeft(); // same as hitting left - jump to previous line //self.keyPressBackspace(); } @@ -859,7 +859,7 @@ function MultiLineEditTextView(options) { if(0 === self.cursorPos.col && 0 === self.textLines[lineIndex].text.length && self.textLines.length > 0) { // - // Start of line and nothing left. Just delete the line + // Start of line and nothing left. Just delete the line // self.removeCharactersFromText( lineIndex, @@ -906,7 +906,7 @@ function MultiLineEditTextView(options) { var move; switch(direction) { // - // Next tabstop to the right + // Next tabstop to the right // case 'right' : move = self.getNextTabStop(self.cursorPos.col) - self.cursorPos.col; @@ -915,7 +915,7 @@ function MultiLineEditTextView(options) { break; // - // Next tabstop to the left + // Next tabstop to the left // case 'left' : move = self.cursorPos.col - self.getPrevTabStop(self.cursorPos.col); @@ -926,7 +926,7 @@ function MultiLineEditTextView(options) { case 'up' : case 'down' : // - // Jump to the tabstop nearest the cursor + // Jump to the tabstop nearest the cursor // var newCol = self.tabStops.reduce(function r(prev, curr) { return (Math.abs(curr - self.cursorPos.col) < Math.abs(prev - self.cursorPos.col) ? curr : prev); @@ -946,43 +946,43 @@ function MultiLineEditTextView(options) { return true; } - return false; // did not fall on a tab + return false; // did not fall on a tab }; this.cursorStartOfDocument = function() { - self.topVisibleIndex = 0; - self.cursorPos = { row : 0, col : 0 }; + self.topVisibleIndex = 0; + self.cursorPos = { row : 0, col : 0 }; self.redraw(); self.moveClientCursorToCursorPos(); }; this.cursorEndOfDocument = function() { - self.topVisibleIndex = Math.max(self.textLines.length - self.dimens.height, 0); - self.cursorPos.row = (self.textLines.length - self.topVisibleIndex) - 1; - self.cursorPos.col = self.getTextEndOfLineColumn(); + self.topVisibleIndex = Math.max(self.textLines.length - self.dimens.height, 0); + self.cursorPos.row = (self.textLines.length - self.topVisibleIndex) - 1; + self.cursorPos.col = self.getTextEndOfLineColumn(); self.redraw(); self.moveClientCursorToCursorPos(); }; this.cursorBeginOfNextLine = function() { - // e.g. when scrolling right past eol + // e.g. when scrolling right past eol var linesBelow = self.getRemainingLinesBelowRow(); if(linesBelow > 0) { - var lastVisibleRow = Math.min(self.dimens.height, self.textLines.length) - 1; + var lastVisibleRow = Math.min(self.dimens.height, self.textLines.length) - 1; if(self.cursorPos.row < lastVisibleRow) { self.cursorPos.row++; } else { self.scrollDocumentUp(); } - self.keyPressHome(); // same as pressing 'home' + self.keyPressHome(); // same as pressing 'home' } }; this.cursorEndOfPreviousLine = function() { - // e.g. when scrolling left past start of line + // e.g. when scrolling left past start of line var moveToEnd; if(self.cursorPos.row > 0) { self.cursorPos.row--; @@ -993,30 +993,30 @@ function MultiLineEditTextView(options) { } if(moveToEnd) { - self.keyPressEnd(); // same as pressing 'end' + self.keyPressEnd(); // same as pressing 'end' } }; /* - this.cusorEndOfNextLine = function() { - var linesBelow = self.getRemainingLinesBelowRow(); + this.cusorEndOfNextLine = function() { + var linesBelow = self.getRemainingLinesBelowRow(); - if(linesBelow > 0) { - var lastVisibleRow = Math.min(self.dimens.height, self.textLines.length) - 1; - if(self.cursorPos.row < lastVisibleRow) { - self.cursorPos.row++; - } else { - self.scrollDocumentUp(); - } - self.keyPressEnd(); // same as pressing 'end' - } - }; - */ + if(linesBelow > 0) { + var lastVisibleRow = Math.min(self.dimens.height, self.textLines.length) - 1; + if(self.cursorPos.row < lastVisibleRow) { + self.cursorPos.row++; + } else { + self.scrollDocumentUp(); + } + self.keyPressEnd(); // same as pressing 'end' + } + }; + */ this.scrollDocumentUp = function() { // - // Note: We scroll *up* when the cursor goes *down* beyond - // the visible area! + // Note: We scroll *up* when the cursor goes *down* beyond + // the visible area! // var linesBelow = self.getRemainingLinesBelowRow(); if(linesBelow > 0) { @@ -1027,8 +1027,8 @@ function MultiLineEditTextView(options) { this.scrollDocumentDown = function() { // - // Note: We scroll *down* when the cursor goes *up* beyond - // the visible area! + // Note: We scroll *down* when the cursor goes *up* beyond + // the visible area! // if(self.topVisibleIndex > 0) { self.topVisibleIndex--; @@ -1037,7 +1037,7 @@ function MultiLineEditTextView(options) { }; this.emitEditPosition = function() { - self.emit('edit position', self.getEditPosition()); + self.emit('edit position', self.getEditPosition()); }; this.toggleTextEditMode = function() { @@ -1045,7 +1045,7 @@ function MultiLineEditTextView(options) { self.emit('text edit mode', self.getTextEditMode()); }; - this.insertRawText(''); // init to blank/empty + this.insertRawText(''); // init to blank/empty } require('util').inherits(MultiLineEditTextView, View); @@ -1074,11 +1074,11 @@ MultiLineEditTextView.prototype.setText = function(text, options = { scrollMode this.addText(text, options); /*this.insertRawText(text); - if(this.isEditMode()) { - this.cursorEndOfDocument(); - } else if(this.isPreviewMode()) { - this.cursorStartOfDocument(); - }*/ + if(this.isEditMode()) { + this.cursorEndOfDocument(); + } else if(this.isPreviewMode()) { + this.cursorStartOfDocument(); + }*/ }; MultiLineEditTextView.prototype.setAnsi = function(ansi, options = { prepped : false }, cb) { @@ -1116,14 +1116,14 @@ MultiLineEditTextView.prototype.getData = function(options = { forceLineTerms : MultiLineEditTextView.prototype.setPropertyValue = function(propName, value) { switch(propName) { - case 'mode' : + case 'mode' : this.mode = value; if('preview' === value && !this.specialKeyMap.next) { this.specialKeyMap.next = [ 'tab' ]; } break; - case 'autoScroll' : + case 'autoScroll' : this.autoScroll = value; break; @@ -1205,9 +1205,9 @@ MultiLineEditTextView.prototype.getEditPosition = function() { var currentIndex = this.getTextLinesIndex() + 1; return { - row : this.getTextLinesIndex(this.cursorPos.row), - col : this.cursorPos.col, - percent : Math.floor(((currentIndex / this.textLines.length) * 100)), - below : this.getRemainingLinesBelowRow(), + row : this.getTextLinesIndex(this.cursorPos.row), + col : this.cursorPos.col, + percent : Math.floor(((currentIndex / this.textLines.length) * 100)), + below : this.getRemainingLinesBelowRow(), }; }; diff --git a/core/new_scan.js b/core/new_scan.js index b84eab52..974519af 100644 --- a/core/new_scan.js +++ b/core/new_scan.js @@ -1,24 +1,24 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const msgArea = require('./message_area.js'); -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const stringFormat = require('./string_format.js'); -const FileEntry = require('./file_entry.js'); -const FileBaseFilters = require('./file_base_filter.js'); -const Errors = require('./enig_error.js').Errors; -const { getAvailableFileAreaTags } = require('./file_base_area.js'); +// ENiGMA½ +const msgArea = require('./message_area.js'); +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const stringFormat = require('./string_format.js'); +const FileEntry = require('./file_entry.js'); +const FileBaseFilters = require('./file_base_filter.js'); +const Errors = require('./enig_error.js').Errors; +const { getAvailableFileAreaTags } = require('./file_base_area.js'); -// deps -const _ = require('lodash'); -const async = require('async'); +// deps +const _ = require('lodash'); +const async = require('async'); exports.moduleInfo = { - name : 'New Scan', - desc : 'Performs a new scan against various areas of the system', - author : 'NuSkooler', + name : 'New Scan', + desc : 'Performs a new scan against various areas of the system', + author : 'NuSkooler', }; /* @@ -30,15 +30,15 @@ exports.moduleInfo = { */ const MciCodeIds = { - ScanStatusLabel : 1, // TL1 - ScanStatusList : 2, // VM2 (appends) + ScanStatusLabel : 1, // TL1 + ScanStatusList : 2, // VM2 (appends) }; const Steps = { - MessageConfs : 'messageConferences', - FileBase : 'fileBase', + MessageConfs : 'messageConferences', + FileBase : 'fileBase', - Finished : 'finished', + Finished : 'finished', }; exports.getModule = class NewScanModule extends MenuModule { @@ -46,17 +46,17 @@ exports.getModule = class NewScanModule extends MenuModule { super(options); - this.newScanFullExit = _.get(options, 'lastMenuResult.fullExit', false); + this.newScanFullExit = _.get(options, 'lastMenuResult.fullExit', false); - this.currentStep = Steps.MessageConfs; - this.currentScanAux = {}; + this.currentStep = Steps.MessageConfs; + this.currentScanAux = {}; // :TODO: Make this conf/area specific: - const config = this.menuConfig.config; - this.scanStartFmt = config.scanStartFmt || 'Scanning {confName} - {areaName}...'; - this.scanFinishNoneFmt = config.scanFinishNoneFmt || 'Nothing new'; - this.scanFinishNewFmt = config.scanFinishNewFmt || '{count} entries found'; - this.scanCompleteMsg = config.scanCompleteMsg || 'Finished newscan'; + const config = this.menuConfig.config; + this.scanStartFmt = config.scanStartFmt || 'Scanning {confName} - {areaName}...'; + this.scanFinishNoneFmt = config.scanFinishNoneFmt || 'Nothing new'; + this.scanFinishNewFmt = config.scanFinishNewFmt || '{count} entries found'; + this.scanCompleteMsg = config.scanCompleteMsg || 'Finished newscan'; } updateScanStatus(statusText) { @@ -76,9 +76,9 @@ exports.getModule = class NewScanModule extends MenuModule { }); // - // Sort conferences by name, other than 'system_internal' which should - // always come first such that we display private mails/etc. before - // other conferences & areas + // Sort conferences by name, other than 'system_internal' which should + // always come first such that we display private mails/etc. before + // other conferences & areas // this.sortedMessageConfs.sort((a, b) => { if('system_internal' === a.confTag) { @@ -110,23 +110,23 @@ exports.getModule = class NewScanModule extends MenuModule { newScanMessageArea(conf, cb) { // :TODO: it would be nice to cache this - must be done by conf! const sortedAreas = msgArea.getSortedAvailMessageAreasByConfTag(conf.confTag, { client : this.client } ); - const currentArea = sortedAreas[this.currentScanAux.area]; + const currentArea = sortedAreas[this.currentScanAux.area]; // - // Scan and update index until we find something. If results are found, - // we'll goto the list module & show them. + // Scan and update index until we find something. If results are found, + // we'll goto the list module & show them. // const self = this; async.waterfall( [ function checkAndUpdateIndex(callback) { - // Advance to next area if possible + // Advance to next area if possible if(sortedAreas.length >= self.currentScanAux.area + 1) { self.currentScanAux.area += 1; return callback(null); } else { self.updateScanStatus(self.scanCompleteMsg); - return callback(Errors.DoesNotExist('No more areas')); // this will stop our scan + return callback(Errors.DoesNotExist('No more areas')); // this will stop our scan } }, function updateStatusScanStarted(callback) { @@ -147,7 +147,7 @@ exports.getModule = class NewScanModule extends MenuModule { }, function displayMessageList(newMessageCount) { if(newMessageCount <= 0) { - return self.newScanMessageArea(conf, cb); // next area, if any + return self.newScanMessageArea(conf, cb); // next area, if any } const nextModuleOpts = { @@ -166,11 +166,11 @@ exports.getModule = class NewScanModule extends MenuModule { } newScanFileBase(cb) { - // :TODO: add in steps + // :TODO: add in steps const filterCriteria = { newerThanFileId : FileBaseFilters.getFileBaseLastViewedFileIdByUser(this.client.user), - areaTag : getAvailableFileAreaTags(this.client), - order : 'ascending', // oldest first + areaTag : getAvailableFileAreaTags(this.client), + order : 'ascending', // oldest first }; FileEntry.findFiles( @@ -195,14 +195,14 @@ exports.getModule = class NewScanModule extends MenuModule { getSaveState() { return { - currentStep : this.currentStep, - currentScanAux : this.currentScanAux, + currentStep : this.currentStep, + currentScanAux : this.currentScanAux, }; } restoreSavedState(savedState) { - this.currentStep = savedState.currentStep; - this.currentScanAux = savedState.currentScanAux; + this.currentStep = savedState.currentStep; + this.currentScanAux = savedState.currentScanAux; } performScanCurrentStep(cb) { @@ -227,7 +227,7 @@ exports.getModule = class NewScanModule extends MenuModule { mciReady(mciData, cb) { if(this.newScanFullExit) { - // user has canceled the entire scan @ message list view + // user has canceled the entire scan @ message list view return cb(null); } @@ -236,18 +236,18 @@ exports.getModule = class NewScanModule extends MenuModule { return cb(err); } - const self = this; - const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); + const self = this; + const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); - // :TODO: display scan step/etc. + // :TODO: display scan step/etc. async.series( [ function loadFromConfig(callback) { const loadOpts = { - callingMenu : self, - mciMap : mciData.menu, - noInput : true, + callingMenu : self, + mciMap : mciData.menu, + noInput : true, }; vc.loadFromMenuConfig(loadOpts, callback); diff --git a/core/nua.js b/core/nua.js index 011ad943..7eafe16d 100644 --- a/core/nua.js +++ b/core/nua.js @@ -1,24 +1,24 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const User = require('./user.js'); -const theme = require('./theme.js'); -const login = require('./system_menu_method.js').login; -const Config = require('./config.js').get; -const messageArea = require('./message_area.js'); +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const User = require('./user.js'); +const theme = require('./theme.js'); +const login = require('./system_menu_method.js').login; +const Config = require('./config.js').get; +const messageArea = require('./message_area.js'); exports.moduleInfo = { - name : 'NUA', - desc : 'New User Application', + name : 'NUA', + desc : 'New User Application', }; const MciViewIds = { - userName : 1, - password : 9, - confirm : 10, - errMsg : 11, + userName : 1, + password : 9, + confirm : 10, + errMsg : 11, }; exports.getModule = class NewUserAppModule extends MenuModule { @@ -30,7 +30,7 @@ exports.getModule = class NewUserAppModule extends MenuModule { this.menuMethods = { // - // Validation stuff + // Validation stuff // validatePassConfirmMatch : function(data, cb) { const passwordView = self.viewControllers.menu.getView(MciViewIds.password); @@ -58,7 +58,7 @@ exports.getModule = class NewUserAppModule extends MenuModule { // - // Submit handlers + // Submit handlers // submitApplication : function(formData, extraArgs, cb) { const newUser = new User(); @@ -67,33 +67,33 @@ exports.getModule = class NewUserAppModule extends MenuModule { newUser.username = formData.value.username; // - // We have to disable ACS checks for initial default areas as the user is not yet ready + // We have to disable ACS checks for initial default areas as the user is not yet ready // - let confTag = messageArea.getDefaultMessageConferenceTag(self.client, true); // true=disableAcsCheck - let areaTag = messageArea.getDefaultMessageAreaTagByConfTag(self.client, confTag, true); // true=disableAcsCheck + let confTag = messageArea.getDefaultMessageConferenceTag(self.client, true); // true=disableAcsCheck + let areaTag = messageArea.getDefaultMessageAreaTagByConfTag(self.client, confTag, true); // true=disableAcsCheck // can't store undefined! confTag = confTag || ''; areaTag = areaTag || ''; newUser.properties = { - real_name : formData.value.realName, - birthdate : new Date(Date.parse(formData.value.birthdate)).toISOString(), // :TODO: Use moment & explicit ISO string format - sex : formData.value.sex, - location : formData.value.location, - affiliation : formData.value.affils, - email_address : formData.value.email, - web_address : formData.value.web, - account_created : new Date().toISOString(), // :TODO: Use moment & explicit ISO string format + real_name : formData.value.realName, + birthdate : new Date(Date.parse(formData.value.birthdate)).toISOString(), // :TODO: Use moment & explicit ISO string format + sex : formData.value.sex, + location : formData.value.location, + affiliation : formData.value.affils, + email_address : formData.value.email, + web_address : formData.value.web, + account_created : new Date().toISOString(), // :TODO: Use moment & explicit ISO string format message_conf_tag : confTag, message_area_tag : areaTag, - term_height : self.client.term.termHeight, - term_width : self.client.term.termWidth, + term_height : self.client.term.termHeight, + term_width : self.client.term.termWidth, - // :TODO: Other defaults - // :TODO: should probably have a place to create defaults/etc. + // :TODO: Other defaults + // :TODO: should probably have a place to create defaults/etc. }; if('*' === config.defaults.theme) { @@ -102,7 +102,7 @@ exports.getModule = class NewUserAppModule extends MenuModule { newUser.properties.theme_id = config.defaults.theme; } - // :TODO: User.create() should validate email uniqueness! + // :TODO: User.create() should validate email uniqueness! newUser.create(formData.value.password, err => { if(err) { self.client.log.info( { error : err, username : formData.value.username }, 'New user creation failed'); @@ -116,12 +116,12 @@ exports.getModule = class NewUserAppModule extends MenuModule { } else { self.client.log.info( { username : formData.value.username, userId : newUser.userId }, 'New user created'); - // Cache SysOp information now - // :TODO: Similar to bbs.js. DRY + // Cache SysOp information now + // :TODO: Similar to bbs.js. DRY if(newUser.isSysOp()) { config.general.sysOp = { - username : formData.value.username, - properties : newUser.properties, + username : formData.value.username, + properties : newUser.properties, }; } @@ -129,7 +129,7 @@ exports.getModule = class NewUserAppModule extends MenuModule { return self.gotoMenu(extraArgs.inactive, cb); } else { // - // If active now, we need to call login() to authenticate + // If active now, we need to call login() to authenticate // return login(self, formData, extraArgs, cb); } diff --git a/core/onelinerz.js b/core/onelinerz.js index d54d7f4f..2a917ad2 100644 --- a/core/onelinerz.js +++ b/core/onelinerz.js @@ -1,56 +1,56 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; const { getModDatabasePath, getTransactionDatabase -} = require('./database.js'); +} = require('./database.js'); -const ViewController = require('./view_controller.js').ViewController; -const theme = require('./theme.js'); -const ansi = require('./ansi_term.js'); -const stringFormat = require('./string_format.js'); +const ViewController = require('./view_controller.js').ViewController; +const theme = require('./theme.js'); +const ansi = require('./ansi_term.js'); +const stringFormat = require('./string_format.js'); -// deps -const sqlite3 = require('sqlite3'); -const async = require('async'); -const _ = require('lodash'); -const moment = require('moment'); +// deps +const sqlite3 = require('sqlite3'); +const async = require('async'); +const _ = require('lodash'); +const moment = require('moment'); /* - Module :TODO: - * Add pipe code support - - override max length & monitor *display* len as user types in order to allow for actual display len with color - * Add preview control: Shows preview with pipe codes resolved - * Add ability to at least alternate formatStrings -- every other + Module :TODO: + * Add pipe code support + - override max length & monitor *display* len as user types in order to allow for actual display len with color + * Add preview control: Shows preview with pipe codes resolved + * Add ability to at least alternate formatStrings -- every other */ exports.moduleInfo = { - name : 'Onelinerz', - desc : 'Standard local onelinerz', - author : 'NuSkooler', - packageName : 'codes.l33t.enigma.onelinerz', + name : 'Onelinerz', + desc : 'Standard local onelinerz', + author : 'NuSkooler', + packageName : 'codes.l33t.enigma.onelinerz', }; const MciViewIds = { - ViewForm : { - Entries : 1, - AddPrompt : 2, + ViewForm : { + Entries : 1, + AddPrompt : 2, }, AddForm : { - NewEntry : 1, - EntryPreview : 2, - AddPrompt : 3, + NewEntry : 1, + EntryPreview : 2, + AddPrompt : 3, } }; const FormIds = { - View : 0, - Add : 1, + View : 0, + Add : 1, }; exports.getModule = class OnelinerzModule extends MenuModule { @@ -66,7 +66,7 @@ exports.getModule = class OnelinerzModule extends MenuModule { addEntry : function(formData, extraArgs, cb) { if(_.isString(formData.value.oneliner) && formData.value.oneliner.length > 0) { - const oneliner = formData.value.oneliner.trim(); // remove any trailing ws + const oneliner = formData.value.oneliner.trim(); // remove any trailing ws self.storeNewOneliner(oneliner, err => { if(err) { @@ -74,18 +74,18 @@ exports.getModule = class OnelinerzModule extends MenuModule { } self.clearAddForm(); - return self.displayViewScreen(true, cb); // true=cls + return self.displayViewScreen(true, cb); // true=cls }); } else { - // empty message - treat as if cancel was hit - return self.displayViewScreen(true, cb); // true=cls + // empty message - treat as if cancel was hit + return self.displayViewScreen(true, cb); // true=cls } }, cancelAdd : function(formData, extraArgs, cb) { self.clearAddForm(); - return self.displayViewScreen(true, cb); // true=cls + return self.displayViewScreen(true, cb); // true=cls } }; } @@ -103,7 +103,7 @@ exports.getModule = class OnelinerzModule extends MenuModule { ], err => { if(err) { - // :TODO: Handle me -- initSequence() should really take a completion callback + // :TODO: Handle me -- initSequence() should really take a completion callback } self.finishedLoading(); } @@ -141,9 +141,9 @@ exports.getModule = class OnelinerzModule extends MenuModule { ); const loadOpts = { - callingMenu : self, - mciMap : artData.mciMap, - formId : FormIds.View, + callingMenu : self, + mciMap : artData.mciMap, + formId : FormIds.View, }; return vc.loadFromMenuConfig(loadOpts, callback); @@ -160,16 +160,16 @@ exports.getModule = class OnelinerzModule extends MenuModule { self.db.each( `SELECT * - FROM ( - SELECT * - FROM onelinerz - ORDER BY timestamp DESC - LIMIT ${limit} - ) - ORDER BY timestamp ASC;`, + FROM ( + SELECT * + FROM onelinerz + ORDER BY timestamp DESC + LIMIT ${limit} + ) + ORDER BY timestamp ASC;`, (err, row) => { if(!err) { - row.timestamp = moment(row.timestamp); // convert -> moment + row.timestamp = moment(row.timestamp); // convert -> moment entries.push(row); } }, @@ -179,15 +179,15 @@ exports.getModule = class OnelinerzModule extends MenuModule { ); }, function populateEntries(entriesView, entries, callback) { - const listFormat = self.menuConfig.config.listFormat || '{username}@{ts}: {oneliner}';// :TODO: should be userName to be consistent - const tsFormat = self.menuConfig.config.timestampFormat || 'ddd h:mma'; + const listFormat = self.menuConfig.config.listFormat || '{username}@{ts}: {oneliner}';// :TODO: should be userName to be consistent + const tsFormat = self.menuConfig.config.timestampFormat || 'ddd h:mma'; entriesView.setItems(entries.map( e => { return stringFormat(listFormat, { - userId : e.user_id, - username : e.user_name, - oneliner : e.oneliner, - ts : e.timestamp.format(tsFormat), + userId : e.user_id, + username : e.user_name, + oneliner : e.oneliner, + ts : e.timestamp.format(tsFormat), } ); })); @@ -197,7 +197,7 @@ exports.getModule = class OnelinerzModule extends MenuModule { }, function finalPrep(callback) { const promptView = self.viewControllers.view.getView(MciViewIds.ViewForm.AddPrompt); - promptView.setFocusItemIndex(1); // default to NO + promptView.setFocusItemIndex(1); // default to NO return callback(null); } ], @@ -235,9 +235,9 @@ exports.getModule = class OnelinerzModule extends MenuModule { ); const loadOpts = { - callingMenu : self, - mciMap : artData.mciMap, - formId : FormIds.Add, + callingMenu : self, + mciMap : artData.mciMap, + formId : FormIds.Add, }; return vc.loadFromMenuConfig(loadOpts, callback); @@ -278,12 +278,12 @@ exports.getModule = class OnelinerzModule extends MenuModule { function createTables(callback) { self.db.run( `CREATE TABLE IF NOT EXISTS onelinerz ( - id INTEGER PRIMARY KEY, - user_id INTEGER_NOT NULL, - user_name VARCHAR NOT NULL, - oneliner VARCHAR NOT NULL, - timestamp DATETIME NOT NULL - );` + id INTEGER PRIMARY KEY, + user_id INTEGER_NOT NULL, + user_name VARCHAR NOT NULL, + oneliner VARCHAR NOT NULL, + timestamp DATETIME NOT NULL + );` , err => { return callback(err); @@ -297,29 +297,29 @@ exports.getModule = class OnelinerzModule extends MenuModule { } storeNewOneliner(oneliner, cb) { - const self = this; - const ts = moment().format('YYYY-MM-DDTHH:mm:ss.SSSZ'); + const self = this; + const ts = moment().format('YYYY-MM-DDTHH:mm:ss.SSSZ'); async.series( [ function addRec(callback) { self.db.run( `INSERT INTO onelinerz (user_id, user_name, oneliner, timestamp) - VALUES (?, ?, ?, ?);`, + VALUES (?, ?, ?, ?);`, [ self.client.user.userId, self.client.user.username, oneliner, ts ], callback ); }, function removeOld(callback) { - // keep 25 max most recent items - remove the older ones + // keep 25 max most recent items - remove the older ones self.db.run( `DELETE FROM onelinerz - WHERE id IN ( - SELECT id - FROM onelinerz - ORDER BY id DESC - LIMIT -1 OFFSET 25 - );`, + WHERE id IN ( + SELECT id + FROM onelinerz + ORDER BY id DESC + LIMIT -1 OFFSET 25 + );`, callback ); } diff --git a/core/plugin_module.js b/core/plugin_module.js index da9410b0..60b878aa 100644 --- a/core/plugin_module.js +++ b/core/plugin_module.js @@ -1,7 +1,7 @@ /* jslint node: true */ 'use strict'; -exports.PluginModule = PluginModule; +exports.PluginModule = PluginModule; function PluginModule(/*options*/) { } diff --git a/core/predefined_mci.js b/core/predefined_mci.js index 584feffa..fe8d4b43 100644 --- a/core/predefined_mci.js +++ b/core/predefined_mci.js @@ -1,24 +1,24 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('./config.js').get; -const Log = require('./logger.js').log; -const getMessageAreaByTag = require('./message_area.js').getMessageAreaByTag; -const getMessageConferenceByTag = require('./message_area.js').getMessageConferenceByTag; -const clientConnections = require('./client_connections.js'); -const StatLog = require('./stat_log.js'); -const FileBaseFilters = require('./file_base_filter.js'); -const formatByteSize = require('./string_util.js').formatByteSize; +// ENiGMA½ +const Config = require('./config.js').get; +const Log = require('./logger.js').log; +const getMessageAreaByTag = require('./message_area.js').getMessageAreaByTag; +const getMessageConferenceByTag = require('./message_area.js').getMessageConferenceByTag; +const clientConnections = require('./client_connections.js'); +const StatLog = require('./stat_log.js'); +const FileBaseFilters = require('./file_base_filter.js'); +const formatByteSize = require('./string_util.js').formatByteSize; -// deps -const packageJson = require('../package.json'); -const os = require('os'); -const _ = require('lodash'); -const moment = require('moment'); +// deps +const packageJson = require('../package.json'); +const os = require('os'); +const _ = require('lodash'); +const moment = require('moment'); -exports.getPredefinedMCIValue = getPredefinedMCIValue; -exports.init = init; +exports.getPredefinedMCIValue = getPredefinedMCIValue; +exports.init = init; function init(cb) { setNextRandomRumor(cb); @@ -39,8 +39,8 @@ function setNextRandomRumor(cb) { function getUserRatio(client, propA, propB) { const a = StatLog.getUserStatNum(client.user, propA); - const b = StatLog.getUserStatNum(client.user, propB); - const ratio = ~~((a / b) * 100); + const b = StatLog.getUserStatNum(client.user, propB); + const ratio = ~~((a / b) * 100); return `${ratio}%`; } @@ -54,72 +54,72 @@ function sysStatAsString(statName, defaultValue) { const PREDEFINED_MCI_GENERATORS = { // - // Board + // Board // - BN : function boardName() { return Config().general.boardName; }, + BN : function boardName() { return Config().general.boardName; }, - // ENiGMA - VL : function versionLabel() { return 'ENiGMA½ v' + packageJson.version; }, - VN : function version() { return packageJson.version; }, + // ENiGMA + VL : function versionLabel() { return 'ENiGMA½ v' + packageJson.version; }, + VN : function version() { return packageJson.version; }, - // +op info - SN : function opUserName() { return StatLog.getSystemStat('sysop_username'); }, - SR : function opRealName() { return StatLog.getSystemStat('sysop_real_name'); }, - SL : function opLocation() { return StatLog.getSystemStat('sysop_location'); }, - SA : function opAffils() { return StatLog.getSystemStat('sysop_affiliation'); }, - SS : function opSex() { return StatLog.getSystemStat('sysop_sex'); }, - SE : function opEmail() { return StatLog.getSystemStat('sysop_email_address'); }, - // :TODO: op age, web, ????? + // +op info + SN : function opUserName() { return StatLog.getSystemStat('sysop_username'); }, + SR : function opRealName() { return StatLog.getSystemStat('sysop_real_name'); }, + SL : function opLocation() { return StatLog.getSystemStat('sysop_location'); }, + SA : function opAffils() { return StatLog.getSystemStat('sysop_affiliation'); }, + SS : function opSex() { return StatLog.getSystemStat('sysop_sex'); }, + SE : function opEmail() { return StatLog.getSystemStat('sysop_email_address'); }, + // :TODO: op age, web, ????? // - // Current user / session + // Current user / session // - UN : function userName(client) { return client.user.username; }, - UI : function userId(client) { return client.user.userId.toString(); }, - UG : function groups(client) { return _.values(client.user.groups).join(', '); }, - UR : function realName(client) { return userStatAsString(client, 'real_name', ''); }, - LO : function location(client) { return userStatAsString(client, 'location', ''); }, - UA : function age(client) { return client.user.getAge().toString(); }, - BD : function birthdate(client) { return moment(client.user.properties.birthdate).format(client.currentTheme.helpers.getDateFormat()); }, // iNiQUiTY - US : function sex(client) { return userStatAsString(client, 'sex', ''); }, - UE : function emailAddres(client) { return userStatAsString(client, 'email_address', ''); }, - UW : function webAddress(client) { return userStatAsString(client, 'web_address', ''); }, - UF : function affils(client) { return userStatAsString(client, 'affiliation', ''); }, - UT : function themeId(client) { return userStatAsString(client, 'theme_id', ''); }, - UC : function loginCount(client) { return userStatAsString(client, 'login_count', 0); }, - ND : function connectedNode(client) { return client.node.toString(); }, - IP : function clientIpAddress(client) { return client.remoteAddress.replace(/^::ffff:/, ''); }, // convert any :ffff: IPv4's to 32bit version - ST : function serverName(client) { return client.session.serverName; }, - FN : function activeFileBaseFilterName(client) { + UN : function userName(client) { return client.user.username; }, + UI : function userId(client) { return client.user.userId.toString(); }, + UG : function groups(client) { return _.values(client.user.groups).join(', '); }, + UR : function realName(client) { return userStatAsString(client, 'real_name', ''); }, + LO : function location(client) { return userStatAsString(client, 'location', ''); }, + UA : function age(client) { return client.user.getAge().toString(); }, + BD : function birthdate(client) { return moment(client.user.properties.birthdate).format(client.currentTheme.helpers.getDateFormat()); }, // iNiQUiTY + US : function sex(client) { return userStatAsString(client, 'sex', ''); }, + UE : function emailAddres(client) { return userStatAsString(client, 'email_address', ''); }, + UW : function webAddress(client) { return userStatAsString(client, 'web_address', ''); }, + UF : function affils(client) { return userStatAsString(client, 'affiliation', ''); }, + UT : function themeId(client) { return userStatAsString(client, 'theme_id', ''); }, + UC : function loginCount(client) { return userStatAsString(client, 'login_count', 0); }, + ND : function connectedNode(client) { return client.node.toString(); }, + IP : function clientIpAddress(client) { return client.remoteAddress.replace(/^::ffff:/, ''); }, // convert any :ffff: IPv4's to 32bit version + ST : function serverName(client) { return client.session.serverName; }, + FN : function activeFileBaseFilterName(client) { const activeFilter = FileBaseFilters.getActiveFilter(client); return activeFilter ? activeFilter.name : ''; }, - DN : function userNumDownloads(client) { return userStatAsString(client, 'dl_total_count', 0); }, // Obv/2 - DK : function userByteDownload(client) { // Obv/2 uses DK=downloaded Kbytes + DN : function userNumDownloads(client) { return userStatAsString(client, 'dl_total_count', 0); }, // Obv/2 + DK : function userByteDownload(client) { // Obv/2 uses DK=downloaded Kbytes const byteSize = StatLog.getUserStatNum(client.user, 'dl_total_bytes'); - return formatByteSize(byteSize, true); // true=withAbbr + return formatByteSize(byteSize, true); // true=withAbbr }, - UP : function userNumUploads(client) { return userStatAsString(client, 'ul_total_count', 0); }, // Obv/2 - UK : function userByteUpload(client) { // Obv/2 uses UK=uploaded Kbytes + UP : function userNumUploads(client) { return userStatAsString(client, 'ul_total_count', 0); }, // Obv/2 + UK : function userByteUpload(client) { // Obv/2 uses UK=uploaded Kbytes const byteSize = StatLog.getUserStatNum(client.user, 'ul_total_bytes'); - return formatByteSize(byteSize, true); // true=withAbbr + return formatByteSize(byteSize, true); // true=withAbbr }, - NR : function userUpDownRatio(client) { // Obv/2 + NR : function userUpDownRatio(client) { // Obv/2 return getUserRatio(client, 'ul_total_count', 'dl_total_count'); }, - KR : function userUpDownByteRatio(client) { // Obv/2 uses KR=upload/download Kbyte ratio + KR : function userUpDownByteRatio(client) { // Obv/2 uses KR=upload/download Kbyte ratio return getUserRatio(client, 'ul_total_bytes', 'dl_total_bytes'); }, - MS : function accountCreatedclient(client) { return moment(client.user.properties.account_created).format(client.currentTheme.helpers.getDateFormat()); }, - PS : function userPostCount(client) { return userStatAsString(client, 'post_count', 0); }, - PC : function userPostCallRatio(client) { return getUserRatio(client, 'post_count', 'login_count'); }, + MS : function accountCreatedclient(client) { return moment(client.user.properties.account_created).format(client.currentTheme.helpers.getDateFormat()); }, + PS : function userPostCount(client) { return userStatAsString(client, 'post_count', 0); }, + PC : function userPostCallRatio(client) { return getUserRatio(client, 'post_count', 'login_count'); }, - MD : function currentMenuDescription(client) { + MD : function currentMenuDescription(client) { return _.has(client, 'currentMenuModule.menuConfig.desc') ? client.currentMenuModule.menuConfig.desc : ''; }, - MA : function messageAreaName(client) { + MA : function messageAreaName(client) { const area = getMessageAreaByTag(client.user.properties.message_area_tag); return area ? area.name : ''; }, @@ -131,102 +131,102 @@ const PREDEFINED_MCI_GENERATORS = { const area = getMessageAreaByTag(client.user.properties.message_area_tag); return area ? area.desc : ''; }, - CM : function messageConfDescription(client) { + CM : function messageConfDescription(client) { const conf = getMessageConferenceByTag(client.user.properties.message_conf_tag); return conf ? conf.desc : ''; }, - SH : function termHeight(client) { return client.term.termHeight.toString(); }, - SW : function termWidth(client) { return client.term.termWidth.toString(); }, + SH : function termHeight(client) { return client.term.termHeight.toString(); }, + SW : function termWidth(client) { return client.term.termWidth.toString(); }, // - // Date/Time + // Date/Time // - // :TODO: change to CD for 'Current Date' - DT : function date(client) { return moment().format(client.currentTheme.helpers.getDateFormat()); }, - CT : function time(client) { return moment().format(client.currentTheme.helpers.getTimeFormat()) ;}, + // :TODO: change to CD for 'Current Date' + DT : function date(client) { return moment().format(client.currentTheme.helpers.getDateFormat()); }, + CT : function time(client) { return moment().format(client.currentTheme.helpers.getTimeFormat()) ;}, // - // OS/System Info + // OS/System Info // - OS : function operatingSystem() { + OS : function operatingSystem() { return { - linux : 'Linux', - darwin : 'Mac OS X', - win32 : 'Windows', - sunos : 'SunOS', - freebsd : 'FreeBSD', + linux : 'Linux', + darwin : 'Mac OS X', + win32 : 'Windows', + sunos : 'SunOS', + freebsd : 'FreeBSD', }[os.platform()] || os.type(); }, - OA : function systemArchitecture() { return os.arch(); }, + OA : function systemArchitecture() { return os.arch(); }, - SC : function systemCpuModel() { + SC : function systemCpuModel() { // - // Clean up CPU strings a bit for better display + // Clean up CPU strings a bit for better display // return os.cpus()[0].model .replace(/\(R\)|\(TM\)|processor|CPU/g, '') .replace(/\s+(?= )/g, ''); }, - // :TODO: MCI for core count, e.g. os.cpus().length + // :TODO: MCI for core count, e.g. os.cpus().length - // :TODO: cpu load average (over N seconds): http://stackoverflow.com/questions/9565912/convert-the-output-of-os-cpus-in-node-js-to-percentage - NV : function nodeVersion() { return process.version; }, + // :TODO: cpu load average (over N seconds): http://stackoverflow.com/questions/9565912/convert-the-output-of-os-cpus-in-node-js-to-percentage + NV : function nodeVersion() { return process.version; }, - AN : function activeNodes() { return clientConnections.getActiveConnections().length.toString(); }, + AN : function activeNodes() { return clientConnections.getActiveConnections().length.toString(); }, - TC : function totalCalls() { return StatLog.getSystemStat('login_count').toLocaleString(); }, + TC : function totalCalls() { return StatLog.getSystemStat('login_count').toLocaleString(); }, - RR : function randomRumor() { - // start the process of picking another random one + RR : function randomRumor() { + // start the process of picking another random one setNextRandomRumor(); return StatLog.getSystemStat('random_rumor'); }, // - // System File Base, Up/Download Info + // System File Base, Up/Download Info // - // :TODO: DD - Today's # of downloads (iNiQUiTY) + // :TODO: DD - Today's # of downloads (iNiQUiTY) // - SD : function systemNumDownloads() { return sysStatAsString('dl_total_count', 0); }, - SO : function systemByteDownload() { + SD : function systemNumDownloads() { return sysStatAsString('dl_total_count', 0); }, + SO : function systemByteDownload() { const byteSize = StatLog.getSystemStatNum('dl_total_bytes'); - return formatByteSize(byteSize, true); // true=withAbbr + return formatByteSize(byteSize, true); // true=withAbbr }, - SU : function systemNumUploads() { return sysStatAsString('ul_total_count', 0); }, - SP : function systemByteUpload() { + SU : function systemNumUploads() { return sysStatAsString('ul_total_count', 0); }, + SP : function systemByteUpload() { const byteSize = StatLog.getSystemStatNum('ul_total_bytes'); - return formatByteSize(byteSize, true); // true=withAbbr + return formatByteSize(byteSize, true); // true=withAbbr }, - TF : function totalFilesOnSystem() { + TF : function totalFilesOnSystem() { const areaStats = StatLog.getSystemStat('file_base_area_stats'); return _.get(areaStats, 'totalFiles', 0).toLocaleString(); }, - TB : function totalBytesOnSystem() { - const areaStats = StatLog.getSystemStat('file_base_area_stats'); - const totalBytes = parseInt(_.get(areaStats, 'totalBytes', 0)); - return formatByteSize(totalBytes, true); // true=withAbbr + TB : function totalBytesOnSystem() { + const areaStats = StatLog.getSystemStat('file_base_area_stats'); + const totalBytes = parseInt(_.get(areaStats, 'totalBytes', 0)); + return formatByteSize(totalBytes, true); // true=withAbbr }, - // :TODO: PT - Messages posted *today* (Obv/2) - // -> Include FTN/etc. - // :TODO: NT - New users today (Obv/2) - // :TODO: CT - Calls *today* (Obv/2) - // :TODO: FT - Files uploaded/added *today* (Obv/2) - // :TODO: DD - Files downloaded *today* (iNiQUiTY) - // :TODO: TP - total message/posts on the system (Obv/2) - // -> Include FTN/etc. - // :TODO: LC - name of last caller to system (Obv/2) - // :TODO: TZ - Average *system* post/call ratio (iNiQUiTY) + // :TODO: PT - Messages posted *today* (Obv/2) + // -> Include FTN/etc. + // :TODO: NT - New users today (Obv/2) + // :TODO: CT - Calls *today* (Obv/2) + // :TODO: FT - Files uploaded/added *today* (Obv/2) + // :TODO: DD - Files downloaded *today* (iNiQUiTY) + // :TODO: TP - total message/posts on the system (Obv/2) + // -> Include FTN/etc. + // :TODO: LC - name of last caller to system (Obv/2) + // :TODO: TZ - Average *system* post/call ratio (iNiQUiTY) // - // Special handling for XY + // Special handling for XY // - XY : function xyHack() { return; /* nothing */ }, + XY : function xyHack() { return; /* nothing */ }, }; function getPredefinedMCIValue(client, code) { diff --git a/core/rumorz.js b/core/rumorz.js index d51e33e8..153a74ee 100644 --- a/core/rumorz.js +++ b/core/rumorz.js @@ -1,42 +1,42 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const theme = require('./theme.js'); -const resetScreen = require('./ansi_term.js').resetScreen; -const StatLog = require('./stat_log.js'); -const renderStringLength = require('./string_util.js').renderStringLength; -const stringFormat = require('./string_format.js'); +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const theme = require('./theme.js'); +const resetScreen = require('./ansi_term.js').resetScreen; +const StatLog = require('./stat_log.js'); +const renderStringLength = require('./string_util.js').renderStringLength; +const stringFormat = require('./string_format.js'); -// deps -const async = require('async'); -const _ = require('lodash'); +// deps +const async = require('async'); +const _ = require('lodash'); exports.moduleInfo = { - name : 'Rumorz', - desc : 'Standard local rumorz', - author : 'NuSkooler', - packageName : 'codes.l33t.enigma.rumorz', + name : 'Rumorz', + desc : 'Standard local rumorz', + author : 'NuSkooler', + packageName : 'codes.l33t.enigma.rumorz', }; -const STATLOG_KEY_RUMORZ = 'system_rumorz'; +const STATLOG_KEY_RUMORZ = 'system_rumorz'; const FormIds = { - View : 0, - Add : 1, + View : 0, + Add : 1, }; const MciCodeIds = { - ViewForm : { - Entries : 1, - AddPrompt : 2, + ViewForm : { + Entries : 1, + AddPrompt : 2, }, AddForm : { - NewEntry : 1, - EntryPreview : 2, - AddPrompt : 3, + NewEntry : 1, + EntryPreview : 2, + AddPrompt : 3, } }; @@ -51,21 +51,21 @@ exports.getModule = class RumorzModule extends MenuModule { addEntry : (formData, extraArgs, cb) => { if(_.isString(formData.value.rumor) && renderStringLength(formData.value.rumor) > 0) { - const rumor = formData.value.rumor.trim(); // remove any trailing ws + const rumor = formData.value.rumor.trim(); // remove any trailing ws StatLog.appendSystemLogEntry(STATLOG_KEY_RUMORZ, rumor, StatLog.KeepDays.Forever, StatLog.KeepType.Forever, () => { this.clearAddForm(); - return this.displayViewScreen(true, cb); // true=cls + return this.displayViewScreen(true, cb); // true=cls }); } else { - // empty message - treat as if cancel was hit - return this.displayViewScreen(true, cb); // true=cls + // empty message - treat as if cancel was hit + return this.displayViewScreen(true, cb); // true=cls } }, cancelAdd : (formData, extraArgs, cb) => { this.clearAddForm(); - return this.displayViewScreen(true, cb); // true=cls + return this.displayViewScreen(true, cb); // true=cls } }; } @@ -73,12 +73,12 @@ exports.getModule = class RumorzModule extends MenuModule { get config() { return this.menuConfig.config; } clearAddForm() { - const newEntryView = this.viewControllers.add.getView(MciCodeIds.AddForm.NewEntry); - const previewView = this.viewControllers.add.getView(MciCodeIds.AddForm.EntryPreview); + const newEntryView = this.viewControllers.add.getView(MciCodeIds.AddForm.NewEntry); + const previewView = this.viewControllers.add.getView(MciCodeIds.AddForm.EntryPreview); newEntryView.setText(''); - // preview is optional + // preview is optional if(previewView) { previewView.setText(''); } @@ -98,7 +98,7 @@ exports.getModule = class RumorzModule extends MenuModule { ], err => { if(err) { - // :TODO: Handle me -- initSequence() should really take a completion callback + // :TODO: Handle me -- initSequence() should really take a completion callback } self.finishedLoading(); } @@ -135,9 +135,9 @@ exports.getModule = class RumorzModule extends MenuModule { ); const loadOpts = { - callingMenu : self, - mciMap : artData.mciMap, - formId : FormIds.View, + callingMenu : self, + mciMap : artData.mciMap, + formId : FormIds.View, }; return vc.loadFromMenuConfig(loadOpts, callback); @@ -155,9 +155,9 @@ exports.getModule = class RumorzModule extends MenuModule { }); }, function populateEntries(entriesView, entries, callback) { - const config = self.config; - const listFormat = config.listFormat || '{rumor}'; - const focusListFormat = config.focusListFormat || listFormat; + const config = self.config; + const listFormat = config.listFormat || '{rumor}'; + const focusListFormat = config.focusListFormat || listFormat; entriesView.setItems(entries.map( e => stringFormat(listFormat, { rumor : e.log_value } ) ) ); entriesView.setFocusItems(entries.map(e => stringFormat(focusListFormat, { rumor : e.log_value } ) ) ); @@ -167,7 +167,7 @@ exports.getModule = class RumorzModule extends MenuModule { }, function finalPrep(callback) { const promptView = self.viewControllers.view.getView(MciCodeIds.ViewForm.AddPrompt); - promptView.setFocusItemIndex(1); // default to NO + promptView.setFocusItemIndex(1); // default to NO return callback(null); } ], @@ -205,9 +205,9 @@ exports.getModule = class RumorzModule extends MenuModule { ); const loadOpts = { - callingMenu : self, - mciMap : artData.mciMap, - formId : FormIds.Add, + callingMenu : self, + mciMap : artData.mciMap, + formId : FormIds.Add, }; return vc.loadFromMenuConfig(loadOpts, callback); @@ -219,8 +219,8 @@ exports.getModule = class RumorzModule extends MenuModule { } }, function initPreviewUpdates(callback) { - const previewView = self.viewControllers.add.getView(MciCodeIds.AddForm.EntryPreview); - const entryView = self.viewControllers.add.getView(MciCodeIds.AddForm.NewEntry); + const previewView = self.viewControllers.add.getView(MciCodeIds.AddForm.EntryPreview); + const entryView = self.viewControllers.add.getView(MciCodeIds.AddForm.NewEntry); if(previewView) { let timerId; entryView.on('key press', () => { diff --git a/core/sauce.js b/core/sauce.js index 29176d8d..6291c16e 100644 --- a/core/sauce.js +++ b/core/sauce.js @@ -1,28 +1,28 @@ /* jslint node: true */ 'use strict'; -const Errors = require('./enig_error.js').Errors; +const Errors = require('./enig_error.js').Errors; -// deps -const iconv = require('iconv-lite'); -const { Parser } = require('binary-parser'); +// deps +const iconv = require('iconv-lite'); +const { Parser } = require('binary-parser'); -exports.readSAUCE = readSAUCE; +exports.readSAUCE = readSAUCE; -const SAUCE_SIZE = 128; -const SAUCE_ID = Buffer.from([0x53, 0x41, 0x55, 0x43, 0x45]); // 'SAUCE' +const SAUCE_SIZE = 128; +const SAUCE_ID = Buffer.from([0x53, 0x41, 0x55, 0x43, 0x45]); // 'SAUCE' -// :TODO read comments -//const COMNT_ID = Buffer.from([0x43, 0x4f, 0x4d, 0x4e, 0x54]); // 'COMNT' +// :TODO read comments +//const COMNT_ID = Buffer.from([0x43, 0x4f, 0x4d, 0x4e, 0x54]); // 'COMNT' -exports.SAUCE_SIZE = SAUCE_SIZE; -// :TODO: SAUCE should be a class -// - with getFontName() -// - ...other methods +exports.SAUCE_SIZE = SAUCE_SIZE; +// :TODO: SAUCE should be a class +// - with getFontName() +// - ...other methods // -// See -// http://www.acid.org/info/sauce/sauce.htm +// See +// http://www.acid.org/info/sauce/sauce.htm // const SAUCE_VALID_DATA_TYPES = [0, 1, 2, 3, 4, 5, 6, 7, 8 ]; @@ -49,8 +49,8 @@ function readSAUCE(data, cb) { .uint16le('tinfo4') .int8('numComments') .int8('flags') - // :TODO: does this need to be optional? - .buffer('tinfos', { length: 22 } ) // SAUCE 00.5 + // :TODO: does this need to be optional? + .buffer('tinfos', { length: 22 } ) // SAUCE 00.5 .parse(data.slice(data.length - SAUCE_SIZE)); } catch(e) { return cb(Errors.Invalid('Invalid SAUCE record')); @@ -72,22 +72,22 @@ function readSAUCE(data, cb) { } const sauce = { - id : iconv.decode(sauceRec.id, 'cp437'), - version : iconv.decode(sauceRec.version, 'cp437').trim(), - title : iconv.decode(sauceRec.title, 'cp437').trim(), - author : iconv.decode(sauceRec.author, 'cp437').trim(), - group : iconv.decode(sauceRec.group, 'cp437').trim(), - date : iconv.decode(sauceRec.date, 'cp437').trim(), - fileSize : sauceRec.fileSize, - dataType : sauceRec.dataType, - fileType : sauceRec.fileType, - tinfo1 : sauceRec.tinfo1, - tinfo2 : sauceRec.tinfo2, - tinfo3 : sauceRec.tinfo3, - tinfo4 : sauceRec.tinfo4, - numComments : sauceRec.numComments, - flags : sauceRec.flags, - tinfos : sauceRec.tinfos, + id : iconv.decode(sauceRec.id, 'cp437'), + version : iconv.decode(sauceRec.version, 'cp437').trim(), + title : iconv.decode(sauceRec.title, 'cp437').trim(), + author : iconv.decode(sauceRec.author, 'cp437').trim(), + group : iconv.decode(sauceRec.group, 'cp437').trim(), + date : iconv.decode(sauceRec.date, 'cp437').trim(), + fileSize : sauceRec.fileSize, + dataType : sauceRec.dataType, + fileType : sauceRec.fileType, + tinfo1 : sauceRec.tinfo1, + tinfo2 : sauceRec.tinfo2, + tinfo3 : sauceRec.tinfo3, + tinfo4 : sauceRec.tinfo4, + numComments : sauceRec.numComments, + flags : sauceRec.flags, + tinfos : sauceRec.tinfos, }; const dt = SAUCE_DATA_TYPES[sauce.dataType]; @@ -98,51 +98,51 @@ function readSAUCE(data, cb) { return cb(null, sauce); } -// :TODO: These need completed: +// :TODO: These need completed: const SAUCE_DATA_TYPES = { - 0 : { name : 'None' }, - 1 : { name : 'Character', parser : parseCharacterSAUCE }, - 2 : 'Bitmap', - 3 : 'Vector', - 4 : 'Audio', - 5 : 'BinaryText', - 6 : 'XBin', - 7 : 'Archive', - 8 : 'Executable', + 0 : { name : 'None' }, + 1 : { name : 'Character', parser : parseCharacterSAUCE }, + 2 : 'Bitmap', + 3 : 'Vector', + 4 : 'Audio', + 5 : 'BinaryText', + 6 : 'XBin', + 7 : 'Archive', + 8 : 'Executable', }; const SAUCE_CHARACTER_FILE_TYPES = { - 0 : 'ASCII', - 1 : 'ANSi', - 2 : 'ANSiMation', - 3 : 'RIP script', - 4 : 'PCBoard', - 5 : 'Avatar', - 6 : 'HTML', - 7 : 'Source', - 8 : 'TundraDraw', + 0 : 'ASCII', + 1 : 'ANSi', + 2 : 'ANSiMation', + 3 : 'RIP script', + 4 : 'PCBoard', + 5 : 'Avatar', + 6 : 'HTML', + 7 : 'Source', + 8 : 'TundraDraw', }; // -// Map of SAUCE font -> encoding hint +// Map of SAUCE font -> encoding hint // -// Note that this is the same mapping that x84 uses. Be compatible! +// Note that this is the same mapping that x84 uses. Be compatible! // const SAUCE_FONT_TO_ENCODING_HINT = { - 'Amiga MicroKnight' : 'amiga', - 'Amiga MicroKnight+' : 'amiga', - 'Amiga mOsOul' : 'amiga', - 'Amiga P0T-NOoDLE' : 'amiga', - 'Amiga Topaz 1' : 'amiga', - 'Amiga Topaz 1+' : 'amiga', - 'Amiga Topaz 2' : 'amiga', - 'Amiga Topaz 2+' : 'amiga', - 'Atari ATASCII' : 'atari', - 'IBM EGA43' : 'cp437', - 'IBM EGA' : 'cp437', - 'IBM VGA25G' : 'cp437', - 'IBM VGA50' : 'cp437', - 'IBM VGA' : 'cp437', + 'Amiga MicroKnight' : 'amiga', + 'Amiga MicroKnight+' : 'amiga', + 'Amiga mOsOul' : 'amiga', + 'Amiga P0T-NOoDLE' : 'amiga', + 'Amiga Topaz 1' : 'amiga', + 'Amiga Topaz 1+' : 'amiga', + 'Amiga Topaz 2' : 'amiga', + 'Amiga Topaz 2+' : 'amiga', + 'Atari ATASCII' : 'atari', + 'IBM EGA43' : 'cp437', + 'IBM EGA' : 'cp437', + 'IBM VGA25G' : 'cp437', + 'IBM VGA50' : 'cp437', + 'IBM VGA' : 'cp437', }; [ @@ -150,20 +150,20 @@ const SAUCE_FONT_TO_ENCODING_HINT = { '860', '861', '862', '863', '864', '865', '866', '869', '872' ].forEach( page => { const codec = 'cp' + page; - SAUCE_FONT_TO_ENCODING_HINT['IBM EGA43 ' + page] = codec; - SAUCE_FONT_TO_ENCODING_HINT['IBM EGA ' + page] = codec; - SAUCE_FONT_TO_ENCODING_HINT['IBM VGA25g ' + page] = codec; - SAUCE_FONT_TO_ENCODING_HINT['IBM VGA50 ' + page] = codec; - SAUCE_FONT_TO_ENCODING_HINT['IBM VGA ' + page] = codec; + SAUCE_FONT_TO_ENCODING_HINT['IBM EGA43 ' + page] = codec; + SAUCE_FONT_TO_ENCODING_HINT['IBM EGA ' + page] = codec; + SAUCE_FONT_TO_ENCODING_HINT['IBM VGA25g ' + page] = codec; + SAUCE_FONT_TO_ENCODING_HINT['IBM VGA50 ' + page] = codec; + SAUCE_FONT_TO_ENCODING_HINT['IBM VGA ' + page] = codec; }); function parseCharacterSAUCE(sauce) { const result = {}; - result.fileType = SAUCE_CHARACTER_FILE_TYPES[sauce.fileType] || 'Unknown'; + result.fileType = SAUCE_CHARACTER_FILE_TYPES[sauce.fileType] || 'Unknown'; if(sauce.fileType === 0 || sauce.fileType === 1 || sauce.fileType === 2) { - // convience: create ansiFlags + // convience: create ansiFlags sauce.ansiFlags = sauce.flags; let i = 0; diff --git a/core/scanner_tossers/ftn_bso.js b/core/scanner_tossers/ftn_bso.js index 98fece9b..2e08b6e2 100644 --- a/core/scanner_tossers/ftn_bso.js +++ b/core/scanner_tossers/ftn_bso.js @@ -1,57 +1,57 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MessageScanTossModule = require('../msg_scan_toss_module.js').MessageScanTossModule; -const Config = require('../config.js').get; -const ftnMailPacket = require('../ftn_mail_packet.js'); -const ftnUtil = require('../ftn_util.js'); -const Address = require('../ftn_address.js'); -const Log = require('../logger.js').log; -const ArchiveUtil = require('../archive_util.js'); -const msgDb = require('../database.js').dbs.message; -const Message = require('../message.js'); -const TicFileInfo = require('../tic_file_info.js'); -const Errors = require('../enig_error.js').Errors; -const FileEntry = require('../file_entry.js'); -const scanFile = require('../file_base_area.js').scanFile; -const getFileAreaByTag = require('../file_base_area.js').getFileAreaByTag; -const getDescFromFileName = require('../file_base_area.js').getDescFromFileName; -const copyFileWithCollisionHandling = require('../file_util.js').copyFileWithCollisionHandling; -const getAreaStorageDirectoryByTag = require('../file_base_area.js').getAreaStorageDirectoryByTag; -const isValidStorageTag = require('../file_base_area.js').isValidStorageTag; -const User = require('../user.js'); +// ENiGMA½ +const MessageScanTossModule = require('../msg_scan_toss_module.js').MessageScanTossModule; +const Config = require('../config.js').get; +const ftnMailPacket = require('../ftn_mail_packet.js'); +const ftnUtil = require('../ftn_util.js'); +const Address = require('../ftn_address.js'); +const Log = require('../logger.js').log; +const ArchiveUtil = require('../archive_util.js'); +const msgDb = require('../database.js').dbs.message; +const Message = require('../message.js'); +const TicFileInfo = require('../tic_file_info.js'); +const Errors = require('../enig_error.js').Errors; +const FileEntry = require('../file_entry.js'); +const scanFile = require('../file_base_area.js').scanFile; +const getFileAreaByTag = require('../file_base_area.js').getFileAreaByTag; +const getDescFromFileName = require('../file_base_area.js').getDescFromFileName; +const copyFileWithCollisionHandling = require('../file_util.js').copyFileWithCollisionHandling; +const getAreaStorageDirectoryByTag = require('../file_base_area.js').getAreaStorageDirectoryByTag; +const isValidStorageTag = require('../file_base_area.js').isValidStorageTag; +const User = require('../user.js'); -// deps -const moment = require('moment'); -const _ = require('lodash'); -const paths = require('path'); -const async = require('async'); -const fs = require('graceful-fs'); -const later = require('later'); -const temptmp = require('temptmp').createTrackedSession('ftn_bso'); -const assert = require('assert'); -const sane = require('sane'); -const fse = require('fs-extra'); -const iconv = require('iconv-lite'); -const uuidV4 = require('uuid/v4'); +// deps +const moment = require('moment'); +const _ = require('lodash'); +const paths = require('path'); +const async = require('async'); +const fs = require('graceful-fs'); +const later = require('later'); +const temptmp = require('temptmp').createTrackedSession('ftn_bso'); +const assert = require('assert'); +const sane = require('sane'); +const fse = require('fs-extra'); +const iconv = require('iconv-lite'); +const uuidV4 = require('uuid/v4'); exports.moduleInfo = { - name : 'FTN BSO', - desc : 'BSO style message scanner/tosser for FTN networks', - author : 'NuSkooler', + name : 'FTN BSO', + desc : 'BSO style message scanner/tosser for FTN networks', + author : 'NuSkooler', }; /* - :TODO: - * Support (approx) max bundle size - * Validate packet passwords!!!! - => secure vs insecure landing areas + :TODO: + * Support (approx) max bundle size + * Validate packet passwords!!!! + => secure vs insecure landing areas */ exports.getModule = FTNMessageScanTossModule; -const SCHEDULE_REGEXP = /(?:^|or )?(@watch:|@immediate)([^\0]+)?$/; +const SCHEDULE_REGEXP = /(?:^|or )?(@watch:|@immediate)([^\0]+)?$/; function FTNMessageScanTossModule() { MessageScanTossModule.call(this); @@ -82,7 +82,7 @@ function FTNMessageScanTossModule() { return config.messageNetworks.ftn.networks[networkName].defaultZone; } - // non-explicit: default to local address zone + // non-explicit: default to local address zone const networkLocalAddress = config.messageNetworks.ftn.networks[networkName].localAddress; if(networkLocalAddress) { const addr = Address.fromString(networkLocalAddress); @@ -91,11 +91,11 @@ function FTNMessageScanTossModule() { }; /* - this.isDefaultDomainZone = function(networkName, address) { - const defaultNetworkName = this.getDefaultNetworkName(); - return(networkName === defaultNetworkName && address.zone === this.moduleConfig.defaultZone); - }; - */ + this.isDefaultDomainZone = function(networkName, address) { + const defaultNetworkName = this.getDefaultNetworkName(); + return(networkName === defaultNetworkName && address.zone === this.moduleConfig.defaultZone); + }; + */ this.getNetworkNameByAddress = function(remoteAddress) { return _.findKey(Config().messageNetworks.ftn.networks, network => { @@ -112,7 +112,7 @@ function FTNMessageScanTossModule() { }; this.getLocalAreaTagByFtnAreaTag = function(ftnAreaTag) { - ftnAreaTag = ftnAreaTag.toUpperCase(); // always compare upper + ftnAreaTag = ftnAreaTag.toUpperCase(); // always compare upper return _.findKey(Config().messageNetworks.ftn.areas, areaConf => { return areaConf.tag.toUpperCase() === ftnAreaTag; }); @@ -123,41 +123,41 @@ function FTNMessageScanTossModule() { }; /* - this.getSeenByAddresses = function(messageSeenBy) { - if(!_.isArray(messageSeenBy)) { - messageSeenBy = [ messageSeenBy ]; - } + this.getSeenByAddresses = function(messageSeenBy) { + if(!_.isArray(messageSeenBy)) { + messageSeenBy = [ messageSeenBy ]; + } - let seenByAddrs = []; - messageSeenBy.forEach(sb => { - seenByAddrs = seenByAddrs.concat(ftnUtil.parseAbbreviatedNetNodeList(sb)); - }); - return seenByAddrs; - }; - */ + let seenByAddrs = []; + messageSeenBy.forEach(sb => { + seenByAddrs = seenByAddrs.concat(ftnUtil.parseAbbreviatedNetNodeList(sb)); + }); + return seenByAddrs; + }; + */ this.messageHasValidMSGID = function(msg) { return _.isString(msg.meta.FtnKludge.MSGID) && msg.meta.FtnKludge.MSGID.length > 0; }; /* - this.getOutgoingEchoMailPacketDir = function(networkName, destAddress) { - let dir = this.moduleConfig.paths.outbound; - if(!this.isDefaultDomainZone(networkName, destAddress)) { - const hexZone = `000${destAddress.zone.toString(16)}`.substr(-3); - dir = paths.join(dir, `${networkName.toLowerCase()}.${hexZone}`); - } - return dir; - }; - */ + this.getOutgoingEchoMailPacketDir = function(networkName, destAddress) { + let dir = this.moduleConfig.paths.outbound; + if(!this.isDefaultDomainZone(networkName, destAddress)) { + const hexZone = `000${destAddress.zone.toString(16)}`.substr(-3); + dir = paths.join(dir, `${networkName.toLowerCase()}.${hexZone}`); + } + return dir; + }; + */ this.getOutgoingEchoMailPacketDir = function(networkName, destAddress) { networkName = networkName.toLowerCase(); let dir = this.moduleConfig.paths.outbound; - const defaultNetworkName = this.getDefaultNetworkName(); - const defaultZone = this.getDefaultZone(networkName); + const defaultNetworkName = this.getDefaultNetworkName(); + const defaultZone = this.getDefaultZone(networkName); let zoneExt; if(defaultZone !== destAddress.zone) { @@ -177,24 +177,24 @@ function FTNMessageScanTossModule() { this.getOutgoingPacketFileName = function(basePath, messageId, isTemp, fileCase) { // - // Generating an outgoing packet file name comes with a few issues: - // * We must use DOS 8.3 filenames due to legacy systems that receive - // the packet not understanding LFNs - // * We need uniqueness; This is especially important with packets that - // end up in bundles and on the receiving/remote system where conflicts - // with other systems could also occur + // Generating an outgoing packet file name comes with a few issues: + // * We must use DOS 8.3 filenames due to legacy systems that receive + // the packet not understanding LFNs + // * We need uniqueness; This is especially important with packets that + // end up in bundles and on the receiving/remote system where conflicts + // with other systems could also occur // - // There are a lot of systems in use here for the name: - // * HEX CRC16/32 of data - // * HEX UNIX timestamp - // * Mystic at least at one point, used Hex8(day of month + seconds past midnight + hundredths of second) - // See https://groups.google.com/forum/#!searchin/alt.bbs.mystic/netmail$20filename/alt.bbs.mystic/m1xLnY8i1pU/YnG2excdl6MJ - // * SBBSEcho uses DDHHMMSS - see https://github.com/ftnapps/pkg-sbbs/blob/master/docs/fidonet.txt - // * We already have a system for 8-character serial number gernation that is - // used for e.g. in FTS-0009.001 MSGIDs... let's use that! + // There are a lot of systems in use here for the name: + // * HEX CRC16/32 of data + // * HEX UNIX timestamp + // * Mystic at least at one point, used Hex8(day of month + seconds past midnight + hundredths of second) + // See https://groups.google.com/forum/#!searchin/alt.bbs.mystic/netmail$20filename/alt.bbs.mystic/m1xLnY8i1pU/YnG2excdl6MJ + // * SBBSEcho uses DDHHMMSS - see https://github.com/ftnapps/pkg-sbbs/blob/master/docs/fidonet.txt + // * We already have a system for 8-character serial number gernation that is + // used for e.g. in FTS-0009.001 MSGIDs... let's use that! // - const name = ftnUtil.getMessageSerialNumber(messageId); - const ext = (true === isTemp) ? 'pk_' : 'pkt'; + const name = ftnUtil.getMessageSerialNumber(messageId); + const ext = (true === isTemp) ? 'pk_' : 'pkt'; let fileName = `${name}.${ext}`; if('upper' === fileCase) { @@ -208,11 +208,11 @@ function FTNMessageScanTossModule() { let ext; switch(flowType) { - case 'mail' : ext = `${exportType.toLowerCase()[0]}ut`; break; - case 'ref' : ext = `${exportType.toLowerCase()[0]}lo`; break; - case 'busy' : ext = 'bsy'; break; - case 'request' : ext = 'req'; break; - case 'requests' : ext = 'hrq'; break; + case 'mail' : ext = `${exportType.toLowerCase()[0]}ut`; break; + case 'ref' : ext = `${exportType.toLowerCase()[0]}lo`; break; + case 'busy' : ext = 'bsy'; break; + case 'request' : ext = 'req'; break; + case 'requests' : ext = 'hrq'; break; } if('upper' === fileCase) { @@ -224,9 +224,9 @@ function FTNMessageScanTossModule() { this.getOutgoingFlowFileName = function(basePath, destAddress, flowType, exportType, fileCase) { // - // Refs - // * http://ftsc.org/docs/fts-5005.003 - // * http://wiki.synchro.net/ref:fidonet_files#flow_files + // Refs + // * http://ftsc.org/docs/fts-5005.003 + // * http://wiki.synchro.net/ref:fidonet_files#flow_files // let controlFileBaseName; let pointDir; @@ -238,30 +238,30 @@ function FTNMessageScanTossModule() { fileCase ); - const netComponent = `0000${destAddress.net.toString(16)}`.substr(-4); - const nodeComponent = `0000${destAddress.node.toString(16)}`.substr(-4); + const netComponent = `0000${destAddress.net.toString(16)}`.substr(-4); + const nodeComponent = `0000${destAddress.node.toString(16)}`.substr(-4); if(destAddress.point) { - // point's go in an extra subdir, e.g. outbound/NNNNnnnn.pnt/00000001.pnt (for a point of 1) - pointDir = `${netComponent}${nodeComponent}.pnt`; + // point's go in an extra subdir, e.g. outbound/NNNNnnnn.pnt/00000001.pnt (for a point of 1) + pointDir = `${netComponent}${nodeComponent}.pnt`; controlFileBaseName = `00000000${destAddress.point.toString(16)}`.substr(-8); } else { pointDir = ''; // - // Use |destAddress| nnnnNNNN.??? where nnnn is dest net and NNNN is dest - // node. This seems to match what Mystic does + // Use |destAddress| nnnnNNNN.??? where nnnn is dest net and NNNN is dest + // node. This seems to match what Mystic does // controlFileBaseName = `${netComponent}${nodeComponent}`; } // - // From FTS-5005.003: "Lower case filenames are prefered if supported by the file system." - // ...but we let the user override. + // From FTS-5005.003: "Lower case filenames are prefered if supported by the file system." + // ...but we let the user override. // if('upper' === fileCase) { - controlFileBaseName = controlFileBaseName.toUpperCase(); - pointDir = pointDir.toUpperCase(); + controlFileBaseName = controlFileBaseName.toUpperCase(); + pointDir = pointDir.toUpperCase(); } return paths.join(basePath, pointDir, `${controlFileBaseName}.${ext}`); @@ -269,12 +269,12 @@ function FTNMessageScanTossModule() { this.flowFileAppendRefs = function(filePath, fileRefs, directive, cb) { // - // We have to ensure the *directory* of |filePath| exists here esp. - // for cases such as point destinations where a subdir may be - // present in the path that doesn't yet exist. + // We have to ensure the *directory* of |filePath| exists here esp. + // for cases such as point destinations where a subdir may be + // present in the path that doesn't yet exist. // const flowFileDir = paths.dirname(filePath); - fse.mkdirs(flowFileDir, () => { // note not checking err; let's try appendFile + fse.mkdirs(flowFileDir, () => { // note not checking err; let's try appendFile const appendLines = fileRefs.reduce( (content, ref) => { return content + `${directive}${ref}\n`; }, ''); @@ -287,14 +287,14 @@ function FTNMessageScanTossModule() { this.getOutgoingBundleFileName = function(basePath, sourceAddress, destAddress, cb) { // - // Base filename is constructed as such: - // * If this |destAddress| is *not* a point address, we use NNNNnnnn where - // NNNN is 0 padded hex of dest net - source net and and nnnn is 0 padded - // hex of dest node - source node. - // * If |destAddress| is a point, NNNN becomes 0000 and nnnn becomes 'p' + - // 3 digit 0 padded hex point + // Base filename is constructed as such: + // * If this |destAddress| is *not* a point address, we use NNNNnnnn where + // NNNN is 0 padded hex of dest net - source net and and nnnn is 0 padded + // hex of dest node - source node. + // * If |destAddress| is a point, NNNN becomes 0000 and nnnn becomes 'p' + + // 3 digit 0 padded hex point // - // Extension is dd? where dd is Su...Mo and ? is 0...Z as collisions arise + // Extension is dd? where dd is Su...Mo and ? is 0...Z as collisions arise // let basename; if(destAddress.point) { @@ -302,13 +302,13 @@ function FTNMessageScanTossModule() { basename = `0000p${pointHex}`; } else { basename = - `0000${Math.abs(sourceAddress.net - destAddress.net).toString(16)}`.substr(-4) + - `0000${Math.abs(sourceAddress.node - destAddress.node).toString(16)}`.substr(-4); + `0000${Math.abs(sourceAddress.net - destAddress.net).toString(16)}`.substr(-4) + + `0000${Math.abs(sourceAddress.node - destAddress.node).toString(16)}`.substr(-4); } // - // We need to now find the first entry that does not exist starting - // with dd0 to ddz + // We need to now find the first entry that does not exist starting + // with dd0 to ddz // const EXT_SUFFIXES = '0123456789abcdefghijklmnopqrstuvwxyz'.split(''); let fileName = `${basename}.${moment().format('dd').toLowerCase()}`; @@ -328,63 +328,63 @@ function FTNMessageScanTossModule() { this.prepareMessage = function(message, options) { // - // Set various FTN kludges/etc. + // Set various FTN kludges/etc. // - const localAddress = new Address(options.network.localAddress); // ensure we have an Address obj not a string version + const localAddress = new Address(options.network.localAddress); // ensure we have an Address obj not a string version - // :TODO: create Address.toMeta() / similar + // :TODO: create Address.toMeta() / similar message.meta.FtnProperty = message.meta.FtnProperty || {}; message.meta.FtnKludge = message.meta.FtnKludge || {}; - message.meta.FtnProperty.ftn_orig_node = localAddress.node; - message.meta.FtnProperty.ftn_orig_network = localAddress.net; - message.meta.FtnProperty.ftn_cost = 0; - message.meta.FtnProperty.ftn_msg_orig_node = localAddress.node; - message.meta.FtnProperty.ftn_msg_orig_net = localAddress.net; + message.meta.FtnProperty.ftn_orig_node = localAddress.node; + message.meta.FtnProperty.ftn_orig_network = localAddress.net; + message.meta.FtnProperty.ftn_cost = 0; + message.meta.FtnProperty.ftn_msg_orig_node = localAddress.node; + message.meta.FtnProperty.ftn_msg_orig_net = localAddress.net; const destAddress = options.routeAddress || options.destAddress; - message.meta.FtnProperty.ftn_dest_node = destAddress.node; - message.meta.FtnProperty.ftn_dest_network = destAddress.net; + message.meta.FtnProperty.ftn_dest_node = destAddress.node; + message.meta.FtnProperty.ftn_dest_network = destAddress.net; if(destAddress.zone) { - message.meta.FtnProperty.ftn_dest_zone = destAddress.zone; + message.meta.FtnProperty.ftn_dest_zone = destAddress.zone; } if(destAddress.point) { - message.meta.FtnProperty.ftn_dest_point = destAddress.point; + message.meta.FtnProperty.ftn_dest_point = destAddress.point; } - // tear line and origin can both go in EchoMail & NetMail - message.meta.FtnProperty.ftn_tear_line = ftnUtil.getTearLine(); - message.meta.FtnProperty.ftn_origin = ftnUtil.getOrigin(localAddress); + // tear line and origin can both go in EchoMail & NetMail + message.meta.FtnProperty.ftn_tear_line = ftnUtil.getTearLine(); + message.meta.FtnProperty.ftn_origin = ftnUtil.getOrigin(localAddress); - let ftnAttribute = ftnMailPacket.Packet.Attribute.Local; // message from our system + let ftnAttribute = ftnMailPacket.Packet.Attribute.Local; // message from our system const config = Config(); if(self.isNetMailMessage(message)) { // - // Set route and message destination properties -- they may differ + // Set route and message destination properties -- they may differ // - message.meta.FtnProperty.ftn_msg_dest_node = options.destAddress.node; - message.meta.FtnProperty.ftn_msg_dest_net = options.destAddress.net; + message.meta.FtnProperty.ftn_msg_dest_node = options.destAddress.node; + message.meta.FtnProperty.ftn_msg_dest_net = options.destAddress.net; ftnAttribute |= ftnMailPacket.Packet.Attribute.Private; // - // NetMail messages need a FRL-1005.001 "Via" line - // http://ftsc.org/docs/frl-1005.001 + // NetMail messages need a FRL-1005.001 "Via" line + // http://ftsc.org/docs/frl-1005.001 // - // :TODO: We need to do this when FORWARDING NetMail + // :TODO: We need to do this when FORWARDING NetMail /* - if(_.isString(message.meta.FtnKludge.Via)) { - message.meta.FtnKludge.Via = [ message.meta.FtnKludge.Via ]; - } - message.meta.FtnKludge.Via = message.meta.FtnKludge.Via || []; - message.meta.FtnKludge.Via.push(ftnUtil.getVia(options.network.localAddress)); - */ + if(_.isString(message.meta.FtnKludge.Via)) { + message.meta.FtnKludge.Via = [ message.meta.FtnKludge.Via ]; + } + message.meta.FtnKludge.Via = message.meta.FtnKludge.Via || []; + message.meta.FtnKludge.Via.push(ftnUtil.getVia(options.network.localAddress)); + */ // - // We need to set INTL, and possibly FMPT and/or TOPT - // See http://retro.fidoweb.ru/docs/index=ftsc&doc=FTS-4001&enc=mac + // We need to set INTL, and possibly FMPT and/or TOPT + // See http://retro.fidoweb.ru/docs/index=ftsc&doc=FTS-4001&enc=mac // message.meta.FtnKludge.INTL = ftnUtil.getIntl(options.destAddress, localAddress); @@ -397,30 +397,30 @@ function FTNMessageScanTossModule() { } } else { // - // Set appropriate attribute flag for export type + // Set appropriate attribute flag for export type // switch(this.getExportType(options.nodeConfig)) { - case 'crash' : ftnAttribute |= ftnMailPacket.Packet.Attribute.Crash; break; - case 'hold' : ftnAttribute |= ftnMailPacket.Packet.Attribute.Hold; break; - // :TODO: Others? + case 'crash' : ftnAttribute |= ftnMailPacket.Packet.Attribute.Crash; break; + case 'hold' : ftnAttribute |= ftnMailPacket.Packet.Attribute.Hold; break; + // :TODO: Others? } // - // EchoMail requires some additional properties & kludges + // EchoMail requires some additional properties & kludges // message.meta.FtnProperty.ftn_area = config.messageNetworks.ftn.areas[message.areaTag].tag; // - // When exporting messages, we should create/update SEEN-BY - // with remote address(s) we are exporting to. + // When exporting messages, we should create/update SEEN-BY + // with remote address(s) we are exporting to. // const seenByAdditions = - [ `${localAddress.net}/${localAddress.node}` ].concat(config.messageNetworks.ftn.areas[message.areaTag].uplinks); + [ `${localAddress.net}/${localAddress.node}` ].concat(config.messageNetworks.ftn.areas[message.areaTag].uplinks); message.meta.FtnProperty.ftn_seen_by = - ftnUtil.getUpdatedSeenByEntries(message.meta.FtnProperty.ftn_seen_by, seenByAdditions); + ftnUtil.getUpdatedSeenByEntries(message.meta.FtnProperty.ftn_seen_by, seenByAdditions); // - // And create/update PATH for ourself + // And create/update PATH for ourself // message.meta.FtnKludge.PATH = ftnUtil.getUpdatedPathEntries(message.meta.FtnKludge.PATH, localAddress); } @@ -428,33 +428,33 @@ function FTNMessageScanTossModule() { message.meta.FtnProperty.ftn_attr_flags = ftnAttribute; // - // Additional kludges + // Additional kludges // - // Check for existence of MSGID as we may already have stored it from a previous - // export that failed to finish + // Check for existence of MSGID as we may already have stored it from a previous + // export that failed to finish // if(!message.meta.FtnKludge.MSGID) { message.meta.FtnKludge.MSGID = ftnUtil.getMessageIdentifier( message, localAddress, - message.isPrivate() // true = isNetMail + message.isPrivate() // true = isNetMail ); } message.meta.FtnKludge.TZUTC = ftnUtil.getUTCTimeZoneOffset(); // - // According to FSC-0046: + // According to FSC-0046: // - // "When a Conference Mail processor adds a TID to a message, it may not - // add a PID. An existing TID should, however, be replaced. TIDs follow - // the same format used for PIDs, as explained above." + // "When a Conference Mail processor adds a TID to a message, it may not + // add a PID. An existing TID should, however, be replaced. TIDs follow + // the same format used for PIDs, as explained above." // message.meta.FtnKludge.TID = ftnUtil.getProductIdentifier(); // - // Determine CHRS and actual internal encoding name. If the message has an - // explicit encoding set, use it. Otherwise, try to preserve any CHRS/encoding already set. + // Determine CHRS and actual internal encoding name. If the message has an + // explicit encoding set, use it. Otherwise, try to preserve any CHRS/encoding already set. // let encoding = options.nodeConfig.encoding || config.scannerTossers.ftn_bso.packetMsgEncoding || 'utf8'; const explicitEncoding = _.get(message.meta, 'System.explicit_encoding'); @@ -468,40 +468,40 @@ function FTNMessageScanTossModule() { } // - // Ensure we ended up with something useable. If not, back to utf8! + // Ensure we ended up with something useable. If not, back to utf8! // if(!iconv.encodingExists(encoding)) { Log.debug( { encoding : encoding }, 'Unknown encoding. Falling back to utf8'); encoding = 'utf8'; } - options.encoding = encoding; // save for later + options.encoding = encoding; // save for later message.meta.FtnKludge.CHRS = ftnUtil.getCharacterSetIdentifierByEncoding(encoding); }; this.setReplyKludgeFromReplyToMsgId = function(message, cb) { // - // Look up MSGID kludge for |message.replyToMsgId|, if any. - // If found, we can create a REPLY kludge with the previously - // discovered MSGID. + // Look up MSGID kludge for |message.replyToMsgId|, if any. + // If found, we can create a REPLY kludge with the previously + // discovered MSGID. // if(0 === message.replyToMsgId) { - return cb(null); // nothing to do + return cb(null); // nothing to do } Message.getMetaValuesByMessageId(message.replyToMsgId, 'FtnKludge', 'MSGID', (err, msgIdVal) => { if(!err) { assert(_.isString(msgIdVal), 'Expected string but got ' + (typeof msgIdVal) + ' (' + msgIdVal + ')'); - // got a MSGID - create a REPLY + // got a MSGID - create a REPLY message.meta.FtnKludge.REPLY = msgIdVal; } - cb(null); // this method always passes + cb(null); // this method always passes }); }; - // check paths, Addresses, etc. + // check paths, Addresses, etc. this.isAreaConfigValid = function(areaConfig) { if(!areaConfig || !_.isString(areaConfig.tag) || !_.isString(areaConfig.network)) { return false; @@ -520,14 +520,14 @@ function FTNMessageScanTossModule() { return false; } - // :TODO: need to check more! + // :TODO: need to check more! return true; }; this.parseScheduleString = function(schedStr) { if(!schedStr) { - return; // nothing to parse! + return; // nothing to parse! } let schedule = {}; @@ -550,7 +550,7 @@ function FTNMessageScanTossModule() { } } - // return undefined if we couldn't parse out anything useful + // return undefined if we couldn't parse out anything useful if(!_.isEmpty(schedule)) { return schedule; } @@ -558,10 +558,10 @@ function FTNMessageScanTossModule() { this.getAreaLastScanId = function(areaTag, cb) { const sql = - `SELECT area_tag, message_id - FROM message_area_last_scan - WHERE scan_toss = "ftn_bso" AND area_tag = ? - LIMIT 1;`; + `SELECT area_tag, message_id + FROM message_area_last_scan + WHERE scan_toss = "ftn_bso" AND area_tag = ? + LIMIT 1;`; msgDb.get(sql, [ areaTag ], (err, row) => { return cb(err, row ? row.message_id : 0); @@ -570,8 +570,8 @@ function FTNMessageScanTossModule() { this.setAreaLastScanId = function(areaTag, lastScanId, cb) { const sql = - `REPLACE INTO message_area_last_scan (scan_toss, area_tag, message_id) - VALUES ("ftn_bso", ?, ?);`; + `REPLACE INTO message_area_last_scan (scan_toss, area_tag, message_id) + VALUES ("ftn_bso", ?, ?);`; msgDb.run(sql, [ areaTag, lastScanId ], err => { return cb(err); @@ -581,7 +581,7 @@ function FTNMessageScanTossModule() { this.getNodeConfigByAddress = function(addr) { addr = _.isString(addr) ? Address.fromString(addr) : addr; - // :TODO: sort wildcard nodes{} entries by most->least explicit according to FTN hierarchy + // :TODO: sort wildcard nodes{} entries by most->least explicit according to FTN hierarchy return _.find(this.moduleConfig.nodes, (node, nodeAddrWildcard) => { return addr.isPatternMatch(nodeAddrWildcard); }); @@ -589,7 +589,7 @@ function FTNMessageScanTossModule() { this.exportNetMailMessagePacket = function(message, exportOpts, cb) { // - // For NetMail, we always create a *single* packet per message. + // For NetMail, we always create a *single* packet per message. // async.series( [ @@ -609,11 +609,11 @@ function FTNMessageScanTossModule() { packetHeader.password = exportOpts.nodeConfig.packetPassword || ''; - // use current message ID for filename seed + // use current message ID for filename seed exportOpts.pktFileName = self.getOutgoingPacketFileName( self.exportTempDir, message.messageId, - false, // createTempPacket=false + false, // createTempPacket=false exportOpts.fileCase ); @@ -645,13 +645,13 @@ function FTNMessageScanTossModule() { this.exportMessagesByUuid = function(messageUuids, exportOpts, cb) { // - // This method has a lot of madness going on: - // - Try to stuff messages into packets until we've hit the target size - // - We need to wait for write streams to finish before proceeding in many cases - // or data will be cut off when closing and creating a new stream + // This method has a lot of madness going on: + // - Try to stuff messages into packets until we've hit the target size + // - We need to wait for write streams to finish before proceeding in many cases + // or data will be cut off when closing and creating a new stream // - let exportedFiles = []; - let currPacketSize = self.moduleConfig.packetTargetByteSize; + let exportedFiles = []; + let currPacketSize = self.moduleConfig.packetTargetByteSize; let packet; let ws; let remainMessageBuf; @@ -684,7 +684,7 @@ function FTNMessageScanTossModule() { return callback(err); } - // General preperation + // General preperation self.prepareMessage(message, exportOpts); self.setReplyKludgeFromReplyToMsgId(message, err => { @@ -703,7 +703,7 @@ function FTNMessageScanTossModule() { packetHeader.password = exportOpts.nodeConfig.packetPassword || ''; - // use current message ID for filename seed + // use current message ID for filename seed const pktFileName = self.getOutgoingPacketFileName( self.exportTempDir, message.messageId, @@ -731,11 +731,11 @@ function FTNMessageScanTossModule() { return callback(err); } - currPacketSize += msgBuf.length; + currPacketSize += msgBuf.length; if(currPacketSize >= self.moduleConfig.packetTargetByteSize) { - remainMessageBuf = msgBuf; // save for next packet - remainMessageId = message.messageId; + remainMessageBuf = msgBuf; // save for next packet + remainMessageId = message.messageId; } else { ws.write(msgBuf); } @@ -750,8 +750,8 @@ function FTNMessageScanTossModule() { }, function storeMsgIdMeta(callback) { // - // We want to store some meta as if we had imported - // this message for later reference + // We want to store some meta as if we had imported + // this message for later reference // if(message.meta.FtnKludge.MSGID) { message.persistMetaValue('FtnKludge', 'MSGID', message.meta.FtnKludge.MSGID, err => { @@ -781,7 +781,7 @@ function FTNMessageScanTossModule() { }, function writeRemainPacket(callback) { if(remainMessageBuf) { - // :TODO: DRY this with the code above -- they are basically identical + // :TODO: DRY this with the code above -- they are basically identical packet = new ftnMailPacket.Packet(); const packetHeader = new ftnMailPacket.PacketHeader( @@ -791,7 +791,7 @@ function FTNMessageScanTossModule() { packetHeader.password = exportOpts.nodeConfig.packetPassword || ''; - // use current message ID for filename seed + // use current message ID for filename seed const pktFileName = self.getOutgoingPacketFileName( self.exportTempDir, remainMessageId, @@ -821,7 +821,7 @@ function FTNMessageScanTossModule() { this.getNetMailRoute = function(dstAddr) { // - // Route full|wildcard -> full adddress/network lookup + // Route full|wildcard -> full adddress/network lookup // const routes = _.get(Config(), 'scannerTossers.ftn_bso.netMail.routes'); if(!routes) { @@ -835,15 +835,15 @@ function FTNMessageScanTossModule() { this.getNetMailRouteInfoFromAddress = function(destAddress, cb) { // - // Attempt to find route information for |destAddress|: + // Attempt to find route information for |destAddress|: // - // 1) Routes: scannerTossers.ftn_bso.netMail.routes{} -> scannerTossers.ftn_bso.nodes{} -> config - // - Where we send may not be where destAddress is (it's routed!) - // 2) Direct to nodes: scannerTossers.ftn_bso.nodes{} -> config - // - Where we send is direct to destAddress + // 1) Routes: scannerTossers.ftn_bso.netMail.routes{} -> scannerTossers.ftn_bso.nodes{} -> config + // - Where we send may not be where destAddress is (it's routed!) + // 2) Direct to nodes: scannerTossers.ftn_bso.nodes{} -> config + // - Where we send is direct to destAddress // - // In both cases, attempt to look up Zone:Net/* to discover local "from" network/address - // falling back to Config.scannerTossers.ftn_bso.defaultNetwork + // In both cases, attempt to look up Zone:Net/* to discover local "from" network/address + // falling back to Config.scannerTossers.ftn_bso.defaultNetwork // const route = this.getNetMailRoute(destAddress); @@ -851,21 +851,21 @@ function FTNMessageScanTossModule() { let networkName; let isRouted; if(route) { - routeAddress = Address.fromString(route.address); - networkName = route.network; - isRouted = true; + routeAddress = Address.fromString(route.address); + networkName = route.network; + isRouted = true; } else { - routeAddress = destAddress; - isRouted = false; + routeAddress = destAddress; + isRouted = false; } networkName = networkName || this.getNetworkNameByAddress(routeAddress); const config = _.find(this.moduleConfig.nodes, (node, nodeAddrWildcard) => { return routeAddress.isPatternMatch(nodeAddrWildcard); - }) || { packetType : '2+', encoding : Config().scannerTossers.ftn_bso.packetMsgEncoding }; + }) || { packetType : '2+', encoding : Config().scannerTossers.ftn_bso.packetMsgEncoding }; - // we should never be failing here; we may just be using defaults. + // we should never be failing here; we may just be using defaults. return cb( networkName ? null : Errors.DoesNotExist(`No NetMail route for ${destAddress.toString()}`), { destAddress, routeAddress, networkName, config, isRouted } @@ -873,7 +873,7 @@ function FTNMessageScanTossModule() { }; this.exportNetMailMessagesToUplinks = function(messagesOrMessageUuids, cb) { - // for each message/UUID, find where to send the thing + // for each message/UUID, find where to send the thing async.each(messagesOrMessageUuids, (msgOrUuid, nextMessageOrUuid) => { const exportOpts = {}; @@ -898,14 +898,14 @@ function FTNMessageScanTossModule() { return callback(err); } - exportOpts.nodeConfig = routeInfo.config; - exportOpts.destAddress = dstAddr; - exportOpts.routeAddress = routeInfo.routeAddress; - exportOpts.fileCase = routeInfo.config.fileCase || 'lower'; - exportOpts.network = Config().messageNetworks.ftn.networks[routeInfo.networkName]; - exportOpts.networkName = routeInfo.networkName; - exportOpts.outgoingDir = self.getOutgoingEchoMailPacketDir(exportOpts.networkName, exportOpts.destAddress); - exportOpts.exportType = self.getExportType(routeInfo.config); + exportOpts.nodeConfig = routeInfo.config; + exportOpts.destAddress = dstAddr; + exportOpts.routeAddress = routeInfo.routeAddress; + exportOpts.fileCase = routeInfo.config.fileCase || 'lower'; + exportOpts.network = Config().messageNetworks.ftn.networks[routeInfo.networkName]; + exportOpts.networkName = routeInfo.networkName; + exportOpts.outgoingDir = self.getOutgoingEchoMailPacketDir(exportOpts.networkName, exportOpts.destAddress); + exportOpts.exportType = self.getExportType(routeInfo.config); if(!exportOpts.network) { return callback(Errors.DoesNotExist(`No configuration found for network ${routeInfo.networkName}`)); @@ -915,7 +915,7 @@ function FTNMessageScanTossModule() { }); }, function createOutgoingDir(callback) { - // ensure outgoing NetMail directory exists + // ensure outgoing NetMail directory exists return fse.mkdirs(exportOpts.outgoingDir, callback); }, function exportPacket(callback) { @@ -945,7 +945,7 @@ function FTNMessageScanTossModule() { return message.persistMetaValue('System', 'state_flags0', Message.StateFlags0.Exported.toString(), callback); }, function storeMsgIdMeta(callback) { - // Store meta as if we had imported this message -- for later reference + // Store meta as if we had imported this message -- for later reference if(message.meta.FtnKludge.MSGID) { return message.persistMetaValue('FtnKludge', 'MSGID', message.meta.FtnKludge.MSGID, callback); } @@ -978,18 +978,18 @@ function FTNMessageScanTossModule() { const exportOpts = { nodeConfig, - network : config.messageNetworks.ftn.networks[areaConfig.network], - destAddress : Address.fromString(uplink), - networkName : areaConfig.network, - fileCase : nodeConfig.fileCase || 'lower', + network : config.messageNetworks.ftn.networks[areaConfig.network], + destAddress : Address.fromString(uplink), + networkName : areaConfig.network, + fileCase : nodeConfig.fileCase || 'lower', }; if(_.isString(exportOpts.network.localAddress)) { exportOpts.network.localAddress = Address.fromString(exportOpts.network.localAddress); } - const outgoingDir = self.getOutgoingEchoMailPacketDir(exportOpts.networkName, exportOpts.destAddress); - const exportType = self.getExportType(exportOpts.nodeConfig); + const outgoingDir = self.getOutgoingEchoMailPacketDir(exportOpts.networkName, exportOpts.destAddress); + const exportType = self.getExportType(exportOpts.nodeConfig); async.waterfall( [ @@ -1003,19 +1003,19 @@ function FTNMessageScanTossModule() { }, function createArcMailBundle(exportedFileNames, callback) { if(self.archUtil.haveArchiver(exportOpts.nodeConfig.archiveType)) { - // :TODO: support bundleTargetByteSize: + // :TODO: support bundleTargetByteSize: // - // Compress to a temp location then we'll move it in the next step + // Compress to a temp location then we'll move it in the next step // - // Note that we must use the *final* output dir for getOutgoingBundleFileName() - // as it checks for collisions in bundle names! + // Note that we must use the *final* output dir for getOutgoingBundleFileName() + // as it checks for collisions in bundle names! // self.getOutgoingBundleFileName(outgoingDir, exportOpts.network.localAddress, exportOpts.destAddress, (err, bundlePath) => { if(err) { return callback(err); } - // adjust back to temp path + // adjust back to temp path const tempBundlePath = paths.join(self.exportTempDir, paths.basename(bundlePath)); self.archUtil.compressTo( @@ -1035,8 +1035,8 @@ function FTNMessageScanTossModule() { const ext = paths.extname(oldPath).toLowerCase(); if('.pk_' === ext.toLowerCase()) { // - // For a given temporary .pk_ file, we need to move it to the outoing - // directory with the appropriate BSO style filename. + // For a given temporary .pk_ file, we need to move it to the outoing + // directory with the appropriate BSO style filename. // const newExt = self.getOutgoingFlowFileExtension( exportOpts.destAddress, @@ -1062,7 +1062,7 @@ function FTNMessageScanTossModule() { } // - // For bundles, we need to append to the appropriate flow file + // For bundles, we need to append to the appropriate flow file // const flowFilePath = self.getOutgoingFlowFileName( outgoingDir, @@ -1072,7 +1072,7 @@ function FTNMessageScanTossModule() { exportOpts.fileCase ); - // directive of '^' = delete file after transfer + // directive of '^' = delete file after transfer self.flowFileAppendRefs(flowFilePath, [ newPath ], '^', err => { if(err) { Log.warn( { path : flowFilePath }, 'Failed appending flow reference record!'); @@ -1085,31 +1085,31 @@ function FTNMessageScanTossModule() { } ], err => { - // :TODO: do something with |err| ? + // :TODO: do something with |err| ? if(err) { Log.warn(err.message); } nextUplink(); } ); - }, cb); // complete + }, cb); // complete }; this.setReplyToMsgIdFtnReplyKludge = function(message, cb) { // - // Given a FTN REPLY kludge, set |message.replyToMsgId|, if possible, - // by looking up an associated MSGID kludge meta. + // Given a FTN REPLY kludge, set |message.replyToMsgId|, if possible, + // by looking up an associated MSGID kludge meta. // - // See also: http://ftsc.org/docs/fts-0009.001 + // See also: http://ftsc.org/docs/fts-0009.001 // if(!_.isString(message.meta.FtnKludge.REPLY)) { - // nothing to do + // nothing to do return cb(); } Message.getMessageIdsByMetaValue('FtnKludge', 'MSGID', message.meta.FtnKludge.REPLY, (err, msgIds) => { if(msgIds && msgIds.length > 0) { - // expect a single match, but dupe checking is not perfect - warn otherwise + // expect a single match, but dupe checking is not perfect - warn otherwise if(1 === msgIds.length) { message.replyToMsgId = msgIds[0]; } else { @@ -1125,7 +1125,7 @@ function FTNMessageScanTossModule() { const aliases = _.get(Config(), 'messageNetworks.ftn.netMail.aliases'); if(!aliases) { - return lookup; // keep orig + return lookup; // keep orig } const alias = _.find(aliases, (localName, alias) => { @@ -1148,7 +1148,7 @@ function FTNMessageScanTossModule() { } const fromPoint = _.get(message, 'meta.FtnKludge.FMPT'); - const toPoint = _.get(message, 'meta.FtnKludge.TOPT'); + const toPoint = _.get(message, 'meta.FtnKludge.TOPT'); if(fromPoint) { from += `.${fromPoint}`; @@ -1172,7 +1172,7 @@ function FTNMessageScanTossModule() { }, function checkForDupeMSGID(callback) { // - // If we have a MSGID, don't allow a dupe + // If we have a MSGID, don't allow a dupe // if(!_.has(message.meta, 'FtnKludge.MSGID')) { return callback(null); @@ -1191,16 +1191,16 @@ function FTNMessageScanTossModule() { function basicSetup(callback) { message.areaTag = config.localAreaTag; - // indicate this was imported from FTN + // indicate this was imported from FTN message.meta.System[Message.SystemMetaNames.ExternalFlavor] = Message.AddressFlavor.FTN; // - // If we *allow* dupes (disabled by default), then just generate - // a random UUID. Otherwise, don't assign the UUID just yet. It will be - // generated at persist() time and should be consistent across import/exports + // If we *allow* dupes (disabled by default), then just generate + // a random UUID. Otherwise, don't assign the UUID just yet. It will be + // generated at persist() time and should be consistent across import/exports // if(true === _.get(Config(), [ 'messageNetworks', 'ftn', 'areas', config.localAreaTag, 'allowDupes' ], false)) { - // just generate a UUID & therefor always allow for dupes + // just generate a UUID & therefor always allow for dupes message.uuid = uuidV4(); } @@ -1213,15 +1213,15 @@ function FTNMessageScanTossModule() { }, function setupPrivateMessage(callback) { // - // If this is a private message (e.g. NetMail) we set the local user ID + // If this is a private message (e.g. NetMail) we set the local user ID // if(Message.WellKnownAreaTags.Private !== config.localAreaTag) { return callback(null); } // - // Create a meta value for the *remote* from user. In the case here with FTN, - // their fully qualified FTN from address + // Create a meta value for the *remote* from user. In the case here with FTN, + // their fully qualified FTN from address // const { from } = self.getAddressesFromNetMailMessage(message); @@ -1236,8 +1236,8 @@ function FTNMessageScanTossModule() { User.getUserIdAndNameByLookup(lookupName, (err, localToUserId, localUserName) => { if(err) { // - // Couldn't find a local username. If the toUserName itself is a FTN address - // we can only assume the message is to the +op, else we'll have to fail. + // Couldn't find a local username. If the toUserName itself is a FTN address + // we can only assume the message is to the +op, else we'll have to fail. // const toUserNameAsAddress = Address.fromString(message.toUserName); if(toUserNameAsAddress.isValid()) { @@ -1261,21 +1261,21 @@ function FTNMessageScanTossModule() { } } - // we do this after such that error cases can be preseved above + // we do this after such that error cases can be preseved above if(lookupName !== message.toUserName) { message.toUserName = localUserName; } - // set the meta information - used elsehwere for retrieval + // set the meta information - used elsehwere for retrieval message.meta.System[Message.SystemMetaNames.LocalToUserID] = localToUserId; return callback(null); }); }, function persistImport(callback) { - // mark as imported + // mark as imported message.meta.System.state_flags0 = Message.StateFlags0.Imported.toString(); - // save to disc + // save to disc message.persist(err => { return callback(err); }); @@ -1298,19 +1298,19 @@ function FTNMessageScanTossModule() { }; // - // Ref. implementations on import: - // * https://github.com/larsks/crashmail/blob/26e5374710c7868dab3d834be14bf4041041aae5/crashmail/pkt.c - // https://github.com/larsks/crashmail/blob/26e5374710c7868dab3d834be14bf4041041aae5/crashmail/handle.c + // Ref. implementations on import: + // * https://github.com/larsks/crashmail/blob/26e5374710c7868dab3d834be14bf4041041aae5/crashmail/pkt.c + // https://github.com/larsks/crashmail/blob/26e5374710c7868dab3d834be14bf4041041aae5/crashmail/handle.c // this.importMessagesFromPacketFile = function(packetPath, password, cb) { let packetHeader; - const packetOpts = { keepTearAndOrigin : false }; // needed so we can calc message UUID without these; we'll add later + const packetOpts = { keepTearAndOrigin : false }; // needed so we can calc message UUID without these; we'll add later let importStats = { - areaSuccess : {}, // areaTag->count - areaFail : {}, // areaTag->count - otherFail : 0, + areaSuccess : {}, // areaTag->count + areaFail : {}, // areaTag->count + otherFail : 0, }; new ftnMailPacket.Packet(packetOpts).read(packetPath, (entryType, entryData, next) => { @@ -1323,7 +1323,7 @@ function FTNMessageScanTossModule() { return next(new Error(`No local configuration for packet addressed to ${addrString}`)); } else { - // :TODO: password needs validated - need to determine if it will use the same node config (which can have wildcards) or something else?! + // :TODO: password needs validated - need to determine if it will use the same node config (which can have wildcards) or something else?! return next(null); } @@ -1337,19 +1337,19 @@ function FTNMessageScanTossModule() { if(!localAreaTag) { // - // No local area configured for this import + // No local area configured for this import // - // :TODO: Handle the "catch all" area bucket case if configured + // :TODO: Handle the "catch all" area bucket case if configured Log.warn( { areaTag : areaTag }, 'No local area configured for this packet file!'); - // bump generic failure + // bump generic failure importStats.otherFail += 1; return next(null); } } else { // - // No area tag: If marked private in attributes, this is a NetMail + // No area tag: If marked private in attributes, this is a NetMail // if(message.meta.FtnProperty.ftn_attr_flags & ftnMailPacket.Packet.Attribute.Private) { localAreaTag = Message.WellKnownAreaTags.Private; @@ -1369,12 +1369,12 @@ function FTNMessageScanTossModule() { self.appendTearAndOrigin(message); const importConfig = { - localAreaTag : localAreaTag, + localAreaTag : localAreaTag, }; self.importMailToArea(importConfig, packetHeader, message, err => { if(err) { - // bump area fail stats + // bump area fail stats importStats.areaFail[localAreaTag] = (importStats.areaFail[localAreaTag] || 0) + 1; if('SQLITE_CONSTRAINT' === err.code || 'DUPE_MSGID' === err.code) { @@ -1386,7 +1386,7 @@ function FTNMessageScanTossModule() { return next(null); } } else { - // bump area success + // bump area success importStats.areaSuccess[localAreaTag] = (importStats.areaSuccess[localAreaTag] || 0) + 1; } @@ -1395,7 +1395,7 @@ function FTNMessageScanTossModule() { } }, err => { // - // try to produce something helpful in the log + // try to produce something helpful in the log // const finalStats = Object.assign(importStats, { packetPath : packetPath } ); if(err || Object.keys(finalStats.areaFail).length > 0) { @@ -1414,11 +1414,11 @@ function FTNMessageScanTossModule() { this.maybeArchiveImportFile = function(origPath, type, status, cb) { // - // type : pkt|tic|bundle - // status : good|reject + // type : pkt|tic|bundle + // status : good|reject // - // Status of "good" is only applied to pkt files & placed - // in |retain| if set. This is generally used for debugging only. + // Status of "good" is only applied to pkt files & placed + // in |retain| if set. This is generally used for debugging only. // let archivePath; const ts = moment().format('YYYY-MM-DDTHH.mm.ss.SSS'); @@ -1433,7 +1433,7 @@ function FTNMessageScanTossModule() { } else if('good' !== status) { archivePath = paths.join(self.moduleConfig.paths.reject, `${status}-${type}--${ts}-${fn}`); } else { - return cb(null); // don't archive non-good/pkt files + return cb(null); // don't archive non-good/pkt files } Log.debug( { origPath : origPath, archivePath : archivePath, type : type, status : status }, 'Archiving import file'); @@ -1443,7 +1443,7 @@ function FTNMessageScanTossModule() { Log.warn( { error : err.message, origPath : origPath, archivePath : archivePath, type : type, status : status }, 'Failed to archive packet file'); } - return cb(null); // never fatal + return cb(null); // never fatal }); }; @@ -1472,13 +1472,13 @@ function FTNMessageScanTossModule() { nextFile(); }); }, err => { - // :TODO: Handle err! we should try to keep going though... + // :TODO: Handle err! we should try to keep going though... callback(err, packetFiles, rejects); }); }, function handleProcessedFiles(packetFiles, rejects, callback) { async.each(packetFiles, (packetFile, nextFile) => { - // possibly archive, then remove original + // possibly archive, then remove original const fullPath = paths.join(importDir, packetFile); self.maybeArchiveImportFile( fullPath, @@ -1504,7 +1504,7 @@ function FTNMessageScanTossModule() { this.importFromDirectory = function(inboundType, importDir, cb) { async.waterfall( [ - // start with .pkt files + // start with .pkt files function importPacketFiles(callback) { self.importPacketFilesFromDirectory(importDir, '', err => { callback(err); @@ -1512,7 +1512,7 @@ function FTNMessageScanTossModule() { }, function discoverBundles(callback) { fs.readdir(importDir, (err, files) => { - // :TODO: if we do much more of this, probably just use the glob module + // :TODO: if we do much more of this, probably just use the glob module const bundleRegExp = /\.(su|mo|tu|we|th|fr|sa)[0-9a-z]/i; files = files.filter(f => { const fext = paths.extname(f); @@ -1540,7 +1540,7 @@ function FTNMessageScanTossModule() { rejects.push(bundleFile.path); - return nextFile(); // unknown archive type + return nextFile(); // unknown archive type } Log.debug( { bundleFile : bundleFile }, 'Processing bundle' ); @@ -1567,10 +1567,10 @@ function FTNMessageScanTossModule() { } // - // All extracted - import .pkt's + // All extracted - import .pkt's // self.importPacketFilesFromDirectory(self.importTempDir, '', () => { - // :TODO: handle |err| + // :TODO: handle |err| callback(null, bundleFiles, rejects); }); }); @@ -1622,7 +1622,7 @@ function FTNMessageScanTossModule() { }); }; - // Starts an export block - returns true if we can proceed + // Starts an export block - returns true if we can proceed this.exportingStart = function() { if(!this.exportRunning) { this.exportRunning = true; @@ -1632,7 +1632,7 @@ function FTNMessageScanTossModule() { return false; }; - // ends an export block + // ends an export block this.exportingEnd = function(cb) { this.exportRunning = false; @@ -1666,9 +1666,9 @@ function FTNMessageScanTossModule() { function generalValidation(callback) { const sysConfig = Config(); const config = { - nodes : sysConfig.scannerTossers.ftn_bso.nodes, - defaultPassword : sysConfig.scannerTossers.ftn_bso.tic.password, - localAreaTags : self.getLocalAreaTagsForTic(), + nodes : sysConfig.scannerTossers.ftn_bso.nodes, + defaultPassword : sysConfig.scannerTossers.ftn_bso.tic.password, + localAreaTags : self.getLocalAreaTagsForTic(), }; return ticFileInfo.validate(config, (err, localInfo) => { @@ -1677,14 +1677,14 @@ function FTNMessageScanTossModule() { return callback(err); } - // We may need to map |localAreaTag| back to real areaTag if it's a mapping/alias + // We may need to map |localAreaTag| back to real areaTag if it's a mapping/alias const mappedLocalAreaTag = _.get(Config().scannerTossers.ftn_bso, [ 'ticAreas', localInfo.areaTag ]); if(mappedLocalAreaTag) { if(_.isString(mappedLocalAreaTag.areaTag)) { - localInfo.areaTag = mappedLocalAreaTag.areaTag; - localInfo.hashTags = mappedLocalAreaTag.hashTags; // override default for node - localInfo.storageTag = mappedLocalAreaTag.storageTag; // override default + localInfo.areaTag = mappedLocalAreaTag.areaTag; + localInfo.hashTags = mappedLocalAreaTag.hashTags; // override default for node + localInfo.storageTag = mappedLocalAreaTag.storageTag; // override default } else if(_.isString(mappedLocalAreaTag)) { localInfo.areaTag = mappedLocalAreaTag; } @@ -1695,18 +1695,18 @@ function FTNMessageScanTossModule() { }, function findExistingItem(localInfo, callback) { // - // We will need to look for an existing item to replace/update if: - // a) The TIC file has a "Replaces" field - // b) The general or node specific |allowReplace| is true + // We will need to look for an existing item to replace/update if: + // a) The TIC file has a "Replaces" field + // b) The general or node specific |allowReplace| is true // - // Replace specifies a DOS 8.3 *pattern* which is allowed to have - // ? and * characters. For example, RETRONET.* + // Replace specifies a DOS 8.3 *pattern* which is allowed to have + // ? and * characters. For example, RETRONET.* // - // Lastly, we will only replace if the item is in the same/specified area - // and that come from the same origin as a previous entry. + // Lastly, we will only replace if the item is in the same/specified area + // and that come from the same origin as a previous entry. // - const allowReplace = _.get(Config().scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'allowReplace' ], Config().scannerTossers.ftn_bso.tic.allowReplace); - const replaces = ticFileInfo.getAsString('Replaces'); + const allowReplace = _.get(Config().scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'allowReplace' ], Config().scannerTossers.ftn_bso.tic.allowReplace); + const replaces = ticFileInfo.getAsString('Replaces'); if(!allowReplace || !replaces) { return callback(null, localInfo); @@ -1714,13 +1714,13 @@ function FTNMessageScanTossModule() { const metaPairs = [ { - name : 'short_file_name', - value : replaces.toUpperCase(), // we store upper as well - wildcards : true, // value may contain wildcards + name : 'short_file_name', + value : replaces.toUpperCase(), // we store upper as well + wildcards : true, // value may contain wildcards }, { - name : 'tic_origin', - value : ticFileInfo.getAsString('Origin'), + name : 'tic_origin', + value : ticFileInfo.getAsString('Origin'), } ]; @@ -1729,11 +1729,11 @@ function FTNMessageScanTossModule() { return callback(err); } - // 0:1 allowed + // 0:1 allowed if(1 === fileIds.length) { localInfo.existingFileId = fileIds[0]; - // fetch old filename - we may need to remove it if replacing with a new name + // fetch old filename - we may need to remove it if replacing with a new name FileEntry.loadBasicEntry(localInfo.existingFileId, {}, (err, info) => { if(info) { Log.trace( @@ -1741,10 +1741,10 @@ function FTNMessageScanTossModule() { 'Existing TIC file target to be replaced' ); - localInfo.oldFileName = info.fileName; - localInfo.oldStorageTag = info.storageTag; + localInfo.oldFileName = info.fileName; + localInfo.oldStorageTag = info.storageTag; } - return callback(null, localInfo); // continue even if we couldn't find an old match + return callback(null, localInfo); // continue even if we couldn't find an old match }); } else if(fileIds.legnth > 1) { return callback(Errors.General(`More than one existing entry for TIC in ${localInfo.areaTag} ([${fileIds.join(', ')}])`)); @@ -1755,13 +1755,13 @@ function FTNMessageScanTossModule() { }, function scan(localInfo, callback) { const scanOpts = { - sha256 : localInfo.sha256, // *may* have already been calculated - meta : { - // some TIC-related metadata we always want - short_file_name : ticFileInfo.getAsString('File').toUpperCase(), // upper to ensure no case issues later; this should be a DOS 8.3 name - tic_origin : ticFileInfo.getAsString('Origin'), - tic_desc : ticFileInfo.getAsString('Desc'), - upload_by_username : _.get(Config().scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'uploadBy' ], Config().scannerTossers.ftn_bso.tic.uploadBy), + sha256 : localInfo.sha256, // *may* have already been calculated + meta : { + // some TIC-related metadata we always want + short_file_name : ticFileInfo.getAsString('File').toUpperCase(), // upper to ensure no case issues later; this should be a DOS 8.3 name + tic_origin : ticFileInfo.getAsString('Origin'), + tic_desc : ticFileInfo.getAsString('Desc'), + upload_by_username : _.get(Config().scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'uploadBy' ], Config().scannerTossers.ftn_bso.tic.uploadBy), } }; @@ -1771,18 +1771,18 @@ function FTNMessageScanTossModule() { } // - // We may have TIC auto-tagging for this node and/or specific (remote) area + // We may have TIC auto-tagging for this node and/or specific (remote) area // const hashTags = - localInfo.hashTags || - _.get(Config().scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'hashTags' ] ); // catch-all*/ + localInfo.hashTags || + _.get(Config().scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'hashTags' ] ); // catch-all*/ if(hashTags) { scanOpts.hashTags = new Set(hashTags.split(/[\s,]+/)); } if(localInfo.crc32) { - scanOpts.meta.file_crc32 = localInfo.crc32.toString(16); // again, *may* have already been calculated + scanOpts.meta.file_crc32 = localInfo.crc32.toString(16); // again, *may* have already been calculated } scanFile( @@ -1800,7 +1800,7 @@ function FTNMessageScanTossModule() { }, function store(localInfo, callback) { // - // Move file to final area storage and persist to DB + // Move file to final area storage and persist to DB // const areaInfo = getFileAreaByTag(localInfo.areaTag); if(!areaInfo) { @@ -1812,15 +1812,15 @@ function FTNMessageScanTossModule() { return callback(Errors.Invalid(`Invalid storage tag: ${storageTag}`)); } - localInfo.fileEntry.storageTag = storageTag; - localInfo.fileEntry.areaTag = localInfo.areaTag; - localInfo.fileEntry.fileName = ticFileInfo.longFileName; + localInfo.fileEntry.storageTag = storageTag; + localInfo.fileEntry.areaTag = localInfo.areaTag; + localInfo.fileEntry.fileName = ticFileInfo.longFileName; // - // We may now have two descriptions: from .DIZ/etc. or the TIC itself. - // Determine which one to use using |descPriority| and availability. + // We may now have two descriptions: from .DIZ/etc. or the TIC itself. + // Determine which one to use using |descPriority| and availability. // - // We will still fallback as needed from -> -> + // We will still fallback as needed from -> -> // const descPriority = _.get( Config().scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'descPriority' ], @@ -1831,7 +1831,7 @@ function FTNMessageScanTossModule() { const origDesc = localInfo.fileEntry.desc; localInfo.fileEntry.desc = ticFileInfo.getAsString('Ldesc') || origDesc || getDescFromFileName(ticFileInfo.filePath); } else { - // see if we got desc from .DIZ/etc. + // see if we got desc from .DIZ/etc. const fromDescFile = 'descFile' === localInfo.fileEntry.descSrc; localInfo.fileEntry.desc = fromDescFile ? localInfo.fileEntry.desc : ticFileInfo.getAsString('Ldesc'); localInfo.fileEntry.desc = localInfo.fileEntry.desc || getDescFromFileName(ticFileInfo.filePath); @@ -1845,7 +1845,7 @@ function FTNMessageScanTossModule() { const isUpdate = localInfo.existingFileId ? true : false; if(isUpdate) { - // we need to *update* an existing record/file + // we need to *update* an existing record/file localInfo.fileEntry.fileId = localInfo.existingFileId; } @@ -1866,14 +1866,14 @@ function FTNMessageScanTossModule() { }); }); }, - // :TODO: from here, we need to re-toss files if needed, before they are removed + // :TODO: from here, we need to re-toss files if needed, before they are removed function cleanupOldFile(localInfo, callback) { if(!localInfo.existingFileId) { return callback(null, localInfo); } const oldStorageDir = getAreaStorageDirectoryByTag(localInfo.oldStorageTag); - const oldPath = paths.join(oldStorageDir, localInfo.oldFileName); + const oldPath = paths.join(oldStorageDir, localInfo.oldFileName); fs.unlink(oldPath, err => { if(err) { @@ -1881,7 +1881,7 @@ function FTNMessageScanTossModule() { } else { Log.debug( { oldPath : oldPath }, 'Removed old physical file during TIC replacement'); } - return callback(null, localInfo); // continue even if err + return callback(null, localInfo); // continue even if err }); }, ], @@ -1902,7 +1902,7 @@ function FTNMessageScanTossModule() { this.removeAssocTicFiles = function(ticFileInfo, cb) { async.each( [ ticFileInfo.path, ticFileInfo.filePath ], (path, nextPath) => { fs.unlink(path, err => { - if(err && 'ENOENT' !== err.code) { // don't log when the file doesn't exist + if(err && 'ENOENT' !== err.code) { // don't log when the file doesn't exist Log.warn( { error : err.message, path : path }, 'Failed unlinking TIC file'); } return nextPath(null); @@ -1915,23 +1915,23 @@ function FTNMessageScanTossModule() { this.performEchoMailExport = function(cb) { // - // Select all messages with a |message_id| > |lastScanId|. - // Additionally exclude messages with the System state_flags0 which will be present for - // imported or already exported messages + // Select all messages with a |message_id| > |lastScanId|. + // Additionally exclude messages with the System state_flags0 which will be present for + // imported or already exported messages // - // NOTE: If StateFlags0 starts to use additional bits, we'll likely need to check them here! + // NOTE: If StateFlags0 starts to use additional bits, we'll likely need to check them here! // const getNewUuidsSql = - `SELECT message_id, message_uuid - FROM message m - WHERE area_tag = ? AND message_id > ? AND - (SELECT COUNT(message_id) - FROM message_meta - WHERE message_id = m.message_id AND meta_category = 'System' AND meta_name = 'state_flags0') = 0 - ORDER BY message_id;` - ; + `SELECT message_id, message_uuid + FROM message m + WHERE area_tag = ? AND message_id > ? AND + (SELECT COUNT(message_id) + FROM message_meta + WHERE message_id = m.message_id AND meta_category = 'System' AND meta_name = 'state_flags0') = 0 + ORDER BY message_id;` + ; - // we shouldn't, but be sure we don't try to pick up private mail here + // we shouldn't, but be sure we don't try to pick up private mail here const config = Config(); const areaTags = Object.keys(config.messageNetworks.ftn.areas) .filter(areaTag => Message.WellKnownAreaTags.Private !== areaTag); @@ -1943,8 +1943,8 @@ function FTNMessageScanTossModule() { } // - // For each message that is newer than that of the last scan - // we need to export to each configured associated uplink(s) + // For each message that is newer than that of the last scan + // we need to export to each configured associated uplink(s) // async.waterfall( [ @@ -1967,7 +1967,7 @@ function FTNMessageScanTossModule() { }); }, function exportToConfiguredUplinks(msgRows, callback) { - const uuidsOnly = msgRows.map(r => r.message_uuid); // convert to array of UUIDs only + const uuidsOnly = msgRows.map(r => r.message_uuid); // convert to array of UUIDs only self.exportEchoMailMessagesToUplinks(uuidsOnly, areaConfig, err => { const newLastScanId = msgRows[msgRows.length - 1].message_id; @@ -1994,35 +1994,35 @@ function FTNMessageScanTossModule() { this.performNetMailExport = function(cb) { // - // Select all messages with a |message_id| > |lastScanId| in the private area - // that are schedule for export to FTN-style networks. + // Select all messages with a |message_id| > |lastScanId| in the private area + // that are schedule for export to FTN-style networks. // - // Just like EchoMail, we additionally exclude messages with the System state_flags0 - // which will be present for imported or already exported messages + // Just like EchoMail, we additionally exclude messages with the System state_flags0 + // which will be present for imported or already exported messages // // - // :TODO: fill out the rest of the consts here - // :TODO: this statement is crazy ugly -- use JOIN / NOT EXISTS for state_flags & 0x02 + // :TODO: fill out the rest of the consts here + // :TODO: this statement is crazy ugly -- use JOIN / NOT EXISTS for state_flags & 0x02 const getNewUuidsSql = - `SELECT message_id, message_uuid - FROM message m - WHERE area_tag = '${Message.WellKnownAreaTags.Private}' AND message_id > ? AND - (SELECT COUNT(message_id) - FROM message_meta - WHERE message_id = m.message_id - AND meta_category = 'System' - AND (meta_name = 'state_flags0' OR meta_name = 'local_to_user_id') - ) = 0 - AND - (SELECT COUNT(message_id) - FROM message_meta - WHERE message_id = m.message_id - AND meta_category = 'System' - AND meta_name = '${Message.SystemMetaNames.ExternalFlavor}' - AND meta_value = '${Message.AddressFlavor.FTN}' - ) = 1 - ORDER BY message_id; - `; + `SELECT message_id, message_uuid + FROM message m + WHERE area_tag = '${Message.WellKnownAreaTags.Private}' AND message_id > ? AND + (SELECT COUNT(message_id) + FROM message_meta + WHERE message_id = m.message_id + AND meta_category = 'System' + AND (meta_name = 'state_flags0' OR meta_name = 'local_to_user_id') + ) = 0 + AND + (SELECT COUNT(message_id) + FROM message_meta + WHERE message_id = m.message_id + AND meta_category = 'System' + AND meta_name = '${Message.SystemMetaNames.ExternalFlavor}' + AND meta_value = '${Message.AddressFlavor.FTN}' + ) = 1 + ORDER BY message_id; + `; async.waterfall( [ @@ -2036,7 +2036,7 @@ function FTNMessageScanTossModule() { } if(0 === rows.length) { - return cb(null); // note |cb| -- early bail out! + return cb(null); // note |cb| -- early bail out! } return callback(null, rows); @@ -2055,17 +2055,17 @@ function FTNMessageScanTossModule() { this.isNetMailMessage = function(message) { return message.isPrivate() && - null === _.get(message, 'meta.System.LocalToUserID', null) && - Message.AddressFlavor.FTN === _.get(message, 'meta.System.external_flavor', null); + null === _.get(message, 'meta.System.LocalToUserID', null) && + Message.AddressFlavor.FTN === _.get(message, 'meta.System.external_flavor', null); }; } require('util').inherits(FTNMessageScanTossModule, MessageScanTossModule); -// :TODO: *scheduled* portion of this stuff should probably use event_scheduler - @immediate would still use record(). +// :TODO: *scheduled* portion of this stuff should probably use event_scheduler - @immediate would still use record(). FTNMessageScanTossModule.prototype.processTicFilesInDirectory = function(importDir, cb) { - // :TODO: pass in 'inbound' vs 'secInbound' -- pass along to processSingleTicFile() where password will be checked + // :TODO: pass in 'inbound' vs 'secInbound' -- pass along to processSingleTicFile() where password will be checked const self = this; async.waterfall( @@ -2103,9 +2103,9 @@ FTNMessageScanTossModule.prototype.processTicFilesInDirectory = function(importD async.eachSeries(ticFilesInfo, (ticFileInfo, nextTicInfo) => { self.processSingleTicFile(ticFileInfo, err => { if(err) { - // archive rejected TIC stuff (.TIC + attach) + // archive rejected TIC stuff (.TIC + attach) async.each( [ ticFileInfo.path, ticFileInfo.filePath ], (path, nextPath) => { - if(!path) { // possibly rejected due to "File" not existing/etc. + if(!path) { // possibly rejected due to "File" not existing/etc. return nextPath(null); } @@ -2170,10 +2170,10 @@ FTNMessageScanTossModule.prototype.startup = function(cb) { if(exportSchedule) { Log.debug( { - schedule : this.moduleConfig.schedule.export, - schedOK : -1 === exportSchedule.sched.error, - next : moment(later.schedule(exportSchedule.sched).next(1)).format('ddd, MMM Do, YYYY @ h:m:ss a'), - immediate : exportSchedule.immediate ? true : false, + schedule : this.moduleConfig.schedule.export, + schedOK : -1 === exportSchedule.sched.error, + next : moment(later.schedule(exportSchedule.sched).next(1)).format('ddd, MMM Do, YYYY @ h:m:ss a'), + immediate : exportSchedule.immediate ? true : false, }, 'Export schedule loaded' ); @@ -2199,10 +2199,10 @@ FTNMessageScanTossModule.prototype.startup = function(cb) { if(importSchedule) { Log.debug( { - schedule : this.moduleConfig.schedule.import, - schedOK : -1 === importSchedule.sched.error, - next : moment(later.schedule(importSchedule.sched).next(1)).format('ddd, MMM Do, YYYY @ h:m:ss a'), - watchFile : _.isString(importSchedule.watchFile) ? importSchedule.watchFile : 'None', + schedule : this.moduleConfig.schedule.import, + schedOK : -1 === importSchedule.sched.error, + next : moment(later.schedule(importSchedule.sched).next(1)).format('ddd, MMM Do, YYYY @ h:m:ss a'), + watchFile : _.isString(importSchedule.watchFile) ? importSchedule.watchFile : 'None', }, 'Import schedule loaded' ); @@ -2231,8 +2231,8 @@ FTNMessageScanTossModule.prototype.startup = function(cb) { }); // - // If the watch file already exists, kick off now - // https://github.com/NuSkooler/enigma-bbs/issues/122 + // If the watch file already exists, kick off now + // https://github.com/NuSkooler/enigma-bbs/issues/122 // fse.exists(importSchedule.watchFile, exists => { if(exists) { @@ -2259,14 +2259,14 @@ FTNMessageScanTossModule.prototype.shutdown = function(cb) { } // - // Clean up temp dir/files we created + // Clean up temp dir/files we created // temptmp.cleanup( paths => { const fullStats = { - exportDir : this.exportTempDir, - importTemp : this.importTempDir, - paths : paths, - sessionId : temptmp.sessionId, + exportDir : this.exportTempDir, + importTemp : this.importTempDir, + paths : paths, + sessionId : temptmp.sessionId, }; Log.trace(fullStats, 'Temporary directories cleaned up'); @@ -2293,8 +2293,8 @@ FTNMessageScanTossModule.prototype.performImport = function(cb) { FTNMessageScanTossModule.prototype.performExport = function(cb) { // - // We're only concerned with areas related to FTN. For each area, loop though - // and let's find out what messages need exported. + // We're only concerned with areas related to FTN. For each area, loop though + // and let's find out what messages need exported. // if(!this.hasValidConfiguration()) { return cb(new Error('Missing or invalid configuration')); @@ -2307,7 +2307,7 @@ FTNMessageScanTossModule.prototype.performExport = function(cb) { if(err) { Log.warn( { error : err.message, type : type }, 'Error(s) during export' ); } - return nextType(null); // try next, always + return nextType(null); // try next, always }); }, () => { return cb(null); @@ -2316,7 +2316,7 @@ FTNMessageScanTossModule.prototype.performExport = function(cb) { FTNMessageScanTossModule.prototype.record = function(message) { // - // This module works off schedules, but we do support @immediate for export + // This module works off schedules, but we do support @immediate for export // if(true !== this.exportImmediate || !this.hasValidConfiguration()) { return; diff --git a/core/server_module.js b/core/server_module.js index 26000c1b..c9ba1cab 100644 --- a/core/server_module.js +++ b/core/server_module.js @@ -1,9 +1,9 @@ /* jslint node: true */ 'use strict'; -var PluginModule = require('./plugin_module.js').PluginModule; +var PluginModule = require('./plugin_module.js').PluginModule; -exports.ServerModule = ServerModule; +exports.ServerModule = ServerModule; function ServerModule() { PluginModule.call(this); diff --git a/core/servers/content/gopher.js b/core/servers/content/gopher.js index bc221b84..019dbffa 100644 --- a/core/servers/content/gopher.js +++ b/core/servers/content/gopher.js @@ -1,62 +1,62 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Log = require('../../logger.js').log; -const { ServerModule } = require('../../server_module.js'); -const Config = require('../../config.js').get; +// ENiGMA½ +const Log = require('../../logger.js').log; +const { ServerModule } = require('../../server_module.js'); +const Config = require('../../config.js').get; const { splitTextAtTerms, isAnsi, cleanControlCodes -} = require('../../string_util.js'); +} = require('../../string_util.js'); const { getMessageConferenceByTag, getMessageAreaByTag, getMessageListForArea, -} = require('../../message_area.js'); -const { sortAreasOrConfs } = require('../../conf_area_util.js'); -const AnsiPrep = require('../../ansi_prep.js'); +} = require('../../message_area.js'); +const { sortAreasOrConfs } = require('../../conf_area_util.js'); +const AnsiPrep = require('../../ansi_prep.js'); -// deps -const net = require('net'); -const _ = require('lodash'); -const fs = require('graceful-fs'); -const paths = require('path'); -const moment = require('moment'); +// deps +const net = require('net'); +const _ = require('lodash'); +const fs = require('graceful-fs'); +const paths = require('path'); +const moment = require('moment'); const ModuleInfo = exports.moduleInfo = { - name : 'Gopher', - desc : 'Gopher Server', - author : 'NuSkooler', - packageName : 'codes.l33t.enigma.gopher.server', + name : 'Gopher', + desc : 'Gopher Server', + author : 'NuSkooler', + packageName : 'codes.l33t.enigma.gopher.server', }; -const Message = require('../../message.js'); +const Message = require('../../message.js'); const ItemTypes = { - Invalid : '', // not really a type, of course! + Invalid : '', // not really a type, of course! - // Canonical, RFC-1436 - TextFile : '0', - SubMenu : '1', - CCSONameserver : '2', - Error : '3', - BinHexFile : '4', - DOSFile : '5', - UuEncodedFile : '6', - FullTextSearch : '7', - Telnet : '8', - BinaryFile : '9', - AltServer : '+', - GIFFile : 'g', - ImageFile : 'I', - Telnet3270 : 'T', + // Canonical, RFC-1436 + TextFile : '0', + SubMenu : '1', + CCSONameserver : '2', + Error : '3', + BinHexFile : '4', + DOSFile : '5', + UuEncodedFile : '6', + FullTextSearch : '7', + Telnet : '8', + BinaryFile : '9', + AltServer : '+', + GIFFile : 'g', + ImageFile : 'I', + Telnet3270 : 'T', - // Non-canonical - HtmlFile : 'h', - InfoMessage : 'i', - SoundFile : 's', + // Non-canonical + HtmlFile : 'h', + InfoMessage : 'i', + SoundFile : 's', }; exports.getModule = class GopherModule extends ServerModule { @@ -64,7 +64,7 @@ exports.getModule = class GopherModule extends ServerModule { constructor() { super(); - this.routes = new Map(); // selector->generator => gopher item + this.routes = new Map(); // selector->generator => gopher item this.log = Log.child( { server : 'Gopher' } ); } @@ -75,7 +75,7 @@ exports.getModule = class GopherModule extends ServerModule { const config = Config(); this.publicHostname = config.contentServers.gopher.publicHostname; - this.publicPort = config.contentServers.gopher.publicPort; + this.publicPort = config.contentServers.gopher.publicPort; this.addRoute(/^\/?\r\n$/, this.defaultGenerator); this.addRoute(/^\/msgarea(\/[a-z0-9_-]+(\/[a-z0-9_-]+)?(\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(_raw)?)?)?\/?\r\n$/, this.messageAreaGenerator); @@ -88,7 +88,7 @@ exports.getModule = class GopherModule extends ServerModule { }); socket.on('error', err => { - if('ECONNRESET' !== err.code) { // normal + if('ECONNRESET' !== err.code) { // normal this.log.trace( { error : err.message }, 'Socket error'); } }); @@ -97,7 +97,7 @@ exports.getModule = class GopherModule extends ServerModule { listen() { if(!this.enabled) { - return true; // nothing to do, but not an error + return true; // nothing to do, but not an error } const config = Config(); @@ -115,10 +115,10 @@ exports.getModule = class GopherModule extends ServerModule { } isConfigured() { - // public hostname & port must be set; responses contain them! + // public hostname & port must be set; responses contain them! const config = Config(); return _.isString(_.get(config, 'contentServers.gopher.publicHostname')) && - _.isNumber(_.get(config, 'contentServers.gopher.publicPort')); + _.isNumber(_.get(config, 'contentServers.gopher.publicPort')); } addRoute(selectorRegExp, generatorHandler) { @@ -149,7 +149,7 @@ exports.getModule = class GopherModule extends ServerModule { } makeItem(itemType, text, selector, hostname, port) { - selector = selector || ''; // e.g. for info + selector = selector || ''; // e.g. for info hostname = hostname || this.publicHostname; port = port || this.publicPort; return `${itemType}${text}\t${selector}\t${hostname}\t${port}\r\n`; @@ -186,10 +186,10 @@ exports.getModule = class GopherModule extends ServerModule { AnsiPrep( body, { - cols : 79, // Gopher std. wants 70, but we'll have to deal with it. - forceLineTerm : true, // ensure each line is term'd - asciiMode : true, // export to ASCII - fillLines : false, // don't fill up to |cols| + cols : 79, // Gopher std. wants 70, but we'll have to deal with it. + forceLineTerm : true, // ensure each line is term'd + asciiMode : true, // export to ASCII + fillLines : false, // don't fill up to |cols| }, (err, prepped) => { return cb(prepped || body); @@ -207,21 +207,21 @@ exports.getModule = class GopherModule extends ServerModule { messageAreaGenerator(selectorMatch, cb) { this.log.trace( { selector : selectorMatch[0] }, 'Serving message area content'); // - // Selector should be: - // /msgarea - list confs - // /msgarea/conftag - list areas in conf - // /msgarea/conftag/areatag - list messages in area - // /msgarea/conftag/areatag/ - message as text - // /msgarea/conftag/areatag/_raw - full message as text + headers + // Selector should be: + // /msgarea - list confs + // /msgarea/conftag - list areas in conf + // /msgarea/conftag/areatag - list messages in area + // /msgarea/conftag/areatag/ - message as text + // /msgarea/conftag/areatag/_raw - full message as text + headers // if(selectorMatch[3] || selectorMatch[4]) { - // message + // message //const raw = selectorMatch[4] ? true : false; - // :TODO: support 'raw' - const msgUuid = selectorMatch[3].replace(/\r\n|\//g, ''); - const confTag = selectorMatch[1].substr(1).split('/')[0]; - const areaTag = selectorMatch[2].replace(/\r\n|\//g, ''); - const message = new Message(); + // :TODO: support 'raw' + const msgUuid = selectorMatch[3].replace(/\r\n|\//g, ''); + const confTag = selectorMatch[1].substr(1).split('/')[0]; + const areaTag = selectorMatch[2].replace(/\r\n|\//g, ''); + const message = new Message(); return message.load( { uuid : msgUuid }, err => { if(err) { @@ -248,15 +248,15 @@ Subject: ${message.subject} ID : ${message.messageUuid} (${message.messageId}) ${'-'.repeat(70)} ${msgBody} - `; + `; return cb(response); }); }); } else if(selectorMatch[2]) { - // list messages in area - const confTag = selectorMatch[1].substr(1).split('/')[0]; - const areaTag = selectorMatch[2].replace(/\r\n|\//g, ''); - const area = getMessageAreaByTag(areaTag); + // list messages in area + const confTag = selectorMatch[1].substr(1).split('/')[0]; + const areaTag = selectorMatch[2].replace(/\r\n|\//g, ''); + const area = getMessageAreaByTag(areaTag); if(Message.isPrivateAreaTag(areaTag)) { this.log.warn( { areaTag }, 'Attempted access to private area!'); @@ -283,10 +283,10 @@ ${msgBody} return cb(response); }); } else if(selectorMatch[1]) { - // list areas in conf + // list areas in conf const sysConfig = Config(); - const confTag = selectorMatch[1].replace(/\r\n|\//g, ''); - const conf = _.get(sysConfig, [ 'contentServers', 'gopher', 'messageConferences', confTag ]) && getMessageConferenceByTag(confTag); + const confTag = selectorMatch[1].replace(/\r\n|\//g, ''); + const conf = _.get(sysConfig, [ 'contentServers', 'gopher', 'messageConferences', confTag ]) && getMessageConferenceByTag(confTag); if(!conf) { return this.notFoundGenerator(selectorMatch, cb); } @@ -310,10 +310,10 @@ ${msgBody} return cb(response); } else { - // message area base (list confs) + // message area base (list confs) const confs = Object.keys(_.get(Config(), 'contentServers.gopher.messageConferences', {})) .map(confTag => Object.assign( { confTag }, getMessageConferenceByTag(confTag))) - .filter(conf => conf); // remove any baddies + .filter(conf => conf); // remove any baddies if(0 === confs.length) { return cb(this.makeItem(ItemTypes.InfoMessage, 'No message conferences available')); diff --git a/core/servers/content/web.js b/core/servers/content/web.js index c31a3720..7088b8f9 100644 --- a/core/servers/content/web.js +++ b/core/servers/content/web.js @@ -1,24 +1,24 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Log = require('../../logger.js').log; -const ServerModule = require('../../server_module.js').ServerModule; -const Config = require('../../config.js').get; +// ENiGMA½ +const Log = require('../../logger.js').log; +const ServerModule = require('../../server_module.js').ServerModule; +const Config = require('../../config.js').get; -// deps -const http = require('http'); -const https = require('https'); -const _ = require('lodash'); -const fs = require('graceful-fs'); -const paths = require('path'); -const mimeTypes = require('mime-types'); +// deps +const http = require('http'); +const https = require('https'); +const _ = require('lodash'); +const fs = require('graceful-fs'); +const paths = require('path'); +const mimeTypes = require('mime-types'); const ModuleInfo = exports.moduleInfo = { - name : 'Web', - desc : 'Web Server', - author : 'NuSkooler', - packageName : 'codes.l33t.enigma.web.server', + name : 'Web', + desc : 'Web Server', + author : 'NuSkooler', + packageName : 'codes.l33t.enigma.web.server', }; class Route { @@ -39,8 +39,8 @@ class Route { isValid() { return ( this.pathRegExp instanceof RegExp && - ( -1 !== [ 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', ].indexOf(this.method) ) || - !_.isFunction(this.handler) + ( -1 !== [ 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', ].indexOf(this.method) ) || + !_.isFunction(this.handler) ); } @@ -55,28 +55,28 @@ exports.getModule = class WebServerModule extends ServerModule { constructor() { super(); - const config = Config(); - this.enableHttp = config.contentServers.web.http.enabled || false; - this.enableHttps = config.contentServers.web.https.enabled || false; + const config = Config(); + this.enableHttp = config.contentServers.web.http.enabled || false; + this.enableHttps = config.contentServers.web.https.enabled || false; this.routes = {}; if(this.isEnabled() && config.contentServers.web.staticRoot) { this.addRoute({ - method : 'GET', - path : '/static/.*$', - handler : this.routeStaticFile.bind(this), + method : 'GET', + path : '/static/.*$', + handler : this.routeStaticFile.bind(this), }); } } buildUrl(pathAndQuery) { // - // Create a URL such as - // https://l33t.codes:44512/ + |pathAndQuery| + // Create a URL such as + // https://l33t.codes:44512/ + |pathAndQuery| // - // Prefer HTTPS over HTTP. Be explicit about the port - // only if non-standard. Allow users to override full prefix in config. + // Prefer HTTPS over HTTP. Be explicit about the port + // only if non-standard. Allow users to override full prefix in config. // const config = Config(); if(_.isString(config.contentServers.web.overrideUrlPrefix)) { @@ -86,13 +86,13 @@ exports.getModule = class WebServerModule extends ServerModule { let schema; let port; if(config.contentServers.web.https.enabled) { - schema = 'https://'; - port = (443 === config.contentServers.web.https.port) ? + schema = 'https://'; + port = (443 === config.contentServers.web.https.port) ? '' : `:${config.contentServers.web.https.port}`; } else { - schema = 'http://'; - port = (80 === config.contentServers.web.http.port) ? + schema = 'http://'; + port = (80 === config.contentServers.web.http.port) ? '' : `:${config.contentServers.web.http.port}`; } @@ -112,11 +112,11 @@ exports.getModule = class WebServerModule extends ServerModule { const config = Config(); if(this.enableHttps) { const options = { - cert : fs.readFileSync(config.contentServers.web.https.certPem), - key : fs.readFileSync(config.contentServers.web.https.keyPem), + cert : fs.readFileSync(config.contentServers.web.https.certPem), + key : fs.readFileSync(config.contentServers.web.https.keyPem), }; - // additional options + // additional options Object.assign(options, config.contentServers.web.https.options || {} ); this.httpsServer = https.createServer(options, (req, resp) => this.routeRequest(req, resp) ); @@ -178,18 +178,18 @@ exports.getModule = class WebServerModule extends ServerModule { if(err) { return resp.end(` - - - - ${title} - - - -
-

${bodyText}

-
- - ` + + + + ${title} + + + +
+

${bodyText}

+
+ + ` ); } @@ -227,8 +227,8 @@ exports.getModule = class WebServerModule extends ServerModule { } const headers = { - 'Content-Type' : mimeTypes.contentType(paths.basename(filePath)) || mimeTypes.contentType('.bin'), - 'Content-Length' : stats.size, + 'Content-Type' : mimeTypes.contentType(paths.basename(filePath)) || mimeTypes.contentType('.bin'), + 'Content-Length' : stats.size, }; const readStream = fs.createReadStream(filePath); @@ -251,8 +251,8 @@ exports.getModule = class WebServerModule extends ServerModule { } const headers = { - 'Content-Type' : contentType || mimeTypes.contentType('.html'), - 'Content-Length' : finalPage.length, + 'Content-Type' : contentType || mimeTypes.contentType('.html'), + 'Content-Length' : finalPage.length, }; resp.writeHead(200, headers); diff --git a/core/servers/login/ssh.js b/core/servers/login/ssh.js index 982ab0a1..016c215e 100644 --- a/core/servers/login/ssh.js +++ b/core/servers/login/ssh.js @@ -1,37 +1,37 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('../../config.js').get; -const baseClient = require('../../client.js'); -const Log = require('../../logger.js').log; -const LoginServerModule = require('../../login_server_module.js'); -const userLogin = require('../../user_login.js').userLogin; -const enigVersion = require('../../../package.json').version; -const theme = require('../../theme.js'); -const stringFormat = require('../../string_format.js'); +// ENiGMA½ +const Config = require('../../config.js').get; +const baseClient = require('../../client.js'); +const Log = require('../../logger.js').log; +const LoginServerModule = require('../../login_server_module.js'); +const userLogin = require('../../user_login.js').userLogin; +const enigVersion = require('../../../package.json').version; +const theme = require('../../theme.js'); +const stringFormat = require('../../string_format.js'); -// deps -const ssh2 = require('ssh2'); -const fs = require('graceful-fs'); -const util = require('util'); -const _ = require('lodash'); -const assert = require('assert'); +// deps +const ssh2 = require('ssh2'); +const fs = require('graceful-fs'); +const util = require('util'); +const _ = require('lodash'); +const assert = require('assert'); const ModuleInfo = exports.moduleInfo = { - name : 'SSH', - desc : 'SSH Server', - author : 'NuSkooler', - isSecure : true, - packageName : 'codes.l33t.enigma.ssh.server', + name : 'SSH', + desc : 'SSH Server', + author : 'NuSkooler', + isSecure : true, + packageName : 'codes.l33t.enigma.ssh.server', }; function SSHClient(clientConn) { baseClient.Client.apply(this, arguments); // - // WARNING: Until we have emit 'ready', self.input, and self.output and - // not yet defined! + // WARNING: Until we have emit 'ready', self.input, and self.output and + // not yet defined! // const self = this; @@ -39,11 +39,11 @@ function SSHClient(clientConn) { let loginAttempts = 0; clientConn.on('authentication', function authAttempt(ctx) { - const username = ctx.username || ''; - const password = ctx.password || ''; + const username = ctx.username || ''; + const password = ctx.password || ''; - const config = Config(); - self.isNewUser = (config.users.newUserNames || []).indexOf(username) > -1; + const config = Config(); + self.isNewUser = (config.users.newUserNames || []).indexOf(username) > -1; self.log.trace( { method : ctx.method, username : username, newUser : self.isNewUser }, 'SSH authentication attempt'); @@ -58,8 +58,8 @@ function SSHClient(clientConn) { } // - // If the system is open and |isNewUser| is true, the login - // sequence is hijacked in order to start the applicaiton process. + // If the system is open and |isNewUser| is true, the login + // sequence is hijacked in order to start the applicaiton process. // if(false === config.general.closedSystem && self.isNewUser) { return ctx.accept(); @@ -85,7 +85,7 @@ function SSHClient(clientConn) { } if(0 === username.length) { - // :TODO: can we display something here? + // :TODO: can we display something here? return ctx.reject(); } @@ -105,9 +105,9 @@ function SSHClient(clientConn) { } const artOpts = { - client : self, - name : 'SSHPMPT.ASC', - readSauce : false, + client : self, + name : 'SSHPMPT.ASC', + readSauce : false, }; theme.getThemeArt(artOpts, (err, artInfo) => { @@ -136,31 +136,31 @@ function SSHClient(clientConn) { this.updateTermInfo = function(info) { // - // From ssh2 docs: - // "rows and cols override width and height when rows and cols are non-zero." + // From ssh2 docs: + // "rows and cols override width and height when rows and cols are non-zero." // let termHeight; let termWidth; if(info.rows > 0 && info.cols > 0) { - termHeight = info.rows; - termWidth = info.cols; + termHeight = info.rows; + termWidth = info.cols; } else if(info.width > 0 && info.height > 0) { - termHeight = info.height; - termWidth = info.width; + termHeight = info.height; + termWidth = info.width; } assert(_.isObject(self.term)); // - // Note that if we fail here, connect.js attempts some non-standard - // queries/etc., and ultimately will default to 80x24 if all else fails + // Note that if we fail here, connect.js attempts some non-standard + // queries/etc., and ultimately will default to 80x24 if all else fails // if(termHeight > 0 && termWidth > 0) { self.term.termHeight = termHeight; - self.term.termWidth = termWidth; + self.term.termWidth = termWidth; - self.clearMciCache(); // term size changes = invalidate cache + self.clearMciCache(); // term size changes = invalidate cache } if(_.isString(info.term) && info.term.length > 0 && 'unknown' === self.term.termType) { @@ -182,7 +182,7 @@ function SSHClient(clientConn) { accept(); } - if(self.input) { // do we have I/O? + if(self.input) { // do we have I/O? self.updateTermInfo(info); } else { self.cachedTermInfo = info; @@ -203,7 +203,7 @@ function SSHClient(clientConn) { delete self.cachedTermInfo; } - // we're ready! + // we're ready! const firstMenu = self.isNewUser ? Config().loginServers.ssh.firstMenuNewUser : Config().loginServers.ssh.firstMenu; self.emit('ready', { firstMenu : firstMenu } ); }); @@ -222,7 +222,7 @@ function SSHClient(clientConn) { }); clientConn.on('end', () => { - self.emit('end'); // remove client connection/tracking + self.emit('end'); // remove client connection/tracking }); clientConn.on('error', err => { @@ -244,13 +244,13 @@ exports.getModule = class SSHServerModule extends LoginServerModule { const serverConf = { hostKeys : [ { - key : fs.readFileSync(config.loginServers.ssh.privateKeyPem), - passphrase : config.loginServers.ssh.privateKeyPass, + key : fs.readFileSync(config.loginServers.ssh.privateKeyPem), + passphrase : config.loginServers.ssh.privateKeyPass, } ], ident : 'enigma-bbs-' + enigVersion + '-srv', - // Note that sending 'banner' breaks at least EtherTerm! + // Note that sending 'banner' breaks at least EtherTerm! debug : (sshDebugLine) => { if(true === config.loginServers.ssh.traceConnections) { Log.trace(`SSH: ${sshDebugLine}`); diff --git a/core/servers/login/telnet.js b/core/servers/login/telnet.js index ce854013..1f8afa6a 100644 --- a/core/servers/login/telnet.js +++ b/core/servers/login/telnet.js @@ -1,166 +1,166 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const baseClient = require('../../client.js'); -const Log = require('../../logger.js').log; -const LoginServerModule = require('../../login_server_module.js'); -const Config = require('../../config.js').get; -const EnigAssert = require('../../enigma_assert.js'); -const { stringFromNullTermBuffer } = require('../../string_util.js'); +// ENiGMA½ +const baseClient = require('../../client.js'); +const Log = require('../../logger.js').log; +const LoginServerModule = require('../../login_server_module.js'); +const Config = require('../../config.js').get; +const EnigAssert = require('../../enigma_assert.js'); +const { stringFromNullTermBuffer } = require('../../string_util.js'); -// deps -const net = require('net'); -const buffers = require('buffers'); -const { Parser } = require('binary-parser'); -const util = require('util'); +// deps +const net = require('net'); +const buffers = require('buffers'); +const { Parser } = require('binary-parser'); +const util = require('util'); -//var debug = require('debug')('telnet'); +//var debug = require('debug')('telnet'); const ModuleInfo = exports.moduleInfo = { - name : 'Telnet', - desc : 'Telnet Server', - author : 'NuSkooler', - isSecure : false, - packageName : 'codes.l33t.enigma.telnet.server', + name : 'Telnet', + desc : 'Telnet Server', + author : 'NuSkooler', + isSecure : false, + packageName : 'codes.l33t.enigma.telnet.server', }; -exports.TelnetClient = TelnetClient; +exports.TelnetClient = TelnetClient; // -// Telnet Protocol Resources -// * http://pcmicro.com/netfoss/telnet.html -// * http://mud-dev.wikidot.com/telnet:negotiation +// Telnet Protocol Resources +// * http://pcmicro.com/netfoss/telnet.html +// * http://mud-dev.wikidot.com/telnet:negotiation // /* - TODO: - * Document COMMANDS -- add any missing - * Document OPTIONS -- add any missing - * Internally handle OPTIONS: - * Some should be emitted generically - * Some should be handled internally -- denied, handled, etc. - * + TODO: + * Document COMMANDS -- add any missing + * Document OPTIONS -- add any missing + * Internally handle OPTIONS: + * Some should be emitted generically + * Some should be handled internally -- denied, handled, etc. + * - * Allow term (ttype) to be set by environ sub negotiation + * Allow term (ttype) to be set by environ sub negotiation - * Process terms in loop.... research needed + * Process terms in loop.... research needed - * Handle will/won't - * Handle do's, .. - * Some won't should close connection + * Handle will/won't + * Handle do's, .. + * Some won't should close connection - * Options/Commands we don't understand shouldn't crash the server!! + * Options/Commands we don't understand shouldn't crash the server!! */ const COMMANDS = { - SE : 240, // End of Sub-Negotation Parameters - NOP : 241, // No Operation - DM : 242, // Data Mark - BRK : 243, // Break - IP : 244, // Interrupt Process - AO : 245, // Abort Output - AYT : 246, // Are You There? - EC : 247, // Erase Character - EL : 248, // Erase Line - GA : 249, // Go Ahead - SB : 250, // Start Sub-Negotiation Parameters - WILL : 251, // - WONT : 252, - DO : 253, - DONT : 254, - IAC : 255, // (Data Byte) + SE : 240, // End of Sub-Negotation Parameters + NOP : 241, // No Operation + DM : 242, // Data Mark + BRK : 243, // Break + IP : 244, // Interrupt Process + AO : 245, // Abort Output + AYT : 246, // Are You There? + EC : 247, // Erase Character + EL : 248, // Erase Line + GA : 249, // Go Ahead + SB : 250, // Start Sub-Negotiation Parameters + WILL : 251, // + WONT : 252, + DO : 253, + DONT : 254, + IAC : 255, // (Data Byte) }; // -// Resources: -// * http://www.faqs.org/rfcs/rfc1572.html +// Resources: +// * http://www.faqs.org/rfcs/rfc1572.html // const SB_COMMANDS = { - IS : 0, - SEND : 1, - INFO : 2, + IS : 0, + SEND : 1, + INFO : 2, }; // -// Telnet Options +// Telnet Options // -// Resources -// * http://mars.netanya.ac.il/~unesco/cdrom/booklet/HTML/NETWORKING/node300.html -// * http://www.networksorcery.com/enp/protocol/telnet.htm +// Resources +// * http://mars.netanya.ac.il/~unesco/cdrom/booklet/HTML/NETWORKING/node300.html +// * http://www.networksorcery.com/enp/protocol/telnet.htm // const OPTIONS = { - TRANSMIT_BINARY : 0, // http://tools.ietf.org/html/rfc856 - ECHO : 1, // http://tools.ietf.org/html/rfc857 - // RECONNECTION : 2 - SUPPRESS_GO_AHEAD : 3, // aka 'SGA': RFC 858 @ http://tools.ietf.org/html/rfc858 - //APPROX_MESSAGE_SIZE : 4 - STATUS : 5, // http://tools.ietf.org/html/rfc859 - TIMING_MARK : 6, // http://tools.ietf.org/html/rfc860 - //RC_TRANS_AND_ECHO : 7, // aka 'RCTE' @ http://www.rfc-base.org/txt/rfc-726.txt - //OUPUT_LINE_WIDTH : 8, - //OUTPUT_PAGE_SIZE : 9, // - //OUTPUT_CARRIAGE_RETURN_DISP : 10, // RFC 652 - //OUTPUT_HORIZ_TABSTOPS : 11, // RFC 653 - //OUTPUT_HORIZ_TAB_DISP : 12, // RFC 654 - //OUTPUT_FORMFEED_DISP : 13, // RFC 655 - //OUTPUT_VERT_TABSTOPS : 14, // RFC 656 - //OUTPUT_VERT_TAB_DISP : 15, // RFC 657 - //OUTPUT_LF_DISP : 16, // RFC 658 - //EXTENDED_ASCII : 17, // RFC 659 - //LOGOUT : 18, // RFC 727 - //BYTE_MACRO : 19, // RFC 753 - //DATA_ENTRY_TERMINAL : 20, // RFC 1043 - //SUPDUP : 21, // RFC 736 - //SUPDUP_OUTPUT : 22, // RFC 749 - SEND_LOCATION : 23, // RFC 779 - TERMINAL_TYPE : 24, // aka 'TTYPE': RFC 1091 @ http://tools.ietf.org/html/rfc1091 - //END_OF_RECORD : 25, // RFC 885 - //TACACS_USER_ID : 26, // RFC 927 - //OUTPUT_MARKING : 27, // RFC 933 - //TERMINCAL_LOCATION_NUMBER : 28, // RFC 946 - //TELNET_3270_REGIME : 29, // RFC 1041 - WINDOW_SIZE : 31, // aka 'NAWS': RFC 1073 @ http://tools.ietf.org/html/rfc1073 - TERMINAL_SPEED : 32, // RFC 1079 @ http://tools.ietf.org/html/rfc1079 - REMOTE_FLOW_CONTROL : 33, // RFC 1072 @ http://tools.ietf.org/html/rfc1372 - LINEMODE : 34, // RFC 1184 @ http://tools.ietf.org/html/rfc1184 - X_DISPLAY_LOCATION : 35, // aka 'XDISPLOC': RFC 1096 @ http://tools.ietf.org/html/rfc1096 - NEW_ENVIRONMENT_DEP : 36, // aka 'NEW-ENVIRON': RFC 1408 @ http://tools.ietf.org/html/rfc1408 (note: RFC 1572 is an update to this) - AUTHENTICATION : 37, // RFC 2941 @ http://tools.ietf.org/html/rfc2941 - ENCRYPT : 38, // RFC 2946 @ http://tools.ietf.org/html/rfc2946 - NEW_ENVIRONMENT : 39, // aka 'NEW-ENVIRON': RFC 1572 @ http://tools.ietf.org/html/rfc1572 (note: update to RFC 1408) - //TN3270E : 40, // RFC 2355 - //XAUTH : 41, - //CHARSET : 42, // RFC 2066 - //REMOTE_SERIAL_PORT : 43, - //COM_PORT_CONTROL : 44, // RFC 2217 - //SUPRESS_LOCAL_ECHO : 45, - //START_TLS : 46, - //KERMIT : 47, // RFC 2840 - //SEND_URL : 48, - //FORWARD_X : 49, + TRANSMIT_BINARY : 0, // http://tools.ietf.org/html/rfc856 + ECHO : 1, // http://tools.ietf.org/html/rfc857 + // RECONNECTION : 2 + SUPPRESS_GO_AHEAD : 3, // aka 'SGA': RFC 858 @ http://tools.ietf.org/html/rfc858 + //APPROX_MESSAGE_SIZE : 4 + STATUS : 5, // http://tools.ietf.org/html/rfc859 + TIMING_MARK : 6, // http://tools.ietf.org/html/rfc860 + //RC_TRANS_AND_ECHO : 7, // aka 'RCTE' @ http://www.rfc-base.org/txt/rfc-726.txt + //OUPUT_LINE_WIDTH : 8, + //OUTPUT_PAGE_SIZE : 9, // + //OUTPUT_CARRIAGE_RETURN_DISP : 10, // RFC 652 + //OUTPUT_HORIZ_TABSTOPS : 11, // RFC 653 + //OUTPUT_HORIZ_TAB_DISP : 12, // RFC 654 + //OUTPUT_FORMFEED_DISP : 13, // RFC 655 + //OUTPUT_VERT_TABSTOPS : 14, // RFC 656 + //OUTPUT_VERT_TAB_DISP : 15, // RFC 657 + //OUTPUT_LF_DISP : 16, // RFC 658 + //EXTENDED_ASCII : 17, // RFC 659 + //LOGOUT : 18, // RFC 727 + //BYTE_MACRO : 19, // RFC 753 + //DATA_ENTRY_TERMINAL : 20, // RFC 1043 + //SUPDUP : 21, // RFC 736 + //SUPDUP_OUTPUT : 22, // RFC 749 + SEND_LOCATION : 23, // RFC 779 + TERMINAL_TYPE : 24, // aka 'TTYPE': RFC 1091 @ http://tools.ietf.org/html/rfc1091 + //END_OF_RECORD : 25, // RFC 885 + //TACACS_USER_ID : 26, // RFC 927 + //OUTPUT_MARKING : 27, // RFC 933 + //TERMINCAL_LOCATION_NUMBER : 28, // RFC 946 + //TELNET_3270_REGIME : 29, // RFC 1041 + WINDOW_SIZE : 31, // aka 'NAWS': RFC 1073 @ http://tools.ietf.org/html/rfc1073 + TERMINAL_SPEED : 32, // RFC 1079 @ http://tools.ietf.org/html/rfc1079 + REMOTE_FLOW_CONTROL : 33, // RFC 1072 @ http://tools.ietf.org/html/rfc1372 + LINEMODE : 34, // RFC 1184 @ http://tools.ietf.org/html/rfc1184 + X_DISPLAY_LOCATION : 35, // aka 'XDISPLOC': RFC 1096 @ http://tools.ietf.org/html/rfc1096 + NEW_ENVIRONMENT_DEP : 36, // aka 'NEW-ENVIRON': RFC 1408 @ http://tools.ietf.org/html/rfc1408 (note: RFC 1572 is an update to this) + AUTHENTICATION : 37, // RFC 2941 @ http://tools.ietf.org/html/rfc2941 + ENCRYPT : 38, // RFC 2946 @ http://tools.ietf.org/html/rfc2946 + NEW_ENVIRONMENT : 39, // aka 'NEW-ENVIRON': RFC 1572 @ http://tools.ietf.org/html/rfc1572 (note: update to RFC 1408) + //TN3270E : 40, // RFC 2355 + //XAUTH : 41, + //CHARSET : 42, // RFC 2066 + //REMOTE_SERIAL_PORT : 43, + //COM_PORT_CONTROL : 44, // RFC 2217 + //SUPRESS_LOCAL_ECHO : 45, + //START_TLS : 46, + //KERMIT : 47, // RFC 2840 + //SEND_URL : 48, + //FORWARD_X : 49, - //PRAGMA_LOGON : 138, - //SSPI_LOGON : 139, - //PRAGMA_HEARTBEAT : 140 + //PRAGMA_LOGON : 138, + //SSPI_LOGON : 139, + //PRAGMA_HEARTBEAT : 140 - ARE_YOU_THERE : 246, // aka 'AYT' RFC 854 @ https://tools.ietf.org/html/rfc854 + ARE_YOU_THERE : 246, // aka 'AYT' RFC 854 @ https://tools.ietf.org/html/rfc854 - EXTENDED_OPTIONS_LIST : 255, // RFC 861 (STD 32) + EXTENDED_OPTIONS_LIST : 255, // RFC 861 (STD 32) }; -// Commands used within NEW_ENVIRONMENT[_DEP] +// Commands used within NEW_ENVIRONMENT[_DEP] const NEW_ENVIRONMENT_COMMANDS = { - VAR : 0, - VALUE : 1, - ESC : 2, - USERVAR : 3, + VAR : 0, + VALUE : 1, + ESC : 2, + USERVAR : 3, }; -const IAC_BUF = Buffer.from([ COMMANDS.IAC ]); -const IAC_SE_BUF = Buffer.from([ COMMANDS.IAC, COMMANDS.SE ]); +const IAC_BUF = Buffer.from([ COMMANDS.IAC ]); +const IAC_SE_BUF = Buffer.from([ COMMANDS.IAC, COMMANDS.SE ]); const COMMAND_NAMES = Object.keys(COMMANDS).reduce(function(names, name) { names[COMMANDS[name]] = name.toLowerCase(); @@ -178,9 +178,9 @@ const COMMAND_IMPLS = {}; }; }); -// :TODO: See TooTallNate's telnet.js: Handle COMMAND_IMPL for IAC in binary mode +// :TODO: See TooTallNate's telnet.js: Handle COMMAND_IMPL for IAC in binary mode -// Create option names such as 'transmit binary' -> OPTIONS.TRANSMIT_BINARY +// Create option names such as 'transmit binary' -> OPTIONS.TRANSMIT_BINARY const OPTION_NAMES = Object.keys(OPTIONS).reduce(function(names, name) { names[OPTIONS[name]] = name.toLowerCase().replace(/_/g, ' '); return names; @@ -193,19 +193,19 @@ function unknownOption(bufs, i, event) { } const OPTION_IMPLS = {}; -// :TODO: fill in the rest... -OPTION_IMPLS.NO_ARGS = -OPTION_IMPLS[OPTIONS.ECHO] = -OPTION_IMPLS[OPTIONS.STATUS] = -OPTION_IMPLS[OPTIONS.LINEMODE] = -OPTION_IMPLS[OPTIONS.TRANSMIT_BINARY] = -OPTION_IMPLS[OPTIONS.AUTHENTICATION] = -OPTION_IMPLS[OPTIONS.TERMINAL_SPEED] = -OPTION_IMPLS[OPTIONS.REMOTE_FLOW_CONTROL] = -OPTION_IMPLS[OPTIONS.X_DISPLAY_LOCATION] = -OPTION_IMPLS[OPTIONS.SEND_LOCATION] = -OPTION_IMPLS[OPTIONS.ARE_YOU_THERE] = -OPTION_IMPLS[OPTIONS.SUPPRESS_GO_AHEAD] = function(bufs, i, event) { +// :TODO: fill in the rest... +OPTION_IMPLS.NO_ARGS = +OPTION_IMPLS[OPTIONS.ECHO] = +OPTION_IMPLS[OPTIONS.STATUS] = +OPTION_IMPLS[OPTIONS.LINEMODE] = +OPTION_IMPLS[OPTIONS.TRANSMIT_BINARY] = +OPTION_IMPLS[OPTIONS.AUTHENTICATION] = +OPTION_IMPLS[OPTIONS.TERMINAL_SPEED] = +OPTION_IMPLS[OPTIONS.REMOTE_FLOW_CONTROL] = +OPTION_IMPLS[OPTIONS.X_DISPLAY_LOCATION] = +OPTION_IMPLS[OPTIONS.SEND_LOCATION] = +OPTION_IMPLS[OPTIONS.ARE_YOU_THERE] = +OPTION_IMPLS[OPTIONS.SUPPRESS_GO_AHEAD] = function(bufs, i, event) { event.buf = bufs.splice(0, i).toBuffer(); return event; }; @@ -214,12 +214,12 @@ OPTION_IMPLS[OPTIONS.TERMINAL_TYPE] = function(bufs, i, event) { if(event.commandCode !== COMMANDS.SB) { OPTION_IMPLS.NO_ARGS(bufs, i, event); } else { - // We need 4 bytes header + data + IAC SE + // We need 4 bytes header + data + IAC SE if(bufs.length < 7) { return MORE_DATA_REQUIRED; } - const end = bufs.indexOf(IAC_SE_BUF, 5); // look past header bytes + const end = bufs.indexOf(IAC_SE_BUF, 5); // look past header bytes if(-1 === end) { return MORE_DATA_REQUIRED; } @@ -232,10 +232,10 @@ OPTION_IMPLS[OPTIONS.TERMINAL_TYPE] = function(bufs, i, event) { .uint8('opt') .uint8('is') .array('ttype', { - type : 'uint8', - readUntil : b => 255 === b, // 255=COMMANDS.IAC + type : 'uint8', + readUntil : b => 255 === b, // 255=COMMANDS.IAC }) - // note we read iac2 above + // note we read iac2 above .uint8('se') .parse(bufs.toBuffer()); } catch(e) { @@ -248,10 +248,10 @@ OPTION_IMPLS[OPTIONS.TERMINAL_TYPE] = function(bufs, i, event) { EnigAssert(OPTIONS.TERMINAL_TYPE === ttypeCmd.opt); EnigAssert(SB_COMMANDS.IS === ttypeCmd.is); EnigAssert(ttypeCmd.ttype.length > 0); - // note we found IAC_SE above + // note we found IAC_SE above - // some terminals such as NetRunner provide a NULL-terminated buffer - // slice to remove IAC + // some terminals such as NetRunner provide a NULL-terminated buffer + // slice to remove IAC event.ttype = stringFromNullTermBuffer(ttypeCmd.ttype.slice(0, -1), 'ascii'); bufs.splice(0, end); @@ -264,7 +264,7 @@ OPTION_IMPLS[OPTIONS.WINDOW_SIZE] = function(bufs, i, event) { if(event.commandCode !== COMMANDS.SB) { OPTION_IMPLS.NO_ARGS(bufs, i, event); } else { - // we need 9 bytes + // we need 9 bytes if(bufs.length < 9) { return MORE_DATA_REQUIRED; } @@ -291,39 +291,39 @@ OPTION_IMPLS[OPTIONS.WINDOW_SIZE] = function(bufs, i, event) { EnigAssert(COMMANDS.IAC === nawsCmd.iac2); EnigAssert(COMMANDS.SE === nawsCmd.se); - event.cols = event.columns = event.width = nawsCmd.width; - event.rows = event.height = nawsCmd.height; + event.cols = event.columns = event.width = nawsCmd.width; + event.rows = event.height = nawsCmd.height; } return event; }; -// Build an array of delimiters for parsing NEW_ENVIRONMENT[_DEP] +// Build an array of delimiters for parsing NEW_ENVIRONMENT[_DEP] const NEW_ENVIRONMENT_DELIMITERS = []; Object.keys(NEW_ENVIRONMENT_COMMANDS).forEach(function onKey(k) { NEW_ENVIRONMENT_DELIMITERS.push(NEW_ENVIRONMENT_COMMANDS[k]); }); -// Handle the deprecated RFC 1408 & the updated RFC 1572: -OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT_DEP] = -OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) { +// Handle the deprecated RFC 1408 & the updated RFC 1572: +OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT_DEP] = +OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) { if(event.commandCode !== COMMANDS.SB) { OPTION_IMPLS.NO_ARGS(bufs, i, event); } else { // - // We need 4 bytes header + + IAC SE - // Many terminals send a empty list: - // IAC SB NEW-ENVIRON IS IAC SE + // We need 4 bytes header + + IAC SE + // Many terminals send a empty list: + // IAC SB NEW-ENVIRON IS IAC SE // if(bufs.length < 6) { return MORE_DATA_REQUIRED; } - let end = bufs.indexOf(IAC_SE_BUF, 4); // look past header bytes + let end = bufs.indexOf(IAC_SE_BUF, 4); // look past header bytes if(-1 === end) { return MORE_DATA_REQUIRED; } - // :TODO: It's likely that we could do all the env name/value parsing directly in Parser. + // :TODO: It's likely that we could do all the env name/value parsing directly in Parser. let envCmd; try { @@ -331,12 +331,12 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) { .uint8('iac1') .uint8('sb') .uint8('opt') - .uint8('isOrInfo') // IS=initial, INFO=updates + .uint8('isOrInfo') // IS=initial, INFO=updates .array('envBlock', { type : 'uint8', - readUntil : b => 255 === b, // 255=COMMANDS.IAC + readUntil : b => 255 === b, // 255=COMMANDS.IAC }) - // note we consume IAC above + // note we consume IAC above .uint8('se') .parse(bufs.splice(0, bufs.length).toBuffer()); } catch(e) { @@ -350,34 +350,34 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) { EnigAssert(SB_COMMANDS.IS === envCmd.isOrInfo || SB_COMMANDS.INFO === envCmd.isOrInfo); if(OPTIONS.NEW_ENVIRONMENT_DEP === envCmd.opt) { - // :TODO: we should probably support this for legacy clients? + // :TODO: we should probably support this for legacy clients? Log.warn('Handling deprecated RFC 1408 NEW-ENVIRON'); } - const envBuf = envCmd.envBlock.slice(0, -1); // remove IAC + const envBuf = envCmd.envBlock.slice(0, -1); // remove IAC - if(envBuf.length < 4) { // TYPE + single char name + sep + single char value - // empty env block + if(envBuf.length < 4) { // TYPE + single char name + sep + single char value + // empty env block return event; } const States = { - Name : 1, - Value : 2, + Name : 1, + Value : 2, }; let state = States.Name; const setVars = {}; const delVars = []; let varName; - // :TODO: handle ESC type!!! + // :TODO: handle ESC type!!! while(envBuf.length) { switch(state) { case States.Name : { const type = parseInt(envBuf.splice(0, 1)); if(![ NEW_ENVIRONMENT_COMMANDS.VAR, NEW_ENVIRONMENT_COMMANDS.USERVAR, NEW_ENVIRONMENT_COMMANDS.ESC ].includes(type)) { - return event; // fail :( + return event; // fail :( } let nameEnd = envBuf.indexOf(NEW_ENVIRONMENT_COMMANDS.VALUE); @@ -387,7 +387,7 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) { varName = envBuf.splice(0, nameEnd); if(!varName) { - return event; // something is wrong. + return event; // something is wrong. } varName = Buffer.from(varName).toString('ascii'); @@ -397,7 +397,7 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) { state = States.Value; } else { state = States.Name; - delVars.push(varName); // no value; del this var + delVars.push(varName); // no value; del this var } } break; @@ -423,15 +423,15 @@ OPTION_IMPLS[OPTIONS.NEW_ENVIRONMENT] = function(bufs, i, event) { } } - // :TODO: Handle deleting previously set vars via delVars - event.type = envCmd.isOrInfo; - event.envVars = setVars; + // :TODO: Handle deleting previously set vars via delVars + event.type = envCmd.isOrInfo; + event.envVars = setVars; } return event; }; -const MORE_DATA_REQUIRED = 0xfeedface; +const MORE_DATA_REQUIRED = 0xfeedface; function parseBufs(bufs) { EnigAssert(bufs.length >= 2); @@ -440,16 +440,16 @@ function parseBufs(bufs) { } function parseCommand(bufs, i, event) { - const command = bufs.get(i); // :TODO: fix deprecation... [i] is not the same - event.commandCode = command; - event.command = COMMAND_NAMES[command]; + const command = bufs.get(i); // :TODO: fix deprecation... [i] is not the same + event.commandCode = command; + event.command = COMMAND_NAMES[command]; const handler = COMMAND_IMPLS[command]; if(handler) { return handler(bufs, i + 1, event); } else { if(2 !== bufs.length) { - Log.warn( { bufsLength : bufs.length }, 'Expected bufs length of 2'); // expected: IAC + COMMAND + Log.warn( { bufsLength : bufs.length }, 'Expected bufs length of 2'); // expected: IAC + COMMAND } event.buf = bufs.splice(0, 2).toBuffer(); @@ -458,9 +458,9 @@ function parseCommand(bufs, i, event) { } function parseOption(bufs, i, event) { - const option = bufs.get(i); // :TODO: fix deprecation... [i] is not the same - event.optionCode = option; - event.option = OPTION_NAMES[option]; + const option = bufs.get(i); // :TODO: fix deprecation... [i] is not the same + event.optionCode = option; + event.option = OPTION_NAMES[option]; const handler = OPTION_IMPLS[option]; return handler ? handler(bufs, i + 1, event) : unknownOption(bufs, i + 1, event); @@ -470,20 +470,20 @@ function parseOption(bufs, i, event) { function TelnetClient(input, output) { baseClient.Client.apply(this, arguments); - const self = this; + const self = this; - let bufs = buffers(); - this.bufs = bufs; + let bufs = buffers(); + this.bufs = bufs; - this.sentDont = {}; // DON'T's we've already sent + this.sentDont = {}; // DON'T's we've already sent this.setInputOutput(input, output); - this.negotiationsComplete = false; // are we in the 'negotiation' phase? - this.didReady = false; // have we emit the 'ready' event? + this.negotiationsComplete = false; // are we in the 'negotiation' phase? + this.didReady = false; // have we emit the 'ready' event? this.subNegotiationState = { - newEnvironRequested : false, + newEnvironRequested : false, }; this.dataHandler = function(b) { @@ -498,7 +498,7 @@ function TelnetClient(input, output) { while((i = bufs.indexOf(IAC_BUF)) >= 0) { // - // Some clients will send even IAC separate from data + // Some clients will send even IAC separate from data // if(bufs.length <= (i + 1)) { i = MORE_DATA_REQUIRED; @@ -517,7 +517,7 @@ function TelnetClient(input, output) { break; } else if(i) { if(i.option) { - self.emit(i.option, i); // "transmit binary", "echo", ... + self.emit(i.option, i); // "transmit binary", "echo", ... } self.handleTelnetEvent(i); @@ -530,8 +530,8 @@ function TelnetClient(input, output) { if(MORE_DATA_REQUIRED !== i && bufs.length > 0) { // - // Standard data payload. This can still be "non-user" data - // such as ANSI control, but we don't handle that here. + // Standard data payload. This can still be "non-user" data + // such as ANSI control, but we don't handle that here. // self.emit('data', bufs.splice(0).toBuffer()); } @@ -576,7 +576,7 @@ function TelnetClient(input, output) { util.inherits(TelnetClient, baseClient.Client); /////////////////////////////////////////////////////////////////////////////// -// Telnet Command/Option handling +// Telnet Command/Option handling /////////////////////////////////////////////////////////////////////////////// TelnetClient.prototype.handleTelnetEvent = function(evt) { @@ -584,14 +584,14 @@ TelnetClient.prototype.handleTelnetEvent = function(evt) { return this.connectionWarn( { evt : evt }, 'No command for event'); } - // handler name e.g. 'handleWontCommand' + // handler name e.g. 'handleWontCommand' const handlerName = `handle${evt.command.charAt(0).toUpperCase()}${evt.command.substr(1)}Command`; if(this[handlerName]) { - // specialized + // specialized this[handlerName](evt); } else { - // generic-ish + // generic-ish this.handleMiscCommand(evt); } }; @@ -599,16 +599,16 @@ TelnetClient.prototype.handleTelnetEvent = function(evt) { TelnetClient.prototype.handleWillCommand = function(evt) { if('terminal type' === evt.option) { // - // See RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html + // See RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html // this.requestTerminalType(); } else if('new environment' === evt.option) { // - // See RFC 1572 @ http://www.faqs.org/rfcs/rfc1572.html + // See RFC 1572 @ http://www.faqs.org/rfcs/rfc1572.html // this.requestNewEnvironment(); } else { - // :TODO: temporary: + // :TODO: temporary: this.connectionTrace(evt, 'WILL'); } }; @@ -628,20 +628,20 @@ TelnetClient.prototype.handleWontCommand = function(evt) { }; TelnetClient.prototype.handleDoCommand = function(evt) { - // :TODO: handle the rest, e.g. echo nd the like + // :TODO: handle the rest, e.g. echo nd the like if('linemode' === evt.option) { // - // Client wants to enable linemode editing. Denied. + // Client wants to enable linemode editing. Denied. // this.wont.linemode(); } else if('encrypt' === evt.option) { // - // Client wants to enable encryption. Denied. + // Client wants to enable encryption. Denied. // this.wont.encrypt(); } else { - // :TODO: temporary: + // :TODO: temporary: this.connectionTrace(evt, 'DO'); } }; @@ -655,33 +655,33 @@ TelnetClient.prototype.handleSbCommand = function(evt) { if('terminal type' === evt.option) { // - // See RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html + // See RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html // - // :TODO: According to RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html - // We should keep asking until we see a repeat. From there, determine the best type/etc. + // :TODO: According to RFC 1091 @ http://www.faqs.org/rfcs/rfc1091.html + // We should keep asking until we see a repeat. From there, determine the best type/etc. self.setTermType(evt.ttype); - self.negotiationsComplete = true; // :TODO: throw in a array of what we've taken care. Complete = array satisified or timeout + self.negotiationsComplete = true; // :TODO: throw in a array of what we've taken care. Complete = array satisified or timeout self.readyNow(); } else if('new environment' === evt.option) { // - // Handling is as follows: - // * Map 'TERM' -> 'termType' and only update if ours is 'unknown' - // * Map COLUMNS -> 'termWidth' and only update if ours is 0 - // * Map ROWS -> 'termHeight' and only update if ours is 0 - // * Add any new variables, ignore any existing + // Handling is as follows: + // * Map 'TERM' -> 'termType' and only update if ours is 'unknown' + // * Map COLUMNS -> 'termWidth' and only update if ours is 0 + // * Map ROWS -> 'termHeight' and only update if ours is 0 + // * Add any new variables, ignore any existing // Object.keys(evt.envVars || {} ).forEach(function onEnv(name) { if('TERM' === name && 'unknown' === self.term.termType) { self.setTermType(evt.envVars[name]); } else if('COLUMNS' === name && 0 === self.term.termWidth) { self.term.termWidth = parseInt(evt.envVars[name]); - self.clearMciCache(); // term size changes = invalidate cache + self.clearMciCache(); // term size changes = invalidate cache self.connectionDebug({ termWidth : self.term.termWidth, source : 'NEW-ENVIRON'}, 'Window width updated'); } else if('ROWS' === name && 0 === self.term.termHeight) { self.term.termHeight = parseInt(evt.envVars[name]); - self.clearMciCache(); // term size changes = invalidate cache + self.clearMciCache(); // term size changes = invalidate cache self.connectionDebug({ termHeight : self.term.termHeight, source : 'NEW-ENVIRON'}, 'Window height updated'); } else { if(name in self.term.env) { @@ -704,11 +704,11 @@ TelnetClient.prototype.handleSbCommand = function(evt) { } else if('window size' === evt.option) { // - // Update termWidth & termHeight. - // Set LINES and COLUMNS environment variables as well. + // Update termWidth & termHeight. + // Set LINES and COLUMNS environment variables as well. // - self.term.termWidth = evt.width; - self.term.termHeight = evt.height; + self.term.termWidth = evt.width; + self.term.termHeight = evt.height; if(evt.width > 0) { self.term.env.COLUMNS = evt.height; @@ -718,7 +718,7 @@ TelnetClient.prototype.handleSbCommand = function(evt) { self.term.env.ROWS = evt.height; } - self.clearMciCache(); // term size changes = invalidate cache + self.clearMciCache(); // term size changes = invalidate cache self.connectionDebug({ termWidth : evt.width , termHeight : evt.height, source : 'NAWS' }, 'Window size updated'); } else { @@ -736,11 +736,11 @@ TelnetClient.prototype.handleMiscCommand = function(evt) { EnigAssert(evt.command !== 'undefined' && evt.command.length > 0); // - // See: - // * RFC 854 @ http://tools.ietf.org/html/rfc854 + // See: + // * RFC 854 @ http://tools.ietf.org/html/rfc854 // if('ip' === evt.command) { - // Interrupt Process (IP) + // Interrupt Process (IP) this.log.debug('Interrupt Process (IP) - Ending'); this.input.end(); @@ -817,33 +817,33 @@ TelnetClient.prototype.banner = function() { }; function Command(command, client) { - this.command = COMMANDS[command.toUpperCase()]; - this.client = client; + this.command = COMMANDS[command.toUpperCase()]; + this.client = client; } -// Create Command objects with echo, transmit_binary, ... +// Create Command objects with echo, transmit_binary, ... Object.keys(OPTIONS).forEach(function(name) { const code = OPTIONS[name]; Command.prototype[name.toLowerCase()] = function() { const buf = Buffer.alloc(3); - buf[0] = COMMANDS.IAC; - buf[1] = this.command; - buf[2] = code; + buf[0] = COMMANDS.IAC; + buf[1] = this.command; + buf[2] = code; return this.client.output.write(buf); }; }); -// Create do, dont, etc. methods on Client +// Create do, dont, etc. methods on Client ['do', 'dont', 'will', 'wont'].forEach(function(command) { const get = function() { return new Command(command, this); }; Object.defineProperty(TelnetClient.prototype, command, { - get : get, - enumerable : true, - configurable : true + get : get, + enumerable : true, + configurable : true }); }); @@ -861,9 +861,9 @@ exports.getModule = class TelnetServerModule extends LoginServerModule { this.handleNewClient(client, sock, ModuleInfo); // - // Set a timeout and attempt to proceed even if we don't know - // the term type yet, which is the preferred trigger - // for moving along + // Set a timeout and attempt to proceed even if we don't know + // the term type yet, which is the preferred trigger + // for moving along // setTimeout( () => { if(!client.didReady) { diff --git a/core/servers/login/websocket.js b/core/servers/login/websocket.js index f7dac07d..fa7d97a3 100644 --- a/core/servers/login/websocket.js +++ b/core/servers/login/websocket.js @@ -1,25 +1,25 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('../../config.js').get; -const TelnetClient = require('./telnet.js').TelnetClient; -const Log = require('../../logger.js').log; -const LoginServerModule = require('../../login_server_module.js'); +// ENiGMA½ +const Config = require('../../config.js').get; +const TelnetClient = require('./telnet.js').TelnetClient; +const Log = require('../../logger.js').log; +const LoginServerModule = require('../../login_server_module.js'); -// deps -const _ = require('lodash'); -const WebSocketServer = require('ws').Server; -const http = require('http'); -const https = require('https'); -const fs = require('graceful-fs'); -const Writable = require('stream'); +// deps +const _ = require('lodash'); +const WebSocketServer = require('ws').Server; +const http = require('http'); +const https = require('https'); +const fs = require('graceful-fs'); +const Writable = require('stream'); const ModuleInfo = exports.moduleInfo = { - name : 'WebSocket', - desc : 'WebSocket Server', - author : 'NuSkooler', - packageName : 'codes.l33t.enigma.websocket.server', + name : 'WebSocket', + desc : 'WebSocket Server', + author : 'NuSkooler', + packageName : 'codes.l33t.enigma.websocket.server', }; function WebSocketClient(ws, req, serverType) { @@ -35,8 +35,8 @@ function WebSocketClient(ws, req, serverType) { }; // - // This bridge makes accessible various calls that client sub classes - // want to access on I/O socket + // This bridge makes accessible various calls that client sub classes + // want to access on I/O socket // this.socketBridge = new class SocketBridge extends Writable { constructor(ws) { @@ -49,12 +49,12 @@ function WebSocketClient(ws, req, serverType) { } write(data, cb) { - cb = cb || ( () => { /* eat it up */} ); // handle data writes after close + cb = cb || ( () => { /* eat it up */} ); // handle data writes after close return this.ws.send(data, { binary : true }, cb); } - // we need to fake some streaming work + // we need to fake some streaming work unpipe() { Log.trace('WebSocket SocketBridge unpipe()'); } @@ -64,7 +64,7 @@ function WebSocketClient(ws, req, serverType) { } get remoteAddress() { - // Support X-Forwarded-For and X-Real-IP headers for proxied connections + // Support X-Forwarded-For and X-Real-IP headers for proxied connections return (self.proxied && (req.headers['x-forwarded-for'] || req.headers['x-real-ip'])) || req.connection.remoteAddress; } }(ws); @@ -72,12 +72,12 @@ function WebSocketClient(ws, req, serverType) { ws.on('message', this.dataHandler); ws.on('close', () => { - // we'll remove client connection which will in turn end() via our SocketBridge above + // we'll remove client connection which will in turn end() via our SocketBridge above return this.emit('end'); }); // - // Montior connection status with ping/pong + // Montior connection status with ping/pong // ws.on('pong', () => { Log.trace(`Pong from ${this.socketBridge.remoteAddress}`); @@ -89,11 +89,11 @@ function WebSocketClient(ws, req, serverType) { Log.trace( { headers : req.headers }, 'WebSocket connection headers' ); // - // If the config allows it, look for 'x-forwarded-proto' as "https" - // to override |isSecure| + // If the config allows it, look for 'x-forwarded-proto' as "https" + // to override |isSecure| // if(true === _.get(Config(), 'loginServers.webSocket.proxied') && - 'https' === req.headers['x-forwarded-proto']) + 'https' === req.headers['x-forwarded-proto']) { Log.debug(`Assuming secure connection due to X-Forwarded-Proto of "${req.headers['x-forwarded-proto']}"`); this.proxied = true; @@ -101,7 +101,7 @@ function WebSocketClient(ws, req, serverType) { this.proxied = false; } - // start handshake process + // start handshake process this.banner(); } @@ -116,40 +116,40 @@ exports.getModule = class WebSocketLoginServer extends LoginServerModule { createServer() { // - // We will actually create up to two servers: - // * insecure websocket (ws://) - // * secure (tls) websocket (wss://) + // We will actually create up to two servers: + // * insecure websocket (ws://) + // * secure (tls) websocket (wss://) // const config = _.get(Config(), 'loginServers.webSocket'); if(!_.isObject(config)) { return; } - const wsPort = _.get(config, 'ws.port'); - const wssPort = _.get(config, 'wss.port'); + const wsPort = _.get(config, 'ws.port'); + const wssPort = _.get(config, 'wss.port'); if(true === _.get(config, 'ws.enabled') && _.isNumber(wsPort)) { const httpServer = http.createServer( (req, resp) => { - // dummy handler + // dummy handler resp.writeHead(200); return resp.end('ENiGMA½ BBS WebSocket Server!'); }); this.insecure = { - httpServer : httpServer, - wsServer : new WebSocketServer( { server : httpServer } ), + httpServer : httpServer, + wsServer : new WebSocketServer( { server : httpServer } ), }; } if(_.isObject(config, 'wss') && true === _.get(config, 'wss.enabled') && _.isNumber(wssPort)) { const httpServer = https.createServer({ - key : fs.readFileSync(config.wss.keyPem), - cert : fs.readFileSync(config.wss.certPem), + key : fs.readFileSync(config.wss.keyPem), + cert : fs.readFileSync(config.wss.certPem), }); this.secure = { - httpServer : httpServer, - wsServer : new WebSocketServer( { server : httpServer } ), + httpServer : httpServer, + wsServer : new WebSocketServer( { server : httpServer } ), }; } } @@ -161,8 +161,8 @@ exports.getModule = class WebSocketLoginServer extends LoginServerModule { return; } - const serverName = `${ModuleInfo.name} (${serverType})`; - const port = parseInt(_.get(Config(), [ 'loginServers', 'webSocket', 'secure' === serverType ? 'wss' : 'ws', 'port' ] )); + const serverName = `${ModuleInfo.name} (${serverType})`; + const port = parseInt(_.get(Config(), [ 'loginServers', 'webSocket', 'secure' === serverType ? 'wss' : 'ws', 'port' ] )); if(isNaN(port)) { Log.error( { server : serverName, port : port }, 'Cannot load server (invalid port)' ); @@ -180,7 +180,7 @@ exports.getModule = class WebSocketLoginServer extends LoginServerModule { }); // - // Send pings every 30s + // Send pings every 30s // setInterval( () => { WSS_SERVER_TYPES.forEach(serverType => { @@ -191,10 +191,10 @@ exports.getModule = class WebSocketLoginServer extends LoginServerModule { return ws.terminate(); } - ws.isConnectionAlive = false; // pong will reset this + ws.isConnectionAlive = false; // pong will reset this Log.trace('Ping to remote WebSocket client'); - return ws.ping('', false); // false=don't mask + return ws.ping('', false); // false=don't mask }); } }); diff --git a/core/set_newscan_date.js b/core/set_newscan_date.js index 85b9cfcd..27a27c21 100644 --- a/core/set_newscan_date.js +++ b/core/set_newscan_date.js @@ -1,40 +1,40 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const Errors = require('./enig_error.js').Errors; -const FileEntry = require('./file_entry.js'); -const FileBaseFilters = require('./file_base_filter.js'); -const { getAvailableFileAreaTags } = require('./file_base_area.js'); +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const Errors = require('./enig_error.js').Errors; +const FileEntry = require('./file_entry.js'); +const FileBaseFilters = require('./file_base_filter.js'); +const { getAvailableFileAreaTags } = require('./file_base_area.js'); const { getSortedAvailMessageConferences, getSortedAvailMessageAreasByConfTag, updateMessageAreaLastReadId, getMessageIdNewerThanTimestampByArea -} = require('./message_area.js'); -const stringFormat = require('./string_format.js'); +} = require('./message_area.js'); +const stringFormat = require('./string_format.js'); -// deps -const async = require('async'); -const moment = require('moment'); -const _ = require('lodash'); +// deps +const async = require('async'); +const moment = require('moment'); +const _ = require('lodash'); exports.moduleInfo = { - name : 'Set New Scan Date', - desc : 'Sets new scan date for applicable scans', - author : 'NuSkooler', + name : 'Set New Scan Date', + desc : 'Sets new scan date for applicable scans', + author : 'NuSkooler', }; const MciViewIds = { main : { - scanDate : 1, - targetSelection : 2, + scanDate : 1, + targetSelection : 2, } }; -// :TODO: for messages, we could insert "conf - all areas" into targets, and allow such +// :TODO: for messages, we could insert "conf - all areas" into targets, and allow such exports.getModule = class SetNewScanDate extends MenuModule { constructor(options) { @@ -42,8 +42,8 @@ exports.getModule = class SetNewScanDate extends MenuModule { const config = this.menuConfig.config; - this.target = config.target || 'message'; - this.scanDateFormat = config.scanDateFormat || 'YYYYMMDD'; + this.target = config.target || 'message'; + this.scanDateFormat = config.scanDateFormat || 'YYYYMMDD'; this.menuMethods = { scanDateSubmit : (formData, extraArgs, cb) => { @@ -57,7 +57,7 @@ exports.getModule = class SetNewScanDate extends MenuModule { return cb(Errors.Invalid(`"${_.get(formData, 'value.scanDate')}" is not a valid date`)); } - const targetSelection = _.get(formData, 'value.targetSelection'); // may be undefined if N/A + const targetSelection = _.get(formData, 'value.targetSelection'); // may be undefined if N/A this[`setNewScanDateFor${_.capitalize(this.target)}Base`](targetSelection, scanDate, () => { return this.prevMenu(cb); @@ -72,12 +72,12 @@ exports.getModule = class SetNewScanDate extends MenuModule { return cb(Errors.UnexpectedState('Unable to get target in which to set new scan')); } - // selected area, or all of 'em + // selected area, or all of 'em let updateAreaTags; if('' === target.area.areaTag) { updateAreaTags = this.targetSelections .map( targetSelection => targetSelection.area.areaTag ) - .filter( areaTag => areaTag ); // remove the blank 'all' entry + .filter( areaTag => areaTag ); // remove the blank 'all' entry } else { updateAreaTags = [ target.area.areaTag ]; } @@ -89,7 +89,7 @@ exports.getModule = class SetNewScanDate extends MenuModule { } if(!messageId) { - return nextAreaTag(null); // nothing to do + return nextAreaTag(null); // nothing to do } messageId = Math.max(messageId - 1, 0); @@ -98,7 +98,7 @@ exports.getModule = class SetNewScanDate extends MenuModule { this.client.user.userId, areaTag, messageId, - true, // allowOlder + true, // allowOlder nextAreaTag ); }); @@ -109,16 +109,16 @@ exports.getModule = class SetNewScanDate extends MenuModule { setNewScanDateForFileBase(targetSelection, scanDate, cb) { // - // ENiGMA doesn't currently have the concept of per-area - // scan pointers for users, so we use all areas avail - // to the user. + // ENiGMA doesn't currently have the concept of per-area + // scan pointers for users, so we use all areas avail + // to the user. // const filterCriteria = { - areaTag : getAvailableFileAreaTags(this.client), - newerThanTimestamp : scanDate, - limit : 1, - orderBy : 'upload_timestamp', - order : 'ascending', + areaTag : getAvailableFileAreaTags(this.client), + newerThanTimestamp : scanDate, + limit : 1, + orderBy : 'upload_timestamp', + order : 'ascending', }; FileEntry.findFiles(filterCriteria, (err, fileIds) => { @@ -127,7 +127,7 @@ exports.getModule = class SetNewScanDate extends MenuModule { } if(!fileIds || 0 === fileIds.length) { - // nothing to do + // nothing to do return cb(null); } @@ -136,7 +136,7 @@ exports.getModule = class SetNewScanDate extends MenuModule { return FileBaseFilters.setFileBaseLastViewedFileIdForUser( this.client.user, pointerFileId, - true, // allowOlder + true, // allowOlder cb ); }); @@ -144,22 +144,22 @@ exports.getModule = class SetNewScanDate extends MenuModule { loadAvailMessageBaseSelections(cb) { // - // Create an array of objects with conf/area information per entry, - // sorted naturally or via the 'sort' member in config + // Create an array of objects with conf/area information per entry, + // sorted naturally or via the 'sort' member in config // const selections = []; getSortedAvailMessageConferences(this.client).forEach(conf => { getSortedAvailMessageAreasByConfTag(conf.confTag, { client : this.client } ).forEach(area => { selections.push({ - conf : { + conf : { confTag : conf.confTag, - name : conf.conf.name, - desc : conf.conf.desc, + name : conf.conf.name, + desc : conf.conf.desc, }, - area : { - areaTag : area.areaTag, - name : area.area.name, - desc : area.area.desc, + area : { + areaTag : area.areaTag, + name : area.area.name, + desc : area.area.desc, } }); }); @@ -167,20 +167,20 @@ exports.getModule = class SetNewScanDate extends MenuModule { selections.unshift({ conf : { - confTag : '', - name : 'All conferences', - desc : 'All conferences', + confTag : '', + name : 'All conferences', + desc : 'All conferences', }, - area : { - areaTag : '', - name : 'All areas', - desc : 'All areas', + area : { + areaTag : '', + name : 'All areas', + desc : 'All areas', } }); - // Find current conf/area & move it directly under "All" - const currConfTag = this.client.user.properties.message_conf_tag; - const currAreaTag = this.client.user.properties.message_area_tag; + // Find current conf/area & move it directly under "All" + const currConfTag = this.client.user.properties.message_conf_tag; + const currAreaTag = this.client.user.properties.message_area_tag; if(currConfTag && currAreaTag) { const confAreaIndex = selections.findIndex( confArea => { return confArea.conf.confTag === currConfTag && confArea.area.areaTag === currAreaTag; @@ -202,8 +202,8 @@ exports.getModule = class SetNewScanDate extends MenuModule { return cb(err); } - const self = this; - const vc = self.addViewController( 'main', new ViewController( { client : this.client } ) ); + const self = this; + const vc = self.addViewController( 'main', new ViewController( { client : this.client } ) ); async.series( [ @@ -211,7 +211,7 @@ exports.getModule = class SetNewScanDate extends MenuModule { if(![ 'message', 'file' ].includes(self.target)) { return callback(Errors.Invalid(`Invalid "target" in config: ${self.target}`)); } - // :TOD0: validate scanDateFormat + // :TOD0: validate scanDateFormat return callback(null); }, function loadFromConfig(callback) { @@ -231,13 +231,13 @@ exports.getModule = class SetNewScanDate extends MenuModule { const scanDateView = vc.getView(MciViewIds.main.scanDate); - // :TODO: MaskTextEditView needs some love: If setText() with input that matches the mask, we should ignore the non-mask chars! Hack in place for now + // :TODO: MaskTextEditView needs some love: If setText() with input that matches the mask, we should ignore the non-mask chars! Hack in place for now const scanDateFormat = self.scanDateFormat.replace(/[/\-. ]/g, ''); scanDateView.setText(today.format(scanDateFormat)); if('message' === self.target) { - const messageSelectionsFormat = self.menuConfig.config.messageSelectionsFormat || '{conf.name} - {area.name}'; - const messageSelectionFocusFormat = self.menuConfig.config.messageSelectionFocusFormat || messageSelectionsFormat; + const messageSelectionsFormat = self.menuConfig.config.messageSelectionsFormat || '{conf.name} - {area.name}'; + const messageSelectionFocusFormat = self.menuConfig.config.messageSelectionFocusFormat || messageSelectionsFormat; const targetSelectionView = vc.getView(MciViewIds.main.targetSelection); diff --git a/core/show_art.js b/core/show_art.js index bb917e91..66e330db 100644 --- a/core/show_art.js +++ b/core/show_art.js @@ -1,20 +1,20 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const Errors = require('../core/enig_error.js').Errors; -const ANSI = require('./ansi_term.js'); -const Config = require('./config.js').get; +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const Errors = require('../core/enig_error.js').Errors; +const ANSI = require('./ansi_term.js'); +const Config = require('./config.js').get; -// deps -const async = require('async'); -const _ = require('lodash'); +// deps +const async = require('async'); +const _ = require('lodash'); exports.moduleInfo = { - name : 'Show Art', - desc : 'Module for more advanced methods of displaying art', - author : 'NuSkooler', + name : 'Show Art', + desc : 'Module for more advanced methods of displaying art', + author : 'NuSkooler', }; exports.getModule = class ShowArtModule extends MenuModule { @@ -36,13 +36,13 @@ exports.getModule = class ShowArtModule extends MenuModule { }, function showArt(callback) { // - // How we show art depends on our configuration + // How we show art depends on our configuration // let handler = { - extraArgs : self.showByExtraArgs, - sequence : self.showBySequence, - random : self.showByRandom, - fileBaseArea : self.showByFileBaseArea, + extraArgs : self.showByExtraArgs, + sequence : self.showBySequence, + random : self.showByRandom, + fileBaseArea : self.showByFileBaseArea, }[self.config.method] || self.showRandomArt; handler = handler.bind(self); @@ -68,8 +68,8 @@ exports.getModule = class ShowArtModule extends MenuModule { return cb(err); } const options = { - pause : this.shouldPause(), - desc : 'extraArgs', + pause : this.shouldPause(), + desc : 'extraArgs', }; return this.displaySingleArtWithOptions(artSpec, options, cb); }); @@ -89,14 +89,14 @@ exports.getModule = class ShowArtModule extends MenuModule { return cb(err); } - // further resolve key -> file base area art + // further resolve key -> file base area art const artSpec = _.get(Config(), [ 'fileBase', 'areas', key, 'art' ]); if(!artSpec) { return cb(Errors.MissingConfig(`No art defined for file base area "${key}"`)); } const options = { - pause : this.shouldPause(), - desc : 'fileBaseArea', + pause : this.shouldPause(), + desc : 'fileBaseArea', }; return this.displaySingleArtWithOptions(artSpec, options, cb); }); @@ -122,7 +122,7 @@ exports.getModule = class ShowArtModule extends MenuModule { async.waterfall( [ function art(callback) { - // :TODO: we really need a way to supply an explicit path to look in, e.g. general/area_art/ + // :TODO: we really need a way to supply an explicit path to look in, e.g. general/area_art/ self.displayAsset( artSpec, self.menuConfig.options, @@ -137,7 +137,7 @@ exports.getModule = class ShowArtModule extends MenuModule { }, function recordCursorPosition(mciData, callback) { if(!options.pause) { - return callback(null, mciData, null); // cursor position not needed + return callback(null, mciData, null); // cursor position not needed } self.client.once('cursor position report', pos => { diff --git a/core/spinner_menu_view.js b/core/spinner_menu_view.js index b46dda51..e6be1232 100644 --- a/core/spinner_menu_view.js +++ b/core/spinner_menu_view.js @@ -1,29 +1,29 @@ /* jslint node: true */ 'use strict'; -const MenuView = require('./menu_view.js').MenuView; -const ansi = require('./ansi_term.js'); -const strUtil = require('./string_util.js'); +const MenuView = require('./menu_view.js').MenuView; +const ansi = require('./ansi_term.js'); +const strUtil = require('./string_util.js'); -const util = require('util'); -const assert = require('assert'); -const _ = require('lodash'); +const util = require('util'); +const assert = require('assert'); +const _ = require('lodash'); -exports.SpinnerMenuView = SpinnerMenuView; +exports.SpinnerMenuView = SpinnerMenuView; function SpinnerMenuView(options) { - options.justify = options.justify || 'left'; - options.cursor = options.cursor || 'hide'; + options.justify = options.justify || 'left'; + options.cursor = options.cursor || 'hide'; MenuView.call(this, options); var self = this; /* - this.cachePositions = function() { - self.positionCacheExpired = false; - }; - */ + this.cachePositions = function() { + self.positionCacheExpired = false; + }; + */ this.updateSelection = function() { //assert(!self.positionCacheExpired); @@ -66,9 +66,9 @@ SpinnerMenuView.prototype.setFocus = function(focused) { }; SpinnerMenuView.prototype.setFocusItemIndex = function(index) { - SpinnerMenuView.super_.prototype.setFocusItemIndex.call(this, index); // sets this.focusedItemIndex + SpinnerMenuView.super_.prototype.setFocusItemIndex.call(this, index); // sets this.focusedItemIndex - this.updateSelection(); // will redraw + this.updateSelection(); // will redraw }; SpinnerMenuView.prototype.onKeyPress = function(ch, key) { diff --git a/core/standard_menu.js b/core/standard_menu.js index f22153b2..a4dacb95 100644 --- a/core/standard_menu.js +++ b/core/standard_menu.js @@ -1,12 +1,12 @@ /* jslint node: true */ 'use strict'; -const MenuModule = require('./menu_module.js').MenuModule; +const MenuModule = require('./menu_module.js').MenuModule; exports.moduleInfo = { - name : 'Standard Menu Module', - desc : 'A Menu Module capable of handing standard configurations', - author : 'NuSkooler', + name : 'Standard Menu Module', + desc : 'A Menu Module capable of handing standard configurations', + author : 'NuSkooler', }; exports.getModule = class StandardMenuModule extends MenuModule { @@ -20,7 +20,7 @@ exports.getModule = class StandardMenuModule extends MenuModule { return cb(err); } - // we do this so other modules can be both customized and still perform standard tasks + // we do this so other modules can be both customized and still perform standard tasks return this.standardMCIReadyHandler(mciData, cb); }); } diff --git a/core/stat_log.js b/core/stat_log.js index 849404c2..4b076e6d 100644 --- a/core/stat_log.js +++ b/core/stat_log.js @@ -1,23 +1,23 @@ /* jslint node: true */ 'use strict'; -const sysDb = require('./database.js').dbs.system; +const sysDb = require('./database.js').dbs.system; -// deps -const _ = require('lodash'); -const moment = require('moment'); +// deps +const _ = require('lodash'); +const moment = require('moment'); /* - System Event Log & Stats - ------------------------ + System Event Log & Stats + ------------------------ - System & user specific: - * Events for generating various statistics, logs such as last callers, etc. - * Stats such as counters + System & user specific: + * Events for generating various statistics, logs such as last callers, etc. + * Stats such as counters - User specific stats are simply an alternate interface to user properties, while - system wide entries are handled on their own. Both are read accessible non-blocking - making them easily available for MCI codes for example. + User specific stats are simply an alternate interface to user properties, while + system wide entries are handled on their own. Both are read accessible non-blocking + making them easily available for MCI codes for example. */ class StatLog { constructor() { @@ -26,13 +26,13 @@ class StatLog { init(cb) { // - // Load previous state/values of |this.systemStats| + // Load previous state/values of |this.systemStats| // const self = this; sysDb.each( `SELECT stat_name, stat_value - FROM system_stat;`, + FROM system_stat;`, (err, row) => { if(row) { self.systemStats[row.stat_name] = row.stat_value; @@ -52,19 +52,19 @@ class StatLog { get KeepType() { return { - Forever : 'forever', - Days : 'days', - Max : 'max', - Count : 'max', + Forever : 'forever', + Days : 'days', + Max : 'max', + Count : 'max', }; } get Order() { return { - Timestamp : 'timestamp_asc', - TimestampAsc : 'timestamp_asc', - TimestampDesc : 'timestamp_desc', - Random : 'random', + Timestamp : 'timestamp_asc', + TimestampAsc : 'timestamp_asc', + TimestampDesc : 'timestamp_desc', + Random : 'random', }; } @@ -73,16 +73,16 @@ class StatLog { } setSystemStat(statName, statValue, cb) { - // live stats + // live stats this.systemStats[statName] = statValue; - // persisted stats + // persisted stats sysDb.run( `REPLACE INTO system_stat (stat_name, stat_value) - VALUES (?, ?);`, + VALUES (?, ?);`, [ statName, statValue ], err => { - // cb optional - callers may fire & forget + // cb optional - callers may fire & forget if(cb) { return cb(err); } @@ -114,11 +114,11 @@ class StatLog { } // - // User specific stats - // These are simply convience methods to the user's properties + // User specific stats + // These are simply convience methods to the user's properties // setUserStat(user, statName, statValue, cb) { - // note: cb is optional in PersistUserProperty + // note: cb is optional in PersistUserProperty return user.persistProperty(statName, statValue, cb); } @@ -147,17 +147,17 @@ class StatLog { return this.setUserStat(user, statName, newValue, cb); } - // the time "now" in the ISO format we use and love :) + // the time "now" in the ISO format we use and love :) get now() { return moment().format('YYYY-MM-DDTHH:mm:ss.SSSZ'); } appendSystemLogEntry(logName, logValue, keep, keepType, cb) { sysDb.run( `INSERT INTO system_event_log (timestamp, log_name, log_value) - VALUES (?, ?, ?);`, + VALUES (?, ?, ?);`, [ this.now, logName, logValue ], () => { // - // Handle keep + // Handle keep // if(-1 === keep) { if(cb) { @@ -167,14 +167,14 @@ class StatLog { } switch(keepType) { - // keep # of days + // keep # of days case 'days' : sysDb.run( `DELETE FROM system_event_log - WHERE log_name = ? AND timestamp <= DATETIME("now", "-${keep} day");`, + WHERE log_name = ? AND timestamp <= DATETIME("now", "-${keep} day");`, [ logName ], err => { - // cb optional - callers may fire & forget + // cb optional - callers may fire & forget if(cb) { return cb(err); } @@ -184,16 +184,16 @@ class StatLog { case 'count': case 'max' : - // keep max of N/count + // keep max of N/count sysDb.run( `DELETE FROM system_event_log - WHERE id IN( - SELECT id - FROM system_event_log - WHERE log_name = ? - ORDER BY id DESC - LIMIT -1 OFFSET ${keep} - );`, + WHERE id IN( + SELECT id + FROM system_event_log + WHERE log_name = ? + ORDER BY id DESC + LIMIT -1 OFFSET ${keep} + );`, [ logName ], err => { if(cb) { @@ -205,7 +205,7 @@ class StatLog { case 'forever' : default : - // nop + // nop break; } } @@ -214,9 +214,9 @@ class StatLog { getSystemLogEntries(logName, order, limit, cb) { let sql = - `SELECT timestamp, log_value - FROM system_event_log - WHERE log_name = ?`; + `SELECT timestamp, log_value + FROM system_event_log + WHERE log_name = ?`; switch(order) { case 'timestamp' : @@ -228,13 +228,13 @@ class StatLog { sql += ' ORDER BY timestamp DESC'; break; - case 'random' : + case 'random' : sql += ' ORDER BY RANDOM()'; } if(!cb && _.isFunction(limit)) { - cb = limit; - limit = 0; + cb = limit; + limit = 0; } else { limit = limit || 0; } @@ -253,11 +253,11 @@ class StatLog { appendUserLogEntry(user, logName, logValue, keepDays, cb) { sysDb.run( `INSERT INTO user_event_log (timestamp, user_id, log_name, log_value) - VALUES (?, ?, ?, ?);`, + VALUES (?, ?, ?, ?);`, [ this.now, user.userId, logName, logValue ], () => { // - // Handle keepDays + // Handle keepDays // if(-1 === keepDays) { if(cb) { @@ -268,10 +268,10 @@ class StatLog { sysDb.run( `DELETE FROM user_event_log - WHERE user_id = ? AND log_name = ? AND timestamp <= DATETIME("now", "-${keepDays} day");`, + WHERE user_id = ? AND log_name = ? AND timestamp <= DATETIME("now", "-${keepDays} day");`, [ user.userId, logName ], err => { - // cb optional - callers may fire & forget + // cb optional - callers may fire & forget if(cb) { return cb(err); } diff --git a/core/string_format.js b/core/string_format.js index 7857f2b3..cef937c4 100644 --- a/core/string_format.js +++ b/core/string_format.js @@ -1,7 +1,7 @@ /* jslint node: true */ 'use strict'; -const EnigError = require('./enig_error.js').EnigError; +const EnigError = require('./enig_error.js').EnigError; const { pad, @@ -10,41 +10,41 @@ const { renderSubstr, formatByteSize, formatByteSizeAbbr, formatCount, formatCountAbbr, -} = require('./string_util.js'); +} = require('./string_util.js'); -// deps -const _ = require('lodash'); +// deps +const _ = require('lodash'); /* - String formatting HEAVILY inspired by David Chambers string-format library - and the mini-language branch specifically which was gratiously released - under the DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE. + String formatting HEAVILY inspired by David Chambers string-format library + and the mini-language branch specifically which was gratiously released + under the DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE. - We need some extra functionality. Namely, support for RA style pipe codes - and ANSI escape sequences. + We need some extra functionality. Namely, support for RA style pipe codes + and ANSI escape sequences. */ class ValueError extends EnigError { } class KeyError extends EnigError { } const SpecRegExp = { - FillAlign : /^(.)?([<>=^])/, - Sign : /^[ +-]/, - Width : /^\d*/, - Precision : /^\d+/, + FillAlign : /^(.)?([<>=^])/, + Sign : /^[ +-]/, + Width : /^\d*/, + Precision : /^\d+/, }; function tokenizeFormatSpec(spec) { const tokens = { - fill : '', - align : '', - sign : '', - '#' : false, - '0' : false, - width : '', - ',' : false, - precision : '', - type : '', + fill : '', + align : '', + sign : '', + '#' : false, + '0' : false, + width : '', + ',' : false, + precision : '', + type : '', }; let index = 0; @@ -110,7 +110,7 @@ function tokenizeFormatSpec(spec) { } if(tokens[','] && 's' === tokens.type) { - throw new ValueError(`Cannot specify ',' with 's'`); // eslint-disable-line quotes + throw new ValueError(`Cannot specify ',' with 's'`); // eslint-disable-line quotes } return tokens; @@ -129,16 +129,16 @@ function getPadAlign(align) { } function formatString(value, tokens) { - const fill = tokens.fill || (tokens['0'] ? '0' : ' '); - const align = tokens.align || (tokens['0'] ? '=' : '<'); - const precision = Number(tokens.precision || renderStringLength(value) + 1); + const fill = tokens.fill || (tokens['0'] ? '0' : ' '); + const align = tokens.align || (tokens['0'] ? '=' : '<'); + const precision = Number(tokens.precision || renderStringLength(value) + 1); if('' !== tokens.type && 's' !== tokens.type) { throw new ValueError(`Unknown format code "${tokens.type}" for String object`); } if(tokens[',']) { - throw new ValueError(`Cannot specify ',' with 's'`); // eslint-disable-line quotes + throw new ValueError(`Cannot specify ',' with 's'`); // eslint-disable-line quotes } if(tokens.sign) { @@ -157,8 +157,8 @@ function formatString(value, tokens) { } const FormatNumRegExp = { - UpperType : /[A-Z]/, - ExponentRep : /e[+-](?=\d$)/, + UpperType : /[A-Z]/, + ExponentRep : /e[+-](?=\d$)/, }; function formatNumberHelper(n, precision, type) { @@ -175,7 +175,7 @@ function formatNumberHelper(n, precision, type) { case 'e' : return n.toExponential(precision).replace(FormatNumRegExp.ExponentRep, '$&0'); case 'f' : return n.toFixed(precision); case 'g' : - // we don't want useless trailing zeros. parseFloat -> back to string fixes this for us + // we don't want useless trailing zeros. parseFloat -> back to string fixes this for us return parseFloat(n.toPrecision(precision || 1)).toString(); case '%' : return formatNumberHelper(n * 100, precision, 'f') + '%'; @@ -187,10 +187,10 @@ function formatNumberHelper(n, precision, type) { } function formatNumber(value, tokens) { - const fill = tokens.fill || (tokens['0'] ? '0' : ' '); - const align = tokens.align || (tokens['0'] ? '=' : '>'); - const width = Number(tokens.width); - const type = tokens.type || (tokens.precision ? 'g' : ''); + const fill = tokens.fill || (tokens['0'] ? '0' : ' '); + const align = tokens.align || (tokens['0'] ? '=' : '>'); + const width = Number(tokens.width); + const type = tokens.type || (tokens.precision ? 'g' : ''); if( [ 'c', 'd', 'b', 'o', 'x', 'X' ].indexOf(type) > -1) { if(0 !== value % 1) { @@ -198,7 +198,7 @@ function formatNumber(value, tokens) { } if('' !== tokens.sign && 'c' !== type) { - throw new ValueError(`Sign not allowed with integer format specifier 'c'`); // eslint-disable-line quotes + throw new ValueError(`Sign not allowed with integer format specifier 'c'`); // eslint-disable-line quotes } if(tokens[','] && 'd' !== type) { @@ -214,16 +214,16 @@ function formatNumber(value, tokens) { } } - const s = formatNumberHelper(Math.abs(value), Number(tokens.precision || 6), type); - const sign = value < 0 || 1 / value < 0 ? + const s = formatNumberHelper(Math.abs(value), Number(tokens.precision || 6), type); + const sign = value < 0 || 1 / value < 0 ? '-' : '-' === tokens.sign ? '' : tokens.sign; - const prefix = tokens['#'] && ( [ 'b', 'o', 'x', 'X' ].indexOf(type) > -1 ) ? '0' + type : ''; + const prefix = tokens['#'] && ( [ 'b', 'o', 'x', 'X' ].indexOf(type) > -1 ) ? '0' + type : ''; if(tokens[',']) { - const match = /^(\d*)(.*)$/.exec(s); - const separated = match[1].replace(/.(?=(...)+$)/g, '$&,') + match[2]; + const match = /^(\d*)(.*)$/.exec(s); + const separated = match[1].replace(/.(?=(...)+$)/g, '$&,') + match[2]; if('=' !== align) { return pad(sign + separated, width, fill, getPadAlign(align)); @@ -231,9 +231,9 @@ function formatNumber(value, tokens) { if('0' === fill) { const shortfall = Math.max(0, width - sign.length - separated.length); - const digits = /^\d*/.exec(separated)[0].length; - let padding = ''; - // :TODO: do this differntly... + const digits = /^\d*/.exec(separated)[0].length; + let padding = ''; + // :TODO: do this differntly... for(let n = 0; n < shortfall; n++) { padding = ((digits + n) % 4 === 3 ? ',' : '0') + padding; } @@ -256,31 +256,31 @@ function formatNumber(value, tokens) { } const transformers = { - // String standard - toUpperCase : String.prototype.toUpperCase, - toLowerCase : String.prototype.toLowerCase, + // String standard + toUpperCase : String.prototype.toUpperCase, + toLowerCase : String.prototype.toLowerCase, - // some super l33b BBS styles!! - styleUpper : (s) => stylizeString(s, 'upper'), - styleLower : (s) => stylizeString(s, 'lower'), - styleTitle : (s) => stylizeString(s, 'title'), - styleFirstLower : (s) => stylizeString(s, 'first lower'), - styleSmallVowels : (s) => stylizeString(s, 'small vowels'), - styleBigVowels : (s) => stylizeString(s, 'big vowels'), - styleSmallI : (s) => stylizeString(s, 'small i'), - styleMixed : (s) => stylizeString(s, 'mixed'), - styleL33t : (s) => stylizeString(s, 'l33t'), + // some super l33b BBS styles!! + styleUpper : (s) => stylizeString(s, 'upper'), + styleLower : (s) => stylizeString(s, 'lower'), + styleTitle : (s) => stylizeString(s, 'title'), + styleFirstLower : (s) => stylizeString(s, 'first lower'), + styleSmallVowels : (s) => stylizeString(s, 'small vowels'), + styleBigVowels : (s) => stylizeString(s, 'big vowels'), + styleSmallI : (s) => stylizeString(s, 'small i'), + styleMixed : (s) => stylizeString(s, 'mixed'), + styleL33t : (s) => stylizeString(s, 'l33t'), - // :TODO: - // toMegs(), toKilobytes(), ... - // toList(), toCommaList(), + // :TODO: + // toMegs(), toKilobytes(), ... + // toList(), toCommaList(), - sizeWithAbbr : (n) => formatByteSize(n, true, 2), - sizeWithoutAbbr : (n) => formatByteSize(n, false, 2), - sizeAbbr : (n) => formatByteSizeAbbr(n), - countWithAbbr : (n) => formatCount(n, true, 0), - countWithoutAbbr : (n) => formatCount(n, false, 0), - countAbbr : (n) => formatCountAbbr(n), + sizeWithAbbr : (n) => formatByteSize(n, true, 2), + sizeWithoutAbbr : (n) => formatByteSize(n, false, 2), + sizeAbbr : (n) => formatByteSizeAbbr(n), + countWithAbbr : (n) => formatCount(n, true, 0), + countWithoutAbbr : (n) => formatCount(n, false, 0), + countAbbr : (n) => formatCountAbbr(n), }; function transformValue(transformerName, value) { @@ -292,8 +292,8 @@ function transformValue(transformerName, value) { return value; } -// :TODO: Use explicit set of chars for paths & function/transforms such that } is allowed as fill/etc. -const REGEXP_BASIC_FORMAT = /{([^.!:}]+(?:\.[^.!:}]+)*)(?:!([^:}]+))?(?::([^}]+))?}/g; +// :TODO: Use explicit set of chars for paths & function/transforms such that } is allowed as fill/etc. +const REGEXP_BASIC_FORMAT = /{([^.!:}]+(?:\.[^.!:}]+)*)(?:!([^:}]+))?(?::([^}]+))?}/g; function getValue(obj, path) { const value = _.get(obj, path); @@ -307,7 +307,7 @@ function getValue(obj, path) { module.exports = function format(fmt, obj) { const re = REGEXP_BASIC_FORMAT; - re.lastIndex = 0; // reset from prev + re.lastIndex = 0; // reset from prev let match; let pos; @@ -319,17 +319,17 @@ module.exports = function format(fmt, obj) { let tokens; do { - pos = re.lastIndex; - match = re.exec(fmt); + pos = re.lastIndex; + match = re.exec(fmt); if(match) { if(match.index > pos) { out += fmt.slice(pos, match.index); } - objPath = match[1]; - transformer = match[2]; - formatSpec = match[3]; + objPath = match[1]; + transformer = match[2]; + formatSpec = match[3]; value = getValue(obj, objPath); if(transformer) { @@ -347,7 +347,7 @@ module.exports = function format(fmt, obj) { } while(0 !== re.lastIndex); - // remainder + // remainder if(pos < fmt.length) { out += fmt.slice(pos); } diff --git a/core/string_util.js b/core/string_util.js index 88f0e57e..8fab88b8 100644 --- a/core/string_util.js +++ b/core/string_util.js @@ -1,35 +1,35 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const ANSI = require('./ansi_term.js'); +// ENiGMA½ +const ANSI = require('./ansi_term.js'); -// deps -const iconv = require('iconv-lite'); -const _ = require('lodash'); +// deps +const iconv = require('iconv-lite'); +const _ = require('lodash'); -exports.stylizeString = stylizeString; -exports.pad = pad; -exports.insert = insert; -exports.replaceAt = replaceAt; -exports.isPrintable = isPrintable; -exports.stripAllLineFeeds = stripAllLineFeeds; -exports.debugEscapedString = debugEscapedString; -exports.stringFromNullTermBuffer = stringFromNullTermBuffer; -exports.stringToNullTermBuffer = stringToNullTermBuffer; -exports.renderSubstr = renderSubstr; -exports.renderStringLength = renderStringLength; -exports.formatByteSizeAbbr = formatByteSizeAbbr; -exports.formatByteSize = formatByteSize; -exports.formatCountAbbr = formatCountAbbr; -exports.formatCount = formatCount; -exports.cleanControlCodes = cleanControlCodes; -exports.isAnsi = isAnsi; -exports.isAnsiLine = isAnsiLine; -exports.isFormattedLine = isFormattedLine; -exports.splitTextAtTerms = splitTextAtTerms; +exports.stylizeString = stylizeString; +exports.pad = pad; +exports.insert = insert; +exports.replaceAt = replaceAt; +exports.isPrintable = isPrintable; +exports.stripAllLineFeeds = stripAllLineFeeds; +exports.debugEscapedString = debugEscapedString; +exports.stringFromNullTermBuffer = stringFromNullTermBuffer; +exports.stringToNullTermBuffer = stringToNullTermBuffer; +exports.renderSubstr = renderSubstr; +exports.renderStringLength = renderStringLength; +exports.formatByteSizeAbbr = formatByteSizeAbbr; +exports.formatByteSize = formatByteSize; +exports.formatCountAbbr = formatCountAbbr; +exports.formatCount = formatCount; +exports.cleanControlCodes = cleanControlCodes; +exports.isAnsi = isAnsi; +exports.isAnsiLine = isAnsiLine; +exports.isFormattedLine = isFormattedLine; +exports.splitTextAtTerms = splitTextAtTerms; -// :TODO: create Unicode verison of this +// :TODO: create Unicode verison of this const VOWELS = [ 'a', 'e', 'i', 'o', 'u' ]; VOWELS.concat(VOWELS.map(l => l.toUpperCase())); @@ -49,36 +49,36 @@ function stylizeString(s, style) { var stylized = ''; switch(style) { - // None/normal + // None/normal case 'normal' : case 'N' : return s; - // UPPERCASE + // UPPERCASE case 'upper' : case 'U' : return s.toUpperCase(); - // lowercase + // lowercase case 'lower' : case 'l' : return s.toLowerCase(); - // Title Case + // Title Case case 'title' : case 'T' : return s.replace(/\w\S*/g, function onProperCaseChar(t) { return t.charAt(0).toUpperCase() + t.substr(1).toLowerCase(); }); - // fIRST lOWER + // fIRST lOWER case 'first lower' : case 'f' : return s.replace(/\w\S*/g, function onFirstLowerChar(t) { return t.charAt(0).toLowerCase() + t.substr(1).toUpperCase(); }); - // SMaLL VoWeLS + // SMaLL VoWeLS case 'small vowels' : case 'v' : for(i = 0; i < len; ++i) { @@ -91,7 +91,7 @@ function stylizeString(s, style) { } return stylized; - // bIg vOwELS + // bIg vOwELS case 'big vowels' : case 'V' : for(i = 0; i < len; ++i) { @@ -104,12 +104,12 @@ function stylizeString(s, style) { } return stylized; - // Small i's: DEMENTiA + // Small i's: DEMENTiA case 'small i' : case 'i' : return s.toUpperCase().replace(/I/g, 'i'); - // mIxeD CaSE (random upper/lower) + // mIxeD CaSE (random upper/lower) case 'mixed' : case 'M' : for(i = 0; i < len; i++) { @@ -121,7 +121,7 @@ function stylizeString(s, style) { } return stylized; - // l337 5p34k + // l337 5p34k case 'l33t' : case '3' : for(i = 0; i < len; ++i) { @@ -135,15 +135,15 @@ function stylizeString(s, style) { } function pad(s, len, padChar, justify, stringSGR, padSGR, useRenderLen) { - len = len || 0; - padChar = padChar || ' '; - justify = justify || 'left'; - stringSGR = stringSGR || ''; - padSGR = padSGR || ''; - useRenderLen = _.isUndefined(useRenderLen) ? true : useRenderLen; + len = len || 0; + padChar = padChar || ' '; + justify = justify || 'left'; + stringSGR = stringSGR || ''; + padSGR = padSGR || ''; + useRenderLen = _.isUndefined(useRenderLen) ? true : useRenderLen; - const renderLen = useRenderLen ? renderStringLength(s) : s.length; - const padlen = len >= renderLen ? len - renderLen : 0; + const renderLen = useRenderLen ? renderStringLength(s) : s.length; + const padlen = len >= renderLen ? len - renderLen : 0; switch(justify) { case 'L' : @@ -155,9 +155,9 @@ function pad(s, len, padChar, justify, stringSGR, padSGR, useRenderLen) { case 'center' : case 'both' : { - const right = Math.ceil(padlen / 2); - const left = padlen - right; - s = `${padSGR}${Array(left + 1).join(padChar)}${stringSGR}${s}${padSGR}${Array(right + 1).join(padChar)}`; + const right = Math.ceil(padlen / 2); + const left = padlen - right; + s = `${padSGR}${Array(left + 1).join(padChar)}${stringSGR}${s}${padSGR}${Array(right + 1).join(padChar)}`; } break; @@ -181,16 +181,16 @@ function replaceAt(s, n, t) { } const RE_NON_PRINTABLE = - /[\0-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/; // eslint-disable-line no-control-regex + /[\0-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BB-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF]/; // eslint-disable-line no-control-regex function isPrintable(s) { // - // See the following: - // https://mathiasbynens.be/notes/javascript-unicode - // http://stackoverflow.com/questions/11598786/how-to-replace-non-printable-unicode-characters-javascript - // http://stackoverflow.com/questions/12052825/regular-expression-for-all-printable-characters-in-javascript + // See the following: + // https://mathiasbynens.be/notes/javascript-unicode + // http://stackoverflow.com/questions/11598786/how-to-replace-non-printable-unicode-characters-javascript + // http://stackoverflow.com/questions/12052825/regular-expression-for-all-printable-characters-in-javascript // - // :TODO: Probably need somthing better here. + // :TODO: Probably need somthing better here. return !RE_NON_PRINTABLE.test(s); } @@ -213,29 +213,29 @@ function stringFromNullTermBuffer(buf, encoding) { function stringToNullTermBuffer(s, options = { encoding : 'utf8', maxBufLen : -1 } ) { let buf = iconv.encode( `${s}\0`, options.encoding ).slice(0, options.maxBufLen); - buf[buf.length - 1] = '\0'; // make abs sure we null term even if truncated + buf[buf.length - 1] = '\0'; // make abs sure we null term even if truncated return buf; } -const PIPE_REGEXP = /(\|[A-Z\d]{2})/g; -//const ANSI_REGEXP = /[\u001b\u009b][[()#;?]*([0-9]{1,4}(?:;[0-9]{0,4})*)?([0-9A-ORZcf-npqrsuy=><])/g; -//const ANSI_OR_PIPE_REGEXP = new RegExp(PIPE_REGEXP.source + '|' + ANSI_REGEXP.source, 'g'); -const ANSI_OR_PIPE_REGEXP = new RegExp(PIPE_REGEXP.source + '|' + ANSI.getFullMatchRegExp().source, 'g'); +const PIPE_REGEXP = /(\|[A-Z\d]{2})/g; +//const ANSI_REGEXP = /[\u001b\u009b][[()#;?]*([0-9]{1,4}(?:;[0-9]{0,4})*)?([0-9A-ORZcf-npqrsuy=><])/g; +//const ANSI_OR_PIPE_REGEXP = new RegExp(PIPE_REGEXP.source + '|' + ANSI_REGEXP.source, 'g'); +const ANSI_OR_PIPE_REGEXP = new RegExp(PIPE_REGEXP.source + '|' + ANSI.getFullMatchRegExp().source, 'g'); // -// Similar to substr() but works with ANSI/Pipe code strings +// Similar to substr() but works with ANSI/Pipe code strings // function renderSubstr(str, start, length) { - // shortcut for empty strings + // shortcut for empty strings if(0 === str.length) { return str; } - start = start || 0; - length = length || str.length - start; + start = start || 0; + length = length || str.length - start; const re = ANSI_OR_PIPE_REGEXP; - re.lastIndex = 0; // we recycle the obj; must reset! + re.lastIndex = 0; // we recycle the obj; must reset! let pos = 0; let match; @@ -243,22 +243,22 @@ function renderSubstr(str, start, length) { let renderLen = 0; let s; do { - pos = re.lastIndex; - match = re.exec(str); + pos = re.lastIndex; + match = re.exec(str); if(match) { if(match.index > pos) { s = str.slice(pos + start, Math.min(match.index, pos + (length - renderLen))); - start = 0; // start offset applies only once - out += s; - renderLen += s.length; + start = 0; // start offset applies only once + out += s; + renderLen += s.length; } out += match[0]; } } while(renderLen < length && 0 !== re.lastIndex); - // remainder + // remainder if(pos + start < str.length && renderLen < length) { out += str.slice(pos + start, (pos + start + (length - renderLen))); //out += str.slice(pos + start, Math.max(1, pos + (length - renderLen - 1))); @@ -268,12 +268,12 @@ function renderSubstr(str, start, length) { } // -// Method to return the "rendered" length taking into account Pipe and ANSI color codes. +// Method to return the "rendered" length taking into account Pipe and ANSI color codes. // -// We additionally account for ANSI *forward* movement ESC sequences -// in the form of ESC[C where is the "go forward" character count. +// We additionally account for ANSI *forward* movement ESC sequences +// in the form of ESC[C where is the "go forward" character count. // -// See also https://github.com/chalk/ansi-regex/blob/master/index.js +// See also https://github.com/chalk/ansi-regex/blob/master/index.js // function renderStringLength(s) { let m; @@ -281,22 +281,22 @@ function renderStringLength(s) { let len = 0; const re = ANSI_OR_PIPE_REGEXP; - re.lastIndex = 0; // we recycle the rege; reset + re.lastIndex = 0; // we recycle the rege; reset // - // Loop counting only literal (non-control) sequences - // paying special attention to ESC[C which means forward + // Loop counting only literal (non-control) sequences + // paying special attention to ESC[C which means forward // do { - pos = re.lastIndex; - m = re.exec(s); + pos = re.lastIndex; + m = re.exec(s); if(m) { if(m.index > pos) { len += s.slice(pos, m.index).length; } - if('C' === m[3]) { // ESC[C is foward/right + if('C' === m[3]) { // ESC[C is foward/right len += parseInt(m[2], 10) || 0; } } @@ -309,19 +309,19 @@ function renderStringLength(s) { return len; } -const BYTE_SIZE_ABBRS = [ 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ]; // :) +const BYTE_SIZE_ABBRS = [ 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB' ]; // :) function formatByteSizeAbbr(byteSize) { if(0 === byteSize) { - return BYTE_SIZE_ABBRS[0]; // B + return BYTE_SIZE_ABBRS[0]; // B } return BYTE_SIZE_ABBRS[Math.floor(Math.log(byteSize) / Math.log(1024))]; } function formatByteSize(byteSize, withAbbr = false, decimals = 2) { - const i = 0 === byteSize ? byteSize : Math.floor(Math.log(byteSize) / Math.log(1024)); - let result = parseFloat((byteSize / Math.pow(1024, i)).toFixed(decimals)); + const i = 0 === byteSize ? byteSize : Math.floor(Math.log(byteSize) / Math.log(1024)); + let result = parseFloat((byteSize / Math.pow(1024, i)).toFixed(decimals)); if(withAbbr) { result += ` ${BYTE_SIZE_ABBRS[i]}`; } @@ -339,8 +339,8 @@ function formatCountAbbr(count) { } function formatCount(count, withAbbr = false, decimals = 2) { - const i = 0 === count ? count : Math.floor(Math.log(count) / Math.log(1000)); - let result = parseFloat((count / Math.pow(1000, i)).toFixed(decimals)); + const i = 0 === count ? count : Math.floor(Math.log(count) / Math.log(1000)); + let result = parseFloat((count / Math.pow(1000, i)).toFixed(decimals)); if(withAbbr) { result += `${COUNT_ABBRS[i]}`; } @@ -348,13 +348,13 @@ function formatCount(count, withAbbr = false, decimals = 2) { } -// :TODO: See notes in word_wrap.js about need to consolidate the various ANSI related RegExp's -//const REGEXP_ANSI_CONTROL_CODES = /(\x1b\x5b)([\?=;0-9]*?)([0-9A-ORZcf-npsu=><])/g; -const REGEXP_ANSI_CONTROL_CODES = /(?:\x1b\x5b)([?=;0-9]*?)([A-ORZcf-npsu=><])/g; // eslint-disable-line no-control-regex -const ANSI_OPCODES_ALLOWED_CLEAN = [ - //'A', 'B', // up, down - //'C', 'D', // right, left - 'm', // color +// :TODO: See notes in word_wrap.js about need to consolidate the various ANSI related RegExp's +//const REGEXP_ANSI_CONTROL_CODES = /(\x1b\x5b)([\?=;0-9]*?)([0-9A-ORZcf-npsu=><])/g; +const REGEXP_ANSI_CONTROL_CODES = /(?:\x1b\x5b)([?=;0-9]*?)([A-ORZcf-npsu=><])/g; // eslint-disable-line no-control-regex +const ANSI_OPCODES_ALLOWED_CLEAN = [ + //'A', 'B', // up, down + //'C', 'D', // right, left + 'm', // color ]; function cleanControlCodes(input, options) { @@ -365,12 +365,12 @@ function cleanControlCodes(input, options) { options = options || {}; // - // Loop through |input| adding only allowed ESC - // sequences and literals to |cleaned| + // Loop through |input| adding only allowed ESC + // sequences and literals to |cleaned| // do { - pos = REGEXP_ANSI_CONTROL_CODES.lastIndex; - m = REGEXP_ANSI_CONTROL_CODES.exec(input); + pos = REGEXP_ANSI_CONTROL_CODES.lastIndex; + m = REGEXP_ANSI_CONTROL_CODES.exec(input); if(m) { if(m.index > pos) { @@ -388,7 +388,7 @@ function cleanControlCodes(input, options) { } while(0 !== REGEXP_ANSI_CONTROL_CODES.lastIndex); - // remainder + // remainder if(pos < input.length) { cleaned += input.slice(pos); } @@ -401,20 +401,20 @@ function isAnsiLine(line) { } // -// Returns true if the line is considered "formatted". A line is -// considered formatted if it contains: -// * ANSI -// * Pipe codes -// * Extended (CP437) ASCII - https://www.ascii-codes.com/ -// * Tabs -// * Contigous 3+ spaces before the end of the line +// Returns true if the line is considered "formatted". A line is +// considered formatted if it contains: +// * ANSI +// * Pipe codes +// * Extended (CP437) ASCII - https://www.ascii-codes.com/ +// * Tabs +// * Contigous 3+ spaces before the end of the line // function isFormattedLine(line) { if(renderStringLength(line) < line.length) { - return true; // ANSI or Pipe Codes + return true; // ANSI or Pipe Codes } - if(line.match(/[\t\x00-\x1f\x80-\xff]/)) { // eslint-disable-line no-control-regex + if(line.match(/[\t\x00-\x1f\x80-\xff]/)) { // eslint-disable-line no-control-regex return true; } @@ -425,35 +425,35 @@ function isFormattedLine(line) { return false; } -// :TODO: rename to containsAnsi() +// :TODO: rename to containsAnsi() function isAnsi(input) { if(!input || 0 === input.length) { return false; } // - // * ANSI found - limited, just colors - // * Full ANSI art - // * + // * ANSI found - limited, just colors + // * Full ANSI art + // * // - // FULL ANSI art: - // * SAUCE present & reports as ANSI art - // * ANSI clear screen within first 2-3 codes - // * ANSI movement codes (goto, right, left, etc.) + // FULL ANSI art: + // * SAUCE present & reports as ANSI art + // * ANSI clear screen within first 2-3 codes + // * ANSI movement codes (goto, right, left, etc.) // - // * + // * /* - readSAUCE(input, (err, sauce) => { - if(!err && ('ANSi' === sauce.fileType || 'ANSiMation' === sauce.fileType)) { - return cb(null, 'ansi'); - } - }); - */ + readSAUCE(input, (err, sauce) => { + if(!err && ('ANSi' === sauce.fileType || 'ANSiMation' === sauce.fileType)) { + return cb(null, 'ansi'); + } + }); + */ - // :TODO: if a similar method is kept, use exec() until threshold - const ANSI_DET_REGEXP = /(?:\x1b\x5b)[?=;0-9]*?[ABCDEFGHJKLMSTfhlmnprsu]/g; // eslint-disable-line no-control-regex + // :TODO: if a similar method is kept, use exec() until threshold + const ANSI_DET_REGEXP = /(?:\x1b\x5b)[?=;0-9]*?[ABCDEFGHJKLMSTfhlmnprsu]/g; // eslint-disable-line no-control-regex const m = input.match(ANSI_DET_REGEXP) || []; - return m.length >= 4; // :TODO: do this reasonably, e.g. a percent or soemthing + return m.length >= 4; // :TODO: do this reasonably, e.g. a percent or soemthing } function splitTextAtTerms(s) { diff --git a/core/system_events.js b/core/system_events.js index e471c712..95316c95 100644 --- a/core/system_events.js +++ b/core/system_events.js @@ -2,24 +2,24 @@ 'use strict'; module.exports = { - ClientConnected : 'codes.l33t.enigma.system.connected', // { client, connectionCount } - ClientDisconnected : 'codes.l33t.enigma.system.disconnected', // { client, connectionCount } - TermDetected : 'codes.l33t.enigma.system.term_detected', // { client } + ClientConnected : 'codes.l33t.enigma.system.connected', // { client, connectionCount } + ClientDisconnected : 'codes.l33t.enigma.system.disconnected', // { client, connectionCount } + TermDetected : 'codes.l33t.enigma.system.term_detected', // { client } - ThemeChanged : 'codes.l33t.enigma.system.theme_changed', // { themeId } - ConfigChanged : 'codes.l33t.enigma.system.config_changed', - MenusChanged : 'codes.l33t.enigma.system.menus_changed', - PromptsChanged : 'codes.l33t.enigma.system.prompts_changed', + ThemeChanged : 'codes.l33t.enigma.system.theme_changed', // { themeId } + ConfigChanged : 'codes.l33t.enigma.system.config_changed', + MenusChanged : 'codes.l33t.enigma.system.menus_changed', + PromptsChanged : 'codes.l33t.enigma.system.prompts_changed', - // User - includes { user, ...} - NewUser : 'codes.l33t.enigma.system.new_user', - UserLogin : 'codes.l33t.enigma.system.user_login', - UserLogoff : 'codes.l33t.enigma.system.user_logoff', - UserUpload : 'codes.l33t.enigma.system.user_upload', // {..., files[ fileEntry, ...] } - UserDownload : 'codes.l33t.enigma.system.user_download', // {..., files[ fileEntry, ...] } + // User - includes { user, ...} + NewUser : 'codes.l33t.enigma.system.new_user', + UserLogin : 'codes.l33t.enigma.system.user_login', + UserLogoff : 'codes.l33t.enigma.system.user_logoff', + UserUpload : 'codes.l33t.enigma.system.user_upload', // {..., files[ fileEntry, ...] } + UserDownload : 'codes.l33t.enigma.system.user_download', // {..., files[ fileEntry, ...] } - // NYI below here: - UserPostMessage : 'codes.l33t.enigma.system.user_post_msg', - UserSendMail : 'codes.l33t.enigma.system.user_send_mail', - UserSendRunDoor : 'codes.l33t.enigma.system.user_run_door', + // NYI below here: + UserPostMessage : 'codes.l33t.enigma.system.user_post_msg', + UserSendMail : 'codes.l33t.enigma.system.user_send_mail', + UserSendRunDoor : 'codes.l33t.enigma.system.user_run_door', }; diff --git a/core/system_menu_method.js b/core/system_menu_method.js index 55da7430..9c9cd6d3 100644 --- a/core/system_menu_method.js +++ b/core/system_menu_method.js @@ -1,61 +1,61 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const removeClient = require('./client_connections.js').removeClient; -const ansiNormal = require('./ansi_term.js').normal; -const userLogin = require('./user_login.js').userLogin; -const messageArea = require('./message_area.js'); +// ENiGMA½ +const removeClient = require('./client_connections.js').removeClient; +const ansiNormal = require('./ansi_term.js').normal; +const userLogin = require('./user_login.js').userLogin; +const messageArea = require('./message_area.js'); -// deps -const _ = require('lodash'); -const iconv = require('iconv-lite'); +// deps +const _ = require('lodash'); +const iconv = require('iconv-lite'); -exports.login = login; -exports.logoff = logoff; -exports.prevMenu = prevMenu; -exports.nextMenu = nextMenu; -exports.prevConf = prevConf; -exports.nextConf = nextConf; -exports.prevArea = prevArea; -exports.nextArea = nextArea; -exports.sendForgotPasswordEmail = sendForgotPasswordEmail; +exports.login = login; +exports.logoff = logoff; +exports.prevMenu = prevMenu; +exports.nextMenu = nextMenu; +exports.prevConf = prevConf; +exports.nextConf = nextConf; +exports.prevArea = prevArea; +exports.nextArea = nextArea; +exports.sendForgotPasswordEmail = sendForgotPasswordEmail; function login(callingMenu, formData, extraArgs, cb) { userLogin(callingMenu.client, formData.value.username, formData.value.password, err => { if(err) { - // login failure + // login failure if(err.existingConn && _.has(callingMenu, 'menuConfig.config.tooNodeMenu')) { return callingMenu.gotoMenu(callingMenu.menuConfig.config.tooNodeMenu, cb); } else { - // Other error + // Other error return callingMenu.prevMenu(cb); } } - // success! + // success! return callingMenu.nextMenu(cb); }); } function logoff(callingMenu, formData, extraArgs, cb) { // - // Simple logoff. Note that recording of @ logoff properties/stats - // occurs elsewhere! + // Simple logoff. Note that recording of @ logoff properties/stats + // occurs elsewhere! // const client = callingMenu.client; setTimeout( () => { // - // For giggles... + // For giggles... // client.term.write( - ansiNormal() + '\n' + - iconv.decode(require('crypto').randomBytes(Math.floor(Math.random() * 65) + 20), client.term.outputEncoding) + - 'NO CARRIER', null, () => { + ansiNormal() + '\n' + + iconv.decode(require('crypto').randomBytes(Math.floor(Math.random() * 65) + 20), client.term.outputEncoding) + + 'NO CARRIER', null, () => { - // after data is written, disconnect & remove the client + // after data is written, disconnect & remove the client removeClient(client); return cb(null); } @@ -65,7 +65,7 @@ function logoff(callingMenu, formData, extraArgs, cb) { function prevMenu(callingMenu, formData, extraArgs, cb) { - // :TODO: this is a pretty big hack -- need the whole key map concep there like other places + // :TODO: this is a pretty big hack -- need the whole key map concep there like other places if(formData.key && 'return' === formData.key.name) { callingMenu.submitFormData = formData; } @@ -87,7 +87,7 @@ function nextMenu(callingMenu, formData, extraArgs, cb) { }); } -// :TODO: prev/nextConf, prev/nextArea should use a NYI MenuModule.redraw() or such -- avoid pop/goto() hack! +// :TODO: prev/nextConf, prev/nextArea should use a NYI MenuModule.redraw() or such -- avoid pop/goto() hack! function reloadMenu(menu, cb) { const prevMenu = menu.client.menuStack.pop(); prevMenu.instance.leave(); @@ -95,12 +95,12 @@ function reloadMenu(menu, cb) { } function prevConf(callingMenu, formData, extraArgs, cb) { - const confs = messageArea.getSortedAvailMessageConferences(callingMenu.client); + const confs = messageArea.getSortedAvailMessageConferences(callingMenu.client); const currIndex = confs.findIndex( e => e.confTag === callingMenu.client.user.properties.message_conf_tag) || confs.length; messageArea.changeMessageConference(callingMenu.client, confs[currIndex - 1].confTag, err => { if(err) { - return cb(err); // logged within changeMessageConference() + return cb(err); // logged within changeMessageConference() } return reloadMenu(callingMenu, cb); @@ -108,8 +108,8 @@ function prevConf(callingMenu, formData, extraArgs, cb) { } function nextConf(callingMenu, formData, extraArgs, cb) { - const confs = messageArea.getSortedAvailMessageConferences(callingMenu.client); - let currIndex = confs.findIndex( e => e.confTag === callingMenu.client.user.properties.message_conf_tag); + const confs = messageArea.getSortedAvailMessageConferences(callingMenu.client); + let currIndex = confs.findIndex( e => e.confTag === callingMenu.client.user.properties.message_conf_tag); if(currIndex === confs.length - 1) { currIndex = -1; @@ -117,7 +117,7 @@ function nextConf(callingMenu, formData, extraArgs, cb) { messageArea.changeMessageConference(callingMenu.client, confs[currIndex + 1].confTag, err => { if(err) { - return cb(err); // logged within changeMessageConference() + return cb(err); // logged within changeMessageConference() } return reloadMenu(callingMenu, cb); @@ -125,12 +125,12 @@ function nextConf(callingMenu, formData, extraArgs, cb) { } function prevArea(callingMenu, formData, extraArgs, cb) { - const areas = messageArea.getSortedAvailMessageAreasByConfTag(callingMenu.client.user.properties.message_conf_tag); + const areas = messageArea.getSortedAvailMessageAreasByConfTag(callingMenu.client.user.properties.message_conf_tag); const currIndex = areas.findIndex( e => e.areaTag === callingMenu.client.user.properties.message_area_tag) || areas.length; messageArea.changeMessageArea(callingMenu.client, areas[currIndex - 1].areaTag, err => { if(err) { - return cb(err); // logged within changeMessageArea() + return cb(err); // logged within changeMessageArea() } return reloadMenu(callingMenu, cb); @@ -138,8 +138,8 @@ function prevArea(callingMenu, formData, extraArgs, cb) { } function nextArea(callingMenu, formData, extraArgs, cb) { - const areas = messageArea.getSortedAvailMessageAreasByConfTag(callingMenu.client.user.properties.message_conf_tag); - let currIndex = areas.findIndex( e => e.areaTag === callingMenu.client.user.properties.message_area_tag); + const areas = messageArea.getSortedAvailMessageAreasByConfTag(callingMenu.client.user.properties.message_conf_tag); + let currIndex = areas.findIndex( e => e.areaTag === callingMenu.client.user.properties.message_area_tag); if(currIndex === areas.length - 1) { currIndex = -1; @@ -147,7 +147,7 @@ function nextArea(callingMenu, formData, extraArgs, cb) { messageArea.changeMessageArea(callingMenu.client, areas[currIndex + 1].areaTag, err => { if(err) { - return cb(err); // logged within changeMessageArea() + return cb(err); // logged within changeMessageArea() } return reloadMenu(callingMenu, cb); diff --git a/core/system_view_validate.js b/core/system_view_validate.js index 397f08c0..5eb0fc4a 100644 --- a/core/system_view_validate.js +++ b/core/system_view_validate.js @@ -1,25 +1,25 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const User = require('./user.js'); -const Config = require('./config.js').get; -const Log = require('./logger.js').log; -const { getAddressedToInfo } = require('./mail_util.js'); -const Message = require('./message.js'); +// ENiGMA½ +const User = require('./user.js'); +const Config = require('./config.js').get; +const Log = require('./logger.js').log; +const { getAddressedToInfo } = require('./mail_util.js'); +const Message = require('./message.js'); -// deps -const fs = require('graceful-fs'); +// deps +const fs = require('graceful-fs'); -exports.validateNonEmpty = validateNonEmpty; -exports.validateMessageSubject = validateMessageSubject; -exports.validateUserNameAvail = validateUserNameAvail; -exports.validateUserNameExists = validateUserNameExists; -exports.validateUserNameOrRealNameExists = validateUserNameOrRealNameExists; -exports.validateGeneralMailAddressedTo = validateGeneralMailAddressedTo; -exports.validateEmailAvail = validateEmailAvail; -exports.validateBirthdate = validateBirthdate; -exports.validatePasswordSpec = validatePasswordSpec; +exports.validateNonEmpty = validateNonEmpty; +exports.validateMessageSubject = validateMessageSubject; +exports.validateUserNameAvail = validateUserNameAvail; +exports.validateUserNameExists = validateUserNameExists; +exports.validateUserNameOrRealNameExists = validateUserNameOrRealNameExists; +exports.validateGeneralMailAddressedTo = validateGeneralMailAddressedTo; +exports.validateEmailAvail = validateEmailAvail; +exports.validateBirthdate = validateBirthdate; +exports.validatePasswordSpec = validatePasswordSpec; function validateNonEmpty(data, cb) { return cb(data && data.length > 0 ? null : new Error('Field cannot be empty')); @@ -34,11 +34,11 @@ function validateUserNameAvail(data, cb) { if(!data || data.length < config.users.usernameMin) { cb(new Error('Username too short')); } else if(data.length > config.users.usernameMax) { - // generally should be unreached due to view restraints + // generally should be unreached due to view restraints return cb(new Error('Username too long')); } else { - const usernameRegExp = new RegExp(config.users.usernamePattern); - const invalidNames = config.users.newUserNames + config.users.badUserNames; + const usernameRegExp = new RegExp(config.users.usernamePattern); + const invalidNames = config.users.newUserNames + config.users.badUserNames; if(!usernameRegExp.test(data)) { return cb(new Error('Username contains invalid characters')); @@ -47,9 +47,9 @@ function validateUserNameAvail(data, cb) { } else if(/^[0-9]+$/.test(data)) { return cb(new Error('Username cannot be a number')); } else { - // a new user name cannot be an existing user name or an existing real name + // a new user name cannot be an existing user name or an existing real name User.getUserIdAndNameByLookup(data, function userIdAndName(err) { - if(!err) { // err is null if we succeeded -- meaning this user exists already + if(!err) { // err is null if we succeeded -- meaning this user exists already return cb(new Error('Username unavailable')); } @@ -83,11 +83,11 @@ function validateUserNameOrRealNameExists(data, cb) { function validateGeneralMailAddressedTo(data, cb) { // - // Allow any supported addressing: - // - Local username or real name - // - Supported remote flavors such as FTN, email, ... + // Allow any supported addressing: + // - Local username or real name + // - Supported remote flavors such as FTN, email, ... // - // :TODO: remove hard-coded FTN check here. We need a decent way to register global supported flavors with modules. + // :TODO: remove hard-coded FTN check here. We need a decent way to register global supported flavors with modules. const addressedToInfo = getAddressedToInfo(data); if(Message.AddressFlavor.FTN === addressedToInfo.flavor) { @@ -99,17 +99,17 @@ function validateGeneralMailAddressedTo(data, cb) { function validateEmailAvail(data, cb) { // - // This particular method allows empty data - e.g. no email entered + // This particular method allows empty data - e.g. no email entered // if(!data || 0 === data.length) { return cb(null); } // - // Otherwise, it must be a valid email. We'll be pretty lose here, like - // the HTML5 spec. + // Otherwise, it must be a valid email. We'll be pretty lose here, like + // the HTML5 spec. // - // See http://stackoverflow.com/questions/7786058/find-the-regex-used-by-html5-forms-for-validation + // See http://stackoverflow.com/questions/7786058/find-the-regex-used-by-html5-forms-for-validation // const emailRegExp = /[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(.[a-z0-9-]+)*/; if(!emailRegExp.test(data)) { @@ -129,7 +129,7 @@ function validateEmailAvail(data, cb) { function validateBirthdate(data, cb) { - // :TODO: check for dates in the future, or > reasonable values + // :TODO: check for dates in the future, or > reasonable values return cb(isNaN(Date.parse(data)) ? new Error('Invalid birthdate') : null); } @@ -139,7 +139,7 @@ function validatePasswordSpec(data, cb) { return cb(new Error('Password too short')); } - // check badpass, if avail + // check badpass, if avail fs.readFile(config.users.badPassFile, 'utf8', (err, passwords) => { if(err) { Log.warn( { error : err.message }, 'Cannot read bad pass file'); diff --git a/core/telnet_bridge.js b/core/telnet_bridge.js index 5276ebb0..a3a4d672 100644 --- a/core/telnet_bridge.js +++ b/core/telnet_bridge.js @@ -1,36 +1,36 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const resetScreen = require('./ansi_term.js').resetScreen; -const setSyncTermFontWithAlias = require('./ansi_term.js').setSyncTermFontWithAlias; +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const resetScreen = require('./ansi_term.js').resetScreen; +const setSyncTermFontWithAlias = require('./ansi_term.js').setSyncTermFontWithAlias; -// deps -const async = require('async'); -const _ = require('lodash'); -const net = require('net'); -const EventEmitter = require('events'); -const buffers = require('buffers'); +// deps +const async = require('async'); +const _ = require('lodash'); +const net = require('net'); +const EventEmitter = require('events'); +const buffers = require('buffers'); /* - Expected configuration block: + Expected configuration block: - { - module: telnet_bridge - ... - config: { - host: somehost.net - port: 23 - } - } + { + module: telnet_bridge + ... + config: { + host: somehost.net + port: 23 + } + } */ -// :TODO: ENH: Support nodeMax and tooManyArt +// :TODO: ENH: Support nodeMax and tooManyArt exports.moduleInfo = { - name : 'Telnet Bridge', - desc : 'Connect to other Telnet Systems', - author : 'Andrew Pamment', + name : 'Telnet Bridge', + desc : 'Connect to other Telnet Systems', + author : 'Andrew Pamment', }; const IAC_DO_TERM_TYPE = Buffer.from( [ 255, 253, 24 ] ); @@ -39,7 +39,7 @@ class TelnetClientConnection extends EventEmitter { constructor(client) { super(); - this.client = client; + this.client = client; } @@ -47,7 +47,7 @@ class TelnetClientConnection extends EventEmitter { if(!this.pipeRestored) { this.pipeRestored = true; - // client may have bailed + // client may have bailed if(null !== _.get(this, 'client.term.output', null)) { if(this.bridgeConnection) { this.client.term.output.unpipe(this.bridgeConnection); @@ -69,9 +69,9 @@ class TelnetClientConnection extends EventEmitter { this.client.term.rawWrite(data); // - // Wait for a terminal type request, and send it eactly once. - // This is enough (in additional to other negotiations handled in telnet.js) - // to get us in on most systems + // Wait for a terminal type request, and send it eactly once. + // This is enough (in additional to other negotiations handled in telnet.js) + // to get us in on most systems // if(!this.termSent && data.indexOf(IAC_DO_TERM_TYPE) > -1) { this.termSent = true; @@ -107,23 +107,23 @@ class TelnetClientConnection extends EventEmitter { getTermTypeNegotiationBuffer() { // - // Create a TERMINAL-TYPE sub negotiation buffer using the - // actual/current terminal type. + // Create a TERMINAL-TYPE sub negotiation buffer using the + // actual/current terminal type. // let bufs = buffers(); bufs.push(Buffer.from( [ - 255, // IAC - 250, // SB - 24, // TERMINAL-TYPE - 0, // IS + 255, // IAC + 250, // SB + 24, // TERMINAL-TYPE + 0, // IS ] )); bufs.push( - Buffer.from(this.client.term.termType), // e.g. "ansi" - Buffer.from( [ 255, 240 ] ) // IAC, SE + Buffer.from(this.client.term.termType), // e.g. "ansi" + Buffer.from( [ 255, 240 ] ) // IAC, SE ); return bufs.toBuffer(); @@ -135,8 +135,8 @@ exports.getModule = class TelnetBridgeModule extends MenuModule { constructor(options) { super(options); - this.config = Object.assign({}, _.get(options, 'menuConfig.config'), options.extraArgs); - this.config.port = this.config.port || 23; + this.config = Object.assign({}, _.get(options, 'menuConfig.config'), options.extraArgs); + this.config.port = this.config.port || 23; } initSequence() { @@ -147,7 +147,7 @@ exports.getModule = class TelnetBridgeModule extends MenuModule { [ function validateConfig(callback) { if(_.isString(self.config.host) && - _.isNumber(self.config.port)) + _.isNumber(self.config.port)) { callback(null); } else { @@ -156,8 +156,8 @@ exports.getModule = class TelnetBridgeModule extends MenuModule { }, function createTelnetBridge(callback) { const connectOpts = { - port : self.config.port, - host : self.config.host, + port : self.config.port, + host : self.config.host, }; self.client.term.write(resetScreen()); diff --git a/core/text_view.js b/core/text_view.js index 69908e50..4611821a 100644 --- a/core/text_view.js +++ b/core/text_view.js @@ -1,26 +1,26 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const View = require('./view.js').View; -const miscUtil = require('./misc_util.js'); -const ansi = require('./ansi_term.js'); -const padStr = require('./string_util.js').pad; -const stylizeString = require('./string_util.js').stylizeString; -const renderSubstr = require('./string_util.js').renderSubstr; -const renderStringLength = require('./string_util.js').renderStringLength; -const pipeToAnsi = require('./color_codes.js').pipeToAnsi; -const stripAllLineFeeds = require('./string_util.js').stripAllLineFeeds; +// ENiGMA½ +const View = require('./view.js').View; +const miscUtil = require('./misc_util.js'); +const ansi = require('./ansi_term.js'); +const padStr = require('./string_util.js').pad; +const stylizeString = require('./string_util.js').stylizeString; +const renderSubstr = require('./string_util.js').renderSubstr; +const renderStringLength = require('./string_util.js').renderStringLength; +const pipeToAnsi = require('./color_codes.js').pipeToAnsi; +const stripAllLineFeeds = require('./string_util.js').stripAllLineFeeds; -// deps -const util = require('util'); -const _ = require('lodash'); +// deps +const util = require('util'); +const _ = require('lodash'); -exports.TextView = TextView; +exports.TextView = TextView; function TextView(options) { if(options.dimens) { - options.dimens.height = 1; // force height of 1 for TextView's & sub classes + options.dimens.height = 1; // force height of 1 for TextView's & sub classes } View.call(this, options); @@ -31,10 +31,10 @@ function TextView(options) { this.maxLength = this.client.term.termWidth - this.position.col; } - this.fillChar = renderSubstr(miscUtil.valueWithDefault(options.fillChar, ' '), 0, 1); - this.justify = options.justify || 'left'; - this.resizable = miscUtil.valueWithDefault(options.resizable, true); - this.horizScroll = miscUtil.valueWithDefault(options.horizScroll, true); + this.fillChar = renderSubstr(miscUtil.valueWithDefault(options.fillChar, ' '), 0, 1); + this.justify = options.justify || 'left'; + this.resizable = miscUtil.valueWithDefault(options.resizable, true); + this.horizScroll = miscUtil.valueWithDefault(options.horizScroll, true); if(_.isString(options.textOverflow)) { this.textOverflow = options.textOverflow; @@ -45,57 +45,57 @@ function TextView(options) { } /* - this.drawText = function(s) { + this.drawText = function(s) { - // - // |<- this.maxLength - // ABCDEFGHIJK - // |ABCDEFG| ^_ this.text.length - // ^-- this.dimens.width - // - let textToDraw = _.isString(this.textMaskChar) ? - new Array(s.length + 1).join(this.textMaskChar) : - stylizeString(s, this.hasFocus ? this.focusTextStyle : this.textStyle); + // + // |<- this.maxLength + // ABCDEFGHIJK + // |ABCDEFG| ^_ this.text.length + // ^-- this.dimens.width + // + let textToDraw = _.isString(this.textMaskChar) ? + new Array(s.length + 1).join(this.textMaskChar) : + stylizeString(s, this.hasFocus ? this.focusTextStyle : this.textStyle); - if(textToDraw.length > this.dimens.width) { - if(this.hasFocus) { - if(this.horizScroll) { - textToDraw = textToDraw.substr(textToDraw.length - this.dimens.width, textToDraw.length); - } - } else { - if(textToDraw.length > this.dimens.width) { - if(this.textOverflow && - this.dimens.width > this.textOverflow.length && - textToDraw.length - this.textOverflow.length >= this.textOverflow.length) - { - textToDraw = textToDraw.substr(0, this.dimens.width - this.textOverflow.length) + this.textOverflow; - } else { - textToDraw = textToDraw.substr(0, this.dimens.width); - } - } - } - } + if(textToDraw.length > this.dimens.width) { + if(this.hasFocus) { + if(this.horizScroll) { + textToDraw = textToDraw.substr(textToDraw.length - this.dimens.width, textToDraw.length); + } + } else { + if(textToDraw.length > this.dimens.width) { + if(this.textOverflow && + this.dimens.width > this.textOverflow.length && + textToDraw.length - this.textOverflow.length >= this.textOverflow.length) + { + textToDraw = textToDraw.substr(0, this.dimens.width - this.textOverflow.length) + this.textOverflow; + } else { + textToDraw = textToDraw.substr(0, this.dimens.width); + } + } + } + } - this.client.term.write(padStr( - textToDraw, - this.dimens.width + 1, - this.fillChar, - this.justify, - this.hasFocus ? this.getFocusSGR() : this.getSGR(), - this.getStyleSGR(1) || this.getSGR() - ), false); - }; + this.client.term.write(padStr( + textToDraw, + this.dimens.width + 1, + this.fillChar, + this.justify, + this.hasFocus ? this.getFocusSGR() : this.getSGR(), + this.getStyleSGR(1) || this.getSGR() + ), false); + }; */ this.drawText = function(s) { // // |<- this.maxLength - // ABCDEFGHIJK - // |ABCDEFG| ^_ this.text.length - // ^-- this.dimens.width + // ABCDEFGHIJK + // |ABCDEFG| ^_ this.text.length + // ^-- this.dimens.width // - let renderLength = renderStringLength(s); // initial; may be adjusted below: + let renderLength = renderStringLength(s); // initial; may be adjusted below: let textToDraw = _.isString(this.textMaskChar) ? new Array(renderLength + 1).join(this.textMaskChar) : @@ -110,8 +110,8 @@ function TextView(options) { } } else { if(this.textOverflow && - this.dimens.width > this.textOverflow.length && - renderLength - this.textOverflow.length >= this.textOverflow.length) + this.dimens.width > this.textOverflow.length && + renderLength - this.textOverflow.length >= this.textOverflow.length) { textToDraw = renderSubstr(textToDraw, 0, this.dimens.width - this.textOverflow.length) + this.textOverflow; } else { @@ -130,9 +130,9 @@ function TextView(options) { this.justify, this.hasFocus ? this.getFocusSGR() : this.getSGR(), this.getStyleSGR(1) || this.getSGR(), - true // use render len + true // use render len ), - false // no converting CRLF needed + false // no converting CRLF needed ); }; @@ -142,16 +142,16 @@ function TextView(options) { return this.position.col + offset; }; - this.setText(options.text || '', false); // false=do not redraw now + this.setText(options.text || '', false); // false=do not redraw now } util.inherits(TextView, View); TextView.prototype.redraw = function() { // - // A lot of views will get an initial redraw() with empty text (''). We can short - // circuit this by NOT doing any of the work if this is the initial drawText - // and there is no actual text (e.g. save SGR's and processing) + // A lot of views will get an initial redraw() with empty text (''). We can short + // circuit this by NOT doing any of the work if this is the initial drawText + // and there is no actual text (e.g. save SGR's and processing) // if(!this.hasDrawnOnce) { if(_.isUndefined(this.text)) { @@ -187,7 +187,7 @@ TextView.prototype.setText = function(text, redraw) { text = text.toString(); } - text = pipeToAnsi(stripAllLineFeeds(text), this.client); // expand MCI/etc. + text = pipeToAnsi(stripAllLineFeeds(text), this.client); // expand MCI/etc. var widthDelta = 0; if(this.text && this.text !== text) { @@ -201,7 +201,7 @@ TextView.prototype.setText = function(text, redraw) { //this.text = this.text.substr(0, this.maxLength); } - // :TODO: it would be nice to be able to stylize strings with MCI and {special} MCI syntax, e.g. "|BN {UN!toUpper}" + // :TODO: it would be nice to be able to stylize strings with MCI and {special} MCI syntax, e.g. "|BN {UN!toUpper}" this.text = stylizeString(this.text, this.hasFocus ? this.focusTextStyle : this.textStyle); if(this.autoScale.width) { @@ -215,32 +215,32 @@ TextView.prototype.setText = function(text, redraw) { /* TextView.prototype.setText = function(text) { - if(!_.isString(text)) { - text = text.toString(); - } + if(!_.isString(text)) { + text = text.toString(); + } - var widthDelta = 0; - if(this.text && this.text !== text) { - widthDelta = Math.abs(this.text.length - text.length); - } + var widthDelta = 0; + if(this.text && this.text !== text) { + widthDelta = Math.abs(this.text.length - text.length); + } - this.text = text; + this.text = text; - if(this.maxLength > 0) { - this.text = this.text.substr(0, this.maxLength); - } + if(this.maxLength > 0) { + this.text = this.text.substr(0, this.maxLength); + } - this.text = stylizeString(this.text, this.hasFocus ? this.focusTextStyle : this.textStyle); + this.text = stylizeString(this.text, this.hasFocus ? this.focusTextStyle : this.textStyle); - //if(this.resizable) { - // this.dimens.width = this.text.length + widthDelta; - //} + //if(this.resizable) { + // this.dimens.width = this.text.length + widthDelta; + //} - if(this.autoScale.width) { - this.dimens.width = this.text.length + widthDelta; - } + if(this.autoScale.width) { + this.dimens.width = this.text.length + widthDelta; + } - this.redraw(); + this.redraw(); }; */ @@ -251,9 +251,9 @@ TextView.prototype.clearText = function() { TextView.prototype.setPropertyValue = function(propName, value) { switch(propName) { case 'textMaskChar' : this.textMaskChar = value.substr(0, 1); break; - case 'textOverflow' : this.textOverflow = value; break; - case 'maxLength' : this.maxLength = parseInt(value, 10); break; - case 'password' : + case 'textOverflow' : this.textOverflow = value; break; + case 'maxLength' : this.maxLength = parseInt(value, 10); break; + case 'password' : if(true === value) { this.textMaskChar = this.client.currentTheme.helpers.getPasswordChar(); } diff --git a/core/theme.js b/core/theme.js index aca8ca2c..6d2bcff6 100644 --- a/core/theme.js +++ b/core/theme.js @@ -1,37 +1,37 @@ /* jslint node: true */ 'use strict'; -const Config = require('./config.js').get; -const art = require('./art.js'); -const ansi = require('./ansi_term.js'); -const Log = require('./logger.js').log; -const ConfigCache = require('./config_cache.js'); -const getFullConfig = require('./config_util.js').getFullConfig; -const asset = require('./asset.js'); -const ViewController = require('./view_controller.js').ViewController; -const Errors = require('./enig_error.js').Errors; -const ErrorReasons = require('./enig_error.js').ErrorReasons; -const Events = require('./events.js'); +const Config = require('./config.js').get; +const art = require('./art.js'); +const ansi = require('./ansi_term.js'); +const Log = require('./logger.js').log; +const ConfigCache = require('./config_cache.js'); +const getFullConfig = require('./config_util.js').getFullConfig; +const asset = require('./asset.js'); +const ViewController = require('./view_controller.js').ViewController; +const Errors = require('./enig_error.js').Errors; +const ErrorReasons = require('./enig_error.js').ErrorReasons; +const Events = require('./events.js'); -const fs = require('graceful-fs'); -const paths = require('path'); -const async = require('async'); -const _ = require('lodash'); -const assert = require('assert'); +const fs = require('graceful-fs'); +const paths = require('path'); +const async = require('async'); +const _ = require('lodash'); +const assert = require('assert'); -exports.getThemeArt = getThemeArt; -exports.getAvailableThemes = getAvailableThemes; -exports.getRandomTheme = getRandomTheme; +exports.getThemeArt = getThemeArt; +exports.getAvailableThemes = getAvailableThemes; +exports.getRandomTheme = getRandomTheme; exports.setClientTheme = setClientTheme; -exports.initAvailableThemes = initAvailableThemes; -exports.displayThemeArt = displayThemeArt; -exports.displayThemedPause = displayThemedPause; -exports.displayThemedPrompt = displayThemedPrompt; -exports.displayThemedAsset = displayThemedAsset; +exports.initAvailableThemes = initAvailableThemes; +exports.displayThemeArt = displayThemeArt; +exports.displayThemedPause = displayThemedPause; +exports.displayThemedPrompt = displayThemedPrompt; +exports.displayThemedAsset = displayThemedAsset; function refreshThemeHelpers(theme) { // - // Create some handy helpers + // Create some handy helpers // theme.helpers = { getPasswordChar : function() { @@ -75,9 +75,9 @@ function loadTheme(themeId, cb) { }; const getOpts = { - filePath : path, - forceReCache : true, - callback : changed, + filePath : path, + forceReCache : true, + callback : changed, }; ConfigCache.getConfigWithOptions(getOpts, (err, theme) => { @@ -86,8 +86,8 @@ function loadTheme(themeId, cb) { } if(!_.isObject(theme.info) || - !_.isString(theme.info.name) || - !_.isString(theme.info.author)) + !_.isString(theme.info.name) || + !_.isString(theme.info.author)) { return cb(Errors.Invalid('Invalid or missing "info" section')); } @@ -113,7 +113,7 @@ function getMergedTheme(menuConfig, promptConfig, theme) { assert(_.isObject(theme)); // :TODO: merge in defaults (customization.defaults{} ) - // :TODO: apply generic stuff, e.g. "VM" (vs "VM1") + // :TODO: apply generic stuff, e.g. "VM" (vs "VM1") // // Create a *clone* of menuConfig (menu.hjson) then bring in @@ -213,7 +213,7 @@ function getMergedTheme(menuConfig, promptConfig, theme) { if(_.has(theme, [ 'customization', sectionName, menuName ])) { const menuTheme = theme.customization[sectionName][menuName]; - // config block is direct assign/overwrite + // config block is direct assign/overwrite // :TODO: should probably be _.merge() if(menuTheme.config) { mergedThemeMenu.config = _.assign(mergedThemeMenu.config || {}, menuTheme.config); @@ -250,7 +250,7 @@ function getMergedTheme(menuConfig, promptConfig, theme) { // * There is no 'prompt' specified // if('menus' === sectionName && !_.isString(mergedThemeMenu.prompt) && - (createdFormSection || !_.isObject(mergedThemeMenu.form))) + (createdFormSection || !_.isObject(mergedThemeMenu.form))) { mergedThemeMenu.runtime = _.merge(mergedThemeMenu.runtime || {}, { autoNext : true } ); } @@ -337,21 +337,21 @@ function initAvailableThemes(cb) { menuConfig, promptConfig, files.filter( f => { - // sync normally not allowed -- initAvailableThemes() is a startup-only method, however + // sync normally not allowed -- initAvailableThemes() is a startup-only method, however return fs.statSync(paths.join(config.paths.themes, f)).isDirectory(); }) ); }); }, function populateAvailable(menuConfig, promptConfig, themeDirectories, callback) { - async.each(themeDirectories, (themeId, nextThemeDir) => { // theme dir = theme ID + async.each(themeDirectories, (themeId, nextThemeDir) => { // theme dir = theme ID loadTheme(themeId, (err, theme) => { if(err) { if(ErrorReasons.NotEnabled !== err.reasonCode) { Log.warn( { themeId : themeId, err : err.message }, 'Failed loading theme'); } - return nextThemeDir(null); // try next + return nextThemeDir(null); // try next } Object.assign(theme.info, { themeId } ); @@ -413,15 +413,15 @@ function setClientTheme(client, themeId) { function getThemeArt(options, cb) { // - // options - required: - // name + // options - required: + // name // - // options - optional - // client - needed for user's theme/etc. - // themeId - // asAnsi - // readSauce - // random + // options - optional + // client - needed for user's theme/etc. + // themeId + // asAnsi + // readSauce + // random // const config = Config(); if(!options.themeId && _.has(options, 'client.user.properties.theme_id')) { @@ -430,30 +430,30 @@ function getThemeArt(options, cb) { options.themeId = config.defaults.theme; } - // :TODO: replace asAnsi stuff with something like retrieveAs = 'ansi' | 'pipe' | ... - // :TODO: Some of these options should only be set if not provided! - options.asAnsi = true; // always convert to ANSI - options.readSauce = true; // read SAUCE, if avail - options.random = _.get(options, 'random', true); // FILENAME.EXT support + // :TODO: replace asAnsi stuff with something like retrieveAs = 'ansi' | 'pipe' | ... + // :TODO: Some of these options should only be set if not provided! + options.asAnsi = true; // always convert to ANSI + options.readSauce = true; // read SAUCE, if avail + options.random = _.get(options, 'random', true); // FILENAME.EXT support // - // We look for themed art in the following order: - // 1) Direct/relative path - // 2) Via theme supplied by |themeId| - // 3) Via default theme - // 4) General art directory + // We look for themed art in the following order: + // 1) Direct/relative path + // 2) Via theme supplied by |themeId| + // 3) Via default theme + // 4) General art directory // async.waterfall( [ function fromPath(callback) { // - // We allow relative (to enigma-bbs) or full paths + // We allow relative (to enigma-bbs) or full paths // if('/' === options.name.charAt(0)) { - // just take the path as-is + // just take the path as-is options.basePath = paths.dirname(options.name); } else if(options.name.indexOf('/') > -1) { - // make relative to base BBS dir + // make relative to base BBS dir options.basePath = paths.join(__dirname, '../', paths.dirname(options.name)); } else { return callback(null, null); @@ -513,11 +513,11 @@ function displayThemeArt(options, cb) { if(err) { return cb(err); } - // :TODO: just use simple merge of options -> displayOptions + // :TODO: just use simple merge of options -> displayOptions const displayOpts = { - sauce : artInfo.sauce, - font : options.font, - trailingLF : options.trailingLF, + sauce : artInfo.sauce, + font : options.font, + trailingLF : options.trailingLF, }; art.display(options.client, artInfo.data, displayOpts, (err, mciMap, extraInfo) => { @@ -543,14 +543,14 @@ function displayThemedPrompt(name, client, options, cb) { } // - // If we did *not* clear the screen, don't let the font change - // doing so messes things up -- most terminals that support font - // changing can only display a single font at at time. + // If we did *not* clear the screen, don't let the font change + // doing so messes things up -- most terminals that support font + // changing can only display a single font at at time. // - // :TODO: We can use term detection to do nifty things like avoid this kind of kludge: + // :TODO: We can use term detection to do nifty things like avoid this kind of kludge: const dispOptions = Object.assign( {}, promptConfig.options ); if(!options.clearScreen) { - dispOptions.font = 'not_really_a_font!'; // kludge :) + dispOptions.font = 'not_really_a_font!'; // kludge :) } displayThemedAsset( @@ -568,7 +568,7 @@ function displayThemedPrompt(name, client, options, cb) { }, function discoverCursorPosition(promptConfig, artInfo, callback) { if(!options.clearPrompt) { - // no need to query cursor - we're not gonna use it + // no need to query cursor - we're not gonna use it return callback(null, promptConfig, artInfo); } @@ -583,9 +583,9 @@ function displayThemedPrompt(name, client, options, cb) { const tempViewController = useTempViewController ? new ViewController( { client : client } ) : options.viewController; const loadOpts = { - promptName : name, - mciMap : artInfo.mciMap, - config : promptConfig, + promptName : name, + mciMap : artInfo.mciMap, + config : promptConfig, }; tempViewController.loadFromPromptConfig(loadOpts, () => { @@ -606,7 +606,7 @@ function displayThemedPrompt(name, client, options, cb) { if(artInfo.startRow && artInfo.height) { client.term.rawWrite(ansi.goto(artInfo.startRow, 1)); - // Note: Does not work properly in NetRunner < 2.0b17: + // Note: Does not work properly in NetRunner < 2.0b17: client.term.rawWrite(ansi.deleteLine(artInfo.height)); } else { client.term.rawWrite(ansi.eraseLine(1)); @@ -631,7 +631,7 @@ function displayThemedPrompt(name, client, options, cb) { } // -// Pause prompts are a special prompt by the name 'pause'. +// Pause prompts are a special prompt by the name 'pause'. // function displayThemedPause(client, options, cb) { @@ -651,7 +651,7 @@ function displayThemedPause(client, options, cb) { function displayThemedAsset(assetSpec, client, options, cb) { assert(_.isObject(client)); - // options are... optional + // options are... optional if(3 === arguments.length) { cb = options; options = {}; @@ -666,12 +666,12 @@ function displayThemedAsset(assetSpec, client, options, cb) { return cb(new Error('Asset not found: ' + assetSpec)); } - // :TODO: just use simple merge of options -> displayOptions + // :TODO: just use simple merge of options -> displayOptions var dispOpts = { - name : artAsset.asset, - client : client, - font : options.font, - trailingLF : options.trailingLF, + name : artAsset.asset, + client : client, + font : options.font, + trailingLF : options.trailingLF, }; switch(artAsset.type) { @@ -682,13 +682,13 @@ function displayThemedAsset(assetSpec, client, options, cb) { break; case 'method' : - // :TODO: fetch & render via method + // :TODO: fetch & render via method break; case 'inline ' : - // :TODO: think about this more in relation to themes, etc. How can this come - // from a theme (with override from menu.json) ??? - // look @ client.currentTheme.inlineArt[name] -> menu/prompt[name] + // :TODO: think about this more in relation to themes, etc. How can this come + // from a theme (with override from menu.json) ??? + // look @ client.currentTheme.inlineArt[name] -> menu/prompt[name] break; default : diff --git a/core/tic_file_info.js b/core/tic_file_info.js index 37aec134..5db24cc2 100644 --- a/core/tic_file_info.js +++ b/core/tic_file_info.js @@ -1,25 +1,25 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Address = require('./ftn_address.js'); -const Errors = require('./enig_error.js').Errors; -const EnigAssert = require('./enigma_assert.js'); +// ENiGMA½ +const Address = require('./ftn_address.js'); +const Errors = require('./enig_error.js').Errors; +const EnigAssert = require('./enigma_assert.js'); -// deps -const fs = require('graceful-fs'); -const CRC32 = require('./crc.js').CRC32; -const _ = require('lodash'); -const async = require('async'); -const paths = require('path'); -const crypto = require('crypto'); +// deps +const fs = require('graceful-fs'); +const CRC32 = require('./crc.js').CRC32; +const _ = require('lodash'); +const async = require('async'); +const paths = require('path'); +const crypto = require('crypto'); // -// Class to read and hold information from a TIC file +// Class to read and hold information from a TIC file // -// * FTS-5006.001 @ http://www.filegate.net/ftsc/FTS-5006.001 -// * FSP-1039.001 @ http://ftsc.org/docs/old/fsp-1039.001 -// * FSC-0087.001 @ http://ftsc.org/docs/fsc-0087.001 +// * FTS-5006.001 @ http://www.filegate.net/ftsc/FTS-5006.001 +// * FSP-1039.001 @ http://ftsc.org/docs/old/fsp-1039.001 +// * FSC-0087.001 @ http://ftsc.org/docs/fsc-0087.001 // module.exports = class TicFileInfo { constructor() { @@ -29,8 +29,8 @@ module.exports = class TicFileInfo { static get requiredFields() { return [ 'Area', 'Origin', 'From', 'File', 'Crc', - // :TODO: validate this: - //'Path', 'Seenby' // these two are questionable; some systems don't send them? + // :TODO: validate this: + //'Path', 'Seenby' // these two are questionable; some systems don't send them? ]; } @@ -42,7 +42,7 @@ module.exports = class TicFileInfo { const value = this.get(key); if(value) { // - // We call toString() on values to ensure numbers, addresses, etc. are converted + // We call toString() on values to ensure numbers, addresses, etc. are converted // joinWith = joinWith || ''; if(Array.isArray(value)) { @@ -67,9 +67,9 @@ module.exports = class TicFileInfo { } validate(config, cb) { - // config.nodes - // config.defaultPassword (optional) - // config.localAreaTags + // config.nodes + // config.defaultPassword (optional) + // config.localAreaTags EnigAssert(config.nodes && config.localAreaTags); const self = this; @@ -84,7 +84,7 @@ module.exports = class TicFileInfo { const area = self.getAsString('Area').toUpperCase(); const localInfo = { - areaTag : config.localAreaTags.find( areaTag => areaTag.toUpperCase() === area ), + areaTag : config.localAreaTags.find( areaTag => areaTag.toUpperCase() === area ), }; if(!localInfo.areaTag) { @@ -96,17 +96,17 @@ module.exports = class TicFileInfo { return callback(Errors.Invalid(`Invalid "From" address: ${self.getAsString('From')}`)); } - // note that our config may have wildcards, such as "80:774/*" + // note that our config may have wildcards, such as "80:774/*" localInfo.node = Object.keys(config.nodes).find( nodeAddrWildcard => from.isPatternMatch(nodeAddrWildcard) ); if(!localInfo.node) { return callback(Errors.Invalid('TIC is not from a known node')); } - // if we require a password, "PW" must match + // if we require a password, "PW" must match const passActual = _.get(config.nodes, [ localInfo.node, 'tic', 'password' ] ) || config.defaultPassword; if(!passActual) { - return callback(null, localInfo); // no pw validation + return callback(null, localInfo); // no pw validation } const passTic = self.getAsString('Pw'); @@ -117,22 +117,22 @@ module.exports = class TicFileInfo { return callback(null, localInfo); }, function checksumAndSize(localInfo, callback) { - const crcTic = self.get('Crc'); - const stream = fs.createReadStream(self.filePath); - const crc = new CRC32(); - let sizeActual = 0; + const crcTic = self.get('Crc'); + const stream = fs.createReadStream(self.filePath); + const crc = new CRC32(); + let sizeActual = 0; - let sha256Tic = self.getAsString('Sha256'); + let sha256Tic = self.getAsString('Sha256'); let sha256; if(sha256Tic) { - sha256Tic = sha256Tic.toLowerCase(); - sha256 = crypto.createHash('sha256'); + sha256Tic = sha256Tic.toLowerCase(); + sha256 = crypto.createHash('sha256'); } stream.on('data', data => { sizeActual += data.length; - // sha256 if possible, else crc32 + // sha256 if possible, else crc32 if(sha256) { sha256.update(data); } else { @@ -141,7 +141,7 @@ module.exports = class TicFileInfo { }); stream.on('end', () => { - // again, use sha256 if possible + // again, use sha256 if possible if(sha256) { const sha256Actual = sha256.digest('hex'); if(sha256Tic != sha256Actual) { @@ -182,20 +182,20 @@ module.exports = class TicFileInfo { isToAddress(address, allowNonExplicit) { // - // FSP-1039.001: - // "This keyword specifies the FTN address of the system where to - // send the file to be distributed and the accompanying TIC file. - // Some File processors (Allfix) only insert a line with this - // keyword when the file and the associated TIC file are to be - // file routed through a third sysem instead of being processed - // by a file processor on that system. Others always insert it. - // Note that the To keyword may cause problems when the TIC file - // is proecessed by software that does not recognise it and - // passes the line "as is" to other systems. + // FSP-1039.001: + // "This keyword specifies the FTN address of the system where to + // send the file to be distributed and the accompanying TIC file. + // Some File processors (Allfix) only insert a line with this + // keyword when the file and the associated TIC file are to be + // file routed through a third sysem instead of being processed + // by a file processor on that system. Others always insert it. + // Note that the To keyword may cause problems when the TIC file + // is proecessed by software that does not recognise it and + // passes the line "as is" to other systems. // - // Example: To 292/854 + // Example: To 292/854 // - // This is an optional keyword." + // This is an optional keyword." // const to = this.get('To'); @@ -212,12 +212,12 @@ module.exports = class TicFileInfo { return cb(err); } - const ticFileInfo = new TicFileInfo(); - ticFileInfo.path = path; + const ticFileInfo = new TicFileInfo(); + ticFileInfo.path = path; // - // Lines in a TIC file should be separated by CRLF (DOS) - // may be separated by LF (UNIX) + // Lines in a TIC file should be separated by CRLF (DOS) + // may be separated by LF (UNIX) // const lines = ticData.split(/\r\n|\n/g); let keyEnd; @@ -226,7 +226,7 @@ module.exports = class TicFileInfo { let entry; lines.forEach(line => { - keyEnd = line.search(/\s/); + keyEnd = line.search(/\s/); if(keyEnd < 0) { keyEnd = line.length; @@ -240,12 +240,12 @@ module.exports = class TicFileInfo { value = line.substr(keyEnd + 1); - // don't trim Ldesc; may mess with FILE_ID.DIZ type descriptions + // don't trim Ldesc; may mess with FILE_ID.DIZ type descriptions if('ldesc' !== key) { value = value.trim(); } - // convert well known keys to a more reasonable format + // convert well known keys to a more reasonable format switch(key) { case 'origin' : case 'from' : diff --git a/core/toggle_menu_view.js b/core/toggle_menu_view.js index 28818189..ae163bd7 100644 --- a/core/toggle_menu_view.js +++ b/core/toggle_menu_view.js @@ -1,13 +1,13 @@ /* jslint node: true */ 'use strict'; -const MenuView = require('./menu_view.js').MenuView; -const strUtil = require('./string_util.js'); +const MenuView = require('./menu_view.js').MenuView; +const strUtil = require('./string_util.js'); -const util = require('util'); -const assert = require('assert'); +const util = require('util'); +const assert = require('assert'); -exports.ToggleMenuView = ToggleMenuView; +exports.ToggleMenuView = ToggleMenuView; function ToggleMenuView (options) { options.cursor = options.cursor || 'hide'; @@ -17,10 +17,10 @@ function ToggleMenuView (options) { var self = this; /* - this.cachePositions = function() { - self.positionCacheExpired = false; - }; - */ + this.cachePositions = function() { + self.positionCacheExpired = false; + }; + */ this.updateSelection = function() { assert(this.focusedItemIndex >= 0 && this.focusedItemIndex <= self.items.length); @@ -47,8 +47,8 @@ ToggleMenuView.prototype.redraw = function() { //console.log(this.styleColor1) //var sepColor = this.getANSIColor(this.styleColor1 || this.getColor()); //console.log(sepColor.substr(1)) - //var sepColor = '\u001b[0m\u001b[1;30m'; // :TODO: FIX ME!!! - // :TODO: sepChar needs to be configurable!!! + //var sepColor = '\u001b[0m\u001b[1;30m'; // :TODO: FIX ME!!! + // :TODO: sepChar needs to be configurable!!! this.client.term.write(this.styleSGR1 + ' / '); //this.client.term.write(sepColor + ' / '); } @@ -59,7 +59,7 @@ ToggleMenuView.prototype.redraw = function() { }; ToggleMenuView.prototype.setFocusItemIndex = function(index) { - ToggleMenuView.super_.prototype.setFocusItemIndex.call(this, index); // sets this.focusedItemIndex + ToggleMenuView.super_.prototype.setFocusItemIndex.call(this, index); // sets this.focusedItemIndex this.updateSelection(); }; @@ -113,9 +113,9 @@ ToggleMenuView.prototype.getData = function() { }; ToggleMenuView.prototype.setItems = function(items) { - items = items.slice(0, 2); // switch/toggle only works with two elements + items = items.slice(0, 2); // switch/toggle only works with two elements ToggleMenuView.super_.prototype.setItems.call(this, items); - this.dimens.width = items.join(' / ').length; // :TODO: allow configurable seperator... string & color, e.g. styleColor1 (same as fillChar color) + this.dimens.width = items.join(' / ').length; // :TODO: allow configurable seperator... string & color, e.g. styleColor1 (same as fillChar color) }; diff --git a/core/upload.js b/core/upload.js index 2279e6b4..87bdcb4a 100644 --- a/core/upload.js +++ b/core/upload.js @@ -1,70 +1,70 @@ /* jslint node: true */ 'use strict'; -// enigma-bbs -const MenuModule = require('./menu_module.js').MenuModule; -const stringFormat = require('./string_format.js'); -const getSortedAvailableFileAreas = require('./file_base_area.js').getSortedAvailableFileAreas; -const getAreaDefaultStorageDirectory = require('./file_base_area.js').getAreaDefaultStorageDirectory; -const scanFile = require('./file_base_area.js').scanFile; -const getFileAreaByTag = require('./file_base_area.js').getFileAreaByTag; -const getDescFromFileName = require('./file_base_area.js').getDescFromFileName; -const ansiGoto = require('./ansi_term.js').goto; -const moveFileWithCollisionHandling = require('./file_util.js').moveFileWithCollisionHandling; -const pathWithTerminatingSeparator = require('./file_util.js').pathWithTerminatingSeparator; -const Log = require('./logger.js').log; -const Errors = require('./enig_error.js').Errors; -const FileEntry = require('./file_entry.js'); -const isAnsi = require('./string_util.js').isAnsi; -const Events = require('./events.js'); +// enigma-bbs +const MenuModule = require('./menu_module.js').MenuModule; +const stringFormat = require('./string_format.js'); +const getSortedAvailableFileAreas = require('./file_base_area.js').getSortedAvailableFileAreas; +const getAreaDefaultStorageDirectory = require('./file_base_area.js').getAreaDefaultStorageDirectory; +const scanFile = require('./file_base_area.js').scanFile; +const getFileAreaByTag = require('./file_base_area.js').getFileAreaByTag; +const getDescFromFileName = require('./file_base_area.js').getDescFromFileName; +const ansiGoto = require('./ansi_term.js').goto; +const moveFileWithCollisionHandling = require('./file_util.js').moveFileWithCollisionHandling; +const pathWithTerminatingSeparator = require('./file_util.js').pathWithTerminatingSeparator; +const Log = require('./logger.js').log; +const Errors = require('./enig_error.js').Errors; +const FileEntry = require('./file_entry.js'); +const isAnsi = require('./string_util.js').isAnsi; +const Events = require('./events.js'); -// deps -const async = require('async'); -const _ = require('lodash'); -const temptmp = require('temptmp').createTrackedSession('upload'); -const paths = require('path'); -const sanatizeFilename = require('sanitize-filename'); +// deps +const async = require('async'); +const _ = require('lodash'); +const temptmp = require('temptmp').createTrackedSession('upload'); +const paths = require('path'); +const sanatizeFilename = require('sanitize-filename'); exports.moduleInfo = { - name : 'Upload', - desc : 'Module for classic file uploads', - author : 'NuSkooler', + name : 'Upload', + desc : 'Module for classic file uploads', + author : 'NuSkooler', }; const FormIds = { - options : 0, - processing : 1, - fileDetails : 2, - dupes : 3, + options : 0, + processing : 1, + fileDetails : 2, + dupes : 3, }; const MciViewIds = { options : { - area : 1, // area selection - uploadType : 2, // blind vs specify filename - fileName : 3, // for non-blind; not editable for blind - navMenu : 4, // next/cancel/etc. - errMsg : 5, // errors (e.g. filename cannot be blank) + area : 1, // area selection + uploadType : 2, // blind vs specify filename + fileName : 3, // for non-blind; not editable for blind + navMenu : 4, // next/cancel/etc. + errMsg : 5, // errors (e.g. filename cannot be blank) }, processing : { - calcHashIndicator : 1, - archiveListIndicator : 2, - descFileIndicator : 3, - logStep : 4, - customRangeStart : 10, // 10+ = customs + calcHashIndicator : 1, + archiveListIndicator : 2, + descFileIndicator : 3, + logStep : 4, + customRangeStart : 10, // 10+ = customs }, fileDetails : { - desc : 1, // defaults to 'desc' (e.g. from FILE_ID.DIZ) - tags : 2, // tag(s) for item - estYear : 3, - accept : 4, // accept fields & continue - customRangeStart : 10, // 10+ = customs + desc : 1, // defaults to 'desc' (e.g. from FILE_ID.DIZ) + tags : 2, // tag(s) for item + estYear : 3, + accept : 4, // accept fields & continue + customRangeStart : 10, // 10+ = customs }, dupes : { - dupeList : 1, + dupeList : 1, } }; @@ -85,14 +85,14 @@ exports.getModule = class UploadModule extends MenuModule { }, fileDetailsContinue : (formData, extraArgs, cb) => { - // see displayFileDetailsPageForUploadEntry() for this hackery: + // see displayFileDetailsPageForUploadEntry() for this hackery: cb(null); - return this.fileDetailsCurrentEntrySubmitCallback(null, formData.value); // move on to the next entry, if any + return this.fileDetailsCurrentEntrySubmitCallback(null, formData.value); // move on to the next entry, if any }, - // validation + // validation validateNonBlindFileName : (fileName, cb) => { - fileName = sanatizeFilename(fileName); // remove unsafe chars, path info, etc. + fileName = sanatizeFilename(fileName); // remove unsafe chars, path info, etc. if(0 === fileName.length) { return cb(new Error('Invalid filename')); } @@ -101,7 +101,7 @@ exports.getModule = class UploadModule extends MenuModule { return cb(new Error('Filename cannot be empty')); } - // At least SEXYZ doesn't like non-blind names that start with a number - it becomes confused + // At least SEXYZ doesn't like non-blind names that start with a number - it becomes confused if(/^[0-9].*$/.test(fileName)) { return cb(new Error('Invalid filename')); } @@ -124,21 +124,21 @@ exports.getModule = class UploadModule extends MenuModule { } getSaveState() { - // if no areas, we're falling back due to lack of access/areas avail to upload to + // if no areas, we're falling back due to lack of access/areas avail to upload to if(this.availAreas.length > 0) { return { - uploadType : this.uploadType, - tempRecvDirectory : this.tempRecvDirectory, - areaInfo : this.availAreas[ this.viewControllers.options.getView(MciViewIds.options.area).getData() ], + uploadType : this.uploadType, + tempRecvDirectory : this.tempRecvDirectory, + areaInfo : this.availAreas[ this.viewControllers.options.getView(MciViewIds.options.area).getData() ], }; } } restoreSavedState(savedState) { if(savedState.areaInfo) { - this.uploadType = savedState.uploadType; - this.areaInfo = savedState.areaInfo; - this.tempRecvDirectory = savedState.tempRecvDirectory; + this.uploadType = savedState.uploadType; + this.areaInfo = savedState.areaInfo; + this.tempRecvDirectory = savedState.tempRecvDirectory; } } @@ -184,24 +184,24 @@ exports.getModule = class UploadModule extends MenuModule { return cb(err); } - // need a terminator for various external protocols + // need a terminator for various external protocols this.tempRecvDirectory = pathWithTerminatingSeparator(tempRecvDirectory); const modOpts = { extraArgs : { - recvDirectory : this.tempRecvDirectory, // we'll move files from here to their area container once processed/confirmed - direction : 'recv', + recvDirectory : this.tempRecvDirectory, // we'll move files from here to their area container once processed/confirmed + direction : 'recv', } }; if(!this.isBlindUpload()) { - // data has been sanatized at this point + // data has been sanatized at this point modOpts.extraArgs.recvFileName = this.viewControllers.options.getView(MciViewIds.options.fileName).getData(); } // - // Move along to protocol selection -> file transfer - // Upon completion, we'll re-enter the module with some file paths handed to us + // Move along to protocol selection -> file transfer + // Upon completion, we'll re-enter the module with some file paths handed to us // return this.gotoMenu( this.menuConfig.config.fileTransferProtocolSelection || 'fileTransferProtocolSelection', @@ -216,7 +216,7 @@ exports.getModule = class UploadModule extends MenuModule { } updateScanStepInfoViews(stepInfo) { - // :TODO: add some blinking (e.g. toggle items) indicators - see OBV.DOC + // :TODO: add some blinking (e.g. toggle items) indicators - see OBV.DOC const fmtObj = Object.assign( {}, stepInfo); let stepIndicatorFmt = ''; @@ -224,8 +224,8 @@ exports.getModule = class UploadModule extends MenuModule { const fmtConfig = this.menuConfig.config; - const indicatorStates = fmtConfig.indicatorStates || [ '|', '/', '-', '\\' ]; - const indicatorFinished = fmtConfig.indicatorFinished || '√'; + const indicatorStates = fmtConfig.indicatorStates || [ '|', '/', '-', '\\' ]; + const indicatorFinished = fmtConfig.indicatorFinished || '√'; const indicator = { }; const self = this; @@ -310,8 +310,8 @@ exports.getModule = class UploadModule extends MenuModule { const self = this; const results = { - newEntries : [], - dupes : [], + newEntries : [], + dupes : [], }; self.client.log.debug('Scanning upload(s)', { paths : this.recvFilePaths } ); @@ -319,22 +319,22 @@ exports.getModule = class UploadModule extends MenuModule { let currentFileNum = 0; async.eachSeries(this.recvFilePaths, (filePath, nextFilePath) => { - // :TODO: virus scanning/etc. should occur around here + // :TODO: virus scanning/etc. should occur around here currentFileNum += 1; self.scanStatus = { - indicatorPos : 0, + indicatorPos : 0, }; const scanOpts = { - areaTag : self.areaInfo.areaTag, - storageTag : self.areaInfo.storageTags[0], + areaTag : self.areaInfo.areaTag, + storageTag : self.areaInfo.storageTags[0], }; function handleScanStep(stepInfo, nextScanStep) { - stepInfo.totalFileNum = self.recvFilePaths.length; - stepInfo.currentFileNum = currentFileNum; + stepInfo.totalFileNum = self.recvFilePaths.length; + stepInfo.currentFileNum = currentFileNum; self.updateScanStepInfoViews(stepInfo); return nextScanStep(null); @@ -347,14 +347,14 @@ exports.getModule = class UploadModule extends MenuModule { return nextFilePath(err); } - // new or dupe? + // new or dupe? if(dupeEntries.length > 0) { - // 1:n dupes found + // 1:n dupes found self.client.log.debug('Duplicate file(s) found', { dupeEntries : dupeEntries } ); results.dupes = results.dupes.concat(dupeEntries); } else { - // new one + // new one results.newEntries.push(fileEntry); } @@ -377,37 +377,37 @@ exports.getModule = class UploadModule extends MenuModule { const self = this; async.eachSeries(newEntries, (newEntry, nextEntry) => { - const src = paths.join(self.tempRecvDirectory, newEntry.fileName); - const dst = paths.join(areaStorageDir, newEntry.fileName); + const src = paths.join(self.tempRecvDirectory, newEntry.fileName); + const dst = paths.join(areaStorageDir, newEntry.fileName); - moveFileWithCollisionHandling(src, dst, (err, finalPath) => { + moveFileWithCollisionHandling(src, dst, (err, finalPath) => { if(err) { self.client.log.error( 'Failed moving physical upload file', { error : err.message, fileName : newEntry.fileName, source : src, dest : dst } ); if(dst !== finalPath) { - // name changed; ajust before persist + // name changed; ajust before persist newEntry.fileName = paths.basename(finalPath); } - return nextEntry(null); // still try next file + return nextEntry(null); // still try next file } self.client.log.debug('Moved upload to area', { path : finalPath } ); - // persist to DB + // persist to DB newEntry.persist(err => { if(err) { self.client.log.error('Failed persisting upload to database', { path : finalPath, error : err.message } ); } - return nextEntry(null); // still try next file + return nextEntry(null); // still try next file }); }); }, () => { // - // Finally, we can remove any temp files that we may have created + // Finally, we can remove any temp files that we may have created // self.cleanupTempFiles(); }); @@ -415,8 +415,8 @@ exports.getModule = class UploadModule extends MenuModule { prepDetailsForUpload(scanResults, cb) { async.eachSeries(scanResults.newEntries, (newEntry, nextEntry) => { - newEntry.meta.upload_by_username = this.client.user.username; - newEntry.meta.upload_by_user_id = this.client.user.userId; + newEntry.meta.upload_by_username = this.client.user.username; + newEntry.meta.upload_by_user_id = this.client.user.userId; this.displayFileDetailsPageForUploadEntry(newEntry, (err, newValues) => { if(err) { @@ -445,8 +445,8 @@ exports.getModule = class UploadModule extends MenuModule { displayDupesPage(dupes, cb) { // - // If we have custom art to show, use it - else just dump basic info. - // Pause at the end in either case. + // If we have custom art to show, use it - else just dump basic info. + // Pause at the end in either case. // const self = this; @@ -469,7 +469,7 @@ exports.getModule = class UploadModule extends MenuModule { ); }, function prepDupeObjects(dupeListView, callback) { - // update dupe objects with additional info that can be used for formatString() and the like + // update dupe objects with additional info that can be used for formatString() and the like async.each(dupes, (dupe, nextDupe) => { FileEntry.loadBasicEntry(dupe.fileId, dupe, err => { if(err) { @@ -478,8 +478,8 @@ exports.getModule = class UploadModule extends MenuModule { const areaInfo = getFileAreaByTag(dupe.areaTag); if(areaInfo) { - dupe.areaName = areaInfo.name; - dupe.areaDesc = areaInfo.desc; + dupe.areaName = areaInfo.name; + dupe.areaDesc = areaInfo.desc; } return nextDupe(null); }); @@ -513,7 +513,7 @@ exports.getModule = class UploadModule extends MenuModule { processUploadedFiles() { // - // For each file uploaded, we need to process & gather information + // For each file uploaded, we need to process & gather information // const self = this; @@ -525,8 +525,8 @@ exports.getModule = class UploadModule extends MenuModule { } // - // For non-blind uploads, batch is not supported, we expect a single file - // in |recvFilePaths|. If not, it's an error (we don't want to process the wrong thing) + // For non-blind uploads, batch is not supported, we expect a single file + // in |recvFilePaths|. If not, it's an error (we don't want to process the wrong thing) // if(self.recvFilePaths.length > 1) { self.client.log.warn( { recvFilePaths : self.recvFilePaths }, 'Non-blind upload received 2:n files' ); @@ -563,9 +563,9 @@ exports.getModule = class UploadModule extends MenuModule { }, function startMovingAndPersistingToDatabase(scanResults, callback) { // - // *Start* the process of moving files from their current |tempRecvDirectory| - // locations -> their final area destinations. Don't make the user wait - // here as I/O can take quite a bit of time. Log any failures. + // *Start* the process of moving files from their current |tempRecvDirectory| + // locations -> their final area destinations. Don't make the user wait + // here as I/O can take quite a bit of time. Log any failures. // self.moveAndPersistUploadsToDatabase(scanResults.newEntries); return callback(null, scanResults.newEntries); @@ -574,8 +574,8 @@ exports.getModule = class UploadModule extends MenuModule { Events.emit( Events.getSystemEvents().UserUpload, { - user : self.client.user, - files : uploadedEntries, + user : self.client.user, + files : uploadedEntries, } ); return callback(null); @@ -584,7 +584,7 @@ exports.getModule = class UploadModule extends MenuModule { err => { if(err) { self.client.log.warn('File upload error encountered', { error : err.message } ); - self.cleanupTempFiles(); // normally called after moveAndPersistUploadsToDatabase() is completed. + self.cleanupTempFiles(); // normally called after moveAndPersistUploadsToDatabase() is completed. } return self.prevMenu(); @@ -609,8 +609,8 @@ exports.getModule = class UploadModule extends MenuModule { const areaSelectView = self.viewControllers.options.getView(MciViewIds.options.area); areaSelectView.setItems( self.availAreas.map(areaInfo => areaInfo.name ) ); - const uploadTypeView = self.viewControllers.options.getView(MciViewIds.options.uploadType); - const fileNameView = self.viewControllers.options.getView(MciViewIds.options.fileName); + const uploadTypeView = self.viewControllers.options.getView(MciViewIds.options.uploadType); + const fileNameView = self.viewControllers.options.getView(MciViewIds.options.fileName); const blindFileNameText = self.menuConfig.config.blindFileNameText || '(blind - filename ignored)'; @@ -626,7 +626,7 @@ exports.getModule = class UploadModule extends MenuModule { } }); - // sanatize filename for display when leaving the view + // sanatize filename for display when leaving the view self.viewControllers.options.on('leave', prevView => { if(prevView.id === MciViewIds.options.fileName) { fileNameView.setText(sanatizeFilename(fileNameView.getData())); @@ -634,7 +634,7 @@ exports.getModule = class UploadModule extends MenuModule { }); self.uploadType = 'blind'; - uploadTypeView.setFocusItemIndex(0); // default to blind + uploadTypeView.setFocusItemIndex(0); // default to blind fileNameView.setText(blindFileNameText); areaSelectView.redraw(); @@ -655,7 +655,7 @@ exports.getModule = class UploadModule extends MenuModule { FormIds.processing, { clearScreen : true, trailingLF : false }, err => { - // note: this art is not required + // note: this art is not required this.hasProcessingArt = !err; return cb(null); @@ -689,7 +689,7 @@ exports.getModule = class UploadModule extends MenuModule { self.updateCustomViewTextsWithFilter('fileDetails', MciViewIds.fileDetails.customRangeStart, fileEntry ); - tagsView.setText( Array.from(fileEntry.hashTags).join(',') ); // :TODO: optional 'hashTagsSep' like file list/browse + tagsView.setText( Array.from(fileEntry.hashTags).join(',') ); // :TODO: optional 'hashTagsSep' like file list/browse yearView.setText(fileEntry.meta.est_release_year || ''); if(isAnsi(fileEntry.desc)) { @@ -698,8 +698,8 @@ exports.getModule = class UploadModule extends MenuModule { return descView.setAnsi( fileEntry.desc, { - prepped : false, - forceLineTerm : true, + prepped : false, + forceLineTerm : true, }, () => { return callback(null, descView, 'preview', MciViewIds.fileDetails.tags); @@ -709,29 +709,29 @@ exports.getModule = class UploadModule extends MenuModule { const hasDesc = self.fileEntryHasDetectedDesc(fileEntry); descView.setText( hasDesc ? fileEntry.desc : getDescFromFileName(fileEntry.fileName), - { scrollMode : 'top' } // override scroll mode; we want to be @ top + { scrollMode : 'top' } // override scroll mode; we want to be @ top ); return callback(null, descView, 'edit', hasDesc ? MciViewIds.fileDetails.tags : MciViewIds.fileDetails.desc); } }, function finalizeViews(descView, descViewMode, focusId, callback) { descView.setPropertyValue('mode', descViewMode); - descView.acceptsFocus = 'preview' === descViewMode ? false : true; + descView.acceptsFocus = 'preview' === descViewMode ? false : true; self.viewControllers.fileDetails.switchFocus(focusId); return callback(null); } ], err => { // - // we only call |cb| here if there is an error - // else, wait for the current from to be submit - then call - - // this way we'll move on to the next file entry when ready + // we only call |cb| here if there is an error + // else, wait for the current from to be submit - then call - + // this way we'll move on to the next file entry when ready // if(err) { return cb(err); } - self.fileDetailsCurrentEntrySubmitCallback = cb; // stash for moduleMethods.fileDetailsContinue + self.fileDetailsCurrentEntrySubmitCallback = cb; // stash for moduleMethods.fileDetailsContinue } ); } diff --git a/core/user.js b/core/user.js index 72808f00..e3314cc7 100644 --- a/core/user.js +++ b/core/user.js @@ -1,53 +1,53 @@ /* jslint node: true */ 'use strict'; -const userDb = require('./database.js').dbs.user; -const Config = require('./config.js').get; -const userGroup = require('./user_group.js'); -const Errors = require('./enig_error.js').Errors; -const Events = require('./events.js'); +const userDb = require('./database.js').dbs.user; +const Config = require('./config.js').get; +const userGroup = require('./user_group.js'); +const Errors = require('./enig_error.js').Errors; +const Events = require('./events.js'); -// deps -const crypto = require('crypto'); -const assert = require('assert'); -const async = require('async'); -const _ = require('lodash'); -const moment = require('moment'); +// deps +const crypto = require('crypto'); +const assert = require('assert'); +const async = require('async'); +const _ = require('lodash'); +const moment = require('moment'); exports.isRootUserId = function(id) { return 1 === id; }; module.exports = class User { constructor() { - this.userId = 0; - this.username = ''; - this.properties = {}; // name:value - this.groups = []; // group membership(s) + this.userId = 0; + this.username = ''; + this.properties = {}; // name:value + this.groups = []; // group membership(s) } - // static property accessors + // static property accessors static get RootUserID() { return 1; } static get PBKDF2() { return { - iterations : 1000, - keyLen : 128, - saltLen : 32, + iterations : 1000, + keyLen : 128, + saltLen : 32, }; } static get StandardPropertyGroups() { return { - password : [ 'pw_pbkdf2_salt', 'pw_pbkdf2_dk' ], + password : [ 'pw_pbkdf2_salt', 'pw_pbkdf2_dk' ], }; } static get AccountStatus() { return { - disabled : 0, - inactive : 1, - active : 2, + disabled : 0, + inactive : 1, + active : 2, }; } @@ -69,14 +69,14 @@ module.exports = class User { } return ((this.properties.pw_pbkdf2_salt.length === User.PBKDF2.saltLen * 2) && - (this.properties.pw_pbkdf2_dk.length === User.PBKDF2.keyLen * 2)); + (this.properties.pw_pbkdf2_dk.length === User.PBKDF2.keyLen * 2)); } isRoot() { return User.isRootUserId(this.userId); } - isSysOp() { // alias to isRoot() + isSysOp() { // alias to isRoot() return this.isRoot(); } @@ -98,7 +98,7 @@ module.exports = class User { return 30; } - return 10; // :TODO: Is this what we want? + return 10; // :TODO: Is this what we want? } authenticate(username, password, cb) { @@ -108,32 +108,32 @@ module.exports = class User { async.waterfall( [ function fetchUserId(callback) { - // get user ID + // get user ID User.getUserIdAndName(username, (err, uid, un) => { - cachedInfo.userId = uid; - cachedInfo.username = un; + cachedInfo.userId = uid; + cachedInfo.username = un; return callback(err); }); }, function getRequiredAuthProperties(callback) { - // fetch properties required for authentication + // fetch properties required for authentication User.loadProperties(cachedInfo.userId, { names : User.StandardPropertyGroups.password }, (err, props) => { return callback(err, props); }); }, function getDkWithSalt(props, callback) { - // get DK from stored salt and password provided + // get DK from stored salt and password provided User.generatePasswordDerivedKey(password, props.pw_pbkdf2_salt, (err, dk) => { return callback(err, dk, props.pw_pbkdf2_dk); }); }, function validateAuth(passDk, propsDk, callback) { // - // Use constant time comparison here for security feel-goods + // Use constant time comparison here for security feel-goods // - const passDkBuf = Buffer.from(passDk, 'hex'); - const propsDkBuf = Buffer.from(propsDk, 'hex'); + const passDkBuf = Buffer.from(passDk, 'hex'); + const propsDkBuf = Buffer.from(propsDk, 'hex'); if(passDkBuf.length !== propsDkBuf.length) { return callback(Errors.AccessDenied('Invalid password')); @@ -167,11 +167,11 @@ module.exports = class User { ], err => { if(!err) { - self.userId = cachedInfo.userId; - self.username = cachedInfo.username; - self.properties = cachedInfo.properties; - self.groups = cachedInfo.groups; - self.authenticated = true; + self.userId = cachedInfo.userId; + self.username = cachedInfo.username; + self.properties = cachedInfo.properties; + self.groups = cachedInfo.groups; + self.authenticated = true; } return cb(err); @@ -189,7 +189,7 @@ module.exports = class User { const self = this; - // :TODO: set various defaults, e.g. default activation status, etc. + // :TODO: set various defaults, e.g. default activation status, etc. self.properties.account_status = config.users.requireActivation ? User.AccountStatus.inactive : User.AccountStatus.active; async.waterfall( @@ -200,16 +200,16 @@ module.exports = class User { function createUserRec(trans, callback) { trans.run( `INSERT INTO user (user_name) - VALUES (?);`, + VALUES (?);`, [ self.username ], - function inserted(err) { // use classic function for |this| + function inserted(err) { // use classic function for |this| if(err) { return callback(err); } self.userId = this.lastID; - // Do not require activation for userId 1 (root/admin) + // Do not require activation for userId 1 (root/admin) if(User.RootUserID === self.userId) { self.properties.account_status = User.AccountStatus.active; } @@ -224,15 +224,15 @@ module.exports = class User { return callback(err); } - self.properties.pw_pbkdf2_salt = info.salt; - self.properties.pw_pbkdf2_dk = info.dk; + self.properties.pw_pbkdf2_salt = info.salt; + self.properties.pw_pbkdf2_dk = info.dk; return callback(null, trans); }); }, function setInitialGroupMembership(trans, callback) { self.groups = config.users.defaultGroups; - if(User.RootUserID === self.userId) { // root/SysOp? + if(User.RootUserID === self.userId) { // root/SysOp? self.groups.push('sysops'); } @@ -285,12 +285,12 @@ module.exports = class User { } persistProperty(propName, propValue, cb) { - // update live props + // update live props this.properties[propName] = propValue; userDb.run( `REPLACE INTO user_property (user_id, prop_name, prop_value) - VALUES (?, ?, ?);`, + VALUES (?, ?, ?);`, [ this.userId, propName, propValue ], err => { if(cb) { @@ -301,12 +301,12 @@ module.exports = class User { } removeProperty(propName, cb) { - // update live + // update live delete this.properties[propName]; userDb.run( `DELETE FROM user_property - WHERE user_id = ? AND prop_name = ?;`, + WHERE user_id = ? AND prop_name = ?;`, [ this.userId, propName ], err => { if(cb) { @@ -324,12 +324,12 @@ module.exports = class User { const self = this; - // update live props + // update live props _.merge(this.properties, properties); const stmt = transOrDb.prepare( `REPLACE INTO user_property (user_id, prop_name, prop_value) - VALUES (?, ?, ?);` + VALUES (?, ?, ?);` ); async.each(Object.keys(properties), (propName, nextProp) => { @@ -355,8 +355,8 @@ module.exports = class User { } const newProperties = { - pw_pbkdf2_salt : info.salt, - pw_pbkdf2_dk : info.dk, + pw_pbkdf2_salt : info.salt, + pw_pbkdf2_dk : info.dk, }; this.persistProperties(newProperties, err => { @@ -392,11 +392,11 @@ module.exports = class User { ], (err, userName, properties, groups) => { const user = new User(); - user.userId = userId; - user.username = userName; - user.properties = properties; - user.groups = groups; - user.authenticated = false; // this is NOT an authenticated user! + user.userId = userId; + user.username = userName; + user.properties = properties; + user.groups = groups; + user.authenticated = false; // this is NOT an authenticated user! return cb(err, user); } @@ -410,8 +410,8 @@ module.exports = class User { static getUserIdAndName(username, cb) { userDb.get( `SELECT id, user_name - FROM user - WHERE user_name LIKE ?;`, + FROM user + WHERE user_name LIKE ?;`, [ username ], (err, row) => { if(err) { @@ -430,12 +430,12 @@ module.exports = class User { static getUserIdAndNameByRealName(realName, cb) { userDb.get( `SELECT id, user_name - FROM user - WHERE id = ( - SELECT user_id - FROM user_property - WHERE prop_name='real_name' AND prop_value LIKE ? - );`, + FROM user + WHERE id = ( + SELECT user_id + FROM user_property + WHERE prop_name='real_name' AND prop_value LIKE ? + );`, [ realName ], (err, row) => { if(err) { @@ -466,8 +466,8 @@ module.exports = class User { static getUserName(userId, cb) { userDb.get( `SELECT user_name - FROM user - WHERE id = ?;`, + FROM user + WHERE id = ?;`, [ userId ], (err, row) => { if(err) { @@ -490,9 +490,9 @@ module.exports = class User { } let sql = - `SELECT prop_name, prop_value - FROM user_property - WHERE user_id = ?`; + `SELECT prop_name, prop_value + FROM user_property + WHERE user_id = ?`; if(options.names) { sql += ` AND prop_name IN("${options.names.join('","')}");`; @@ -511,14 +511,14 @@ module.exports = class User { }); } - // :TODO: make this much more flexible - propValue should allow for case-insensitive compare, etc. + // :TODO: make this much more flexible - propValue should allow for case-insensitive compare, etc. static getUserIdsWithProperty(propName, propValue, cb) { let userIds = []; userDb.each( `SELECT user_id - FROM user_property - WHERE prop_name = ? AND prop_value = ?;`, + FROM user_property + WHERE prop_name = ? AND prop_value = ?;`, [ propName, propValue ], (err, row) => { if(row) { @@ -537,13 +537,13 @@ module.exports = class User { userDb.each( `SELECT id, user_name - FROM user - ${orderClause};`, + FROM user + ${orderClause};`, (err, row) => { if(row) { userList.push({ - userId : row.id, - userName : row.user_name, + userId : row.id, + userName : row.user_name, }); } }, @@ -552,8 +552,8 @@ module.exports = class User { async.map(userList, (user, nextUser) => { userDb.each( `SELECT prop_name, prop_value - FROM user_property - WHERE user_id = ? AND prop_name IN ("${options.properties.join('","')}");`, + FROM user_property + WHERE user_id = ? AND prop_name IN ("${options.properties.join('","')}");`, [ user.userId ], (err, row) => { if(row) { diff --git a/core/user_config.js b/core/user_config.js index b5e124b6..f9aad6c4 100644 --- a/core/user_config.js +++ b/core/user_config.js @@ -1,38 +1,38 @@ /* jslint node: true */ 'use strict'; -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const theme = require('./theme.js'); -const sysValidate = require('./system_view_validate.js'); +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const theme = require('./theme.js'); +const sysValidate = require('./system_view_validate.js'); -const async = require('async'); -const assert = require('assert'); -const _ = require('lodash'); -const moment = require('moment'); +const async = require('async'); +const assert = require('assert'); +const _ = require('lodash'); +const moment = require('moment'); exports.moduleInfo = { - name : 'User Configuration', - desc : 'Module for user configuration', - author : 'NuSkooler', + name : 'User Configuration', + desc : 'Module for user configuration', + author : 'NuSkooler', }; const MciCodeIds = { - RealName : 1, - BirthDate : 2, - Sex : 3, - Loc : 4, - Affils : 5, - Email : 6, - Web : 7, - TermHeight : 8, - Theme : 9, - Password : 10, - PassConfirm : 11, - ThemeInfo : 20, - ErrorMsg : 21, + RealName : 1, + BirthDate : 2, + Sex : 3, + Loc : 4, + Affils : 5, + Email : 6, + Web : 7, + TermHeight : 8, + Theme : 9, + Password : 10, + PassConfirm : 11, + ThemeInfo : 20, + ErrorMsg : 21, - SaveCancel : 25, + SaveCancel : 25, }; exports.getModule = class UserConfigModule extends MenuModule { @@ -43,29 +43,29 @@ exports.getModule = class UserConfigModule extends MenuModule { this.menuMethods = { // - // Validation support + // Validation support // validateEmailAvail : function(data, cb) { // - // If nothing changed, we know it's OK + // If nothing changed, we know it's OK // if(self.client.user.properties.email_address.toLowerCase() === data.toLowerCase()) { return cb(null); } - // Otherwise we can use the standard system method + // Otherwise we can use the standard system method return sysValidate.validateEmailAvail(data, cb); }, validatePassword : function(data, cb) { // - // Blank is OK - this means we won't be changing it + // Blank is OK - this means we won't be changing it // if(!data || 0 === data.length) { return cb(null); } - // Otherwise we can use the standard system method + // Otherwise we can use the standard system method return sysValidate.validatePasswordSpec(data, cb); }, @@ -95,21 +95,21 @@ exports.getModule = class UserConfigModule extends MenuModule { }, // - // Handlers + // Handlers // saveChanges : function(formData, extraArgs, cb) { assert(formData.value.password === formData.value.passwordConfirm); const newProperties = { - real_name : formData.value.realName, - birthdate : new Date(Date.parse(formData.value.birthdate)).toISOString(), - sex : formData.value.sex, - location : formData.value.location, - affiliation : formData.value.affils, - email_address : formData.value.email, - web_address : formData.value.web, - term_height : formData.value.termHeight.toString(), - theme_id : self.availThemeInfo[formData.value.theme].themeId, + real_name : formData.value.realName, + birthdate : new Date(Date.parse(formData.value.birthdate)).toISOString(), + sex : formData.value.sex, + location : formData.value.location, + affiliation : formData.value.affils, + email_address : formData.value.email, + web_address : formData.value.web, + term_height : formData.value.termHeight.toString(), + theme_id : self.availThemeInfo[formData.value.theme].themeId, }; // runtime set theme @@ -119,11 +119,11 @@ exports.getModule = class UserConfigModule extends MenuModule { self.client.user.persistProperties(newProperties, err => { if(err) { self.client.log.warn( { error : err.toString() }, 'Failed persisting updated properties'); - // :TODO: warn end user! + // :TODO: warn end user! return self.prevMenu(cb); } // - // New password if it's not empty + // New password if it's not empty // self.client.log.info('User updated properties'); @@ -154,8 +154,8 @@ exports.getModule = class UserConfigModule extends MenuModule { return cb(err); } - const self = this; - const vc = self.viewControllers.menu = new ViewController( { client : self.client} ); + const self = this; + const vc = self.viewControllers.menu = new ViewController( { client : self.client} ); let currentThemeIdIndex = 0; async.series( @@ -167,11 +167,11 @@ exports.getModule = class UserConfigModule extends MenuModule { self.availThemeInfo = _.sortBy([...theme.getAvailableThemes()].map(entry => { const theme = entry[1]; return { - themeId : theme.info.themeId, - name : theme.info.name, - author : theme.info.author, - desc : _.isString(theme.info.desc) ? theme.info.desc : '', - group : _.isString(theme.info.group) ? theme.info.group : '', + themeId : theme.info.themeId, + name : theme.info.name, + author : theme.info.author, + desc : _.isString(theme.info.desc) ? theme.info.desc : '', + group : _.isString(theme.info.group) ? theme.info.group : '', }; }), 'name'); @@ -202,7 +202,7 @@ exports.getModule = class UserConfigModule extends MenuModule { var realNameView = self.getView(MciCodeIds.RealName); if(realNameView) { - realNameView.setFocus(true); // :TODO: HACK! menu.hjson sets focus, but manual population above breaks this. Needs a real fix! + realNameView.setFocus(true); // :TODO: HACK! menu.hjson sets focus, but manual population above breaks this. Needs a real fix! } callback(null); diff --git a/core/user_group.js b/core/user_group.js index a350e5b2..4b1548b8 100644 --- a/core/user_group.js +++ b/core/user_group.js @@ -1,21 +1,21 @@ /* jslint node: true */ 'use strict'; -const userDb = require('./database.js').dbs.user; +const userDb = require('./database.js').dbs.user; -const async = require('async'); -const _ = require('lodash'); +const async = require('async'); +const _ = require('lodash'); -exports.getGroupsForUser = getGroupsForUser; -exports.addUserToGroup = addUserToGroup; -exports.addUserToGroups = addUserToGroups; -exports.removeUserFromGroup = removeUserFromGroup; +exports.getGroupsForUser = getGroupsForUser; +exports.addUserToGroup = addUserToGroup; +exports.addUserToGroups = addUserToGroups; +exports.removeUserFromGroup = removeUserFromGroup; function getGroupsForUser(userId, cb) { const sql = - `SELECT group_name - FROM user_group_member - WHERE user_id=?;`; + `SELECT group_name + FROM user_group_member + WHERE user_id=?;`; const groups = []; @@ -39,7 +39,7 @@ function addUserToGroup(userId, groupName, transOrDb, cb) { transOrDb.run( `REPLACE INTO user_group_member (group_name, user_id) - VALUES(?, ?);`, + VALUES(?, ?);`, [ groupName, userId ], err => { return cb(err); @@ -59,7 +59,7 @@ function addUserToGroups(userId, groups, transOrDb, cb) { function removeUserFromGroup(userId, groupName, cb) { userDb.run( `DELETE FROM user_group_member - WHERE group_name=? AND user_id=?;`, + WHERE group_name=? AND user_id=?;`, [ groupName, userId ], err => { return cb(err); diff --git a/core/user_list.js b/core/user_list.js index 212eb9ea..8af9690b 100644 --- a/core/user_list.js +++ b/core/user_list.js @@ -1,35 +1,35 @@ /* jslint node: true */ 'use strict'; -const MenuModule = require('./menu_module.js').MenuModule; -const User = require('./user.js'); -const ViewController = require('./view_controller.js').ViewController; -const stringFormat = require('./string_format.js'); +const MenuModule = require('./menu_module.js').MenuModule; +const User = require('./user.js'); +const ViewController = require('./view_controller.js').ViewController; +const stringFormat = require('./string_format.js'); -const moment = require('moment'); -const async = require('async'); -const _ = require('lodash'); +const moment = require('moment'); +const async = require('async'); +const _ = require('lodash'); /* - Available listFormat/focusListFormat object members: + Available listFormat/focusListFormat object members: - userId : User ID - userName : User name/handle - lastLoginTs : Last login timestamp - status : Status: active | inactive - location : Location - affiliation : Affils - note : User note + userId : User ID + userName : User name/handle + lastLoginTs : Last login timestamp + status : Status: active | inactive + location : Location + affiliation : Affils + note : User note */ exports.moduleInfo = { - name : 'User List', - desc : 'Lists all system users', - author : 'NuSkooler', + name : 'User List', + desc : 'Lists all system users', + author : 'NuSkooler', }; const MciViewIds = { - UserList : 1, + UserList : 1, }; exports.getModule = class UserListModule extends MenuModule { @@ -43,8 +43,8 @@ exports.getModule = class UserListModule extends MenuModule { return cb(err); } - const self = this; - const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); + const self = this; + const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); let userList = []; @@ -56,14 +56,14 @@ exports.getModule = class UserListModule extends MenuModule { [ function loadFromConfig(callback) { var loadOpts = { - callingMenu : self, - mciMap : mciData.menu, + callingMenu : self, + mciMap : mciData.menu, }; vc.loadFromMenuConfig(loadOpts, callback); }, function fetchUserList(callback) { - // :TODO: Currently fetching all users - probably always OK, but this could be paged + // :TODO: Currently fetching all users - probably always OK, but this could be paged User.getUserList(USER_LIST_OPTS, function got(err, ul) { userList = ul; callback(err); @@ -72,19 +72,19 @@ exports.getModule = class UserListModule extends MenuModule { function populateList(callback) { var userListView = vc.getView(MciViewIds.UserList); - var listFormat = self.menuConfig.config.listFormat || '{userName} - {affils}'; - var focusListFormat = self.menuConfig.config.focusListFormat || listFormat; // :TODO: default changed color! - var dateTimeFormat = self.menuConfig.config.dateTimeFormat || 'ddd MMM DD'; + var listFormat = self.menuConfig.config.listFormat || '{userName} - {affils}'; + var focusListFormat = self.menuConfig.config.focusListFormat || listFormat; // :TODO: default changed color! + var dateTimeFormat = self.menuConfig.config.dateTimeFormat || 'ddd MMM DD'; function getUserFmtObj(ue) { return { - userId : ue.userId, - userName : ue.userName, - affils : ue.affiliation, - location : ue.location, - // :TODO: the rest! - note : ue.note || '', - lastLoginTs : moment(ue.last_login_timestamp).format(dateTimeFormat), + userId : ue.userId, + userName : ue.userName, + affils : ue.affiliation, + location : ue.location, + // :TODO: the rest! + note : ue.note || '', + lastLoginTs : moment(ue.last_login_timestamp).format(dateTimeFormat), }; } diff --git a/core/user_login.js b/core/user_login.js index aa3cfe7b..07a0a2d2 100644 --- a/core/user_login.js +++ b/core/user_login.js @@ -1,34 +1,34 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const setClientTheme = require('./theme.js').setClientTheme; -const clientConnections = require('./client_connections.js').clientConnections; -const StatLog = require('./stat_log.js'); -const logger = require('./logger.js'); -const Events = require('./events.js'); +// ENiGMA½ +const setClientTheme = require('./theme.js').setClientTheme; +const clientConnections = require('./client_connections.js').clientConnections; +const StatLog = require('./stat_log.js'); +const logger = require('./logger.js'); +const Events = require('./events.js'); -// deps -const async = require('async'); +// deps +const async = require('async'); -exports.userLogin = userLogin; +exports.userLogin = userLogin; function userLogin(client, username, password, cb) { client.user.authenticate(username, password, function authenticated(err) { if(err) { client.log.info( { username : username, error : err.message }, 'Failed login attempt'); - // :TODO: if username exists, record failed login attempt to properties - // :TODO: check Config max failed logon attempts/etc. - set err.maxAttempts = true + // :TODO: if username exists, record failed login attempt to properties + // :TODO: check Config max failed logon attempts/etc. - set err.maxAttempts = true return cb(err); } - const user = client.user; + const user = client.user; // - // Ensure this user is not already logged in. - // Loop through active connections -- which includes the current -- - // and check for matching user ID. If the count is > 1, disallow. + // Ensure this user is not already logged in. + // Loop through active connections -- which includes the current -- + // and check for matching user ID. If the count is > 1, disallow. // let existingClientConnection; clientConnections.forEach(function connEntry(cc) { @@ -40,9 +40,9 @@ function userLogin(client, username, password, cb) { if(existingClientConnection) { client.log.info( { - existingClientId : existingClientConnection.session.id, - username : user.username, - userId : user.userId + existingClientId : existingClientConnection.session.id, + username : user.username, + userId : user.userId }, 'Already logged in' ); @@ -50,23 +50,23 @@ function userLogin(client, username, password, cb) { const existingConnError = new Error('Already logged in as supplied user'); existingConnError.existingConn = true; - // :TODO: We should use EnigError & pass existing connection as second param + // :TODO: We should use EnigError & pass existing connection as second param return cb(existingConnError); } - // update client logger with addition of username + // update client logger with addition of username client.log = logger.log.child( { - clientId : client.log.fields.clientId, - sessionId : client.log.fields.sessionId, - username : user.username, + clientId : client.log.fields.clientId, + sessionId : client.log.fields.sessionId, + username : user.username, } ); client.log.info('Successful login'); - // User's unique session identifier is the same as the connection itself - user.sessionId = client.session.uniqueId; // convienence + // User's unique session identifier is the same as the connection itself + user.sessionId = client.session.uniqueId; // convienence Events.emit(Events.getSystemEvents().UserLogin, { user } ); @@ -86,7 +86,7 @@ function userLogin(client, username, password, cb) { return StatLog.incrementUserStat(user, 'login_count', 1, callback); }, function recordLoginHistory(callback) { - const LOGIN_HISTORY_MAX = 200; // history of up to last 200 callers + const LOGIN_HISTORY_MAX = 200; // history of up to last 200 callers return StatLog.appendSystemLogEntry('user_login_history', user.userId, LOGIN_HISTORY_MAX, StatLog.KeepType.Max, callback); } ], diff --git a/core/uuid_util.js b/core/uuid_util.js index 7cf582f5..f731ecc0 100644 --- a/core/uuid_util.js +++ b/core/uuid_util.js @@ -1,14 +1,14 @@ /* jslint node: true */ 'use strict'; -const createHash = require('crypto').createHash; +const createHash = require('crypto').createHash; -exports.createNamedUUID = createNamedUUID; +exports.createNamedUUID = createNamedUUID; function createNamedUUID(namespaceUuid, key) { // - // v5 UUID generation code based on the work here: - // https://github.com/download13/uuidv5/blob/master/uuid.js + // v5 UUID generation code based on the work here: + // https://github.com/download13/uuidv5/blob/master/uuid.js // if(!Buffer.isBuffer(namespaceUuid)) { namespaceUuid = Buffer.from(namespaceUuid); @@ -24,12 +24,12 @@ function createNamedUUID(namespaceUuid, key) { let u = Buffer.alloc(16); // bbbb - bb - bb - bb - bbbbbb - digest.copy(u, 0, 0, 4); // time_low - digest.copy(u, 4, 4, 6); // time_mid - digest.copy(u, 6, 6, 8); // time_hi_and_version + digest.copy(u, 0, 0, 4); // time_low + digest.copy(u, 4, 4, 6); // time_mid + digest.copy(u, 6, 6, 8); // time_hi_and_version - u[6] = (u[6] & 0x0f) | 0x50; // version, 4 most significant bits are set to version 5 (0101) - u[8] = (digest[8] & 0x3f) | 0x80; // clock_seq_hi_and_reserved, 2msb are set to 10 + u[6] = (u[6] & 0x0f) | 0x50; // version, 4 most significant bits are set to version 5 (0101) + u[8] = (digest[8] & 0x3f) | 0x80; // clock_seq_hi_and_reserved, 2msb are set to 10 u[9] = digest[9]; digest.copy(u, 10, 10, 16); diff --git a/core/vertical_menu_view.js b/core/vertical_menu_view.js index e56207a2..ea071010 100644 --- a/core/vertical_menu_view.js +++ b/core/vertical_menu_view.js @@ -1,32 +1,32 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuView = require('./menu_view.js').MenuView; -const ansi = require('./ansi_term.js'); -const strUtil = require('./string_util.js'); -const formatString = require('./string_format'); -const pipeToAnsi = require('./color_codes.js').pipeToAnsi; +// ENiGMA½ +const MenuView = require('./menu_view.js').MenuView; +const ansi = require('./ansi_term.js'); +const strUtil = require('./string_util.js'); +const formatString = require('./string_format'); +const pipeToAnsi = require('./color_codes.js').pipeToAnsi; -// deps -const util = require('util'); -const _ = require('lodash'); +// deps +const util = require('util'); +const _ = require('lodash'); -exports.VerticalMenuView = VerticalMenuView; +exports.VerticalMenuView = VerticalMenuView; function VerticalMenuView(options) { - options.cursor = options.cursor || 'hide'; + options.cursor = options.cursor || 'hide'; options.justify = options.justify || 'left'; MenuView.call(this, options); const self = this; - // we want page up/page down by default + // we want page up/page down by default if(!_.isObject(options.specialKeyMap)) { Object.assign(this.specialKeyMap, { - 'page up' : [ 'page up' ], - 'page down' : [ 'page down' ], + 'page up' : [ 'page up' ], + 'page down' : [ 'page down' ], }); } @@ -53,8 +53,8 @@ function VerticalMenuView(options) { self.maxVisibleItems = Math.ceil(self.dimens.height / (self.itemSpacing + 1)); self.viewWindow = { - top : self.focusedItemIndex, - bottom : Math.min(self.focusedItemIndex + self.maxVisibleItems, self.items.length) - 1, + top : self.focusedItemIndex, + bottom : Math.min(self.focusedItemIndex + self.maxVisibleItems, self.items.length) - 1, }; }; @@ -94,7 +94,7 @@ util.inherits(VerticalMenuView, MenuView); VerticalMenuView.prototype.redraw = function() { VerticalMenuView.super_.prototype.redraw.call(this); - // :TODO: rename positionCacheExpired to something that makese sense; combine methods for such + // :TODO: rename positionCacheExpired to something that makese sense; combine methods for such if(this.positionCacheExpired) { this.performAutoScale(); this.updateViewVisibleItems(); @@ -102,13 +102,13 @@ VerticalMenuView.prototype.redraw = function() { this.positionCacheExpired = false; } - // erase old items - // :TODO: optimize this: only needed if a item is removed or new max width < old. + // erase old items + // :TODO: optimize this: only needed if a item is removed or new max width < old. if(this.oldDimens) { - const blank = new Array(Math.max(this.oldDimens.width, this.dimens.width)).join(' '); - let seq = ansi.goto(this.position.row, this.position.col) + this.getSGR() + blank; - let row = this.position.row + 1; - const endRow = (row + this.oldDimens.height) - 2; + const blank = new Array(Math.max(this.oldDimens.width, this.dimens.width)).join(' '); + let seq = ansi.goto(this.position.row, this.position.col) + this.getSGR() + blank; + let row = this.position.row + 1; + const endRow = (row + this.oldDimens.height) - 2; while(row <= endRow) { seq += ansi.goto(row, this.position.col) + blank; @@ -148,16 +148,16 @@ VerticalMenuView.prototype.setFocus = function(focused) { }; VerticalMenuView.prototype.setFocusItemIndex = function(index) { - VerticalMenuView.super_.prototype.setFocusItemIndex.call(this, index); // sets this.focusedItemIndex + VerticalMenuView.super_.prototype.setFocusItemIndex.call(this, index); // sets this.focusedItemIndex const remainAfterFocus = this.items.length - index; if(remainAfterFocus >= this.maxVisibleItems) { this.viewWindow = { - top : this.focusedItemIndex, - bottom : Math.min(this.focusedItemIndex + this.maxVisibleItems, this.items.length) - 1 + top : this.focusedItemIndex, + bottom : Math.min(this.focusedItemIndex + this.maxVisibleItems, this.items.length) - 1 }; - this.positionCacheExpired = false; // skip standard behavior + this.positionCacheExpired = false; // skip standard behavior this.performAutoScale(); } @@ -190,7 +190,7 @@ VerticalMenuView.prototype.getData = function() { }; VerticalMenuView.prototype.setItems = function(items) { - // if we have items already, save off their drawing area so we don't leave fragments at redraw + // if we have items already, save off their drawing area so we don't leave fragments at redraw if(this.items && this.items.length) { this.oldDimens = Object.assign({}, this.dimens); } @@ -208,15 +208,15 @@ VerticalMenuView.prototype.removeItem = function(index) { VerticalMenuView.super_.prototype.removeItem.call(this, index); }; -// :TODO: Apply draw optimizaitons when only two items need drawn vs entire view! +// :TODO: Apply draw optimizaitons when only two items need drawn vs entire view! VerticalMenuView.prototype.focusNext = function() { if(this.items.length - 1 === this.focusedItemIndex) { this.focusedItemIndex = 0; this.viewWindow = { - top : 0, - bottom : Math.min(this.maxVisibleItems, this.items.length) - 1 + top : 0, + bottom : Math.min(this.maxVisibleItems, this.items.length) - 1 }; } else { this.focusedItemIndex++; @@ -237,9 +237,9 @@ VerticalMenuView.prototype.focusPrevious = function() { this.focusedItemIndex = this.items.length - 1; this.viewWindow = { - //top : this.items.length - this.maxVisibleItems, - top : Math.max(this.items.length - this.maxVisibleItems, 0), - bottom : this.items.length - 1 + //top : this.items.length - this.maxVisibleItems, + top : Math.max(this.items.length - this.maxVisibleItems, 0), + bottom : this.items.length - 1 }; } else { @@ -249,7 +249,7 @@ VerticalMenuView.prototype.focusPrevious = function() { this.viewWindow.top--; this.viewWindow.bottom--; - // adjust for focus index being set & window needing expansion as we scroll up + // adjust for focus index being set & window needing expansion as we scroll up const rem = (this.viewWindow.bottom - this.viewWindow.top) + 1; if(rem < this.maxVisibleItems && (this.items.length - 1) > this.focusedItemIndex) { this.viewWindow.bottom = this.items.length - 1; @@ -264,11 +264,11 @@ VerticalMenuView.prototype.focusPrevious = function() { VerticalMenuView.prototype.focusPreviousPageItem = function() { // - // Jump to current - up to page size or top - // If already at the top, jump to bottom + // Jump to current - up to page size or top + // If already at the top, jump to bottom // if(0 === this.focusedItemIndex) { - return this.focusPrevious(); // will jump to bottom + return this.focusPrevious(); // will jump to bottom } const index = Math.max(this.focusedItemIndex - this.dimens.height, 0); @@ -284,11 +284,11 @@ VerticalMenuView.prototype.focusPreviousPageItem = function() { VerticalMenuView.prototype.focusNextPageItem = function() { // - // Jump to current + up to page size or bottom - // If already at the bottom, jump to top + // Jump to current + up to page size or bottom + // If already at the bottom, jump to top // if(this.items.length - 1 === this.focusedItemIndex) { - return this.focusNext(); // will jump to top + return this.focusNext(); // will jump to top } const index = Math.min(this.focusedItemIndex + this.maxVisibleItems, this.items.length - 1); @@ -299,8 +299,8 @@ VerticalMenuView.prototype.focusNextPageItem = function() { this.focusedItemIndex = index; this.viewWindow = { - top : this.focusedItemIndex, - bottom : Math.min(this.focusedItemIndex + this.maxVisibleItems, this.items.length) - 1 + top : this.focusedItemIndex, + bottom : Math.min(this.focusedItemIndex + this.maxVisibleItems, this.items.length) - 1 }; this.redraw(); @@ -328,8 +328,8 @@ VerticalMenuView.prototype.focusLast = function() { this.focusedItemIndex = index; this.viewWindow = { - top : this.focusedItemIndex, - bottom : Math.min(this.focusedItemIndex + this.maxVisibleItems, this.items.length) - 1 + top : this.focusedItemIndex, + bottom : Math.min(this.focusedItemIndex + this.maxVisibleItems, this.items.length) - 1 }; this.redraw(); diff --git a/core/view.js b/core/view.js index fd46428f..702be949 100644 --- a/core/view.js +++ b/core/view.js @@ -1,35 +1,35 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const events = require('events'); -const util = require('util'); -const ansi = require('./ansi_term.js'); -const colorCodes = require('./color_codes.js'); -const enigAssert = require('./enigma_assert.js'); -const { renderSubstr } = require('./string_util.js'); +// ENiGMA½ +const events = require('events'); +const util = require('util'); +const ansi = require('./ansi_term.js'); +const colorCodes = require('./color_codes.js'); +const enigAssert = require('./enigma_assert.js'); +const { renderSubstr } = require('./string_util.js'); -// deps -const _ = require('lodash'); +// deps +const _ = require('lodash'); -exports.View = View; +exports.View = View; const VIEW_SPECIAL_KEY_MAP_DEFAULT = { - accept : [ 'return' ], - exit : [ 'esc' ], - backspace : [ 'backspace', 'del' ], - del : [ 'del' ], - next : [ 'tab' ], - up : [ 'up arrow' ], - down : [ 'down arrow' ], - end : [ 'end' ], - home : [ 'home' ], - left : [ 'left arrow' ], - right : [ 'right arrow' ], - clearLine : [ 'ctrl + y' ], + accept : [ 'return' ], + exit : [ 'esc' ], + backspace : [ 'backspace', 'del' ], + del : [ 'del' ], + next : [ 'tab' ], + up : [ 'up arrow' ], + down : [ 'down arrow' ], + end : [ 'end' ], + home : [ 'home' ], + left : [ 'left arrow' ], + right : [ 'right arrow' ], + clearLine : [ 'ctrl + y' ], }; -exports.VIEW_SPECIAL_KEY_MAP_DEFAULT = VIEW_SPECIAL_KEY_MAP_DEFAULT; +exports.VIEW_SPECIAL_KEY_MAP_DEFAULT = VIEW_SPECIAL_KEY_MAP_DEFAULT; function View(options) { events.EventEmitter.call(this); @@ -37,21 +37,21 @@ function View(options) { enigAssert(_.isObject(options)); enigAssert(_.isObject(options.client)); - var self = this; + var self = this; - this.client = options.client; + this.client = options.client; - this.cursor = options.cursor || 'show'; - this.cursorStyle = options.cursorStyle || 'default'; + this.cursor = options.cursor || 'show'; + this.cursorStyle = options.cursorStyle || 'default'; - this.acceptsFocus = options.acceptsFocus || false; - this.acceptsInput = options.acceptsInput || false; + this.acceptsFocus = options.acceptsFocus || false; + this.acceptsInput = options.acceptsInput || false; - this.position = { x : 0, y : 0 }; - this.dimens = { height : 1, width : 0 }; + this.position = { x : 0, y : 0 }; + this.dimens = { height : 1, width : 0 }; - this.textStyle = options.textStyle || 'normal'; - this.focusTextStyle = options.focusTextStyle || this.textStyle; + this.textStyle = options.textStyle || 'normal'; + this.focusTextStyle = options.focusTextStyle || this.textStyle; if(options.id) { this.setId(options.id); @@ -72,17 +72,17 @@ function View(options) { this.autoScale = { height : false, width : false }; } else { this.dimens = { - width : options.width || 0, - height : 0 + width : options.width || 0, + height : 0 }; } - // :TODO: Just use styleSGRx for these, e.g. styleSGR0, styleSGR1 = norm/focus - this.ansiSGR = options.ansiSGR || ansi.getSGRFromGraphicRendition( { fg : 39, bg : 49 }, true); - this.ansiFocusSGR = options.ansiFocusSGR || this.ansiSGR; + // :TODO: Just use styleSGRx for these, e.g. styleSGR0, styleSGR1 = norm/focus + this.ansiSGR = options.ansiSGR || ansi.getSGRFromGraphicRendition( { fg : 39, bg : 49 }, true); + this.ansiFocusSGR = options.ansiFocusSGR || this.ansiSGR; - this.styleSGR1 = options.styleSGR1 || this.ansiSGR; - this.styleSGR2 = options.styleSGR2 || this.ansiFocusSGR; + this.styleSGR1 = options.styleSGR1 || this.ansiSGR; + this.styleSGR2 = options.styleSGR2 || this.ansiFocusSGR; if(this.acceptsInput) { this.specialKeyMap = options.specialKeyMap || VIEW_SPECIAL_KEY_MAP_DEFAULT; @@ -126,7 +126,7 @@ View.prototype.getId = function() { View.prototype.setPosition = function(pos) { // - // Allow the following forms: [row, col], { row : r, col : c }, or (row, col) + // Allow the following forms: [row, col], { row : r, col : c }, or (row, col) // if(util.isArray(pos)) { this.position.row = pos[0]; @@ -139,34 +139,34 @@ View.prototype.setPosition = function(pos) { this.position.col = parseInt(arguments[1], 10); } - // sanatize - this.position.row = Math.max(this.position.row, 1); - this.position.col = Math.max(this.position.col, 1); - this.position.row = Math.min(this.position.row, this.client.term.termHeight); - this.position.col = Math.min(this.position.col, this.client.term.termWidth); + // sanatize + this.position.row = Math.max(this.position.row, 1); + this.position.col = Math.max(this.position.col, 1); + this.position.row = Math.min(this.position.row, this.client.term.termHeight); + this.position.col = Math.min(this.position.col, this.client.term.termWidth); }; View.prototype.setDimension = function(dimens) { enigAssert(_.isObject(dimens) && _.isNumber(dimens.height) && _.isNumber(dimens.width)); - this.dimens = dimens; - this.autoScale = { height : false, width : false }; + this.dimens = dimens; + this.autoScale = { height : false, width : false }; }; View.prototype.setHeight = function(height) { - height = parseInt(height) || 1; - height = Math.min(height, this.client.term.termHeight); + height = parseInt(height) || 1; + height = Math.min(height, this.client.term.termHeight); - this.dimens.height = height; - this.autoScale.height = false; + this.dimens.height = height; + this.autoScale.height = false; }; View.prototype.setWidth = function(width) { - width = parseInt(width) || 1; - width = Math.min(width, this.client.term.termWidth); + width = parseInt(width) || 1; + width = Math.min(width, this.client.term.termWidth); - this.dimens.width = width; - this.autoScale.width = false; + this.dimens.width = width; + this.autoScale.width = false; }; View.prototype.getSGR = function() { @@ -188,20 +188,20 @@ View.prototype.setSpecialKeyMapOverride = function(specialKeyMapOverride) { View.prototype.setPropertyValue = function(propName, value) { switch(propName) { - case 'height' : this.setHeight(value); break; - case 'width' : this.setWidth(value); break; - case 'focus' : this.setFocus(value); break; + case 'height' : this.setHeight(value); break; + case 'width' : this.setWidth(value); break; + case 'focus' : this.setFocus(value); break; - case 'text' : + case 'text' : if('setText' in this) { this.setText(value); } break; - case 'textStyle' : this.textStyle = value; break; - case 'focusTextStyle' : this.focusTextStyle = value; break; + case 'textStyle' : this.textStyle = value; break; + case 'focusTextStyle' : this.focusTextStyle = value; break; - case 'justify' : this.justify = value; break; + case 'justify' : this.justify = value; break; case 'fillChar' : if('fillChar' in this) { @@ -217,9 +217,9 @@ View.prototype.setPropertyValue = function(propName, value) { if(_.isBoolean(value)) { this.submit = value; }/* else { - this.submit = _.isArray(value) && value.length > 0; - } - */ + this.submit = _.isArray(value) && value.length > 0; + } + */ break; case 'resizable' : @@ -258,8 +258,8 @@ View.prototype.setFocus = function(focused) { }; View.prototype.onKeyPress = function(ch, key) { - enigAssert(this.hasFocus, 'View does not have focus'); - enigAssert(this.acceptsInput, 'View does not accept input'); + enigAssert(this.hasFocus, 'View does not have focus'); + enigAssert(this.acceptsInput, 'View does not accept input'); if(!this.hasFocus || !this.acceptsInput) { return; diff --git a/core/view_controller.js b/core/view_controller.js index f6a2bc2b..0df09084 100644 --- a/core/view_controller.js +++ b/core/view_controller.js @@ -1,23 +1,23 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -var MCIViewFactory = require('./mci_view_factory.js').MCIViewFactory; -var menuUtil = require('./menu_util.js'); -var asset = require('./asset.js'); -var ansi = require('./ansi_term.js'); +// ENiGMA½ +var MCIViewFactory = require('./mci_view_factory.js').MCIViewFactory; +var menuUtil = require('./menu_util.js'); +var asset = require('./asset.js'); +var ansi = require('./ansi_term.js'); -// deps -var events = require('events'); -var util = require('util'); -var assert = require('assert'); -var async = require('async'); -var _ = require('lodash'); -var paths = require('path'); +// deps +var events = require('events'); +var util = require('util'); +var assert = require('assert'); +var async = require('async'); +var _ = require('lodash'); +var paths = require('path'); -exports.ViewController = ViewController; +exports.ViewController = ViewController; -var MCI_REGEXP = /([A-Z]{2})([0-9]{1,2})/; +var MCI_REGEXP = /([A-Z]{2})([0-9]{1,2})/; function ViewController(options) { assert(_.isObject(options)); @@ -25,29 +25,29 @@ function ViewController(options) { events.EventEmitter.call(this); - var self = this; + var self = this; - this.client = options.client; - this.views = {}; // map of ID -> view - this.formId = options.formId || 0; - this.mciViewFactory = new MCIViewFactory(this.client); // :TODO: can this not be a singleton? - this.noInput = _.isBoolean(options.noInput) ? options.noInput : false; + this.client = options.client; + this.views = {}; // map of ID -> view + this.formId = options.formId || 0; + this.mciViewFactory = new MCIViewFactory(this.client); // :TODO: can this not be a singleton? + this.noInput = _.isBoolean(options.noInput) ? options.noInput : false; - this.actionKeyMap = {}; + this.actionKeyMap = {}; // - // Small wrapper/proxy around handleAction() to ensure we do not allow - // input/additional actions queued while performing an action + // Small wrapper/proxy around handleAction() to ensure we do not allow + // input/additional actions queued while performing an action // this.handleActionWrapper = function(formData, actionBlock) { if(self.waitActionCompletion) { - return; // ignore until this is finished! + return; // ignore until this is finished! } self.waitActionCompletion = true; menuUtil.handleAction(self.client, formData, actionBlock, (err) => { if(err) { - // :TODO: What can we really do here? + // :TODO: What can we really do here? if('ALREADYTHERE' === err.reasonCode) { self.client.log.trace( err.reason ); } else { @@ -61,22 +61,22 @@ function ViewController(options) { this.clientKeyPressHandler = function(ch, key) { // - // Process key presses treating form submit mapped keys special. - // Everything else is forwarded on to the focused View, if any. + // Process key presses treating form submit mapped keys special. + // Everything else is forwarded on to the focused View, if any. // var actionForKey = key ? self.actionKeyMap[key.name] : self.actionKeyMap[ch]; if(actionForKey) { if(_.isNumber(actionForKey.viewId)) { // - // Key works on behalf of a view -- switch focus & submit + // Key works on behalf of a view -- switch focus & submit // self.switchFocus(actionForKey.viewId); self.submitForm(key); } else if(_.isString(actionForKey.action)) { const formData = self.getFocusedView() ? self.getFormData() : { }; self.handleActionWrapper( - Object.assign( { ch : ch, key : key }, formData ), // formData + key info - actionForKey); // actionBlock + Object.assign( { ch : ch, key : key }, formData ), // formData + key info + actionForKey); // actionBlock } } else { if(self.focusedView && self.focusedView.acceptsInput) { @@ -94,7 +94,7 @@ function ViewController(options) { case 'accept' : if(self.focusedView && self.focusedView.submit) { - // :TODO: need to do validation here!!! + // :TODO: need to do validation here!!! var focusedView = self.focusedView; self.validateView(focusedView, function validated(err, newFocusedViewId) { if(err) { @@ -116,9 +116,9 @@ function ViewController(options) { self.emit('submit', this.getFormData(key)); }; - // :TODO: replace this in favor of overriding toJSON() for various things such that logging will *never* output them + // :TODO: replace this in favor of overriding toJSON() for various things such that logging will *never* output them this.getLogFriendlyFormData = function(formData) { - // :TODO: these fields should be part of menu.json sensitiveMembers[] + // :TODO: these fields should be part of menu.json sensitiveMembers[] var safeFormData = _.cloneDeep(formData); if(safeFormData.value.password) { safeFormData.value.password = '*****'; @@ -141,8 +141,8 @@ function ViewController(options) { this.createViewsFromMCI = function(mciMap, cb) { async.each(Object.keys(mciMap), (name, nextItem) => { - const mci = mciMap[name]; - const view = self.mciViewFactory.createFromMCI(mci); + const mci = mciMap[name]; + const view = self.mciViewFactory.createFromMCI(mci); if(view) { if(false === self.noInput) { @@ -160,7 +160,7 @@ function ViewController(options) { }); }; - // :TODO: move this elsewhere + // :TODO: move this elsewhere this.setViewPropertiesFromMCIConf = function(view, conf) { var propAsset; @@ -178,14 +178,14 @@ function ViewController(options) { propValue = asset.resolveSystemStatAsset(conf[propName]); break; - // :TODO: handle @art (e.g. text : @art ...) + // :TODO: handle @art (e.g. text : @art ...) case 'method' : case 'systemMethod' : if('validate' === propName) { - // :TODO: handle propAsset.location for @method script specification + // :TODO: handle propAsset.location for @method script specification if('systemMethod' === propAsset.type) { - // :TODO: implementation validation @systemMethod handling! + // :TODO: implementation validation @systemMethod handling! var methodModule = require(paths.join(__dirname, 'system_view_validate.js')); if(_.isFunction(methodModule[propAsset.asset])) { propValue = methodModule[propAsset.asset]; @@ -200,12 +200,12 @@ function ViewController(options) { // :TODO: clean this code up! } else { if('systemMethod' === propAsset.type) { - // :TODO: + // :TODO: } else { - // local to current module + // local to current module var currentModule = self.client.currentMenuModule; if(_.isFunction(currentModule.menuMethods[propAsset.asset])) { - // :TODO: Fix formData & extraArgs... this all needs general processing + // :TODO: Fix formData & extraArgs... this all needs general processing propValue = currentModule.menuMethods[propAsset.asset]({}, {});//formData, conf.extraArgs); } } @@ -233,14 +233,14 @@ function ViewController(options) { let initialFocusId = 1; async.each(Object.keys(config.mci || {}), function entry(mci, nextItem) { - const mciMatch = mci.match(MCI_REGEXP); // :TODO: How to handle auto-generated IDs???? + const mciMatch = mci.match(MCI_REGEXP); // :TODO: How to handle auto-generated IDs???? if(null === mciMatch) { self.client.log.warn( { mci : mci }, 'Unable to parse MCI code'); return; } const viewId = parseInt(mciMatch[2]); - assert(!isNaN(viewId), 'Cannot parse view ID: ' + mciMatch[2]); // shouldn't be possible with RegExp used + assert(!isNaN(viewId), 'Cannot parse view ID: ' + mciMatch[2]); // shouldn't be possible with RegExp used if(viewId > highestId) { highestId = viewId; @@ -269,7 +269,7 @@ function ViewController(options) { nextItem(null); }, err => { - // default to highest ID if no 'submit' entry present + // default to highest ID if no 'submit' entry present if(!submitId) { var highestIdView = self.getView(highestId); if(highestIdView) { @@ -283,19 +283,19 @@ function ViewController(options) { }); }; - // method for comparing submitted form data to configuration entries + // method for comparing submitted form data to configuration entries this.actionBlockValueComparator = function(formValue, actionValue) { // - // For a match to occur, one of the following must be true: + // For a match to occur, one of the following must be true: // - // * actionValue is a Object: - // a) All key/values must exactly match - // b) value is null; The key (view ID or "argName") must be present - // in formValue. This is a wildcard/any match. - // * actionValue is a Number: This represents a view ID that - // must be present in formValue. - // * actionValue is a string: This represents a view with - // "argName" set that must be present in formValue. + // * actionValue is a Object: + // a) All key/values must exactly match + // b) value is null; The key (view ID or "argName") must be present + // in formValue. This is a wildcard/any match. + // * actionValue is a Number: This represents a view ID that + // must be present in formValue. + // * actionValue is a string: This represents a view with + // "argName" set that must be present in formValue. // if(_.isUndefined(actionValue)) { return false; @@ -307,12 +307,12 @@ function ViewController(options) { } } else { /* - :TODO: support: - value: { - someArgName: [ "key1", "key2", ... ], - someOtherArg: [ "key1, ... ] - } - */ + :TODO: support: + value: { + someArgName: [ "key1", "key2", ... ], + someOtherArg: [ "key1, ... ] + } + */ var actionValueKeys = Object.keys(actionValue); for(var i = 0; i < actionValueKeys.length; ++i) { var viewId = actionValueKeys[i]; @@ -328,8 +328,8 @@ function ViewController(options) { self.client.log.trace( { - formValue : formValue, - actionValue : actionValue + formValue : formValue, + actionValue : actionValue }, 'Action match' ); @@ -362,7 +362,7 @@ function ViewController(options) { var viewValidationListener = self.client.currentMenuModule.menuMethods.viewValidationListener; if(_.isFunction(viewValidationListener)) { if(err) { - err.view = view; // pass along the view that failed + err.view = view; // pass along the view that failed } viewValidationListener(err, function validationComplete(newViewFocusId) { @@ -390,7 +390,7 @@ ViewController.prototype.attachClientEvents = function() { this.client.on('key press', this.clientKeyPressHandler); Object.keys(this.views).forEach(function vid(i) { - // remove, then add to ensure we only have one listener + // remove, then add to ensure we only have one listener self.views[i].removeListener('action', self.viewActionListener); self.views[i].on('action', self.viewActionListener); }); @@ -462,10 +462,10 @@ ViewController.prototype.resetInitialFocus = function() { ViewController.prototype.switchFocus = function(id) { // - // Perform focus switching validation now + // Perform focus switching validation now // - var self = this; - var focusedView = self.focusedView; + var self = this; + var focusedView = self.focusedView; self.validateView(focusedView, function validated(err, newFocusedViewId) { if(err) { @@ -474,10 +474,10 @@ ViewController.prototype.switchFocus = function(id) { } else { self.attachClientEvents(); - // remove from old + // remove from old self.setViewFocusWithEvents(focusedView, false); - // set to new + // set to new self.setViewFocusWithEvents(self.getView(id), true); } }); @@ -486,7 +486,7 @@ ViewController.prototype.switchFocus = function(id) { ViewController.prototype.nextFocus = function() { let nextFocusView = this.focusedView ? this.focusedView : this.views[this.firstId]; - // find the next view that accepts focus + // find the next view that accepts focus while(nextFocusView && nextFocusView.nextId) { nextFocusView = this.getView(nextFocusView.nextId); if(!nextFocusView || nextFocusView.acceptsFocus) { @@ -531,7 +531,7 @@ ViewController.prototype.redrawAll = function(initialFocusId) { for(var id in this.views) { if(initialFocusId === id) { - continue; // will draw @ focus + continue; // will draw @ focus } this.views[id].redraw(); } @@ -543,9 +543,9 @@ ViewController.prototype.loadFromPromptConfig = function(options, cb) { assert(_.isObject(options)); assert(_.isObject(options.mciMap)); - var self = this; - var promptConfig = _.isObject(options.config) ? options.config : self.client.currentMenuModule.menuConfig.promptConfig; - var initialFocusId = 1; // default to first + var self = this; + var promptConfig = _.isObject(options.config) ? options.config : self.client.currentMenuModule.menuConfig.promptConfig; + var initialFocusId = 1; // default to first async.waterfall( [ @@ -574,12 +574,12 @@ ViewController.prototype.loadFromPromptConfig = function(options, cb) { self.handleActionWrapper(formData, self.client.currentMenuModule.menuConfig); } else { // - // Menus that reference prompts can have a sepcial "submit" block without the - // hassle of by-form-id configurations, etc. + // Menus that reference prompts can have a sepcial "submit" block without the + // hassle of by-form-id configurations, etc. // - // "submit" : [ - // { ... } - // ] + // "submit" : [ + // { ... } + // ] // var menuSubmit = self.client.currentMenuModule.menuConfig.submit; if(!_.isArray(menuSubmit)) { @@ -588,15 +588,15 @@ ViewController.prototype.loadFromPromptConfig = function(options, cb) { } // - // Locate matching action block + // Locate matching action block // - // :TODO: this is basically the same as for menus -- DRY it up! + // :TODO: this is basically the same as for menus -- DRY it up! for(var c = 0; c < menuSubmit.length; ++c) { var actionBlock = menuSubmit[c]; if(_.isEqualWith(formData.value, actionBlock.value, self.actionBlockValueComparator)) { self.handleActionWrapper(formData, actionBlock); - break; // there an only be one... + break; // there an only be one... } } } @@ -612,13 +612,13 @@ ViewController.prototype.loadFromPromptConfig = function(options, cb) { promptConfig.actionKeys.forEach(ak => { // - // * 'keys' must be present and be an array of key names - // * If 'viewId' is present, key(s) will focus & submit on behalf - // of the specified view. - // * If 'action' is present, that action will be procesed when - // triggered by key(s) + // * 'keys' must be present and be an array of key names + // * If 'viewId' is present, key(s) will focus & submit on behalf + // of the specified view. + // * If 'action' is present, that action will be procesed when + // triggered by key(s) // - // Ultimately, create a map of key -> { action block } + // Ultimately, create a map of key -> { action block } // if(!_.isArray(ak.keys)) { return; @@ -657,12 +657,12 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) { return; } - var self = this; - var formIdKey = options.formId ? options.formId.toString() : '0'; - this.formInitialFocusId = 1; // default to first + var self = this; + var formIdKey = options.formId ? options.formId.toString() : '0'; + this.formInitialFocusId = 1; // default to first var formConfig; - // :TODO: honor options.withoutForm + // :TODO: honor options.withoutForm async.waterfall( [ @@ -671,7 +671,7 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) { formConfig = fc; if(err) { - // non-fatal + // non-fatal self.client.log.trace( { reason : err.message, mci : Object.keys(options.mciMap), formId : formIdKey }, 'Unable to find matching form configuration'); @@ -686,28 +686,28 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) { }); }, /* - function applyThemeCustomization(callback) { - formConfig = formConfig || {}; - formConfig.mci = formConfig.mci || {}; - //self.client.currentMenuModule.menuConfig.config = self.client.currentMenuModule.menuConfig.config || {}; + function applyThemeCustomization(callback) { + formConfig = formConfig || {}; + formConfig.mci = formConfig.mci || {}; + //self.client.currentMenuModule.menuConfig.config = self.client.currentMenuModule.menuConfig.config || {}; - //console.log('menu config.....'); - //console.log(self.client.currentMenuModule.menuConfig) + //console.log('menu config.....'); + //console.log(self.client.currentMenuModule.menuConfig) - menuUtil.applyMciThemeCustomization({ - name : self.client.currentMenuModule.menuName, - type : 'menus', - client : self.client, - mci : formConfig.mci, - //config : self.client.currentMenuModule.menuConfig.config, - formId : formIdKey, - }); + menuUtil.applyMciThemeCustomization({ + name : self.client.currentMenuModule.menuName, + type : 'menus', + client : self.client, + mci : formConfig.mci, + //config : self.client.currentMenuModule.menuConfig.config, + formId : formIdKey, + }); - //console.log('after theme...') - //console.log(self.client.currentMenuModule.menuConfig.config) + //console.log('after theme...') + //console.log(self.client.currentMenuModule.menuConfig.config) - callback(null); - }, + callback(null); + }, */ function applyViewConfiguration(callback) { if(_.isObject(formConfig)) { @@ -730,7 +730,7 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) { self.client.log.trace( { formData : self.getLogFriendlyFormData(formData) }, 'Form submit'); // - // Locate configuration for this form ID + // Locate configuration for this form ID // var confForFormId; if(_.isObject(formConfig.submit[formData.submitId])) { @@ -738,20 +738,20 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) { } else if(_.isObject(formConfig.submit['*'])) { confForFormId = formConfig.submit['*']; } else { - // no configuration for this submitId + // no configuration for this submitId self.client.log.debug( { formId : formData.submitId }, 'No configuration for form ID'); return; } // - // Locate a matching action block based on the submitted data + // Locate a matching action block based on the submitted data // for(var c = 0; c < confForFormId.length; ++c) { var actionBlock = confForFormId[c]; if(_.isEqualWith(formData.value, actionBlock.value, self.actionBlockValueComparator)) { self.handleActionWrapper(formData, actionBlock); - break; // there an only be one... + break; // there an only be one... } } }); @@ -766,13 +766,13 @@ ViewController.prototype.loadFromMenuConfig = function(options, cb) { formConfig.actionKeys.forEach(function akEntry(ak) { // - // * 'keys' must be present and be an array of key names - // * If 'viewId' is present, key(s) will focus & submit on behalf - // of the specified view. - // * If 'action' is present, that action will be procesed when - // triggered by key(s) + // * 'keys' must be present and be an array of key names + // * If 'viewId' is present, key(s) will focus & submit on behalf + // of the specified view. + // * If 'action' is present, that action will be procesed when + // triggered by key(s) // - // Ultimately, create a map of key -> { action block } + // Ultimately, create a map of key -> { action block } // if(!_.isArray(ak.keys)) { return; @@ -822,23 +822,23 @@ ViewController.prototype.formatMCIString = function(format) { ViewController.prototype.getFormData = function(key) { /* - Example form data: - { - id : 0, - submitId : 1, - value : { - "1" : "hurp", - "2" : [ 'a', 'b', ... ], - "3" 2, - "pants" : "no way" - } + Example form data: + { + id : 0, + submitId : 1, + value : { + "1" : "hurp", + "2" : [ 'a', 'b', ... ], + "3" 2, + "pants" : "no way" + } - } - */ + } + */ const formData = { - id : this.formId, - submitId : this.focusedView.id, - value : {}, + id : this.formId, + submitId : this.focusedView.id, + value : {}, }; if(key) { @@ -848,7 +848,7 @@ ViewController.prototype.getFormData = function(key) { let viewData; _.each(this.views, view => { try { - // don't fill forms with static, non user-editable data data + // don't fill forms with static, non user-editable data data if(!view.acceptsInput) { return; } diff --git a/core/web_password_reset.js b/core/web_password_reset.js index 7f30425f..6ca916da 100644 --- a/core/web_password_reset.js +++ b/core/web_password_reset.js @@ -1,29 +1,29 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const Config = require('./config.js').get; -const Errors = require('./enig_error.js').Errors; -const getServer = require('./listening_server.js').getServer; -const webServerPackageName = require('./servers/content/web.js').moduleInfo.packageName; -const User = require('./user.js'); -const userDb = require('./database.js').dbs.user; -const getISOTimestampString = require('./database.js').getISOTimestampString; -const Log = require('./logger.js').log; +// ENiGMA½ +const Config = require('./config.js').get; +const Errors = require('./enig_error.js').Errors; +const getServer = require('./listening_server.js').getServer; +const webServerPackageName = require('./servers/content/web.js').moduleInfo.packageName; +const User = require('./user.js'); +const userDb = require('./database.js').dbs.user; +const getISOTimestampString = require('./database.js').getISOTimestampString; +const Log = require('./logger.js').log; -// deps -const async = require('async'); -const crypto = require('crypto'); -const fs = require('graceful-fs'); -const url = require('url'); -const querystring = require('querystring'); +// deps +const async = require('async'); +const crypto = require('crypto'); +const fs = require('graceful-fs'); +const url = require('url'); +const querystring = require('querystring'); const PW_RESET_EMAIL_TEXT_TEMPLATE_DEFAULT = - `%USERNAME%: + `%USERNAME%: a password reset has been requested for your account on %BOARDNAME%. - * If this was not you, please ignore this email. - * Otherwise, follow this link: %RESET_URL% + * If this was not you, please ignore this email. + * Otherwise, follow this link: %RESET_URL% `; function getWebServer() { @@ -67,7 +67,7 @@ class WebPasswordReset { }, function generateAndStoreResetToken(user, callback) { // - // Reset "token" is simply HEX encoded cryptographically generated bytes + // Reset "token" is simply HEX encoded cryptographically generated bytes // crypto.randomBytes(256, (err, token) => { if(err) { @@ -77,11 +77,11 @@ class WebPasswordReset { token = token.toString('hex'); const newProperties = { - email_password_reset_token : token, - email_password_reset_token_ts : getISOTimestampString(), + email_password_reset_token : token, + email_password_reset_token_ts : getISOTimestampString(), }; - // we simply place the reset token in the user's properties + // we simply place the reset token in the user's properties user.persistProperties(newProperties, err => { return callback(err, user); }); @@ -107,10 +107,10 @@ class WebPasswordReset { function replaceTokens(s) { return s - .replace(/%BOARDNAME%/g, Config().general.boardName) - .replace(/%USERNAME%/g, user.username) - .replace(/%TOKEN%/g, user.properties.email_password_reset_token) - .replace(/%RESET_URL%/g, resetUrl) + .replace(/%BOARDNAME%/g, Config().general.boardName) + .replace(/%USERNAME%/g, user.username) + .replace(/%TOKEN%/g, user.properties.email_password_reset_token) + .replace(/%RESET_URL%/g, resetUrl) ; } @@ -120,11 +120,11 @@ class WebPasswordReset { } const message = { - to : `${user.properties.display_name||user.username} <${user.properties.email_address}>`, - // from will be filled in - subject : 'Forgot Password', - text : textTemplate, - html : htmlTemplate, + to : `${user.properties.display_name||user.username} <${user.properties.email_address}>`, + // from will be filled in + subject : 'Forgot Password', + text : textTemplate, + html : htmlTemplate, }; sendMail(message, (err, info) => { @@ -145,32 +145,32 @@ class WebPasswordReset { } static scheduleEvents(cb) { - // :TODO: schedule ~daily cleanup task + // :TODO: schedule ~daily cleanup task return cb(null); } static registerRoutes(cb) { const webServer = getWebServer(); if(!webServer) { - return cb(null); // no webserver enabled + return cb(null); // no webserver enabled } if(!webServer.instance.isEnabled()) { - return cb(null); // no error, but we're not serving web stuff + return cb(null); // no error, but we're not serving web stuff } [ { - // this is the page displayed to user when they GET it - method : 'GET', - path : '^\\/reset_password\\?token\\=[a-f0-9]+$', // Config.contentServers.web.forgotPasswordPageTemplate - handler : WebPasswordReset.routeResetPasswordGet, + // this is the page displayed to user when they GET it + method : 'GET', + path : '^\\/reset_password\\?token\\=[a-f0-9]+$', // Config.contentServers.web.forgotPasswordPageTemplate + handler : WebPasswordReset.routeResetPasswordGet, }, - // POST handler for performing the actual reset + // POST handler for performing the actual reset { - method : 'POST', - path : '^\\/reset_password$', - handler : WebPasswordReset.routeResetPasswordPost, + method : 'POST', + path : '^\\/reset_password$', + handler : WebPasswordReset.routeResetPasswordPost, } ].forEach(r => { webServer.instance.addRoute(r); @@ -213,10 +213,10 @@ class WebPasswordReset { } static routeResetPasswordGet(req, resp) { - const webServer = getWebServer(); // must be valid, we just got a req! + const webServer = getWebServer(); // must be valid, we just got a req! - const urlParts = url.parse(req.url, true); - const token = urlParts.query && urlParts.query.token; + const urlParts = url.parse(req.url, true); + const token = urlParts.query && urlParts.query.token; if(!token) { return WebPasswordReset.accessDenied(webServer, resp); @@ -224,7 +224,7 @@ class WebPasswordReset { WebPasswordReset.getUserByToken(token, (err, user) => { if(err) { - // assume it's expired + // assume it's expired return webServer.instance.respondWithError(resp, 410, 'Invalid or expired reset link.', 'Expired Link'); } @@ -236,11 +236,11 @@ class WebPasswordReset { (templateData, preprocessFinished) => { const finalPage = templateData - .replace(/%BOARDNAME%/g, config.general.boardName) - .replace(/%USERNAME%/g, user.username) - .replace(/%TOKEN%/g, token) - .replace(/%RESET_URL%/g, postResetUrl) - ; + .replace(/%BOARDNAME%/g, config.general.boardName) + .replace(/%USERNAME%/g, user.username) + .replace(/%TOKEN%/g, token) + .replace(/%RESET_URL%/g, postResetUrl) + ; return preprocessFinished(null, finalPage); }, @@ -250,7 +250,7 @@ class WebPasswordReset { } static routeResetPasswordPost(req, resp) { - const webServer = getWebServer(); // must be valid, we just got a req! + const webServer = getWebServer(); // must be valid, we just got a req! let bodyData = ''; req.on('data', data => { @@ -266,8 +266,8 @@ class WebPasswordReset { const config = Config(); if(!formData.token || !formData.password || !formData.confirm_password || - formData.password !== formData.confirm_password || - formData.password.length < config.users.passwordMin || formData.password.length > config.users.passwordMax) + formData.password !== formData.confirm_password || + formData.password.length < config.users.passwordMin || formData.password.length > config.users.passwordMax) { return badRequest(); } @@ -282,7 +282,7 @@ class WebPasswordReset { return badRequest(); } - // delete assoc properties - no need to wait for completion + // delete assoc properties - no need to wait for completion user.removeProperty('email_password_reset_token'); user.removeProperty('email_password_reset_token_ts'); @@ -298,15 +298,15 @@ function performMaintenanceTask(args, cb) { const forgotPassExpireTime = args[0] || '24 hours'; - // remove all reset token associated properties older than |forgotPassExpireTime| + // remove all reset token associated properties older than |forgotPassExpireTime| userDb.run( `DELETE FROM user_property - WHERE user_id IN ( - SELECT user_id - FROM user_property - WHERE prop_name = "email_password_reset_token_ts" - AND DATETIME("now") >= DATETIME(prop_value, "+${forgotPassExpireTime}") - ) AND prop_name IN ("email_password_reset_token_ts", "email_password_reset_token");`, + WHERE user_id IN ( + SELECT user_id + FROM user_property + WHERE prop_name = "email_password_reset_token_ts" + AND DATETIME("now") >= DATETIME(prop_value, "+${forgotPassExpireTime}") + ) AND prop_name IN ("email_password_reset_token_ts", "email_password_reset_token");`, err => { if(err) { Log.warn( { error : err.message }, 'Failed deleting old email reset tokens'); @@ -316,5 +316,5 @@ function performMaintenanceTask(args, cb) { ); } -exports.WebPasswordReset = WebPasswordReset; -exports.performMaintenanceTask = performMaintenanceTask; \ No newline at end of file +exports.WebPasswordReset = WebPasswordReset; +exports.performMaintenanceTask = performMaintenanceTask; \ No newline at end of file diff --git a/core/whos_online.js b/core/whos_online.js index edda6c20..28b71703 100644 --- a/core/whos_online.js +++ b/core/whos_online.js @@ -1,25 +1,25 @@ /* jslint node: true */ 'use strict'; -// ENiGMA½ -const MenuModule = require('./menu_module.js').MenuModule; -const ViewController = require('./view_controller.js').ViewController; -const getActiveNodeList = require('./client_connections.js').getActiveNodeList; -const stringFormat = require('./string_format.js'); +// ENiGMA½ +const MenuModule = require('./menu_module.js').MenuModule; +const ViewController = require('./view_controller.js').ViewController; +const getActiveNodeList = require('./client_connections.js').getActiveNodeList; +const stringFormat = require('./string_format.js'); -// deps -const async = require('async'); -const _ = require('lodash'); +// deps +const async = require('async'); +const _ = require('lodash'); exports.moduleInfo = { - name : 'Who\'s Online', - desc : 'Who is currently online', - author : 'NuSkooler', - packageName : 'codes.l33t.enigma.whosonline' + name : 'Who\'s Online', + desc : 'Who is currently online', + author : 'NuSkooler', + packageName : 'codes.l33t.enigma.whosonline' }; const MciViewIds = { - OnlineList : 1, + OnlineList : 1, }; exports.getModule = class WhosOnlineModule extends MenuModule { @@ -33,26 +33,26 @@ exports.getModule = class WhosOnlineModule extends MenuModule { return cb(err); } - const self = this; - const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); + const self = this; + const vc = self.viewControllers.allViews = new ViewController( { client : self.client } ); async.series( [ function loadFromConfig(callback) { const loadOpts = { - callingMenu : self, - mciMap : mciData.menu, - noInput : true, + callingMenu : self, + mciMap : mciData.menu, + noInput : true, }; return vc.loadFromMenuConfig(loadOpts, callback); }, function populateList(callback) { - const onlineListView = vc.getView(MciViewIds.OnlineList); - const listFormat = self.menuConfig.config.listFormat || '{node} - {userName} - {action} - {timeOn}'; - const nonAuthUser = self.menuConfig.config.nonAuthUser || 'Logging In'; - const otherUnknown = self.menuConfig.config.otherUnknown || 'N/A'; - const onlineList = getActiveNodeList(self.menuConfig.config.authUsersOnly).slice(0, onlineListView.height); + const onlineListView = vc.getView(MciViewIds.OnlineList); + const listFormat = self.menuConfig.config.listFormat || '{node} - {userName} - {action} - {timeOn}'; + const nonAuthUser = self.menuConfig.config.nonAuthUser || 'Logging In'; + const otherUnknown = self.menuConfig.config.otherUnknown || 'N/A'; + const onlineList = getActiveNodeList(self.menuConfig.config.authUsersOnly).slice(0, onlineListView.height); onlineListView.setItems(_.map(onlineList, oe => { if(oe.authenticated) { diff --git a/core/word_wrap.js b/core/word_wrap.js index a42dd2ea..94773283 100644 --- a/core/word_wrap.js +++ b/core/word_wrap.js @@ -1,15 +1,15 @@ /* jslint node: true */ 'use strict'; -const renderStringLength = require('./string_util.js').renderStringLength; +const renderStringLength = require('./string_util.js').renderStringLength; -// deps -const assert = require('assert'); -const _ = require('lodash'); +// deps +const assert = require('assert'); +const _ = require('lodash'); -exports.wordWrapText = wordWrapText; +exports.wordWrapText = wordWrapText; -const SPACE_CHARS = [ +const SPACE_CHARS = [ ' ', '\f', '\n', '\r', '\v', '​\u00a0', '\u1680', '​\u180e', '\u2000​', '\u2001', '\u2002', '​\u2003', '\u2004', '\u2005', '\u2006​', '\u2007', '\u2008​', '\u2009', '\u200a​', '\u2028', '\u2029​', @@ -22,25 +22,25 @@ function wordWrapText(text, options) { assert(_.isObject(options)); assert(_.isNumber(options.width)); - options.tabHandling = options.tabHandling || 'expand'; - options.tabWidth = options.tabWidth || 4; - options.tabChar = options.tabChar || ' '; + options.tabHandling = options.tabHandling || 'expand'; + options.tabWidth = options.tabWidth || 4; + options.tabChar = options.tabChar || ' '; //const REGEXP_GOBBLE = new RegExp(`.{0,${options.width}}`, 'g'); // - // For a given word, match 0->options.width chars -- alwasy include a full trailing ESC - // sequence if present! + // For a given word, match 0->options.width chars -- alwasy include a full trailing ESC + // sequence if present! // - // :TODO: Need to create ansi.getMatchRegex or something - this is used all over + // :TODO: Need to create ansi.getMatchRegex or something - this is used all over const REGEXP_GOBBLE = new RegExp(`.{0,${options.width}}\\x1b\\[[\\?=;0-9]*[ABCDEFGHJKLMSTfhlmnprsu]|.{0,${options.width}}`, 'g'); let m; let word; let c; let renderLen; - let i = 0; - let wordStart = 0; - let result = { wrapped : [ '' ], renderLen : [ 0 ] }; + let i = 0; + let wordStart = 0; + let result = { wrapped : [ '' ], renderLen : [ 0 ] }; function expandTab(column) { const remainWidth = options.tabWidth - (column % options.tabWidth); @@ -56,26 +56,26 @@ function wordWrapText(text, options) { result.firstWrapRange = { start : wordStart, end : wordStart + w.length }; } - result.wrapped[++i] = w; - result.renderLen[i] = renderLen; + result.wrapped[++i] = w; + result.renderLen[i] = renderLen; } else { - result.wrapped[i] += w; + result.wrapped[i] += w; result.renderLen[i] = (result.renderLen[i] || 0) + renderLen; } }); } // - // Some of the way we word wrap is modeled after Sublime Test 3: + // Some of the way we word wrap is modeled after Sublime Test 3: // - // * Sublime Text 3 for example considers spaces after a word - // part of said word. For example, "word " would be wraped - // in it's entirity. + // * Sublime Text 3 for example considers spaces after a word + // part of said word. For example, "word " would be wraped + // in it's entirity. // - // * Tabs in Sublime Text 3 are also treated as a word, so, e.g. - // "\t" may resolve to " " and must fit within the space. + // * Tabs in Sublime Text 3 are also treated as a word, so, e.g. + // "\t" may resolve to " " and must fit within the space. // - // * If a word is ultimately too long to fit, break it up until it does. + // * If a word is ultimately too long to fit, break it up until it does. // while(null !== (m = REGEXP_WORD_WRAP.exec(text))) { word = text.substring(wordStart, REGEXP_WORD_WRAP.lastIndex - 1); @@ -85,7 +85,7 @@ function wordWrapText(text, options) { word += m[0]; } else if('\t' === c) { if('expand' === options.tabHandling) { - // Good info here: http://c-for-dummies.com/blog/?p=424 + // Good info here: http://c-for-dummies.com/blog/?p=424 word += expandTab(result.wrapped[i].length + word.length) + options.tabChar; } else { word += m[0];