diff --git a/core/abracadabra.js b/core/abracadabra.js index 1ddec925..32c4466a 100644 --- a/core/abracadabra.js +++ b/core/abracadabra.js @@ -7,6 +7,7 @@ const Door = require('./door.js'); const theme = require('./theme.js'); const ansi = require('./ansi_term.js'); const Events = require('./events.js'); +const { Errors } = require('./enig_error.js'); const async = require('async'); const assert = require('assert'); @@ -98,7 +99,7 @@ exports.getModule = class AbracadabraModule extends MenuModule { if(_.isString(self.config.tooManyArt)) { theme.displayThemeArt( { client : self.client, name : self.config.tooManyArt }, function displayed() { self.pausePrompt( () => { - callback(new Error('Too many active instances')); + return callback(Errors.AccessDenied('Too many active instances')); }); }); } else { @@ -106,7 +107,7 @@ exports.getModule = class AbracadabraModule extends MenuModule { // :TODO: Use MenuModule.pausePrompt() self.pausePrompt( () => { - callback(new Error('Too many active instances')); + return callback(Errors.AccessDenied('Too many active instances')); }); } } else { diff --git a/core/art.js b/core/art.js index 315950e1..d709d366 100644 --- a/core/art.js +++ b/core/art.js @@ -2,11 +2,12 @@ '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'); +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'); +const { Errors } = require('./enig_error.js'); // deps const fs = require('graceful-fs'); @@ -209,7 +210,7 @@ function getArt(name, options, cb) { return getArtFromPath(readPath, options, cb); } - return cb(new Error(`No matching art for supplied criteria: ${name}`)); + return cb(Errors.DoesNotExist(`No matching art for supplied criteria: ${name}`)); }); } @@ -236,7 +237,7 @@ function display(client, art, options, cb) { } if(!art || !art.length) { - return cb(new Error('Empty art')); + return cb(Errors.Invalid('No art supplied!')); } options.mciReplaceChar = options.mciReplaceChar || ' '; diff --git a/core/bbs_link.js b/core/bbs_link.js index cbb7c036..71fa04c1 100644 --- a/core/bbs_link.js +++ b/core/bbs_link.js @@ -1,11 +1,12 @@ /* 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'); +const { resetScreen } = require('./ansi_term.js'); +const { Errors } = require('./enig_error.js'); +// deps const async = require('async'); -const _ = require('lodash'); const http = require('http'); const net = require('net'); const crypto = require('crypto'); @@ -60,15 +61,17 @@ exports.getModule = class BBSLinkModule extends MenuModule { async.series( [ function validateConfig(callback) { - if(_.isString(self.config.sysCode) && - _.isString(self.config.authCode) && - _.isString(self.config.schemeCode) && - _.isString(self.config.door)) - { - callback(null); - } else { - callback(new Error('Configuration is missing option(s)')); - } + return self.validateConfigFields( + { + host : 'string', + sysCode : 'string', + authCode : 'string', + schemeCode : 'string', + door : 'string', + port : 'number', + }, + callback + ); }, function acquireToken(callback) { // @@ -112,10 +115,9 @@ exports.getModule = class BBSLinkModule extends MenuModule { var status = body.trim(); if('complete' === status) { - callback(null); - } else { - callback(new Error('Bad authentication status: ' + status)); + return callback(null); } + return callback(Errors.AccessDenied(`Bad authentication status: ${status}`)); }); }, function createTelnetBridge(callback) { @@ -158,7 +160,7 @@ exports.getModule = class BBSLinkModule extends MenuModule { bridgeConnection.on('end', function connectionEnd() { restorePipe(); - callback(clientTerminated ? new Error('Client connection terminated') : null); + return callback(clientTerminated ? Errors.General('Client connection terminated') : null); }); bridgeConnection.on('error', function error(err) { diff --git a/core/combatnet.js b/core/combatnet.js index 1b53a153..abb9a889 100644 --- a/core/combatnet.js +++ b/core/combatnet.js @@ -2,12 +2,12 @@ 'use strict'; // enigma-bbs -const MenuModule = require('../core/menu_module.js').MenuModule; -const resetScreen = require('../core/ansi_term.js').resetScreen; +const { MenuModule } = require('../core/menu_module.js'); +const { resetScreen } = require('../core/ansi_term.js'); +const { Errors } = require('./enig_error.js'); // deps const async = require('async'); -const _ = require('lodash'); const RLogin = require('rlogin'); exports.moduleInfo = { @@ -32,13 +32,15 @@ exports.getModule = class CombatNetModule extends MenuModule { async.series( [ function validateConfig(callback) { - if(!_.isString(self.config.password)) { - return callback(new Error('Config requires "password"!')); - } - if(!_.isString(self.config.bbsTag)) { - return callback(new Error('Config requires "bbsTag"!')); - } - return callback(null); + return self.validateConfigFields( + { + host : 'string', + password : 'string', + bbsTag : 'string', + rloginPort : 'number', + }, + callback + ); }, function establishRloginConnection(callback) { self.client.term.write(resetScreen()); @@ -51,12 +53,13 @@ exports.getModule = class CombatNetModule extends MenuModule { }; const rlogin = new RLogin( - { 'clientUsername' : self.config.password, - 'serverUsername' : `${self.config.bbsTag}${self.client.user.username}`, - 'host' : self.config.host, - 'port' : self.config.rloginPort, - 'terminalType' : self.client.term.termClient, - 'terminalSpeed' : 57600 + { + clientUsername : self.config.password, + serverUsername : `${self.config.bbsTag}${self.client.user.username}`, + host : self.config.host, + port : self.config.rloginPort, + terminalType : self.client.term.termClient, + terminalSpeed : 57600 } ); @@ -88,7 +91,7 @@ exports.getModule = class CombatNetModule extends MenuModule { self.client.term.output.on('data', sendToRloginBuffer); } else { - return callback(new Error('Failed to establish establish CombatNet connection')); + return callback(Errors.General('Failed to establish establish CombatNet connection')); } } ); diff --git a/core/connect.js b/core/connect.js index 44277813..0b83f7f0 100644 --- a/core/connect.js +++ b/core/connect.js @@ -2,8 +2,9 @@ 'use strict'; // ENiGMA½ -const ansi = require('./ansi_term.js'); -const Events = require('./events.js'); +const ansi = require('./ansi_term.js'); +const Events = require('./events.js'); +const { Errors } = require('./enig_error.js'); // deps const async = require('async'); @@ -15,7 +16,7 @@ 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. + // our positioning to accommodate for such. // const done = function(err) { client.removeListener('cursor position report', cprListener); @@ -32,7 +33,7 @@ function ansiDiscoverHomePosition(client, cb) { // if(h > 1 || w > 1) { client.log.warn( { height : h, width : w }, 'Ignoring ANSI home position CPR due to unexpected values'); - return done(new Error('Home position CPR expected to be 0,0, or 1,1')); + return done(Errors.UnexpectedState('Home position CPR expected to be 0,0, or 1,1')); } if(0 === h & 0 === w) { @@ -49,7 +50,7 @@ function ansiDiscoverHomePosition(client, cb) { client.once('cursor position report', cprListener); const giveUpTimer = setTimeout( () => { - return done(new Error('Giving up on home position CPR')); + return done(Errors.General('Giving up on home position CPR')); }, 3000); // 3s client.term.write(`${ansi.goHome()}${ansi.queryPos()}`); // go home, query pos @@ -78,14 +79,14 @@ function ansiQueryTermSizeIfNeeded(client, cb) { const w = pos[1]; // - // Netrunner for example gives us 1x1 here. Not really useful. Ignore + // NetRunner for example gives us 1x1 here. Not really useful. Ignore // values that seem obviously bad. // if(h < 10 || w < 10) { client.log.warn( { height : h, width : w }, 'Ignoring ANSI CPR screen size query response due to very small values'); - return done(new Error('Term size <= 10 considered invalid')); + return done(Errors.Invalid('Term size <= 10 considered invalid')); } client.term.termHeight = h; @@ -107,7 +108,7 @@ function ansiQueryTermSizeIfNeeded(client, cb) { // give up after 2s const giveUpTimer = setTimeout( () => { - return done(new Error('No term size established by CPR within timeout')); + return done(Errors.General('No term size established by CPR within timeout')); }, 2000); // Start the process: Query for CPR @@ -116,8 +117,6 @@ function ansiQueryTermSizeIfNeeded(client, cb) { function prepareTerminal(term) { term.rawWrite(ansi.normal()); - //term.rawWrite(ansi.disableVT100LineWrapping()); - // :TODO: set xterm stuff -- see x84/others } function displayBanner(term) { diff --git a/core/door_party.js b/core/door_party.js index 63247f69..f6bc7be9 100644 --- a/core/door_party.js +++ b/core/door_party.js @@ -2,12 +2,12 @@ 'use strict'; // enigma-bbs -const MenuModule = require('../core/menu_module.js').MenuModule; -const resetScreen = require('../core/ansi_term.js').resetScreen; +const { MenuModule } = require('./menu_module.js'); +const { resetScreen } = require('./ansi_term.js'); +const { Errors } = require('./enig_error.js'); // deps const async = require('async'); -const _ = require('lodash'); const SSHClient = require('ssh2').Client; exports.moduleInfo = { @@ -34,16 +34,17 @@ exports.getModule = class DoorPartyModule extends MenuModule { async.series( [ function validateConfig(callback) { - if(!_.isString(self.config.username)) { - return callback(new Error('Config requires "username"!')); - } - if(!_.isString(self.config.password)) { - return callback(new Error('Config requires "password"!')); - } - if(!_.isString(self.config.bbsTag)) { - return callback(new Error('Config requires "bbsTag"!')); - } - return callback(null); + return self.validateConfigFields( + { + host : 'string', + username : 'string', + password : 'string', + bbsTag : 'string', + sshPort : 'number', + rloginPort : 'number', + }, + callback + ); }, function establishSecureConnection(callback) { self.client.term.write(resetScreen()); @@ -71,7 +72,7 @@ exports.getModule = class DoorPartyModule extends MenuModule { // 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')); + return callback(Errors.General('Failed to establish tunnel')); } // diff --git a/core/event_scheduler.js b/core/event_scheduler.js index 95de5a97..4b7da062 100644 --- a/core/event_scheduler.js +++ b/core/event_scheduler.js @@ -162,7 +162,7 @@ class ScheduledEvent { { eventName : this.name, action : this.action, exitCode : exitCode }, 'Bad exit code while performing scheduled event action'); } - return cb(exitCode ? new Error(`Bad exit code while performing scheduled event action: ${exitCode}`) : null); + return cb(exitCode ? Errors.ExternalProcess(`Bad exit code while performing scheduled event action: ${exitCode}`) : null); }); } } diff --git a/core/fnv1a.js b/core/fnv1a.js index 9acc8f27..b85e4241 100644 --- a/core/fnv1a.js +++ b/core/fnv1a.js @@ -1,7 +1,9 @@ /* jslint node: true */ 'use strict'; -let _ = require('lodash'); +const { Errors } = require('./enig_error.js'); + +const _ = require('lodash'); // FNV-1a based on work here: https://github.com/wiedi/node-fnv module.exports = class FNV1a { @@ -23,7 +25,7 @@ module.exports = class FNV1a { } if(!Buffer.isBuffer(data)) { - throw new Error('data must be String or Buffer!'); + throw Errors.Invalid('data must be String or Buffer!'); } for(let b of data) { diff --git a/core/listening_server.js b/core/listening_server.js index 00cf0a86..f28bea7f 100644 --- a/core/listening_server.js +++ b/core/listening_server.js @@ -3,6 +3,7 @@ // ENiGMA½ const logger = require('./logger.js'); +const { ErrorReasons } = require('./enig_error.js'); // deps const async = require('async'); @@ -30,9 +31,8 @@ function startListening(cb) { async.each( [ 'login', 'content' ], (category, next) => { moduleUtil.loadModulesForCategory(`${category}Servers`, (err, module) => { - // :TODO: use enig error here! if(err) { - if('EENIGMODDISABLED' === err.code) { + if(ErrorReasons.Disabled === err.reasonCode) { logger.log.debug(err.message); } else { logger.log.info( { err : err }, 'Failed loading module'); diff --git a/core/module_util.js b/core/module_util.js index 273ddaf0..0e8e5976 100644 --- a/core/module_util.js +++ b/core/module_util.js @@ -4,6 +4,10 @@ // ENiGMA½ const Config = require('./config.js').get; const Log = require('./logger.js').log; +const { + Errors, + ErrorReasons +} = require('./enig_error.js'); // deps const fs = require('graceful-fs'); @@ -28,9 +32,7 @@ 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'; - return cb(err); + return cb(Errors.AccessDenied(`Module "${options.name}" is disabled`, ErrorReasons.Disabled)); } // @@ -56,11 +58,11 @@ function loadModuleEx(options, cb) { } if(!_.isObject(mod.moduleInfo)) { - return cb(new Error('Module is missing "moduleInfo" section')); + return cb(Errors.Invalid(`No exported "moduleInfo" block for module ${modPath}!`)); } if(!_.isFunction(mod.getModule)) { - return cb(new Error('Invalid or missing "getModule" method for module!')); + return cb(Errors.Invalid(`No exported "getModule" method for module ${modPath}!`)); } return cb(null, mod); @@ -70,7 +72,7 @@ function loadModule(name, category, cb) { const path = Config().paths[category]; if(!_.isString(path)) { - return cb(new Error(`Not sure where to look for "${name}" of category "${category}"`)); + return cb(Errors.DoesNotExist(`Not sure where to look for module "${name}" of category "${category}"`)); } loadModuleEx( { name : name, path : path, category : category }, function loaded(err, mod) {