Merge branch 'master' of github.com:NuSkooler/enigma-bbs into 0.0.7-alpha

This commit is contained in:
Bryan Ashby 2017-06-11 20:22:10 -06:00
commit 2055c76f98
7 changed files with 111 additions and 41 deletions

View File

@ -11,6 +11,7 @@ const logger = require('./logger.js');
const database = require('./database.js'); const database = require('./database.js');
const clientConns = require('./client_connections.js'); const clientConns = require('./client_connections.js');
const resolvePath = require('./misc_util.js').resolvePath; const resolvePath = require('./misc_util.js').resolvePath;
const events = require('./events.js');
// deps // deps
const async = require('async'); const async = require('async');
@ -27,7 +28,7 @@ exports.main = main;
const initServices = {}; const initServices = {};
const ENIGMA_COPYRIGHT = 'ENiGMA½ Copyright (c) 2014-2017 Bryan Ashby'; const ENIGMA_COPYRIGHT = 'ENiGMA½ Copyright (c) 2014-2017 Bryan Ashby';
const HELP = const HELP =
`${ENIGMA_COPYRIGHT} `${ENIGMA_COPYRIGHT}
usage: main.js <args> usage: main.js <args>
@ -60,7 +61,7 @@ function main() {
conf.init(resolvePath(configPath), function configInit(err) { 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 // then it's a fatal error
// //
if(err) { if(err) {
@ -84,15 +85,17 @@ function main() {
} }
return callback(err); return callback(err);
}); });
}, }
], ],
function complete(err) { function complete(err) {
events.registerModules();
// note this is escaped: // note this is escaped:
fs.readFile(paths.join(__dirname, '../misc/startup_banner.asc'), 'utf8', (err, banner) => { fs.readFile(paths.join(__dirname, '../misc/startup_banner.asc'), 'utf8', (err, banner) => {
console.info(ENIGMA_COPYRIGHT); console.info(ENIGMA_COPYRIGHT);
if(!err) { if(!err) {
console.info(banner); console.info(banner);
} }
console.info('System started!'); console.info('System started!');
}); });
@ -140,7 +143,7 @@ function shutdownSystem() {
}, },
function stopMsgNetwork(callback) { function stopMsgNetwork(callback) {
require('./msg_network.js').shutdown(callback); require('./msg_network.js').shutdown(callback);
} }
], ],
() => { () => {
console.info('Goodbye!'); console.info('Goodbye!');
@ -173,9 +176,9 @@ function initialize(cb) {
process.on('SIGINT', shutdownSystem); process.on('SIGINT', shutdownSystem);
require('later').date.localTime(); // use local times for later.js/scheduling require('later').date.localTime(); // use local times for later.js/scheduling
return callback(null); return callback(null);
}, },
function initDatabases(callback) { function initDatabases(callback) {
return database.initializeDatabases(callback); return database.initializeDatabases(callback);
}, },
@ -195,7 +198,7 @@ function initialize(cb) {
// * Makes this accessible for MCI codes, easy non-blocking access, etc. // * 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 // * We do this every time as the op is free to change this information just
// like any other user // like any other user
// //
const User = require('./user.js'); const User = require('./user.js');
async.waterfall( async.waterfall(
@ -223,7 +226,7 @@ function initialize(cb) {
opProps.username = opUserName; opProps.username = opUserName;
_.each(opProps, (v, k) => { _.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); return require('./predefined_mci.js').init(callback);
}, },
function readyMessageNetworkSupport(callback) { function readyMessageNetworkSupport(callback) {
return require('./msg_network.js').startup(callback); return require('./msg_network.js').startup(callback);
}, },
function listenConnections(callback) { function listenConnections(callback) {
return require('./listening_server.js').startup(callback); return require('./listening_server.js').startup(callback);

View File

@ -3,6 +3,7 @@
// ENiGMA½ // ENiGMA½
const logger = require('./logger.js'); const logger = require('./logger.js');
const events = require('./events.js');
// deps // deps
const _ = require('lodash'); const _ = require('lodash');
@ -22,7 +23,7 @@ function getActiveConnections() { return clientConnections; }
function getActiveNodeList(authUsersOnly) { function getActiveNodeList(authUsersOnly) {
if(!_.isBoolean(authUsersOnly)) { if(!_.isBoolean(authUsersOnly)) {
authUsersOnly = true; authUsersOnly = true;
} }
const now = moment(); const now = moment();
@ -30,7 +31,7 @@ function getActiveNodeList(authUsersOnly) {
const activeConnections = getActiveConnections().filter(ac => { const activeConnections = getActiveConnections().filter(ac => {
return ((authUsersOnly && ac.user.isAuthenticated()) || !authUsersOnly); return ((authUsersOnly && ac.user.isAuthenticated()) || !authUsersOnly);
}); });
return _.map(activeConnections, ac => { return _.map(activeConnections, ac => {
const entry = { const entry = {
node : ac.node, node : ac.node,
@ -41,7 +42,7 @@ function getActiveNodeList(authUsersOnly) {
// //
// 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()) { if(ac.user.isAuthenticated()) {
entry.userName = ac.user.username; entry.userName = ac.user.username;
entry.realName = ac.user.properties.real_name; entry.realName = ac.user.properties.real_name;
@ -49,7 +50,7 @@ function getActiveNodeList(authUsersOnly) {
entry.affils = ac.user.properties.affiliation; entry.affils = ac.user.properties.affiliation;
const diff = now.diff(moment(ac.user.properties.last_login_timestamp), 'minutes'); 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; return entry;
}); });
@ -59,7 +60,7 @@ function addNewClient(client, clientSock) {
const id = client.session.id = clientConnections.push(client) - 1; const id = client.session.id = clientConnections.push(client) - 1;
const remoteAddress = client.remoteAddress = clientSock.remoteAddress; 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 // Note that this will be updated @ login with additional information
client.log = logger.log.child( { clientId : id } ); client.log = logger.log.child( { clientId : id } );
@ -76,6 +77,8 @@ function addNewClient(client, clientSock) {
client.log.info(connInfo, 'Client connected'); client.log.info(connInfo, 'Client connected');
events.emit('codes.l33t.enigma.system.connected', {'client': client});
return id; return id;
} }
@ -85,17 +88,19 @@ function removeClient(client) {
const i = clientConnections.indexOf(client); const i = clientConnections.indexOf(client);
if(i > -1) { if(i > -1) {
clientConnections.splice(i, 1); clientConnections.splice(i, 1);
logger.log.info( logger.log.info(
{ {
connectionCount : clientConnections.length, connectionCount : clientConnections.length,
clientId : client.session.id clientId : client.session.id
}, },
'Client disconnected' 'Client disconnected'
); );
events.emit('codes.l33t.enigma.system.disconnected', {'client': client});
} }
} }
function getConnectionByUserId(userId) { function getConnectionByUserId(userId) {
return getActiveConnections().find( ac => userId === ac.user.userId ); return getActiveConnections().find( ac => userId === ac.user.userId );
} }

View File

@ -3,6 +3,7 @@
// ENiGMA½ // ENiGMA½
const ansi = require('./ansi_term.js'); const ansi = require('./ansi_term.js');
const events = require('./events.js');
// deps // deps
const async = require('async'); const async = require('async');
@ -82,7 +83,7 @@ function ansiQueryTermSizeIfNeeded(client, cb) {
// //
if(h < 10 || w < 10) { if(h < 10 || w < 10) {
client.log.warn( client.log.warn(
{ height : h, width : w }, { height : h, width : w },
'Ignoring ANSI CPR screen size query response due to very small values'); 'Ignoring ANSI CPR screen size query response due to very small values');
return done(new Error('Term size <= 10 considered invalid')); return done(new Error('Term size <= 10 considered invalid'));
} }
@ -91,11 +92,11 @@ function ansiQueryTermSizeIfNeeded(client, cb) {
client.term.termWidth = w; client.term.termWidth = w;
client.log.debug( client.log.debug(
{ {
termWidth : client.term.termWidth, termWidth : client.term.termWidth,
termHeight : client.term.termHeight, termHeight : client.term.termHeight,
source : 'ANSI CPR' source : 'ANSI CPR'
}, },
'Window size updated' 'Window size updated'
); );
@ -109,7 +110,7 @@ function ansiQueryTermSizeIfNeeded(client, cb) {
return done(new Error('No term size established by CPR within timeout')); return done(new Error('No term size established by CPR within timeout'));
}, 2000); }, 2000);
// Start the process: Query for CPR // Start the process: Query for CPR
client.term.rawWrite(ansi.queryScreenSize()); client.term.rawWrite(ansi.queryScreenSize());
} }
@ -123,7 +124,7 @@ function displayBanner(term) {
// note: intentional formatting: // note: intentional formatting:
term.pipeWrite(` term.pipeWrite(`
|06Connected to |02EN|10i|02GMA|10½ |06BBS version |12|VN |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/ |06Updates & source |14- |12https://github.com/NuSkooler/enigma-bbs/
|00` |00`
); );
@ -153,11 +154,11 @@ function connectEntry(client, nextMenu) {
if(0 === term.termHeight || 0 === term.termWidth) { if(0 === term.termHeight || 0 === term.termWidth) {
// //
// We still don't have something good for term height/width. // 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!'); client.log.warn( { reason : err.message }, 'Failed to negotiate term size; Defaulting to 80x25!');
term.termHeight = 25; term.termHeight = 25;
term.termWidth = 80; term.termWidth = 80;
} }
@ -165,8 +166,8 @@ function connectEntry(client, nextMenu) {
return callback(null); return callback(null);
}); });
}, },
], ],
() => { () => {
prepareTerminal(term); prepareTerminal(term);
@ -175,6 +176,9 @@ function connectEntry(client, nextMenu) {
// //
displayBanner(term); displayBanner(term);
// fire event
events.emit('codes.l33t.enigma.system.term_detected', {'client': client});
setTimeout( () => { setTimeout( () => {
return client.menuStack.goto(nextMenu); return client.menuStack.goto(nextMenu);
}, 500); }, 500);

50
core/events.js Normal file
View File

@ -0,0 +1,50 @@
/* 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() {
const moduleUtil = require('./module_util.js');
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';
}
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");
}
});
});
}
}

View File

@ -15,6 +15,7 @@ const async = require('async');
exports.loadModuleEx = loadModuleEx; exports.loadModuleEx = loadModuleEx;
exports.loadModule = loadModule; exports.loadModule = loadModule;
exports.loadModulesForCategory = loadModulesForCategory; exports.loadModulesForCategory = loadModulesForCategory;
exports.getModulePaths = getModulePaths;
function loadModuleEx(options, cb) { function loadModuleEx(options, cb) {
assert(_.isObject(options)); assert(_.isObject(options));
@ -25,7 +26,7 @@ function loadModuleEx(options, cb) {
if(_.isObject(modConfig) && false === modConfig.enabled) { if(_.isObject(modConfig) && false === modConfig.enabled) {
const err = new Error(`Module "${options.name}" is disabled`); const err = new Error(`Module "${options.name}" is disabled`);
err.code = 'EENIGMODDISABLED'; err.code = 'EENIGMODDISABLED';
return cb(err); return cb(err);
} }
@ -36,7 +37,7 @@ function loadModuleEx(options, cb) {
// //
let mod; 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 { try {
mod = require(modPath); mod = require(modPath);
} catch(e) { } catch(e) {
if('MODULE_NOT_FOUND' === e.code) { if('MODULE_NOT_FOUND' === e.code) {
@ -48,7 +49,7 @@ function loadModuleEx(options, cb) {
} }
} else { } else {
return cb(e); return cb(e);
} }
} }
if(!_.isObject(mod.moduleInfo)) { if(!_.isObject(mod.moduleInfo)) {
@ -75,7 +76,7 @@ function loadModule(name, category, cb) {
} }
function loadModulesForCategory(category, iterator, complete) { function loadModulesForCategory(category, iterator, complete) {
fs.readdir(Config.paths[category], (err, files) => { fs.readdir(Config.paths[category], (err, files) => {
if(err) { if(err) {
return iterator(err); return iterator(err);
@ -97,3 +98,9 @@ function loadModulesForCategory(category, iterator, complete) {
}); });
}); });
} }
function getModulePaths() {
return [
Config.paths.mods
];
}

View File

@ -4,4 +4,4 @@
exports.PluginModule = PluginModule; exports.PluginModule = PluginModule;
function PluginModule(options) { function PluginModule(options) {
} }

View File

@ -41,7 +41,7 @@ function WebSocketClient(ws, req, serverType) {
} }
end() { end() {
return ws.terminate(); return ws.terminate();
} }
write(data, cb) { write(data, cb) {
@ -59,7 +59,8 @@ function WebSocketClient(ws, req, serverType) {
}); });
ws.on('close', () => { ws.on('close', () => {
this.end(); // we'll remove client connection which will in turn end() via our SocketBridge above
return this.emit('end');
}); });
// //