From d6f5429ce8bf7c428d473d5c0b6f4180bc1ebdee Mon Sep 17 00:00:00 2001 From: "Josh M. McKee" Date: Sat, 10 Jun 2017 23:08:16 +0000 Subject: [PATCH 1/7] add northern bloc hjson to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ee9fc3d8..00740686 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ logs/ db/ dropfiles/ -node_modules/ \ No newline at end of file +node_modules/ +mods/northern_bloc.hjson From 002b0e941edca907579934ebf902780fd574224f Mon Sep 17 00:00:00 2001 From: "Josh M. McKee" Date: Sat, 10 Jun 2017 17:29:00 -0700 Subject: [PATCH 2/7] Events system, first pass --- core/bbs.js | 25 ++++++++++-------- core/connect.js | 30 ++++++++++++--------- core/events.js | 47 +++++++++++++++++++++++++++++++++ core/plugin_module.js | 2 +- core/servers/login/websocket.js | 20 +++++++++----- 5 files changed, 92 insertions(+), 32 deletions(-) create mode 100644 core/events.js diff --git a/core/bbs.js b/core/bbs.js index ea351480..39b69767 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -11,6 +11,7 @@ const logger = require('./logger.js'); const database = require('./database.js'); const clientConns = require('./client_connections.js'); const resolvePath = require('./misc_util.js').resolvePath; +const events = require('./events.js'); // deps const async = require('async'); @@ -27,7 +28,7 @@ exports.main = main; const initServices = {}; const ENIGMA_COPYRIGHT = 'ENiGMA½ Copyright (c) 2014-2017 Bryan Ashby'; -const HELP = +const HELP = `${ENIGMA_COPYRIGHT} usage: main.js @@ -60,7 +61,7 @@ function main() { conf.init(resolvePath(configPath), function configInit(err) { // - // If the user supplied a path and we can't read/parse it + // If the user supplied a path and we can't read/parse it // then it's a fatal error // if(err) { @@ -84,15 +85,17 @@ function main() { } return callback(err); }); - }, + } ], function complete(err) { + events.registerModules(); + // note this is escaped: fs.readFile(paths.join(__dirname, '../misc/startup_banner.asc'), 'utf8', (err, banner) => { console.info(ENIGMA_COPYRIGHT); - if(!err) { + if(!err) { console.info(banner); - } + } console.info('System started!'); }); @@ -140,7 +143,7 @@ function shutdownSystem() { }, function stopMsgNetwork(callback) { require('./msg_network.js').shutdown(callback); - } + } ], () => { console.info('Goodbye!'); @@ -173,9 +176,9 @@ function initialize(cb) { process.on('SIGINT', shutdownSystem); require('later').date.localTime(); // use local times for later.js/scheduling - + return callback(null); - }, + }, function initDatabases(callback) { return database.initializeDatabases(callback); }, @@ -195,7 +198,7 @@ function initialize(cb) { // * Makes this accessible for MCI codes, easy non-blocking access, etc. // * We do this every time as the op is free to change this information just // like any other user - // + // const User = require('./user.js'); async.waterfall( @@ -223,7 +226,7 @@ function initialize(cb) { opProps.username = opUserName; _.each(opProps, (v, k) => { - StatLog.setNonPeristentSystemStat(`sysop_${k}`, v); + StatLog.setNonPeristentSystemStat(`sysop_${k}`, v); }); } @@ -235,7 +238,7 @@ function initialize(cb) { return require('./predefined_mci.js').init(callback); }, function readyMessageNetworkSupport(callback) { - return require('./msg_network.js').startup(callback); + return require('./msg_network.js').startup(callback); }, function listenConnections(callback) { return require('./listening_server.js').startup(callback); diff --git a/core/connect.js b/core/connect.js index ffd28a0c..21ab421a 100644 --- a/core/connect.js +++ b/core/connect.js @@ -3,6 +3,7 @@ // ENiGMA½ const ansi = require('./ansi_term.js'); +const events = require('./events.js'); // deps const async = require('async'); @@ -82,7 +83,7 @@ function ansiQueryTermSizeIfNeeded(client, cb) { // if(h < 10 || w < 10) { client.log.warn( - { height : h, width : w }, + { 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')); } @@ -91,11 +92,11 @@ function ansiQueryTermSizeIfNeeded(client, cb) { 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' ); @@ -109,7 +110,7 @@ function ansiQueryTermSizeIfNeeded(client, cb) { 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()); } @@ -123,7 +124,7 @@ function displayBanner(term) { // note: intentional formatting: term.pipeWrite(` |06Connected to |02EN|10i|02GMA|10½ |06BBS version |12|VN -|06Copyright (c) 2014-2017 Bryan Ashby |14- |12http://l33t.codes/ +|06Copyright (c) 2014-2017 Bryan Ashby |14- |12http://l33t.codes/ |06Updates & source |14- |12https://github.com/NuSkooler/enigma-bbs/ |00` ); @@ -153,11 +154,11 @@ function connectEntry(client, nextMenu) { if(0 === term.termHeight || 0 === term.termWidth) { // // We still don't have something good for term height/width. - // Default to DOS size 80x25. + // 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; } @@ -165,8 +166,8 @@ function connectEntry(client, nextMenu) { return callback(null); }); - }, - ], + }, + ], () => { prepareTerminal(term); @@ -175,6 +176,9 @@ function connectEntry(client, nextMenu) { // displayBanner(term); + // fire event + events.emit('codes.l33t.enigma.system.connect', {'client': client}); + setTimeout( () => { return client.menuStack.goto(nextMenu); }, 500); diff --git a/core/events.js b/core/events.js new file mode 100644 index 00000000..f558ed21 --- /dev/null +++ b/core/events.js @@ -0,0 +1,47 @@ +/* jslint node: true */ +'use strict'; + +const Config = require('./config.js'); +const fs = require('fs'); +const path = require('path'); +const events = require('events'); +const logger = require('./logger.js'); + +var eventEmitter = new events.EventEmitter(); + +var self = module.exports = { + emit: function(eventName, args) { + logger.log.debug("Emit "+eventName); + eventEmitter.emit(eventName, args); + }, + on: function(eventName, listener) { + logger.log.debug("Register listener for "+eventName); + eventEmitter.on(eventName, listener); + }, + remove: function(eventName, listener) { + logger.log.debug("Remove listener for "+eventName); + eventEmitter.removeListener(eventName, listener); + }, + registerModules: function() { + var mods = fs.readdirSync(Config.config.paths.mods); + + mods.forEach(function(item) { + var modPath = Config.config.paths.mods+item; + if (item.substr(item.length-3) != '.js') { + modPath += path.sep+item+'.js'; + } + if (fs.existsSync(modPath)) { + var module = require(modPath); + + if (module.registerEvents !== undefined) { + logger.log.debug(modPath+" calling registerEvents function"); + module.registerEvents(); + } else { + logger.log.debug(modPath+" has no registerEvents function"); + } + } else { + logger.log.debug(modPath+" - file not found"); + } + }); + } +} diff --git a/core/plugin_module.js b/core/plugin_module.js index aeac4950..31ba6f01 100644 --- a/core/plugin_module.js +++ b/core/plugin_module.js @@ -4,4 +4,4 @@ exports.PluginModule = PluginModule; function PluginModule(options) { -} \ No newline at end of file +} diff --git a/core/servers/login/websocket.js b/core/servers/login/websocket.js index 41a3e838..6f826cc9 100644 --- a/core/servers/login/websocket.js +++ b/core/servers/login/websocket.js @@ -25,9 +25,11 @@ const ModuleInfo = exports.moduleInfo = { function WebSocketClient(ws, req, serverType) { Object.defineProperty(this, 'isSecure', { - get : () => ('secure' === serverType || true === this.secureProxyConnection) ? true : false, + get : () => ('secure' === serverType || true === this.proxied) ? true : false, }); + const self = this; + // // This bridge makes accessible various calls that client sub classes // want to access on I/O socket @@ -39,7 +41,7 @@ function WebSocketClient(ws, req, serverType) { } end() { - return ws.terminate(); + return ws.terminate(); } write(data, cb) { @@ -47,7 +49,8 @@ function WebSocketClient(ws, req, serverType) { } get remoteAddress() { - return req.connection.remoteAddress; + // 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); @@ -56,7 +59,8 @@ function WebSocketClient(ws, req, serverType) { }); ws.on('close', () => { - this.end(); + // we'll remove client connection which will in turn end() via our SocketBridge above + return this.emit('end'); }); // @@ -75,11 +79,13 @@ function WebSocketClient(ws, req, serverType) { // If the config allows it, look for 'x-forwarded-proto' as "https" // to override |isSecure| // - if(true === _.get(Config, 'loginServers.webSocket.secureProxy') && + if(true === _.get(Config, 'loginServers.webSocket.proxied') && 'https' === req.headers['x-forwarded-proto']) { - Log.debug(`Assuming secure connection due to X-Forwarded-Proto of ${req.headers['x-forwarded-proto']}`); - this.secureProxyConnection = true; + Log.debug(`Assuming secure connection due to X-Forwarded-Proto of "${req.headers['x-forwarded-proto']}"`); + this.proxied = true; + } else { + this.proxied = false; } // start handshake process From 5572ca5d249ac27cfd89d4ebaf6f55040203a58c Mon Sep 17 00:00:00 2001 From: "Josh M. McKee" Date: Sat, 10 Jun 2017 17:30:27 -0700 Subject: [PATCH 3/7] ignore suprapi --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 00740686..a3349600 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ db/ dropfiles/ node_modules/ mods/northern_bloc.hjson +mods/suprapi/* +mods/suprapi From c45824b3ad40c252ce8d17111c90655d09619e2a Mon Sep 17 00:00:00 2001 From: "Josh M. McKee" Date: Sat, 10 Jun 2017 18:46:28 -0700 Subject: [PATCH 4/7] Rename connect event to term_detected, add connected and disconnected events --- core/client_connections.js | 25 +++++++++++++++---------- core/connect.js | 2 +- core/events.js | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/core/client_connections.js b/core/client_connections.js index 5cbeb3b9..471b1b79 100644 --- a/core/client_connections.js +++ b/core/client_connections.js @@ -3,6 +3,7 @@ // ENiGMA½ const logger = require('./logger.js'); +const events = require('./events.js'); // deps const _ = require('lodash'); @@ -22,7 +23,7 @@ function getActiveConnections() { return clientConnections; } function getActiveNodeList(authUsersOnly) { if(!_.isBoolean(authUsersOnly)) { - authUsersOnly = true; + authUsersOnly = true; } const now = moment(); @@ -30,7 +31,7 @@ function getActiveNodeList(authUsersOnly) { const activeConnections = getActiveConnections().filter(ac => { return ((authUsersOnly && ac.user.isAuthenticated()) || !authUsersOnly); }); - + return _.map(activeConnections, ac => { const entry = { node : ac.node, @@ -41,7 +42,7 @@ function getActiveNodeList(authUsersOnly) { // // 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; @@ -49,7 +50,7 @@ function getActiveNodeList(authUsersOnly) { entry.affils = ac.user.properties.affiliation; const diff = now.diff(moment(ac.user.properties.last_login_timestamp), 'minutes'); - entry.timeOn = moment.duration(diff, 'minutes'); + entry.timeOn = moment.duration(diff, 'minutes'); } return entry; }); @@ -59,7 +60,7 @@ function addNewClient(client, clientSock) { const id = client.session.id = clientConnections.push(client) - 1; const remoteAddress = client.remoteAddress = clientSock.remoteAddress; - // Create a client specific logger + // Create a client specific logger // Note that this will be updated @ login with additional information client.log = logger.log.child( { clientId : id } ); @@ -76,6 +77,8 @@ function addNewClient(client, clientSock) { client.log.info(connInfo, 'Client connected'); + events.emit('codes.l33t.enigma.system.connected', {'client': client}); + return id; } @@ -85,17 +88,19 @@ function removeClient(client) { const i = clientConnections.indexOf(client); if(i > -1) { clientConnections.splice(i, 1); - + logger.log.info( - { + { connectionCount : clientConnections.length, - clientId : client.session.id - }, + clientId : client.session.id + }, 'Client disconnected' ); + + events.emit('codes.l33t.enigma.system.disconnected', {'client': client}); } } function getConnectionByUserId(userId) { return getActiveConnections().find( ac => userId === ac.user.userId ); -} \ No newline at end of file +} diff --git a/core/connect.js b/core/connect.js index 21ab421a..0939906e 100644 --- a/core/connect.js +++ b/core/connect.js @@ -177,7 +177,7 @@ function connectEntry(client, nextMenu) { displayBanner(term); // fire event - events.emit('codes.l33t.enigma.system.connect', {'client': client}); + events.emit('codes.l33t.enigma.system.term_detected', {'client': client}); setTimeout( () => { return client.menuStack.goto(nextMenu); diff --git a/core/events.js b/core/events.js index f558ed21..1c3b28b9 100644 --- a/core/events.js +++ b/core/events.js @@ -32,7 +32,7 @@ var self = module.exports = { } if (fs.existsSync(modPath)) { var module = require(modPath); - + if (module.registerEvents !== undefined) { logger.log.debug(modPath+" calling registerEvents function"); module.registerEvents(); From 06e84eee94914cffbc1a04271b896f5ab5d59350 Mon Sep 17 00:00:00 2001 From: "Josh M. McKee" Date: Sat, 10 Jun 2017 18:52:11 -0700 Subject: [PATCH 5/7] Spaces to tabs, to match upstream convention --- .gitignore | 2 ++ core/bbs.js | 2 +- core/connect.js | 4 ++-- core/events.js | 18 +++++++++--------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index a3349600..0a42d92b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ node_modules/ mods/northern_bloc.hjson mods/suprapi/* mods/suprapi + +.editor-settings diff --git a/core/bbs.js b/core/bbs.js index 39b69767..4fb50c3a 100644 --- a/core/bbs.js +++ b/core/bbs.js @@ -88,7 +88,7 @@ function main() { } ], function complete(err) { - events.registerModules(); + events.registerModules(); // note this is escaped: fs.readFile(paths.join(__dirname, '../misc/startup_banner.asc'), 'utf8', (err, banner) => { diff --git a/core/connect.js b/core/connect.js index 0939906e..85273577 100644 --- a/core/connect.js +++ b/core/connect.js @@ -176,8 +176,8 @@ function connectEntry(client, nextMenu) { // displayBanner(term); - // fire event - events.emit('codes.l33t.enigma.system.term_detected', {'client': client}); + // fire event + events.emit('codes.l33t.enigma.system.term_detected', {'client': client}); setTimeout( () => { return client.menuStack.goto(nextMenu); diff --git a/core/events.js b/core/events.js index 1c3b28b9..73c9739b 100644 --- a/core/events.js +++ b/core/events.js @@ -2,10 +2,10 @@ 'use strict'; const Config = require('./config.js'); -const fs = require('fs'); -const path = require('path'); -const events = require('events'); -const logger = require('./logger.js'); +const fs = require('fs'); +const path = require('path'); +const events = require('events'); +const logger = require('./logger.js'); var eventEmitter = new events.EventEmitter(); @@ -18,10 +18,10 @@ var self = module.exports = { logger.log.debug("Register listener for "+eventName); eventEmitter.on(eventName, listener); }, - remove: function(eventName, listener) { - logger.log.debug("Remove listener for "+eventName); - eventEmitter.removeListener(eventName, listener); - }, + remove: function(eventName, listener) { + logger.log.debug("Remove listener for "+eventName); + eventEmitter.removeListener(eventName, listener); + }, registerModules: function() { var mods = fs.readdirSync(Config.config.paths.mods); @@ -32,7 +32,7 @@ var self = module.exports = { } if (fs.existsSync(modPath)) { var module = require(modPath); - + if (module.registerEvents !== undefined) { logger.log.debug(modPath+" calling registerEvents function"); module.registerEvents(); From b3839503147e3b92031b12b8ae8507327fc9fdd3 Mon Sep 17 00:00:00 2001 From: "Josh M. McKee" Date: Sun, 11 Jun 2017 19:44:34 -0700 Subject: [PATCH 6/7] Add getModulePaths to module_util, call it from events.registerModules --- core/events.js | 37 ++++++++++++++++++++----------------- core/module_util.js | 15 +++++++++++---- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/core/events.js b/core/events.js index 73c9739b..79b41429 100644 --- a/core/events.js +++ b/core/events.js @@ -23,25 +23,28 @@ var self = module.exports = { eventEmitter.removeListener(eventName, listener); }, registerModules: function() { - var mods = fs.readdirSync(Config.config.paths.mods); + const moduleUtil = require('./module_util.js'); - mods.forEach(function(item) { - var modPath = Config.config.paths.mods+item; - if (item.substr(item.length-3) != '.js') { - modPath += path.sep+item+'.js'; - } - if (fs.existsSync(modPath)) { - var module = require(modPath); - - if (module.registerEvents !== undefined) { - logger.log.debug(modPath+" calling registerEvents function"); - module.registerEvents(); - } else { - logger.log.debug(modPath+" has no registerEvents function"); + moduleUtil.getModulePaths().forEach(function(modulePath) { + var mods = fs.readdirSync(modulePath); + mods.forEach(function(item) { + var modPath = modulePath+item; + if (item.substr(item.length-3) != '.js') { + modPath += path.sep+item+'.js'; } - } else { - logger.log.debug(modPath+" - file not found"); - } + if (fs.existsSync(modPath)) { + var module = require(modPath); + + if (module.registerEvents !== undefined) { + logger.log.debug(modPath+" calling registerEvents function"); + module.registerEvents(); + } else { + logger.log.debug(modPath+" has no registerEvents function"); + } + } else { + logger.log.debug(modPath+" - file not found"); + } + }); }); } } diff --git a/core/module_util.js b/core/module_util.js index 1cd4bed3..194efca5 100644 --- a/core/module_util.js +++ b/core/module_util.js @@ -15,6 +15,7 @@ const async = require('async'); exports.loadModuleEx = loadModuleEx; exports.loadModule = loadModule; exports.loadModulesForCategory = loadModulesForCategory; +exports.getModulePaths = getModulePaths; function loadModuleEx(options, cb) { assert(_.isObject(options)); @@ -25,7 +26,7 @@ function loadModuleEx(options, cb) { if(_.isObject(modConfig) && false === modConfig.enabled) { const err = new Error(`Module "${options.name}" is disabled`); - err.code = 'EENIGMODDISABLED'; + err.code = 'EENIGMODDISABLED'; return cb(err); } @@ -36,7 +37,7 @@ function loadModuleEx(options, cb) { // let mod; let modPath = paths.join(options.path, `${options.name}.js`); // general case first - try { + try { mod = require(modPath); } catch(e) { if('MODULE_NOT_FOUND' === e.code) { @@ -48,7 +49,7 @@ function loadModuleEx(options, cb) { } } else { return cb(e); - } + } } if(!_.isObject(mod.moduleInfo)) { @@ -75,7 +76,7 @@ function loadModule(name, category, cb) { } function loadModulesForCategory(category, iterator, complete) { - + fs.readdir(Config.paths[category], (err, files) => { if(err) { return iterator(err); @@ -97,3 +98,9 @@ function loadModulesForCategory(category, iterator, complete) { }); }); } + +function getModulePaths() { + return [ + Config.paths.mods + ]; +} From 99e2efa7112df2c921c836a5236e74fd9cb06e4a Mon Sep 17 00:00:00 2001 From: "Josh M. McKee" Date: Sun, 11 Jun 2017 19:49:11 -0700 Subject: [PATCH 7/7] Revert local .gitignore to upstream --- .gitignore | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 0a42d92b..ee9fc3d8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,4 @@ logs/ db/ dropfiles/ -node_modules/ -mods/northern_bloc.hjson -mods/suprapi/* -mods/suprapi - -.editor-settings +node_modules/ \ No newline at end of file