From 1fe46894d36a7bb2c7d45d1cf39847277b6ca6bf Mon Sep 17 00:00:00 2001 From: Bryan Ashby Date: Wed, 20 Jun 2018 19:57:06 -0600 Subject: [PATCH] More Hot-Reload related changes * Config.get(): Returns the latest config * Update code all over the place to use Config.get() vs Config.conf (which will be deprecated) --- WHATSNEW.md | 3 +- core/archive_util.js | 22 ++++---- core/art.js | 4 +- core/asset.js | 4 +- core/client.js | 8 +-- core/config.js | 10 ++-- core/config_util.js | 13 ++--- core/dropfile.js | 8 +-- core/email.js | 9 ++-- core/enigma_assert.js | 4 +- core/event_scheduler.js | 7 +-- core/exodus.js | 4 +- core/file_area_list.js | 7 ++- core/file_area_web.js | 10 ++-- core/file_base_area.js | 43 ++++++++-------- core/file_base_list_export.js | 8 +-- core/file_base_web_download_manager.js | 7 +-- core/file_entry.js | 7 +-- core/file_transfer.js | 9 ++-- core/file_transfer_protocol_select.js | 4 +- core/fse.js | 4 +- core/ftn_util.js | 24 ++++----- core/logger.js | 2 +- core/menu_module.js | 4 +- core/menu_util.js | 8 +-- core/message_area.js | 32 +++++++----- core/module_util.js | 15 +++--- core/nua.js | 9 ++-- core/oputil/oputil_config.js | 14 ++--- core/oputil/oputil_file_base.js | 4 +- core/predefined_mci.js | 4 +- core/scanner_tossers/ftn_bso.js | 71 ++++++++++++++------------ core/servers/content/gopher.js | 30 ++++++----- core/servers/content/web.js | 44 ++++++++-------- core/servers/login/ssh.js | 27 +++++----- core/servers/login/telnet.js | 11 ++-- core/servers/login/websocket.js | 8 +-- core/show_art.js | 4 +- core/system_view_validate.js | 16 +++--- core/theme.js | 43 ++++++++-------- core/user.js | 11 ++-- core/web_password_reset.js | 17 +++--- 42 files changed, 320 insertions(+), 273 deletions(-) diff --git a/WHATSNEW.md b/WHATSNEW.md index 99c47786..27f9380d 100644 --- a/WHATSNEW.md +++ b/WHATSNEW.md @@ -10,7 +10,8 @@ This document attempts to track **major** changes and additions in ENiGMA½. For * Setting the `data` member of an object will cause form submissions to use this value instead of the selected items index. * See the default `luciano_blocktronics` `matrix` menu for example usage. * You can now set the `sort` property on a menu to sort items. If `true` items are sorted by `text`. If the value is a string, it represents the key in menu objects to sort by. - +* Hot-reload of configuration files such as menu.hjson, config.hjson, your themes.hjson, etc.: When a file is saved, it will be hot-reloaded into the running system + * Note that any custom modules should make use of the new Config.get() method. ## 0.0.8-alpha * [Mystic BBS style](http://wiki.mysticbbs.com/doku.php?id=displaycodes) extended pipe color codes. These allow for example, to set "iCE" background colors. diff --git a/core/archive_util.js b/core/archive_util.js index 71bad641..e4604b62 100644 --- a/core/archive_util.js +++ b/core/archive_util.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('./config.js').config; +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; @@ -61,10 +61,11 @@ module.exports = class ArchiveUtil { // // Load configuration // - if(_.has(Config, 'archives.archivers')) { - Object.keys(Config.archives.archivers).forEach(archKey => { + const config = Config(); + if(_.has(config, 'archives.archivers')) { + Object.keys(config.archives.archivers).forEach(archKey => { - const archConfig = Config.archives.archivers[archKey]; + const archConfig = config.archives.archivers[archKey]; const archiver = new Archiver(archConfig); if(!archiver.ok()) { @@ -75,7 +76,7 @@ module.exports = class ArchiveUtil { }); } - if(_.isObject(Config.fileTypes)) { + if(_.isObject(config.fileTypes)) { const updateSig = (ft) => { ft.sig = Buffer.from(ft.sig, 'hex'); ft.offset = ft.offset || 0; @@ -87,8 +88,8 @@ module.exports = class ArchiveUtil { } }; - Object.keys(Config.fileTypes).forEach(mimeType => { - const fileType = Config.fileTypes[mimeType]; + Object.keys(config.fileTypes).forEach(mimeType => { + const fileType = config.fileTypes[mimeType]; if(Array.isArray(fileType)) { fileType.forEach(ft => { if(ft.sig) { @@ -109,7 +110,8 @@ module.exports = class ArchiveUtil { return; } - let fileType = _.get(Config, [ 'fileTypes', mimeType ] ); + const config = Config(); + let fileType = _.get(config, [ 'fileTypes', mimeType ] ); if(Array.isArray(fileType)) { if(!justExtention) { @@ -125,7 +127,7 @@ module.exports = class ArchiveUtil { } if(fileType.archiveHandler) { - return _.get( Config, [ 'archives', 'archivers', fileType.archiveHandler ] ); + return _.get( config, [ 'archives', 'archivers', fileType.archiveHandler ] ); } } @@ -151,7 +153,7 @@ module.exports = class ArchiveUtil { return cb(err); } - const archFormat = _.findKey(Config.fileTypes, fileTypeInfo => { + const archFormat = _.findKey(Config().fileTypes, fileTypeInfo => { const fileTypeInfos = Array.isArray(fileTypeInfo) ? fileTypeInfo : [ fileTypeInfo ]; return fileTypeInfos.find(fti => { if(!fti.sig || !fti.archiveHandler) { diff --git a/core/art.js b/core/art.js index 9234542d..dc873836 100644 --- a/core/art.js +++ b/core/art.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('./config.js').config; +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'); @@ -126,7 +126,7 @@ 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.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 diff --git a/core/asset.js b/core/asset.js index 3f44a604..43c881c0 100644 --- a/core/asset.js +++ b/core/asset.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('./config.js').config; +const Config = require('./config.js').get; const StatLog = require('./stat_log.js'); // deps @@ -95,7 +95,7 @@ function resolveConfigAsset(spec) { assert('config' === asset.type); const path = asset.asset.split('.'); - let conf = Config; + let conf = Config(); for(let i = 0; i < path.length; ++i) { if(_.isUndefined(conf[path[i]])) { return spec; diff --git a/core/client.js b/core/client.js index d3692cd5..10409c70 100644 --- a/core/client.js +++ b/core/client.js @@ -35,7 +35,7 @@ const term = require('./client_term.js'); const ansi = require('./ansi_term.js'); const User = require('./user.js'); -const Config = require('./config.js').config; +const Config = require('./config.js').get; const MenuStack = require('./menu_stack.js'); const ACS = require('./acs.js'); const Events = require('./events.js'); @@ -400,7 +400,7 @@ function Client(/*input, output*/) { } if(key || ch) { - if(Config.logging.traceUserKeyboardInput) { + if(Config().logging.traceUserKeyboardInput) { self.log.trace( { key : key, ch : escape(ch) }, 'User keyboard input'); // jshint ignore:line } @@ -440,8 +440,8 @@ Client.prototype.startIdleMonitor = function() { const nowMs = Date.now(); const idleLogoutSeconds = this.user.isAuthenticated() ? - Config.misc.idleLogoutSeconds : - Config.misc.preAuthIdleLogoutSeconds; + Config().misc.idleLogoutSeconds : + Config().misc.preAuthIdleLogoutSeconds; if(nowMs - this.lastKeyPressMs >= (idleLogoutSeconds * 1000)) { this.emit('idle timeout'); diff --git a/core/config.js b/core/config.js index d8f1c873..38962b1f 100644 --- a/core/config.js +++ b/core/config.js @@ -13,6 +13,8 @@ const assert = require('assert'); exports.init = init; exports.getDefaultPath = getDefaultPath; +let currentConfiguration = {}; + function hasMessageConferenceAndArea(config) { assert(_.isObject(config.messageConferences)); // we create one ourself! @@ -67,12 +69,10 @@ function mergeValidateAndFinalize(config, cb) { return callback(null, mergedConfig); }, function setIt(mergedConfig, callback) { - exports.config = mergedConfig; - - exports.config.get = (path) => { - return _.get(exports.config, path); - }; + // :TODO: .config property is to be deprecated once conversions are done + exports.config = currentConfiguration = mergedConfig; + exports.get = () => currentConfiguration; return callback(null); } ], diff --git a/core/config_util.js b/core/config_util.js index 94ce4344..4b7ce5ed 100644 --- a/core/config_util.js +++ b/core/config_util.js @@ -1,7 +1,7 @@ /* jslint node: true */ 'use strict'; -const Config = require('./config.js').config; +const Config = require('./config.js').get; const ConfigCache = require('./config_cache.js'); const Events = require('./events.js'); @@ -15,7 +15,7 @@ exports.getFullConfig = getFullConfig; function getConfigPath(filePath) { // |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); + filePath = paths.join(Config().paths.config, filePath); } return filePath; } @@ -24,19 +24,20 @@ function init(cb) { // 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)) { + if(reCachedPath === getConfigPath(Config().general.menuFile)) { Events.emit(Events.getSystemEvents().MenusChanged); - } else if(reCachedPath === getConfigPath(Config.general.promptFile)) { + } else if(reCachedPath === getConfigPath(Config().general.promptFile)) { Events.emit(Events.getSystemEvents().PromptsChanged); } }; + const config = Config(); async.series( [ function menu(callback) { return ConfigCache.getConfigWithOptions( { - filePath : getConfigPath(Config.general.menuFile), + filePath : getConfigPath(config.general.menuFile), callback : changed, }, callback @@ -45,7 +46,7 @@ function init(cb) { function prompt(callback) { return ConfigCache.getConfigWithOptions( { - filePath : getConfigPath(Config.general.promptFile), + filePath : getConfigPath(config.general.promptFile), callback : changed, }, callback diff --git a/core/dropfile.js b/core/dropfile.js index 20c027e3..bdb3f3d1 100644 --- a/core/dropfile.js +++ b/core/dropfile.js @@ -1,7 +1,7 @@ /* jslint node: true */ 'use strict'; -var Config = require('./config.js').config; +var Config = require('./config.js').get; const StatLog = require('./stat_log.js'); var fs = require('graceful-fs'); @@ -29,7 +29,7 @@ function DropFile(client, fileType) { Object.defineProperty(this, 'fullPath', { get : function() { - return paths.join(Config.paths.dropFiles, ('node' + self.client.node), self.fileName); + return paths.join(Config().paths.dropFiles, ('node' + self.client.node), self.fileName); } }); @@ -157,7 +157,7 @@ function DropFile(client, fileType) { // :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, + Config().general.boardName, self.client.user.userId.toString(), self.client.user.properties.real_name || self.client.user.username, self.client.user.username, @@ -183,7 +183,7 @@ function DropFile(client, fileType) { var secLevel = self.client.user.getLegacySecurityLevel().toString(); return iconv.encode( [ - Config.general.boardName, // "The name of the system." + 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." diff --git a/core/email.js b/core/email.js index 0daf06b2..5cc66836 100644 --- a/core/email.js +++ b/core/email.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('./config.js').config; +const Config = require('./config.js').get; const Errors = require('./enig_error.js').Errors; const Log = require('./logger.js').log; @@ -13,13 +13,14 @@ const nodeMailer = require('nodemailer'); exports.sendMail = sendMail; function sendMail(message, cb) { - if(!_.has(Config, 'email.transport')) { + const config = Config(); + if(!_.has(config, 'email.transport')) { return cb(Errors.MissingConfig('Email "email::transport" configuration missing')); } - message.from = message.from || Config.email.defaultFrom; + message.from = message.from || config.email.defaultFrom; - const transportOptions = Object.assign( {}, Config.email.transport, { + const transportOptions = Object.assign( {}, config.email.transport, { logger : Log, }); diff --git a/core/enigma_assert.js b/core/enigma_assert.js index 9217ea49..2b72227a 100644 --- a/core/enigma_assert.js +++ b/core/enigma_assert.js @@ -2,14 +2,14 @@ 'use strict'; // ENiGMA½ -const Config = require('./config.js').config; +const Config = require('./config.js').get; const Log = require('./logger.js').log; // deps const assert = require('assert'); module.exports = function(condition, message) { - if(Config.debug.assertsEnabled) { + if(Config().debug.assertsEnabled) { assert.apply(this, arguments); } else if(!(condition)) { const stack = new Error().stack; diff --git a/core/event_scheduler.js b/core/event_scheduler.js index 10c59f72..e425d3bd 100644 --- a/core/event_scheduler.js +++ b/core/event_scheduler.js @@ -3,7 +3,7 @@ // ENiGMA½ const PluginModule = require('./plugin_module.js').PluginModule; -const Config = require('./config.js').config; +const Config = require('./config.js').get; const Log = require('./logger.js').log; const _ = require('lodash'); @@ -155,8 +155,9 @@ class ScheduledEvent { function EventSchedulerModule(options) { PluginModule.call(this, options); - if(_.has(Config, 'eventScheduler')) { - this.moduleConfig = Config.eventScheduler; + const config = Config(); + if(_.has(config, 'eventScheduler')) { + this.moduleConfig = config.eventScheduler; } const self = this; diff --git a/core/exodus.js b/core/exodus.js index 129671cb..409b5f2a 100644 --- a/core/exodus.js +++ b/core/exodus.js @@ -4,7 +4,7 @@ // ENiGMA½ const MenuModule = require('./menu_module.js').MenuModule; const resetScreen = require('./ansi_term.js').resetScreen; -const Config = require('./config.js').config; +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; @@ -66,7 +66,7 @@ exports.getModule = class ExodusModule extends MenuModule { 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.sshKeyPem = this.config.sshKeyPem || joinPath(Config().paths.misc, 'exodus.id_rsa'); } initSequence() { diff --git a/core/file_area_list.js b/core/file_area_list.js index b3d55e2c..ae03ee11 100644 --- a/core/file_area_list.js +++ b/core/file_area_list.js @@ -12,7 +12,7 @@ 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').config; +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'); @@ -255,7 +255,7 @@ exports.getModule = class FileAreaList extends MenuModule { const mimeType = resolveMimeType(entryInfo.archiveType); let desc; if(mimeType) { - let fileType = _.get(Config, [ 'fileTypes', mimeType ] ); + let fileType = _.get(Config(), [ 'fileTypes', mimeType ] ); if(Array.isArray(fileType)) { // further refine by extention @@ -264,7 +264,6 @@ exports.getModule = class FileAreaList extends MenuModule { desc = fileType && fileType.desc; } entryInfo.archiveTypeDesc = desc || mimeType || entryInfo.archiveType; - //entryInfo.archiveTypeDesc = mimeType ? _.get(Config, [ 'fileTypes', mimeType, 'desc' ] ) || mimeType : entryInfo.archiveType; } else { entryInfo.archiveTypeDesc = 'N/A'; } @@ -510,7 +509,7 @@ exports.getModule = class FileAreaList extends MenuModule { return callback(null); } - const expireTime = moment().add(Config.fileBase.web.expireMinutes, 'minutes'); + const expireTime = moment().add(Config().fileBase.web.expireMinutes, 'minutes'); FileAreaWeb.createAndServeTempDownload( self.client, diff --git a/core/file_area_web.js b/core/file_area_web.js index 928be942..94a2a664 100644 --- a/core/file_area_web.js +++ b/core/file_area_web.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('./config.js').config; +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'); @@ -31,7 +31,7 @@ function notEnabledError() { class FileAreaWebAccess { constructor() { - this.hashids = new hashids(Config.general.boardName); + this.hashids = new hashids(Config().general.boardName); this.expireTimers = {}; // hashId->timer } @@ -52,7 +52,7 @@ class FileAreaWebAccess { if(self.isEnabled()) { const routeAdded = self.webServer.instance.addRoute({ method : 'GET', - path : Config.fileBase.web.routePath, + path : Config().fileBase.web.routePath, handler : self.routeWebRequest.bind(self), }); return callback(routeAdded ? null : Errors.General('Failed adding route')); @@ -184,11 +184,11 @@ class FileAreaWebAccess { buildSingleFileTempDownloadLink(client, fileEntry, hashId) { hashId = hashId || this.getSingleFileHashId(client, fileEntry); - return this.webServer.instance.buildUrl(`${Config.fileBase.web.path}${hashId}`); + return this.webServer.instance.buildUrl(`${Config().fileBase.web.path}${hashId}`); } buildBatchArchiveTempDownloadLink(client, hashId) { - return this.webServer.instance.buildUrl(`${Config.fileBase.web.path}${hashId}`); + return this.webServer.instance.buildUrl(`${Config().fileBase.web.path}${hashId}`); } getExistingTempDownloadServeItem(client, fileEntry, cb) { diff --git a/core/file_base_area.js b/core/file_base_area.js index 3e0f9ff2..6400ed6f 100644 --- a/core/file_base_area.js +++ b/core/file_base_area.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('./config.js').config; +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'); @@ -66,7 +66,7 @@ function getAvailableFileAreas(client, options) { options = options || { }; // perform ACS check per conf & omit internal if desired - const allAreas = _.map(Config.fileBase.areas, (areaInfo, areaTag) => Object.assign(areaInfo, { areaTag : areaTag } )); + const allAreas = _.map(Config().fileBase.areas, (areaInfo, areaTag) => Object.assign(areaInfo, { areaTag : areaTag } )); return _.omitBy(allAreas, areaInfo => { if(!options.includeSystemInternal && isInternalArea(areaInfo.areaTag)) { @@ -96,16 +96,17 @@ function getSortedAvailableFileAreas(client, options) { } function getDefaultFileAreaTag(client, disableAcsCheck) { - let defaultArea = _.findKey(Config.fileBase, o => o.default); + const config = Config(); + let defaultArea = _.findKey(config.fileBase, o => o.default); if(defaultArea) { - const area = Config.fileBase.areas[defaultArea]; + const area = config.fileBase.areas[defaultArea]; if(true === disableAcsCheck || client.acs.hasFileAreaRead(area)) { return defaultArea; } } // just use anything we can - defaultArea = _.findKey(Config.fileBase.areas, (area, areaTag) => { + defaultArea = _.findKey(config.fileBase.areas, (area, areaTag) => { return WellKnownAreaTags.MessageAreaAttach !== areaTag && (true === disableAcsCheck || client.acs.hasFileAreaRead(area)); }); @@ -113,7 +114,7 @@ function getDefaultFileAreaTag(client, disableAcsCheck) { } function getFileAreaByTag(areaTag) { - const areaInfo = Config.fileBase.areas[areaTag]; + const areaInfo = Config().fileBase.areas[areaTag]; if(areaInfo) { areaInfo.areaTag = areaTag; // convienence! areaInfo.storage = getAreaStorageLocations(areaInfo); @@ -157,13 +158,14 @@ function changeFileAreaWithOptions(client, areaTag, options, cb) { } function isValidStorageTag(storageTag) { - return storageTag in Config.fileBase.storageTags; + return storageTag in Config().fileBase.storageTags; } function getAreaStorageDirectoryByTag(storageTag) { - const storageLocation = (storageTag && Config.fileBase.storageTags[storageTag]); + const config = Config(); + const storageLocation = (storageTag && config.fileBase.storageTags[storageTag]); - return paths.resolve(Config.fileBase.areaStoragePrefix, storageLocation || ''); + return paths.resolve(config.fileBase.areaStoragePrefix, storageLocation || ''); } function getAreaDefaultStorageDirectory(areaInfo) { @@ -176,7 +178,7 @@ function getAreaStorageLocations(areaInfo) { areaInfo.storageTags : [ areaInfo.storageTags || '' ]; - const avail = Config.fileBase.storageTags; + const avail = Config().fileBase.storageTags; return _.compact(storageTags.map(storageTag => { if(avail[storageTag]) { @@ -233,7 +235,7 @@ 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')); + const patterns = Config().fileBase.yearEstPatterns.map( p => new RegExp(p, 'gmi')); function getMatch(input) { if(input) { @@ -290,11 +292,11 @@ function extractAndProcessDescFiles(fileEntry, filePath, archiveEntries, cb) { function extractDescFiles(callback) { // :TODO: would be nice if these RegExp's were cached // :TODO: this is long winded... - + const config = Config(); const extractList = []; const shortDescFile = archiveEntries.find( e => { - return Config.fileBase.fileNamePatterns.desc.find( pat => new RegExp(pat, 'i').test(e.fileName) ); + return config.fileBase.fileNamePatterns.desc.find( pat => new RegExp(pat, 'i').test(e.fileName) ); }); if(shortDescFile) { @@ -302,7 +304,7 @@ function extractAndProcessDescFiles(fileEntry, filePath, archiveEntries, cb) { } const longDescFile = archiveEntries.find( e => { - return Config.fileBase.fileNamePatterns.descLong.find( pat => new RegExp(pat, 'i').test(e.fileName) ); + return config.fileBase.fileNamePatterns.descLong.find( pat => new RegExp(pat, 'i').test(e.fileName) ); }); if(longDescFile) { @@ -334,6 +336,7 @@ function extractAndProcessDescFiles(fileEntry, filePath, archiveEntries, cb) { }); }, function readDescFiles(descFiles, callback) { + const config = Config(); async.each(Object.keys(descFiles), (descType, next) => { const path = descFiles[descType]; if(!path) { @@ -346,10 +349,9 @@ function extractAndProcessDescFiles(fileEntry, filePath, archiveEntries, cb) { } // 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` ); + 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` ); return next(null); } @@ -488,7 +490,8 @@ function populateFileEntryWithArchive(fileEntry, filePath, stepInfo, iterator, c } function getInfoExtractUtilForDesc(mimeType, filePath, descType) { - let fileType = _.get(Config, [ 'fileTypes', mimeType ] ); + const config = Config(); + let fileType = _.get(config, [ 'fileTypes', mimeType ] ); if(Array.isArray(fileType)) { // further refine by extention @@ -504,7 +507,7 @@ function getInfoExtractUtilForDesc(mimeType, filePath, descType) { return; } - util = _.get(Config, [ 'infoExtractUtils', util ]); + util = _.get(config, [ 'infoExtractUtils', util ]); if(!util || !_.isString(util.cmd)) { return; } diff --git a/core/file_base_list_export.js b/core/file_base_list_export.js index 442946e3..8b45da83 100644 --- a/core/file_base_list_export.js +++ b/core/file_base_list_export.js @@ -5,7 +5,7 @@ const stringFormat = require('./string_format.js'); const FileEntry = require('./file_entry.js'); const FileArea = require('./file_base_area.js'); -const Config = require('./config.js').config; +const Config = require('./config.js').get; const { Errors } = require('./enig_error.js'); const { splitTextAtTerms, @@ -64,12 +64,14 @@ function exportFileList(filterCriteria, options, cb) { { name : options.headerTemplate, req : false }, { name : options.entryTemplate, req : true } ]; + + const config = Config(); async.map(templateFiles, (template, nextTemplate) => { if(!template.name && !template.req) { return nextTemplate(null, Buffer.from([])); } - template.name = paths.isAbsolute(template.name) ? template.name : paths.join(Config.paths.misc, template.name); + template.name = paths.isAbsolute(template.name) ? template.name : paths.join(config.paths.misc, template.name); fs.readFile(template.name, (err, data) => { return nextTemplate(err, data); }); @@ -221,7 +223,7 @@ function exportFileList(filterCriteria, options, cb) { const headerFormatObj = { nowTs : moment().format(options.tsFormat), - boardName : Config.general.boardName, + boardName : Config().general.boardName, totalFileCount : totals.fileCount, totalFileSize : totals.bytes, filterAreaTag : filterCriteria.areaTag || '-ALL-', diff --git a/core/file_base_web_download_manager.js b/core/file_base_web_download_manager.js index 13b7de33..f046de86 100644 --- a/core/file_base_web_download_manager.js +++ b/core/file_base_web_download_manager.js @@ -11,7 +11,7 @@ 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').config; +const Config = require('./config.js').get; // deps const async = require('async'); @@ -139,7 +139,7 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { } generateAndDisplayBatchLink(cb) { - const expireTime = moment().add(Config.fileBase.web.expireMinutes, 'minutes'); + const expireTime = moment().add(Config().fileBase.web.expireMinutes, 'minutes'); FileAreaWeb.createAndServeTempBatchDownload( this.client, @@ -183,6 +183,7 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { function prepareQueueDownloadLinks(callback) { const webDlExpireTimeFormat = self.menuConfig.config.webDlExpireTimeFormat || 'YYYY-MMM-DD @ h:mm'; + const config = Config(); async.each(self.dlQueue.items, (fileEntry, nextFileEntry) => { FileAreaWeb.getExistingTempDownloadServeItem(self.client, fileEntry, (err, serveItem) => { if(err) { @@ -190,7 +191,7 @@ exports.getModule = class FileBaseWebDownloadQueueManager extends MenuModule { return nextFileEntry(err); // we should have caught this prior } - const expireTime = moment().add(Config.fileBase.web.expireMinutes, 'minutes'); + const expireTime = moment().add(config.fileBase.web.expireMinutes, 'minutes'); FileAreaWeb.createAndServeTempDownload( self.client, diff --git a/core/file_entry.js b/core/file_entry.js index f6b86c64..1310d40a 100644 --- a/core/file_entry.js +++ b/core/file_entry.js @@ -7,7 +7,7 @@ const { getISOTimestampString, sanatizeString } = require('./database.js'); -const Config = require('./config.js').config; +const Config = require('./config.js').get; // deps const async = require('async'); @@ -202,7 +202,8 @@ module.exports = class FileEntry { } static getAreaStorageDirectoryByTag(storageTag) { - const storageLocation = (storageTag && Config.fileBase.storageTags[storageTag]); + const config = Config(); + const storageLocation = (storageTag && config.fileBase.storageTags[storageTag]); // absolute paths as-is if(storageLocation && '/' === storageLocation.charAt(0)) { @@ -210,7 +211,7 @@ module.exports = class FileEntry { } // relative to |areaStoragePrefix| - return paths.join(Config.fileBase.areaStoragePrefix, storageLocation || ''); + return paths.join(config.fileBase.areaStoragePrefix, storageLocation || ''); } get filePath() { diff --git a/core/file_transfer.js b/core/file_transfer.js index a7aee20c..456898c9 100644 --- a/core/file_transfer.js +++ b/core/file_transfer.js @@ -3,7 +3,7 @@ // enigma-bbs const MenuModule = require('./menu_module.js').MenuModule; -const Config = require('./config.js').config; +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'); @@ -56,9 +56,10 @@ exports.getModule = class TransferFileModule extends MenuModule { // // Most options can be set via extraArgs or config block // + const config = Config(); if(options.extraArgs) { if(options.extraArgs.protocol) { - this.protocolConfig = Config.fileTransferProtocols[options.extraArgs.protocol]; + this.protocolConfig = config.fileTransferProtocols[options.extraArgs.protocol]; } if(options.extraArgs.direction) { @@ -78,7 +79,7 @@ exports.getModule = class TransferFileModule extends MenuModule { } } else { if(this.config.protocol) { - this.protocolConfig = Config.fileTransferProtocols[this.config.protocol]; + this.protocolConfig = config.fileTransferProtocols[this.config.protocol]; } if(this.config.direction) { @@ -98,7 +99,7 @@ exports.getModule = class TransferFileModule extends MenuModule { } } - this.protocolConfig = this.protocolConfig || Config.fileTransferProtocols.zmodem8kSz; // try for *something* + this.protocolConfig = this.protocolConfig || config.fileTransferProtocols.zmodem8kSz; // try for *something* this.direction = this.direction || 'send'; this.sendQueue = this.sendQueue || []; diff --git a/core/file_transfer_protocol_select.js b/core/file_transfer_protocol_select.js index 299c8af2..3d3bd37b 100644 --- a/core/file_transfer_protocol_select.js +++ b/core/file_transfer_protocol_select.js @@ -3,7 +3,7 @@ // enigma-bbs const MenuModule = require('./menu_module.js').MenuModule; -const Config = require('./config.js').config; +const Config = require('./config.js').get; const stringFormat = require('./string_format.js'); const ViewController = require('./view_controller.js').ViewController; @@ -129,7 +129,7 @@ exports.getModule = class FileTransferProtocolSelectModule extends MenuModule { } loadAvailProtocols() { - this.protocols = _.map(Config.fileTransferProtocols, (protInfo, protocol) => { + this.protocols = _.map(Config().fileTransferProtocols, (protInfo, protocol) => { return { protocol : protocol, name : protInfo.name, diff --git a/core/fse.js b/core/fse.js index 70b1ecec..736f735a 100644 --- a/core/fse.js +++ b/core/fse.js @@ -14,7 +14,7 @@ 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').config; +const Config = require('./config.js').get; const { getAddressedToInfo } = require('./mail_util.js'); // deps @@ -335,7 +335,7 @@ exports.FullScreenEditorModule = exports.getModule = class FullScreenEditorModul // to packetAnsiMsgEncoding (generally cp437) as various boards // really don't like ANSI messages in UTF-8 encoding (they should!) // - msgOpts.meta = { System : { 'explicit_encoding' : Config.scannerTossers.ftn_bso.packetAnsiMsgEncoding || 'cp437' } }; + 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}`; } } diff --git a/core/ftn_util.js b/core/ftn_util.js index d1f69e0f..3fc51cd3 100644 --- a/core/ftn_util.js +++ b/core/ftn_util.js @@ -1,18 +1,17 @@ /* jslint node: true */ 'use strict'; -let Config = require('./config.js').config; -let Address = require('./ftn_address.js'); -let FNV1a = require('./fnv1a.js'); +const Config = require('./config.js').get; +const Address = require('./ftn_address.js'); +const FNV1a = require('./fnv1a.js'); const getCleanEnigmaVersion = require('./misc_util.js').getCleanEnigmaVersion; -let _ = require('lodash'); -let iconv = require('iconv-lite'); -let moment = require('moment'); -//let uuid = require('node-uuid'); -let os = require('os'); +const _ = require('lodash'); +const iconv = require('iconv-lite'); +const moment = require('moment'); +const os = require('os'); -let 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; @@ -199,9 +198,10 @@ function getQuotePrefix(name) { // http://ftsc.org/docs/fts-0004.001 // function getOrigin(address) { - const origin = _.has(Config, 'messageNetworks.originLine') ? - Config.messageNetworks.originLine : - Config.general.boardName; + const config = Config(); + const origin = _.has(config, 'messageNetworks.originLine') ? + config.messageNetworks.originLine : + config.general.boardName; const addrStr = new Address(address).toString('5D'); return ` * Origin: ${origin} (${addrStr})`; diff --git a/core/logger.js b/core/logger.js index 063757ff..c9a75faf 100644 --- a/core/logger.js +++ b/core/logger.js @@ -10,7 +10,7 @@ const _ = require('lodash'); module.exports = class Log { static init() { - const Config = require('./config.js').config; + const Config = require('./config.js').get(); const logPath = Config.paths.logs; const err = this.checkLogPath(logPath); diff --git a/core/menu_module.js b/core/menu_module.js index 97536bc7..20680354 100644 --- a/core/menu_module.js +++ b/core/menu_module.js @@ -6,7 +6,7 @@ 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').config; +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; @@ -29,7 +29,7 @@ exports.MenuModule = class MenuModule extends PluginModule { 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.cls = _.isBoolean(this.menuConfig.options.cls) ? this.menuConfig.options.cls : Config().menus.cls; this.viewControllers = {}; } diff --git a/core/menu_util.js b/core/menu_util.js index 4a4665b8..c6ad3a85 100644 --- a/core/menu_util.js +++ b/core/menu_util.js @@ -4,7 +4,7 @@ // ENiGMA½ var moduleUtil = require('./module_util.js'); var Log = require('./logger.js').log; -var Config = require('./config.js').config; +var Config = require('./config.js').get; var asset = require('./asset.js'); var MCIViewFactory = require('./mci_view_factory.js').MCIViewFactory; @@ -75,7 +75,7 @@ function loadMenu(options, cb) { const modLoadOpts = { name : modSupplied ? modAsset.asset : 'standard_menu', - path : (!modSupplied || 'systemModule' === modAsset.type) ? __dirname : Config.paths.mods, + path : (!modSupplied || 'systemModule' === modAsset.type) ? __dirname : Config().paths.mods, category : (!modSupplied || 'systemModule' === modAsset.type) ? null : 'mods', }; @@ -189,7 +189,7 @@ function handleAction(client, formData, conf, cb) { return callModuleMenuMethod( client, actionAsset, - paths.join(Config.paths.mods, actionAsset.location), + paths.join(Config().paths.mods, actionAsset.location), formData, conf.extraArgs, cb); @@ -234,7 +234,7 @@ function handleNext(client, nextSpec, conf, cb) { case 'method' : case 'systemMethod' : if(_.isString(nextAsset.location)) { - return callModuleMenuMethod(client, nextAsset, paths.join(Config.paths.mods, nextAsset.location), {}, extraArgs, cb); + 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 return callModuleMenuMethod(client, nextAsset, paths.join(__dirname, 'system_menu_method.js'), {}, extraArgs, cb); diff --git a/core/message_area.js b/core/message_area.js index 985da046..44a94c9a 100644 --- a/core/message_area.js +++ b/core/message_area.js @@ -3,7 +3,7 @@ // ENiGMA½ const msgDb = require('./database.js').dbs.message; -const Config = require('./config.js').config; +const Config = require('./config.js').get; const Message = require('./message.js'); const Log = require('./logger.js').log; const msgNetRecord = require('./msg_network.js').recordMessage; @@ -40,7 +40,7 @@ function getAvailableMessageConferences(client, options) { assert(client || true === options.noClient); // perform ACS check per conf & omit system_internal if desired - return _.omitBy(Config.messageConferences, (conf, confTag) => { + return _.omitBy(Config().messageConferences, (conf, confTag) => { if(!options.includeSystemInternal && 'system_internal' === confTag) { return true; } @@ -68,8 +68,9 @@ function getAvailableMessageAreasByConfTag(confTag, options) { // :TODO: confTag === "" then find default - if(_.has(Config.messageConferences, [ confTag, 'areas' ])) { - const areas = Config.messageConferences[confTag].areas; + const config = Config(); + if(_.has(config.messageConferences, [ confTag, 'areas' ])) { + const areas = config.messageConferences[confTag].areas; if(!options.client || true === options.noAcsCheck) { // everything - no ACS checks @@ -109,16 +110,17 @@ function getDefaultMessageConferenceTag(client, disableAcsCheck) { // // Note that built in 'system_internal' is always ommited here // - let defaultConf = _.findKey(Config.messageConferences, o => o.default); + const config = Config(); + let defaultConf = _.findKey(config.messageConferences, o => o.default); if(defaultConf) { - const conf = Config.messageConferences[defaultConf]; + const conf = config.messageConferences[defaultConf]; if(true === disableAcsCheck || client.acs.hasMessageConfRead(conf)) { return defaultConf; } } // just use anything we can - defaultConf = _.findKey(Config.messageConferences, (conf, confTag) => { + defaultConf = _.findKey(config.messageConferences, (conf, confTag) => { return 'system_internal' !== confTag && (true === disableAcsCheck || client.acs.hasMessageConfRead(conf)); }); @@ -135,8 +137,9 @@ function getDefaultMessageAreaTagByConfTag(client, confTag, disableAcsCheck) { // confTag = confTag || getDefaultMessageConferenceTag(client); - if(confTag && _.has(Config.messageConferences, [ confTag, 'areas' ])) { - const areaPool = Config.messageConferences[confTag].areas; + const config = Config(); + if(confTag && _.has(config.messageConferences, [ confTag, 'areas' ])) { + const areaPool = config.messageConferences[confTag].areas; let defaultArea = _.findKey(areaPool, o => o.default); if(defaultArea) { const area = areaPool[defaultArea]; @@ -154,18 +157,18 @@ function getDefaultMessageAreaTagByConfTag(client, confTag, disableAcsCheck) { } function getMessageConferenceByTag(confTag) { - return Config.messageConferences[confTag]; + return Config().messageConferences[confTag]; } function getMessageConfTagByAreaTag(areaTag) { - const confs = Config.messageConferences; + const confs = Config().messageConferences; return Object.keys(confs).find( (confTag) => { return _.has(confs, [ confTag, 'areas', areaTag]); }); } function getMessageAreaByTag(areaTag, optionalConfTag) { - const confs = Config.messageConferences; + const confs = Config().messageConferences; // :TODO: this could be cached if(_.isString(optionalConfTag)) { @@ -535,10 +538,11 @@ function trimMessageAreasScheduledEvent(args, cb) { let areaInfos = []; // determine maxMessages & maxAgeDays per area + const config = Config(); areaTags.forEach(areaTag => { - let maxMessages = Config.messageAreaDefaults.maxMessages; - let maxAgeDays = Config.messageAreaDefaults.maxAgeDays; + let maxMessages = config.messageAreaDefaults.maxMessages; + let maxAgeDays = config.messageAreaDefaults.maxAgeDays; const area = getMessageAreaByTag(areaTag); // note: we don't know the conf here if(area) { diff --git a/core/module_util.js b/core/module_util.js index 67e87306..3bfd88d2 100644 --- a/core/module_util.js +++ b/core/module_util.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('./config.js').config; +const Config = require('./config.js').get; // deps const fs = require('graceful-fs'); @@ -64,7 +64,7 @@ function loadModuleEx(options, cb) { } function loadModule(name, category, cb) { - const path = Config.paths[category]; + const path = Config().paths[category]; if(!_.isString(path)) { return cb(new Error(`Not sure where to look for "${name}" of category "${category}"`)); @@ -77,7 +77,7 @@ function loadModule(name, category, cb) { function loadModulesForCategory(category, iterator, complete) { - fs.readdir(Config.paths[category], (err, files) => { + fs.readdir(Config().paths[category], (err, files) => { if(err) { return iterator(err); } @@ -100,10 +100,11 @@ function loadModulesForCategory(category, iterator, complete) { } function getModulePaths() { + const config = Config(); return [ - Config.paths.mods, - Config.paths.loginServers, - Config.paths.contentServers, - Config.paths.scannerTossers, + config.paths.mods, + config.paths.loginServers, + config.paths.contentServers, + config.paths.scannerTossers, ]; } diff --git a/core/nua.js b/core/nua.js index 61dbeb62..cb7e16a7 100644 --- a/core/nua.js +++ b/core/nua.js @@ -6,7 +6,7 @@ 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').config; +const Config = require('./config.js').get; const messageArea = require('./message_area.js'); exports.moduleInfo = { @@ -62,6 +62,7 @@ exports.getModule = class NewUserAppModule extends MenuModule { // submitApplication : function(formData, extraArgs, cb) { const newUser = new User(); + const config = Config(); newUser.username = formData.value.username; @@ -95,10 +96,10 @@ exports.getModule = class NewUserAppModule extends MenuModule { // :TODO: should probably have a place to create defaults/etc. }; - if('*' === Config.defaults.theme) { + if('*' === config.defaults.theme) { newUser.properties.theme_id = theme.getRandomTheme(); } else { - newUser.properties.theme_id = Config.defaults.theme; + newUser.properties.theme_id = config.defaults.theme; } // :TODO: User.create() should validate email uniqueness! @@ -118,7 +119,7 @@ exports.getModule = class NewUserAppModule extends MenuModule { // Cache SysOp information now // :TODO: Similar to bbs.js. DRY if(newUser.isSysOp()) { - Config.general.sysOp = { + config.general.sysOp = { username : formData.value.username, properties : newUser.properties, }; diff --git a/core/oputil/oputil_config.js b/core/oputil/oputil_config.js index 4d1eb54b..3edfc1f3 100644 --- a/core/oputil/oputil_config.js +++ b/core/oputil/oputil_config.js @@ -333,7 +333,7 @@ function importAreas() { }, function validateAndCollectInput(callback) { const msgArea = require('../../core/message_area.js'); - const Config = require('../../core/config.js').config; + const sysConfig = require('../../core/config.js').get(); let msgConfs = msgArea.getSortedAvailMessageConferences(null, { noClient : true } ); if(!msgConfs) { @@ -355,8 +355,8 @@ function importAreas() { } let existingNetworkNames = []; - if(_.has(Config, 'messageNetworks.ftn.networks')) { - existingNetworkNames = Object.keys(Config.messageNetworks.ftn.networks); + if(_.has(sysConfig, 'messageNetworks.ftn.networks')) { + existingNetworkNames = Object.keys(sysConfig.messageNetworks.ftn.networks); } if(0 === existingNetworkNames.length) { @@ -366,7 +366,7 @@ function importAreas() { if(networkName && !existingNetworkNames.find(net => networkName === net)) { return callback(Errors.DoesNotExist(`FTN style Network "${networkName}" does not exist`)); } - + getAnswers([ { name : 'confTag', @@ -407,13 +407,13 @@ function importAreas() { }); }, function confirmWithUser(callback) { - const Config = require('../../core/config.js').config; + const sysConfig = require('../../core/config.js').get(); - console.info(`Importing the following for "${confTag}" - (${Config.messageConferences[confTag].name} - ${Config.messageConferences[confTag].desc})`); + console.info(`Importing the following for "${confTag}" - (${sysConfig.messageConferences[confTag].name} - ${sysConfig.messageConferences[confTag].desc})`); importEntries.forEach(ie => { console.info(` ${ie.ftnTag} - ${ie.name}`); }); - + console.info(''); console.info('Importing will NOT create required FTN network configurations.'); console.info('If you have not yet done this, you will need to complete additional steps after importing.'); diff --git a/core/oputil/oputil_file_base.js b/core/oputil/oputil_file_base.js index 68f190fb..b9ae8aeb 100644 --- a/core/oputil/oputil_file_base.js +++ b/core/oputil/oputil_file_base.js @@ -390,10 +390,10 @@ function displayFileAreaInfo() { return initConfigAndDatabases(callback); }, function dumpInfo(callback) { - const Config = require('../../core/config.js').config; + const sysConfig = require('../../core/config.js').get(); let suppliedAreas = argv._.slice(2); if(!suppliedAreas || 0 === suppliedAreas.length) { - suppliedAreas = _.map(Config.fileBase.areas, (areaInfo, areaTag) => areaTag); + suppliedAreas = _.map(sysConfig.fileBase.areas, (areaInfo, areaTag) => areaTag); } const areaAndStorageInfo = getAreaAndStorage(suppliedAreas); diff --git a/core/predefined_mci.js b/core/predefined_mci.js index 47370a66..c1f7e9fb 100644 --- a/core/predefined_mci.js +++ b/core/predefined_mci.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('./config.js').config; +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; @@ -56,7 +56,7 @@ const PREDEFINED_MCI_GENERATORS = { // // Board // - BN : function boardName() { return Config.general.boardName; }, + BN : function boardName() { return Config().general.boardName; }, // ENiGMA VL : function versionLabel() { return 'ENiGMA½ v' + packageJson.version; }, diff --git a/core/scanner_tossers/ftn_bso.js b/core/scanner_tossers/ftn_bso.js index a98c6f4d..2d978852 100644 --- a/core/scanner_tossers/ftn_bso.js +++ b/core/scanner_tossers/ftn_bso.js @@ -3,7 +3,7 @@ // ENiGMA½ const MessageScanTossModule = require('../msg_scan_toss_module.js').MessageScanTossModule; -const Config = require('../config.js').config; +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'); @@ -60,8 +60,9 @@ function FTNMessageScanTossModule() { this.archUtil = ArchiveUtil.getInstance(); - if(_.has(Config, 'scannerTossers.ftn_bso')) { - this.moduleConfig = Config.scannerTossers.ftn_bso; + const config = Config(); + if(_.has(config, 'scannerTossers.ftn_bso')) { + this.moduleConfig = config.scannerTossers.ftn_bso; } this.getDefaultNetworkName = function() { @@ -69,19 +70,20 @@ function FTNMessageScanTossModule() { return this.moduleConfig.defaultNetwork.toLowerCase(); } - const networkNames = Object.keys(Config.messageNetworks.ftn.networks); + const networkNames = Object.keys(config.messageNetworks.ftn.networks); if(1 === networkNames.length) { return networkNames[0].toLowerCase(); } }; this.getDefaultZone = function(networkName) { - if(_.isNumber(Config.messageNetworks.ftn.networks[networkName].defaultZone)) { - return Config.messageNetworks.ftn.networks[networkName].defaultZone; + const config = Config(); + if(_.isNumber(config.messageNetworks.ftn.networks[networkName].defaultZone)) { + return config.messageNetworks.ftn.networks[networkName].defaultZone; } // non-explicit: default to local address zone - const networkLocalAddress = Config.messageNetworks.ftn.networks[networkName].localAddress; + const networkLocalAddress = config.messageNetworks.ftn.networks[networkName].localAddress; if(networkLocalAddress) { const addr = Address.fromString(networkLocalAddress); return addr.zone; @@ -96,14 +98,14 @@ function FTNMessageScanTossModule() { */ this.getNetworkNameByAddress = function(remoteAddress) { - return _.findKey(Config.messageNetworks.ftn.networks, network => { + return _.findKey(Config().messageNetworks.ftn.networks, network => { const localAddress = Address.fromString(network.localAddress); return !_.isUndefined(localAddress) && localAddress.isEqual(remoteAddress); }); }; this.getNetworkNameByAddressPattern = function(remoteAddressPattern) { - return _.findKey(Config.messageNetworks.ftn.networks, network => { + return _.findKey(Config().messageNetworks.ftn.networks, network => { const localAddress = Address.fromString(network.localAddress); return !_.isUndefined(localAddress) && localAddress.isPatternMatch(remoteAddressPattern); }); @@ -111,7 +113,7 @@ function FTNMessageScanTossModule() { this.getLocalAreaTagByFtnAreaTag = function(ftnAreaTag) { ftnAreaTag = ftnAreaTag.toUpperCase(); // always compare upper - return _.findKey(Config.messageNetworks.ftn.areas, areaConf => { + return _.findKey(Config().messageNetworks.ftn.areas, areaConf => { return areaConf.tag.toUpperCase() === ftnAreaTag; }); }; @@ -357,6 +359,7 @@ function FTNMessageScanTossModule() { 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 @@ -405,14 +408,14 @@ function FTNMessageScanTossModule() { // // EchoMail requires some additional properties & kludges // - message.meta.FtnProperty.ftn_area = Config.messageNetworks.ftn.areas[message.areaTag].tag; + 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. // 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); @@ -453,7 +456,7 @@ function FTNMessageScanTossModule() { // 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'; + let encoding = options.nodeConfig.encoding || config.scannerTossers.ftn_bso.packetMsgEncoding || 'utf8'; const explicitEncoding = _.get(message.meta, 'System.explicit_encoding'); if(explicitEncoding) { encoding = explicitEncoding; @@ -513,7 +516,7 @@ function FTNMessageScanTossModule() { this.hasValidConfiguration = function() { - if(!_.has(this, 'moduleConfig.nodes') || !_.has(Config, 'messageNetworks.ftn.areas')) { + if(!_.has(this, 'moduleConfig.nodes') || !_.has(Config(), 'messageNetworks.ftn.areas')) { return false; } @@ -820,7 +823,7 @@ function FTNMessageScanTossModule() { // // Route full|wildcard -> full adddress/network lookup // - const routes = _.get(Config, 'scannerTossers.ftn_bso.netMail.routes'); + const routes = _.get(Config(), 'scannerTossers.ftn_bso.netMail.routes'); if(!routes) { return; } @@ -860,7 +863,7 @@ function FTNMessageScanTossModule() { 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. return cb( @@ -899,7 +902,7 @@ function FTNMessageScanTossModule() { exportOpts.destAddress = dstAddr; exportOpts.routeAddress = routeInfo.routeAddress; exportOpts.fileCase = routeInfo.config.fileCase || 'lower'; - exportOpts.network = Config.messageNetworks.ftn.networks[routeInfo.networkName]; + 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); @@ -966,6 +969,7 @@ function FTNMessageScanTossModule() { }; this.exportEchoMailMessagesToUplinks = function(messageUuids, areaConfig, cb) { + const config = Config(); async.each(areaConfig.uplinks, (uplink, nextUplink) => { const nodeConfig = self.getNodeConfigByAddress(uplink); if(!nodeConfig) { @@ -974,7 +978,7 @@ function FTNMessageScanTossModule() { const exportOpts = { nodeConfig, - network : Config.messageNetworks.ftn.networks[areaConfig.network], + network : config.messageNetworks.ftn.networks[areaConfig.network], destAddress : Address.fromString(uplink), networkName : areaConfig.network, fileCase : nodeConfig.fileCase || 'lower', @@ -1119,7 +1123,7 @@ function FTNMessageScanTossModule() { this.getLocalUserNameFromAlias = function(lookup) { lookup = lookup.toLowerCase(); - const aliases = _.get(Config, 'messageNetworks.ftn.netMail.aliases'); + const aliases = _.get(Config(), 'messageNetworks.ftn.netMail.aliases'); if(!aliases) { return lookup; // keep orig } @@ -1195,7 +1199,7 @@ function FTNMessageScanTossModule() { // 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)) { + if(true === _.get(Config(), [ 'messageNetworks', 'ftn', 'areas', config.localAreaTag, 'allowDupes' ], false)) { // just generate a UUID & therefor always allow for dupes message.uuid = uuidV4(); } @@ -1650,7 +1654,8 @@ function FTNMessageScanTossModule() { }; this.getLocalAreaTagsForTic = function() { - return _.union(Object.keys(Config.scannerTossers.ftn_bso.ticAreas || {} ), Object.keys(Config.fileBase.areas)); + const config = Config(); + return _.union(Object.keys(config.scannerTossers.ftn_bso.ticAreas || {} ), Object.keys(config.fileBase.areas)); }; this.processSingleTicFile = function(ticFileInfo, cb) { @@ -1659,9 +1664,10 @@ function FTNMessageScanTossModule() { async.waterfall( [ function generalValidation(callback) { + const sysConfig = Config(); const config = { - nodes : Config.scannerTossers.ftn_bso.nodes, - defaultPassword : Config.scannerTossers.ftn_bso.tic.password, + nodes : sysConfig.scannerTossers.ftn_bso.nodes, + defaultPassword : sysConfig.scannerTossers.ftn_bso.tic.password, localAreaTags : self.getLocalAreaTagsForTic(), }; @@ -1672,7 +1678,7 @@ function FTNMessageScanTossModule() { } // 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 ]); + const mappedLocalAreaTag = _.get(Config().scannerTossers.ftn_bso, [ 'ticAreas', localInfo.areaTag ]); if(mappedLocalAreaTag) { if(_.isString(mappedLocalAreaTag.areaTag)) { @@ -1699,7 +1705,7 @@ function FTNMessageScanTossModule() { // 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 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) { @@ -1755,7 +1761,7 @@ function FTNMessageScanTossModule() { 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), + upload_by_username : _.get(Config().scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'uploadBy' ], Config().scannerTossers.ftn_bso.tic.uploadBy), } }; @@ -1769,7 +1775,7 @@ function FTNMessageScanTossModule() { // const hashTags = localInfo.hashTags || - _.get(Config.scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'hashTags' ] ); // catch-all*/ + _.get(Config().scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'hashTags' ] ); // catch-all*/ if(hashTags) { scanOpts.hashTags = new Set(hashTags.split(/[\s,]+/)); @@ -1817,8 +1823,8 @@ function FTNMessageScanTossModule() { // We will still fallback as needed from -> -> // const descPriority = _.get( - Config.scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'descPriority' ], - Config.scannerTossers.ftn_bso.tic.descPriority + Config().scannerTossers.ftn_bso.nodes, [ localInfo.node, 'tic', 'descPriority' ], + Config().scannerTossers.ftn_bso.tic.descPriority ); if('tic' === descPriority) { @@ -1926,11 +1932,12 @@ function FTNMessageScanTossModule() { ; // we shouldn't, but be sure we don't try to pick up private mail here - const areaTags = Object.keys(Config.messageNetworks.ftn.areas) + const config = Config(); + const areaTags = Object.keys(config.messageNetworks.ftn.areas) .filter(areaTag => Message.WellKnownAreaTags.Private !== areaTag); async.each(areaTags, (areaTag, nextArea) => { - const areaConfig = Config.messageNetworks.ftn.areas[areaTag]; + const areaConfig = config.messageNetworks.ftn.areas[areaTag]; if(!this.isAreaConfigValid(areaConfig)) { return nextArea(); } @@ -2336,7 +2343,7 @@ FTNMessageScanTossModule.prototype.record = function(message) { } else if(message.areaTag) { Object.assign(info, { type : 'EchoMail' } ); - const areaConfig = Config.messageNetworks.ftn.areas[message.areaTag]; + const areaConfig = Config().messageNetworks.ftn.areas[message.areaTag]; if(!this.isAreaConfigValid(areaConfig)) { return; } diff --git a/core/servers/content/gopher.js b/core/servers/content/gopher.js index 5e0ee42f..d613052a 100644 --- a/core/servers/content/gopher.js +++ b/core/servers/content/gopher.js @@ -4,7 +4,7 @@ // ENiGMA½ const Log = require('../../logger.js').log; const { ServerModule } = require('../../server_module.js'); -const Config = require('../../config.js').config; +const Config = require('../../config.js').get; const { splitTextAtTerms, isAnsi, @@ -73,8 +73,9 @@ exports.getModule = class GopherModule extends ServerModule { return; } - this.publicHostname = Config.contentServers.gopher.publicHostname; - this.publicPort = Config.contentServers.gopher.publicPort; + const config = Config(); + this.publicHostname = config.contentServers.gopher.publicHostname; + 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); @@ -99,9 +100,10 @@ exports.getModule = class GopherModule extends ServerModule { return true; // nothing to do, but not an error } - const port = parseInt(Config.contentServers.gopher.port); + const config = Config(); + const port = parseInt(config.contentServers.gopher.port); if(isNaN(port)) { - this.log.warn( { port : Config.contentServers.gopher.port, server : ModuleInfo.name }, 'Invalid port' ); + this.log.warn( { port : config.contentServers.gopher.port, server : ModuleInfo.name }, 'Invalid port' ); return false; } @@ -109,13 +111,14 @@ exports.getModule = class GopherModule extends ServerModule { } get enabled() { - return _.get(Config, 'contentServers.gopher.enabled', false) && this.isConfigured(); + return _.get(Config(), 'contentServers.gopher.enabled', false) && this.isConfigured(); } isConfigured() { // public hostname & port must be set; responses contain them! - return _.isString(_.get(Config, 'contentServers.gopher.publicHostname')) && - _.isNumber(_.get(Config, 'contentServers.gopher.publicPort')); + const config = Config(); + return _.isString(_.get(config, 'contentServers.gopher.publicHostname')) && + _.isNumber(_.get(config, 'contentServers.gopher.publicPort')); } addRoute(selectorRegExp, generatorHandler) { @@ -155,7 +158,7 @@ exports.getModule = class GopherModule extends ServerModule { defaultGenerator(selectorMatch, cb) { this.log.trace( { selector : selectorMatch[0] }, 'Serving default content'); - let bannerFile = _.get(Config, 'contentServers.gopher.bannerFile', 'startup_banner.asc'); + let bannerFile = _.get(Config(), 'contentServers.gopher.bannerFile', 'startup_banner.asc'); bannerFile = paths.isAbsolute(bannerFile) ? bannerFile : paths.join(__dirname, '../../../misc', bannerFile); fs.readFile(bannerFile, 'utf8', (err, banner) => { if(err) { @@ -174,7 +177,7 @@ exports.getModule = class GopherModule extends ServerModule { } isAreaAndConfExposed(confTag, areaTag) { - const conf = _.get(Config, [ 'contentServers', 'gopher', 'messageConferences', confTag ]); + const conf = _.get(Config(), [ 'contentServers', 'gopher', 'messageConferences', confTag ]); return Array.isArray(conf) && conf.includes(areaTag); } @@ -281,13 +284,14 @@ ${msgBody} }); } else if(selectorMatch[1]) { // list areas in conf + const sysConfig = Config(); const confTag = selectorMatch[1].replace(/\r\n|\//g, ''); - const conf = _.get(Config, [ 'contentServers', 'gopher', 'messageConferences', confTag ]) && getMessageConferenceByTag(confTag); + const conf = _.get(sysConfig, [ 'contentServers', 'gopher', 'messageConferences', confTag ]) && getMessageConferenceByTag(confTag); if(!conf) { return this.notFoundGenerator(selectorMatch, cb); } - const areas = _.get(Config, [ 'contentServers', 'gopher', 'messageConferences', confTag ], {}) + const areas = _.get(sysConfig, [ 'contentServers', 'gopher', 'messageConferences', confTag ], {}) .map(areaTag => Object.assign( { areaTag }, getMessageAreaByTag(areaTag))) .filter(area => area && !Message.isPrivateAreaTag(area.areaTag)); @@ -307,7 +311,7 @@ ${msgBody} return cb(response); } else { // message area base (list confs) - const confs = Object.keys(_.get(Config, 'contentServers.gopher.messageConferences', {})) + const confs = Object.keys(_.get(Config(), 'contentServers.gopher.messageConferences', {})) .map(confTag => Object.assign( { confTag }, getMessageConferenceByTag(confTag))) .filter(conf => conf); // remove any baddies diff --git a/core/servers/content/web.js b/core/servers/content/web.js index d1edb221..47e0661f 100644 --- a/core/servers/content/web.js +++ b/core/servers/content/web.js @@ -4,7 +4,7 @@ // ENiGMA½ const Log = require('../../logger.js').log; const ServerModule = require('../../server_module.js').ServerModule; -const Config = require('../../config.js').config; +const Config = require('../../config.js').get; // deps const http = require('http'); @@ -55,12 +55,13 @@ exports.getModule = class WebServerModule extends ServerModule { constructor() { super(); - 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) { + if(this.isEnabled() && config.contentServers.web.staticRoot) { this.addRoute({ method : 'GET', path : '/static/.*$', @@ -77,25 +78,26 @@ exports.getModule = class WebServerModule extends ServerModule { // Prefer HTTPS over HTTP. Be explicit about the port // only if non-standard. Allow users to override full prefix in config. // - if(_.isString(Config.contentServers.web.overrideUrlPrefix)) { - return `${Config.contentServers.web.overrideUrlPrefix}${pathAndQuery}`; + const config = Config(); + if(_.isString(config.contentServers.web.overrideUrlPrefix)) { + return `${config.contentServers.web.overrideUrlPrefix}${pathAndQuery}`; } let schema; let port; - if(Config.contentServers.web.https.enabled) { + if(config.contentServers.web.https.enabled) { schema = 'https://'; - port = (443 === Config.contentServers.web.https.port) ? + port = (443 === config.contentServers.web.https.port) ? '' : - `:${Config.contentServers.web.https.port}`; + `:${config.contentServers.web.https.port}`; } else { schema = 'http://'; - port = (80 === Config.contentServers.web.http.port) ? + port = (80 === config.contentServers.web.http.port) ? '' : - `:${Config.contentServers.web.http.port}`; + `:${config.contentServers.web.http.port}`; } - return `${schema}${Config.contentServers.web.domain}${port}${pathAndQuery}`; + return `${schema}${config.contentServers.web.domain}${port}${pathAndQuery}`; } isEnabled() { @@ -107,14 +109,15 @@ exports.getModule = class WebServerModule extends ServerModule { this.httpServer = http.createServer( (req, resp) => this.routeRequest(req, resp) ); } + 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 - Object.assign(options, Config.contentServers.web.https.options || {} ); + Object.assign(options, config.contentServers.web.https.options || {} ); this.httpsServer = https.createServer(options, (req, resp) => this.routeRequest(req, resp) ); } @@ -123,13 +126,14 @@ exports.getModule = class WebServerModule extends ServerModule { listen() { let ok = true; + const config = Config(); [ 'http', 'https' ].forEach(service => { const name = `${service}Server`; if(this[name]) { - const port = parseInt(Config.contentServers.web[service].port); + const port = parseInt(config.contentServers.web[service].port); if(isNaN(port)) { ok = false; - return Log.warn( { port : Config.contentServers.web[service].port, server : ModuleInfo.name }, `Invalid port (${service})` ); + return Log.warn( { port : config.contentServers.web[service].port, server : ModuleInfo.name }, `Invalid port (${service})` ); } return this[name].listen(port); } @@ -167,7 +171,7 @@ exports.getModule = class WebServerModule extends ServerModule { } respondWithError(resp, code, bodyText, title) { - const customErrorPage = paths.join(Config.contentServers.web.staticRoot, `${code}.html`); + const customErrorPage = paths.join(Config().contentServers.web.staticRoot, `${code}.html`); fs.readFile(customErrorPage, 'utf8', (err, data) => { resp.writeHead(code, { 'Content-Type' : 'text/html' } ); @@ -202,14 +206,14 @@ exports.getModule = class WebServerModule extends ServerModule { } routeIndex(req, resp) { - const filePath = paths.join(Config.contentServers.web.staticRoot, 'index.html'); + const filePath = paths.join(Config().contentServers.web.staticRoot, 'index.html'); return this.returnStaticPage(filePath, resp); } routeStaticFile(req, resp) { const fileName = req.url.substr(req.url.indexOf('/', 1)); - const filePath = paths.join(Config.contentServers.web.staticRoot, fileName); + const filePath = paths.join(Config().contentServers.web.staticRoot, fileName); return this.returnStaticPage(filePath, resp); } diff --git a/core/servers/login/ssh.js b/core/servers/login/ssh.js index c50a7d70..0b8e3082 100644 --- a/core/servers/login/ssh.js +++ b/core/servers/login/ssh.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('../../config.js').config; +const Config = require('../../config.js').get; const baseClient = require('../../client.js'); const Log = require('../../logger.js').log; const LoginServerModule = require('../../login_server_module.js'); @@ -42,7 +42,8 @@ function SSHClient(clientConn) { const username = ctx.username || ''; const password = ctx.password || ''; - 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'); @@ -60,7 +61,7 @@ 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(false === Config.general.closedSystem && self.isNewUser) { + if(false === config.general.closedSystem && self.isNewUser) { return ctx.accept(); } @@ -99,7 +100,7 @@ function SSHClient(clientConn) { return alreadyLoggedIn(username); } - if(loginAttempts >= Config.general.loginAttempts) { + if(loginAttempts >= config.general.loginAttempts) { return terminateConnection(); } @@ -113,8 +114,8 @@ function SSHClient(clientConn) { if(err) { interactivePrompt.prompt = `Access denied\n${ctx.username}'s password: `; } else { - const newUserNameList = _.has(Config, 'users.newUserNames') && Config.users.newUserNames.length > 0 ? - Config.users.newUserNames.map(newName => '"' + newName + '"').join(', ') : + const newUserNameList = _.has(config, 'users.newUserNames') && config.users.newUserNames.length > 0 ? + config.users.newUserNames.map(newName => '"' + newName + '"').join(', ') : '(No new user names enabled!)'; interactivePrompt.prompt = `Access denied\n${stringFormat(artInfo.data, { newUserNames : newUserNameList })}\n${ctx.username}'s password'`; @@ -203,7 +204,7 @@ function SSHClient(clientConn) { } // we're ready! - const firstMenu = self.isNewUser ? Config.loginServers.ssh.firstMenuNewUser : Config.loginServers.ssh.firstMenu; + const firstMenu = self.isNewUser ? Config().loginServers.ssh.firstMenuNewUser : Config().loginServers.ssh.firstMenu; self.emit('ready', { firstMenu : firstMenu } ); }); @@ -239,18 +240,19 @@ exports.getModule = class SSHServerModule extends LoginServerModule { } createServer() { + const config = Config(); 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! debug : (sshDebugLine) => { - if(true === Config.loginServers.ssh.traceConnections) { + if(true === config.loginServers.ssh.traceConnections) { Log.trace(`SSH: ${sshDebugLine}`); } }, @@ -265,9 +267,10 @@ exports.getModule = class SSHServerModule extends LoginServerModule { } listen() { - const port = parseInt(Config.loginServers.ssh.port); + const config = Config(); + const port = parseInt(config.loginServers.ssh.port); if(isNaN(port)) { - Log.error( { server : ModuleInfo.name, port : Config.loginServers.ssh.port }, 'Cannot load server (invalid port)' ); + Log.error( { server : ModuleInfo.name, port : config.loginServers.ssh.port }, 'Cannot load server (invalid port)' ); return false; } diff --git a/core/servers/login/telnet.js b/core/servers/login/telnet.js index 3004513d..3a58ae4e 100644 --- a/core/servers/login/telnet.js +++ b/core/servers/login/telnet.js @@ -5,7 +5,7 @@ const baseClient = require('../../client.js'); const Log = require('../../logger.js').log; const LoginServerModule = require('../../login_server_module.js'); -const Config = require('../../config.js').config; +const Config = require('../../config.js').get; const EnigAssert = require('../../enigma_assert.js'); const { stringFromNullTermBuffer } = require('../../string_util.js'); @@ -549,7 +549,7 @@ function TelnetClient(input, output) { }); this.connectionTrace = (info, msg) => { - if(Config.loginServers.telnet.traceConnections) { + if(Config().loginServers.telnet.traceConnections) { const logger = self.log || Log; return logger.trace(info, `Telnet: ${msg}`); } @@ -568,7 +568,7 @@ function TelnetClient(input, output) { this.readyNow = () => { if(!this.didReady) { this.didReady = true; - this.emit('ready', { firstMenu : Config.loginServers.telnet.firstMenu } ); + this.emit('ready', { firstMenu : Config().loginServers.telnet.firstMenu } ); } }; } @@ -879,9 +879,10 @@ exports.getModule = class TelnetServerModule extends LoginServerModule { } listen() { - const port = parseInt(Config.loginServers.telnet.port); + const config = Config(); + const port = parseInt(config.loginServers.telnet.port); if(isNaN(port)) { - Log.error( { server : ModuleInfo.name, port : Config.loginServers.telnet.port }, 'Cannot load server (invalid port)' ); + Log.error( { server : ModuleInfo.name, port : config.loginServers.telnet.port }, 'Cannot load server (invalid port)' ); return false; } diff --git a/core/servers/login/websocket.js b/core/servers/login/websocket.js index ed12bf0b..e30d0303 100644 --- a/core/servers/login/websocket.js +++ b/core/servers/login/websocket.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('../../config.js').config; +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'); @@ -92,7 +92,7 @@ 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.proxied') && + 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']}"`); @@ -120,7 +120,7 @@ exports.getModule = class WebSocketLoginServer extends LoginServerModule { // * insecure websocket (ws://) // * secure (tls) websocket (wss://) // - const config = _.get(Config, 'loginServers.webSocket'); + const config = _.get(Config(), 'loginServers.webSocket'); if(!_.isObject(config)) { return; } @@ -162,7 +162,7 @@ exports.getModule = class WebSocketLoginServer extends LoginServerModule { } const serverName = `${ModuleInfo.name} (${serverType})`; - const port = parseInt(_.get(Config, [ 'loginServers', 'webSocket', 'secure' === serverType ? 'wss' : 'ws', 'port' ] )); + 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)' ); diff --git a/core/show_art.js b/core/show_art.js index fbbb3141..6fb8d01b 100644 --- a/core/show_art.js +++ b/core/show_art.js @@ -5,7 +5,7 @@ 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').config; +const Config = require('./config.js').get; // deps const async = require('async'); @@ -90,7 +90,7 @@ exports.getModule = class ShowArtModule extends MenuModule { } // further resolve key -> file base area art - const artSpec = _.get(Config, [ 'fileBase', 'areas', key, 'art' ]); + const artSpec = _.get(Config(), [ 'fileBase', 'areas', key, 'art' ]); if(!artSpec) { return cb(Errors.MissingConfig(`No art defined for file base area "${key}"`)); } diff --git a/core/system_view_validate.js b/core/system_view_validate.js index beb6bcce..e2d01a89 100644 --- a/core/system_view_validate.js +++ b/core/system_view_validate.js @@ -3,7 +3,7 @@ // ENiGMA½ const User = require('./user.js'); -const Config = require('./config.js').config; +const Config = require('./config.js').get; const Log = require('./logger.js').log; const { getAddressedToInfo } = require('./mail_util.js'); const Message = require('./message.js'); @@ -30,14 +30,15 @@ function validateMessageSubject(data, cb) { } function validateUserNameAvail(data, cb) { - if(!data || data.length < Config.users.usernameMin) { + const config = Config(); + if(!data || data.length < config.users.usernameMin) { cb(new Error('Username too short')); - } else if(data.length > Config.users.usernameMax) { + } else if(data.length > config.users.usernameMax) { // 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')); @@ -133,12 +134,13 @@ function validateBirthdate(data, cb) { } function validatePasswordSpec(data, cb) { - if(!data || data.length < Config.users.passwordMin) { + const config = Config(); + if(!data || data.length < config.users.passwordMin) { return cb(new Error('Password too short')); } // check badpass, if avail - fs.readFile(Config.users.badPassFile, 'utf8', (err, passwords) => { + fs.readFile(config.users.badPassFile, 'utf8', (err, passwords) => { if(err) { Log.warn( { error : err.message }, 'Cannot read bad pass file'); return cb(null); diff --git a/core/theme.js b/core/theme.js index 77c737fc..5545fad3 100644 --- a/core/theme.js +++ b/core/theme.js @@ -1,7 +1,7 @@ /* jslint node: true */ 'use strict'; -const Config = require('./config.js').config; +const Config = require('./config.js').get; const art = require('./art.js'); const ansi = require('./ansi_term.js'); const Log = require('./logger.js').log; @@ -38,7 +38,7 @@ function refreshThemeHelpers(theme) { let pwChar = _.get( theme, 'customization.defaults.general.passwordChar', - Config.defaults.passwordChar + Config().defaults.passwordChar ); if(_.isString(pwChar)) { @@ -50,22 +50,22 @@ function refreshThemeHelpers(theme) { return pwChar; }, getDateFormat : function(style = 'short') { - const format = Config.defaults.dateFormat[style] || 'MM/DD/YYYY'; + const format = Config().defaults.dateFormat[style] || 'MM/DD/YYYY'; return _.get(theme, `customization.defaults.dateFormat.${style}`, format); }, getTimeFormat : function(style = 'short') { - const format = Config.defaults.timeFormat[style] || 'h:mm a'; + const format = Config().defaults.timeFormat[style] || 'h:mm a'; return _.get(theme, `customization.defaults.timeFormat.${style}`, format); }, getDateTimeFormat : function(style = 'short') { - const format = Config.defaults.dateTimeFormat[style] || 'MM/DD/YYYY h:mm a'; + const format = Config().defaults.dateTimeFormat[style] || 'MM/DD/YYYY h:mm a'; return _.get(theme, `customization.defaults.dateTimeFormat.${style}`, format); } }; } function loadTheme(themeId, cb) { - const path = paths.join(Config.paths.themes, themeId, 'theme.hjson'); + const path = paths.join(Config().paths.themes, themeId, 'theme.hjson'); const changed = ( { fileName, fileRoot } ) => { const reCachedPath = paths.join(fileRoot, fileName); @@ -262,15 +262,16 @@ function getMergedTheme(menuConfig, promptConfig, theme) { } function reloadTheme(themeId) { + const config = Config(); async.waterfall( [ function loadMenuConfig(callback) { - getFullConfig(Config.general.menuFile, (err, menuConfig) => { + getFullConfig(config.general.menuFile, (err, menuConfig) => { return callback(err, menuConfig); }); }, function loadPromptConfig(menuConfig, callback) { - getFullConfig(Config.general.promptFile, (err, promptConfig) => { + getFullConfig(config.general.promptFile, (err, promptConfig) => { return callback(err, menuConfig, promptConfig); }); }, @@ -312,21 +313,21 @@ function reloadAllThemes() } function initAvailableThemes(cb) { - + const config = Config(); async.waterfall( [ function loadMenuConfig(callback) { - getFullConfig(Config.general.menuFile, (err, menuConfig) => { + getFullConfig(config.general.menuFile, (err, menuConfig) => { return callback(err, menuConfig); }); }, function loadPromptConfig(menuConfig, callback) { - getFullConfig(Config.general.promptFile, (err, promptConfig) => { + getFullConfig(config.general.promptFile, (err, promptConfig) => { return callback(err, menuConfig, promptConfig); }); }, function getThemeDirectories(menuConfig, promptConfig, callback) { - fs.readdir(Config.paths.themes, (err, files) => { + fs.readdir(config.paths.themes, (err, files) => { if(err) { return callback(err); } @@ -337,7 +338,7 @@ function initAvailableThemes(cb) { promptConfig, files.filter( f => { // sync normally not allowed -- initAvailableThemes() is a startup-only method, however - return fs.statSync(paths.join(Config.paths.themes, f)).isDirectory(); + return fs.statSync(paths.join(config.paths.themes, f)).isDirectory(); }) ); }); @@ -394,12 +395,13 @@ function setClientTheme(client, themeId) { let msg; let setThemeId; + const config = Config(); if(availThemes.has(themeId)) { msg = 'Set client theme'; setThemeId = themeId; - } else if(availThemes.has(Config.defaults.theme)) { + } else if(availThemes.has(config.defaults.theme)) { msg = 'Failed setting theme by supplied ID; Using default'; - setThemeId = Config.defaults.theme; + setThemeId = config.defaults.theme; } else { msg = 'Failed setting theme by system default ID; Using the first one we can find'; setThemeId = availThemes.keys().next().value; @@ -421,10 +423,11 @@ function getThemeArt(options, cb) { // readSauce // random // + const config = Config(); if(!options.themeId && _.has(options, 'client.user.properties.theme_id')) { options.themeId = options.client.user.properties.theme_id; } else { - options.themeId = Config.defaults.theme; + options.themeId = config.defaults.theme; } // :TODO: replace asAnsi stuff with something like retrieveAs = 'ansi' | 'pipe' | ... @@ -465,17 +468,17 @@ function getThemeArt(options, cb) { return callback(null, artInfo); } - options.basePath = paths.join(Config.paths.themes, options.themeId); + options.basePath = paths.join(config.paths.themes, options.themeId); art.getArt(options.name, options, (err, artInfo) => { return callback(null, artInfo); }); }, function fromDefaultTheme(artInfo, callback) { - if(artInfo || Config.defaults.theme === options.themeId) { + if(artInfo || config.defaults.theme === options.themeId) { return callback(null, artInfo); } - options.basePath = paths.join(Config.paths.themes, Config.defaults.theme); + options.basePath = paths.join(config.paths.themes, config.defaults.theme); art.getArt(options.name, options, (err, artInfo) => { return callback(null, artInfo); }); @@ -485,7 +488,7 @@ function getThemeArt(options, cb) { return callback(null, artInfo); } - options.basePath = Config.paths.art; + options.basePath = config.paths.art; art.getArt(options.name, options, (err, artInfo) => { return callback(err, artInfo); }); diff --git a/core/user.js b/core/user.js index 7f50223d..457604b3 100644 --- a/core/user.js +++ b/core/user.js @@ -2,7 +2,7 @@ 'use strict'; const userDb = require('./database.js').dbs.user; -const Config = require('./config.js').config; +const Config = require('./config.js').get; const userGroup = require('./user_group.js'); const Errors = require('./enig_error.js').Errors; const Events = require('./events.js'); @@ -56,7 +56,7 @@ module.exports = class User { } isValid() { - if(this.userId <= 0 || this.username.length < Config.users.usernameMin) { + if(this.userId <= 0 || this.username.length < Config().users.usernameMin) { return false; } @@ -181,15 +181,16 @@ module.exports = class User { create(password, cb) { assert(0 === this.userId); + const config = Config(); - if(this.username.length < Config.users.usernameMin || this.username.length > Config.users.usernameMax) { + if(this.username.length < config.users.usernameMin || this.username.length > config.users.usernameMax) { return cb(Errors.Invalid('Invalid username length')); } const self = this; // :TODO: set various defaults, e.g. default activation status, etc. - self.properties.account_status = Config.users.requireActivation ? User.AccountStatus.inactive : User.AccountStatus.active; + self.properties.account_status = config.users.requireActivation ? User.AccountStatus.inactive : User.AccountStatus.active; async.waterfall( [ @@ -229,7 +230,7 @@ module.exports = class User { }); }, function setInitialGroupMembership(trans, callback) { - self.groups = Config.users.defaultGroups; + self.groups = config.users.defaultGroups; if(User.RootUserID === self.userId) { // root/SysOp? self.groups.push('sysops'); diff --git a/core/web_password_reset.js b/core/web_password_reset.js index 6ea1e6f8..2f98823c 100644 --- a/core/web_password_reset.js +++ b/core/web_password_reset.js @@ -2,7 +2,7 @@ 'use strict'; // ENiGMA½ -const Config = require('./config.js').config; +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; @@ -89,12 +89,13 @@ class WebPasswordReset { }, function getEmailTemplates(user, callback) { - fs.readFile(Config.contentServers.web.resetPassword.resetPassEmailText, 'utf8', (err, textTemplate) => { + const config = Config(); + fs.readFile(config.contentServers.web.resetPassword.resetPassEmailText, 'utf8', (err, textTemplate) => { if(err) { textTemplate = PW_RESET_EMAIL_TEXT_TEMPLATE_DEFAULT; } - fs.readFile(Config.contentServers.web.resetPassword.resetPassEmailHtml, 'utf8', (err, htmlTemplate) => { + fs.readFile(config.contentServers.web.resetPassword.resetPassEmailHtml, 'utf8', (err, htmlTemplate) => { return callback(null, user, textTemplate, htmlTemplate); }); }); @@ -106,7 +107,7 @@ class WebPasswordReset { function replaceTokens(s) { return s - .replace(/%BOARDNAME%/g, Config.general.boardName) + .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) @@ -229,12 +230,13 @@ class WebPasswordReset { const postResetUrl = webServer.instance.buildUrl('/reset_password'); + const config = Config(); return webServer.instance.routeTemplateFilePage( - Config.contentServers.web.resetPassword.resetPageTemplate, + config.contentServers.web.resetPassword.resetPageTemplate, (templateData, preprocessFinished) => { const finalPage = templateData - .replace(/%BOARDNAME%/g, Config.general.boardName) + .replace(/%BOARDNAME%/g, config.general.boardName) .replace(/%USERNAME%/g, user.username) .replace(/%TOKEN%/g, token) .replace(/%RESET_URL%/g, postResetUrl) @@ -262,9 +264,10 @@ class WebPasswordReset { req.on('end', () => { const formData = querystring.parse(bodyData); + 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.length < config.users.passwordMin || formData.password.length > config.users.passwordMax) { return badRequest(); }