2014-10-17 02:21:06 +00:00
/* jslint node: true */
'use strict' ;
2017-01-28 19:33:06 +00:00
// ENiGMA½
const miscUtil = require ( './misc_util.js' ) ;
2014-10-17 02:21:06 +00:00
2017-01-28 19:33:06 +00:00
// deps
const fs = require ( 'fs' ) ;
const paths = require ( 'path' ) ;
const async = require ( 'async' ) ;
const _ = require ( 'lodash' ) ;
const hjson = require ( 'hjson' ) ;
const assert = require ( 'assert' ) ;
2015-04-17 04:29:53 +00:00
2017-01-28 19:33:06 +00:00
exports . init = init ;
exports . getDefaultPath = getDefaultPath ;
2015-04-19 08:13:13 +00:00
2016-02-03 04:35:59 +00:00
function hasMessageConferenceAndArea ( config ) {
2016-09-20 03:28:21 +00:00
assert ( _ . isObject ( config . messageConferences ) ) ; // we create one ourself!
const nonInternalConfs = Object . keys ( config . messageConferences ) . filter ( confTag => {
return 'system_internal' !== confTag ;
} ) ;
if ( 0 === nonInternalConfs . length ) {
return false ;
}
// :TODO: there is likely a better/cleaner way of doing this
let result = false ;
_ . forEach ( nonInternalConfs , confTag => {
if ( _ . has ( config . messageConferences [ confTag ] , 'areas' ) &&
Object . keys ( config . messageConferences [ confTag ] . areas ) > 0 )
{
result = true ;
return false ; // stop iteration
}
} ) ;
return result ;
2016-02-03 04:35:59 +00:00
}
2015-04-19 08:13:13 +00:00
function init ( configPath , cb ) {
async . waterfall (
[
function loadUserConfig ( callback ) {
2017-01-28 19:33:06 +00:00
if ( ! _ . isString ( configPath ) ) {
return callback ( null , { } ) ;
2015-11-21 06:46:48 +00:00
}
2017-01-28 19:33:06 +00:00
fs . readFile ( configPath , { encoding : 'utf8' } , ( err , configData ) => {
if ( err ) {
return callback ( err ) ;
}
let configJson ;
try {
configJson = hjson . parse ( configData ) ;
} catch ( e ) {
return callback ( e ) ;
}
return callback ( null , configJson ) ;
} ) ;
2015-04-19 08:13:13 +00:00
} ,
2015-08-06 04:22:17 +00:00
function mergeWithDefaultConfig ( configJson , callback ) {
2017-01-28 19:33:06 +00:00
const mergedConfig = _ . mergeWith (
getDefaultConfig ( ) ,
configJson , ( conf1 , conf2 ) => {
// Arrays should always concat
if ( _ . isArray ( conf1 ) ) {
// :TODO: look for collisions & override dupes
return conf1 . concat ( conf2 ) ;
}
2015-08-20 22:35:04 +00:00
}
2017-01-28 19:33:06 +00:00
) ;
2015-08-20 22:35:04 +00:00
2017-01-28 19:33:06 +00:00
return callback ( null , mergedConfig ) ;
2015-11-21 06:46:48 +00:00
} ,
function validate ( mergedConfig , callback ) {
//
// Various sections must now exist in config
//
2017-01-28 19:33:06 +00:00
// :TODO: Logic is broken here:
2016-09-20 03:28:21 +00:00
if ( hasMessageConferenceAndArea ( mergedConfig ) ) {
var msgAreasErr = new Error ( 'Please create at least one message conference and area!' ) ;
2015-11-21 06:46:48 +00:00
msgAreasErr . code = 'EBADCONFIG' ;
2016-09-20 03:28:21 +00:00
return callback ( msgAreasErr ) ;
} else {
return callback ( null , mergedConfig ) ;
}
2015-04-19 08:13:13 +00:00
}
] ,
function complete ( err , mergedConfig ) {
exports . config = mergedConfig ;
2016-09-20 03:28:21 +00:00
return cb ( err ) ;
2014-10-17 02:21:06 +00:00
}
2015-04-19 08:13:13 +00:00
) ;
}
2014-10-17 02:21:06 +00:00
2015-04-19 08:13:13 +00:00
function getDefaultPath ( ) {
2017-01-28 19:33:06 +00:00
const base = miscUtil . resolvePath ( '~/' ) ;
2015-04-19 08:13:13 +00:00
if ( base ) {
2015-11-12 23:18:28 +00:00
// e.g. /home/users/joeuser/.config/enigma-bbs/config.hjson
return paths . join ( base , '.config' , 'enigma-bbs' , 'config.hjson' ) ;
2015-04-19 08:13:13 +00:00
}
}
2014-10-17 02:21:06 +00:00
2015-04-19 08:13:13 +00:00
function getDefaultConfig ( ) {
return {
general : {
2015-08-03 00:27:05 +00:00
boardName : 'Another Fine ENiGMA½ BBS' ,
2015-10-22 04:51:35 +00:00
2015-10-28 03:12:55 +00:00
closedSystem : false , // is the system closed to new users?
2015-10-22 18:22:03 +00:00
2015-10-22 04:51:35 +00:00
loginAttempts : 3 ,
2015-12-11 20:58:58 +00:00
menuFile : 'menu.hjson' , // Override to use something else, e.g. demo.hjson. Can be a full path (defaults to ./mods)
2016-01-15 05:44:33 +00:00
promptFile : 'prompt.hjson' , // Override to use soemthing else, e.g. myprompt.hjson. Can be a full path (defaults to ./mods)
2015-04-19 08:13:13 +00:00
} ,
2014-10-17 02:21:06 +00:00
2015-12-11 20:58:58 +00:00
// :TODO: see notes below about 'theme' section - move this!
2015-12-13 23:47:28 +00:00
preLoginTheme : 'luciano_blocktronics' ,
2014-10-17 02:21:06 +00:00
2015-04-19 08:13:13 +00:00
users : {
usernameMin : 2 ,
2015-06-25 04:45:21 +00:00
usernameMax : 16 , // Note that FidoNet wants 36 max
2016-09-02 05:39:49 +00:00
usernamePattern : '^[A-Za-z0-9~!@#$%^&*()\\-\\_+ ]+$' ,
2015-12-24 18:51:49 +00:00
2015-04-19 08:13:13 +00:00
passwordMin : 6 ,
2015-04-27 02:46:16 +00:00
passwordMax : 128 ,
2015-12-24 18:51:49 +00:00
realNameMax : 32 ,
locationMax : 32 ,
affilsMax : 32 ,
emailMax : 255 ,
webMax : 255 ,
2016-07-15 03:30:02 +00:00
requireActivation : false , // require SysOp activation? false = auto-activate
2015-04-19 08:13:13 +00:00
invalidUsernames : [ ] ,
2015-08-21 04:29:16 +00:00
groups : [ 'users' , 'sysops' ] , // built in groups
2015-10-22 17:04:50 +00:00
defaultGroups : [ 'users' ] , // default groups new users belong to
newUserNames : [ 'new' , 'apply' ] , // Names reserved for applying
2015-10-28 03:12:55 +00:00
// :TODO: Mystic uses TRASHCAN.DAT for this -- is there a reason to support something like that?
2015-11-05 06:04:55 +00:00
badUserNames : [ 'sysop' , 'admin' , 'administrator' , 'root' , 'all' ] ,
2015-04-19 08:13:13 +00:00
} ,
2015-04-17 04:29:53 +00:00
2015-09-09 04:08:45 +00:00
// :TODO: better name for "defaults"... which is redundant here!
/ *
Concept
"theme" : {
"default" : "defaultThemeName" , // or "*"
"preLogin" : "*" ,
"passwordChar" : "*" ,
...
}
* /
2015-04-19 08:13:13 +00:00
defaults : {
2015-09-28 04:05:40 +00:00
theme : 'luciano_blocktronics' ,
2015-04-19 08:13:13 +00:00
passwordChar : '*' , // TODO: move to user ?
2015-07-24 04:23:44 +00:00
dateFormat : {
2015-07-23 03:35:35 +00:00
short : 'MM/DD/YYYY' ,
2015-07-24 04:23:44 +00:00
} ,
timeFormat : {
2015-07-25 22:10:12 +00:00
short : 'h:mm a' ,
2015-09-02 04:42:54 +00:00
} ,
dateTimeFormat : {
short : 'MM/DD/YYYY h:mm a' ,
2015-07-23 03:35:35 +00:00
}
2015-04-19 08:13:13 +00:00
} ,
2015-04-15 04:27:07 +00:00
2015-09-09 04:08:45 +00:00
menus : {
cls : true , // Clear screen before each menu by default?
} ,
2015-05-14 04:21:55 +00:00
2015-04-19 08:13:13 +00:00
paths : {
mods : paths . join ( _ _dirname , './../mods/' ) ,
2016-09-20 03:28:21 +00:00
loginServers : paths . join ( _ _dirname , './servers/login/' ) ,
contentServers : paths . join ( _ _dirname , './servers/content/' ) ,
2016-02-10 05:30:59 +00:00
scannerTossers : paths . join ( _ _dirname , './scanner_tossers/' ) ,
mailers : paths . join ( _ _dirname , './mailers/' ) ,
2015-04-19 08:13:13 +00:00
art : paths . join ( _ _dirname , './../mods/art/' ) ,
2015-05-14 22:49:19 +00:00
themes : paths . join ( _ _dirname , './../mods/themes/' ) ,
2015-04-19 08:13:13 +00:00
logs : paths . join ( _ _dirname , './../logs/' ) , // :TODO: set up based on system, e.g. /var/logs/enigmabbs or such
db : paths . join ( _ _dirname , './../db/' ) ,
2016-06-26 04:37:02 +00:00
modsDb : paths . join ( _ _dirname , './../db/mods/' ) ,
2015-08-03 00:27:05 +00:00
dropFiles : paths . join ( _ _dirname , './../dropfiles/' ) , // + "/node<x>/
2015-11-07 01:30:08 +00:00
misc : paths . join ( _ _dirname , './../misc/' ) ,
2015-04-19 08:13:13 +00:00
} ,
2016-09-20 03:28:21 +00:00
loginServers : {
2015-04-19 08:13:13 +00:00
telnet : {
port : 8888 ,
enabled : true ,
2015-10-22 16:36:08 +00:00
firstMenu : 'telnetConnected' ,
2014-10-17 02:21:06 +00:00
} ,
2015-04-19 08:13:13 +00:00
ssh : {
2015-10-22 18:22:03 +00:00
port : 8889 ,
2016-03-23 04:29:08 +00:00
enabled : false , // defualt to false as PK/pass in config.hjson are required
2015-10-28 02:46:30 +00:00
//
// Private key in PEM format
//
// Generating your PK:
// > openssl genrsa -des3 -out ./misc/ssh_private_key.pem 2048
//
// Then, set servers.ssh.privateKeyPass to the password you use above
// in your config.hjson
//
privateKeyPem : paths . join ( _ _dirname , './../misc/ssh_private_key.pem' ) ,
2015-10-22 18:22:03 +00:00
firstMenu : 'sshConnected' ,
firstMenuNewUser : 'sshConnectedNewUser' ,
2015-04-19 08:13:13 +00:00
}
} ,
2015-07-14 06:13:29 +00:00
2016-10-25 03:49:45 +00:00
contentServers : {
web : {
domain : 'another-fine-enigma-bbs.org' ,
http : {
enabled : false ,
port : 8080 ,
} ,
https : {
enabled : false ,
port : 8443 ,
certPem : paths . join ( _ _dirname , './../misc/https_cert.pem' ) ,
keyPem : paths . join ( _ _dirname , './../misc/https_cert_key.pem' ) ,
}
}
} ,
2016-10-06 05:22:59 +00:00
archives : {
archivers : {
2016-10-04 04:03:32 +00:00
'7Zip' : {
compress : {
2016-10-06 05:22:59 +00:00
cmd : '7za' ,
2016-10-04 04:03:32 +00:00
args : [ 'a' , '-tzip' , '{archivePath}' , '{fileList}' ] ,
} ,
decompress : {
2016-10-06 05:22:59 +00:00
cmd : '7za' ,
2016-10-04 04:03:32 +00:00
args : [ 'e' , '-o{extractPath}' , '{archivePath}' ]
} ,
list : {
2016-10-06 05:22:59 +00:00
cmd : '7za' ,
2016-10-04 04:03:32 +00:00
args : [ 'l' , '{archivePath}' ] ,
entryMatch : '^[0-9]{4}-[0-9]{2}-[0-9]{2}\\s[0-9]{2}:[0-9]{2}:[0-9]{2}\\s[A-Za-z\\.]{5}\\s+([0-9]+)\\s+[0-9]+\\s+([^\\r\\n]+)$' ,
} ,
extract : {
2016-10-06 05:22:59 +00:00
cmd : '7za' ,
args : [ 'e' , '-o{extractPath}' , '{archivePath}' , '{fileList}' ] ,
2016-10-04 04:03:32 +00:00
} ,
2017-01-23 04:30:49 +00:00
} ,
Lha : {
//
// 'lha' command can be obtained from:
// * apt-get: lhasa
//
// (compress not currently supported)
//
decompress : {
cmd : 'lha' ,
args : [ '-ew={extractPath}' , '{archivePath}' ] ,
} ,
list : {
cmd : 'lha' ,
args : [ '-l' , '{archivePath}' ] ,
entryMatch : '^[\\[a-z\\]]+(?:\\s+[0-9]+\\s+[0-9]+|\\s+)([0-9]+)\\s+[0-9]{2}\\.[0-9]\\%\\s+[A-Za-z]{3}\\s+[0-9]{1,2}\\s+[0-9]{4}\\s+([^\\r\\n]+)$' ,
} ,
extract : {
cmd : 'lha' ,
args : [ '-ew={extractPath}' , '{archivePath}' , '{fileList}' ]
}
2016-10-04 04:03:32 +00:00
}
} ,
2017-01-23 04:30:49 +00:00
2016-10-04 04:03:32 +00:00
formats : {
2017-01-23 04:30:49 +00:00
//
// Resources
// * http://www.garykessler.net/library/file_sigs.html
//
2016-10-04 04:03:32 +00:00
zip : {
sig : '504b0304' ,
offset : 0 ,
exts : [ 'zip' ] ,
2016-10-06 05:22:59 +00:00
handler : '7Zip' ,
2016-10-13 04:07:22 +00:00
desc : 'ZIP Archive' ,
2016-10-04 04:03:32 +00:00
} ,
'7z' : {
sig : '377abcaf271c' ,
offset : 0 ,
exts : [ '7z' ] ,
2016-10-06 05:22:59 +00:00
handler : '7Zip' ,
2016-10-13 04:07:22 +00:00
desc : '7-Zip Archive' ,
2016-10-04 04:03:32 +00:00
} ,
arj : {
sig : '60ea' ,
offset : 0 ,
exts : [ 'arj' ] ,
2016-10-06 05:22:59 +00:00
handler : '7Zip' ,
2016-10-13 04:07:22 +00:00
desc : 'ARJ Archive' ,
2016-10-04 04:03:32 +00:00
} ,
rar : {
sig : '526172211a0700' ,
offset : 0 ,
exts : [ 'rar' ] ,
2016-10-06 05:22:59 +00:00
handler : '7Zip' ,
2016-10-13 04:07:22 +00:00
desc : 'RAR Archive' ,
2017-01-22 05:09:29 +00:00
} ,
gzip : {
sig : '1f8b' ,
offset : 0 ,
exts : [ 'gz' ] ,
handler : '7Zip' ,
desc : 'Gzip Archive' ,
2017-01-23 04:30:49 +00:00
} ,
bzip : {
sig : '425a68' ,
offset : 0 ,
exts : [ 'bz2' ] ,
handler : '7Zip' ,
desc : 'BZip2 Archive' ,
} ,
lzh : {
sig : '2d6c68' ,
offset : 2 ,
exts : [ 'lzh' , 'ice' ] ,
handler : 'Lha' ,
desc : 'LHArc Archive' ,
2016-10-04 04:03:32 +00:00
}
2016-02-24 04:56:22 +00:00
}
} ,
2016-06-20 03:09:45 +00:00
2016-09-29 03:54:25 +00:00
fileTransferProtocols : {
zmodem8kSz : {
name : 'ZModem 8k' ,
type : 'external' ,
2017-01-12 05:47:00 +00:00
external : {
2016-09-29 03:54:25 +00:00
sendCmd : 'sz' , // Avail on Debian/Ubuntu based systems as the package "lrzsz"
sendArgs : [
2017-01-12 05:47:00 +00:00
// :TODO: try -q
'--zmodem' , '--try-8k' , '--binary' , '--restricted' , '{filePaths}'
] ,
recvCmd : 'rz' , // Avail on Debian/Ubuntu based systems as the package "lrzsz"
recvArgs : [
2017-01-22 05:09:29 +00:00
'--zmodem' , '--binary' , '--restricted' , '--keep-uppercase' , // dumps to CWD which is set to {uploadDir}
2016-09-29 03:54:25 +00:00
] ,
2017-01-12 05:47:00 +00:00
// :TODO: can we not just use --escape ?
escapeTelnet : true , // set to true to escape Telnet codes such as IAC
2016-12-31 21:50:29 +00:00
supportsBatch : true ,
2016-09-29 03:54:25 +00:00
}
} ,
zmodem8kSexyz : {
2016-12-31 21:50:29 +00:00
name : 'ZModem 8k (SEXYZ)' ,
2016-09-29 03:54:25 +00:00
type : 'external' ,
external : {
// :TODO: Look into shipping sexyz binaries or at least hosting them somewhere for common systems
sendCmd : 'sexyz' ,
sendArgs : [
2016-12-31 21:50:29 +00:00
'-telnet' , '-8' , 'sz' , '@{fileListPath}'
] ,
recvCmd : 'sexyz' ,
recvArgs : [
'-telnet' , '-8' , 'rz' , '{uploadDir}'
2016-09-29 03:54:25 +00:00
] ,
2016-10-01 19:25:32 +00:00
escapeTelnet : false , // -telnet option does this for us
2016-12-31 21:50:29 +00:00
supportsBatch : true ,
2016-09-29 03:54:25 +00:00
}
}
} ,
2016-06-20 03:09:45 +00:00
messageAreaDefaults : {
//
// The following can be override per-area as well
//
maxMessages : 1024 , // 0 = unlimited
maxAgeDays : 0 , // 0 = unlimited
} ,
2016-02-24 04:56:22 +00:00
2016-06-20 03:09:45 +00:00
messageConferences : {
2016-02-03 04:35:59 +00:00
system _internal : {
name : 'System Internal' ,
desc : 'Built in conference for private messages, bulletins, etc.' ,
areas : {
private _mail : {
name : 'Private Mail' ,
desc : 'Private user to user mail/email' ,
} ,
local _bulletin : {
name : 'System Bulletins' ,
desc : 'Bulletin messages for all users' ,
}
}
}
} ,
2016-02-17 05:11:55 +00:00
scannerTossers : {
ftn _bso : {
paths : {
2016-02-24 04:56:22 +00:00
outbound : paths . join ( _ _dirname , './../mail/ftn_out/' ) ,
inbound : paths . join ( _ _dirname , './../mail/ftn_in/' ) ,
secInbound : paths . join ( _ _dirname , './../mail/ftn_secin/' ) ,
2016-02-17 05:11:55 +00:00
} ,
2016-02-29 05:04:03 +00:00
//
// Packet and (ArcMail) bundle target sizes are just that: targets.
// Actual sizes may be slightly larger when we must place a full
// PKT contents *somewhere*
//
packetTargetByteSize : 512000 , // 512k, before placing messages in a new pkt
bundleTargetByteSize : 2048000 , // 2M, before creating another archive
2016-02-17 05:11:55 +00:00
}
} ,
2016-09-20 03:28:21 +00:00
fileBase : {
// areas with an explicit |storageDir| will be stored relative to |areaStoragePrefix|:
areaStoragePrefix : paths . join ( _ _dirname , './../file_base/' ) ,
fileNamePatterns : {
2016-10-13 04:07:22 +00:00
// These are NOT case sensitive
2016-12-07 01:58:56 +00:00
// FILE_ID.DIZ - https://en.wikipedia.org/wiki/FILE_ID.DIZ
2016-10-13 04:07:22 +00:00
shortDesc : [
'^FILE_ID\.DIZ$' , '^DESC\.SDI$' , '^DESCRIPT\.ION$' , '^FILE\.DES$' , '$FILE\.SDI$' , '^DISK\.ID$'
] ,
2016-12-07 01:58:56 +00:00
// common README filename - https://en.wikipedia.org/wiki/README
longDesc : [
'^.*\.NFO$' , '^README\.1ST$' , '^README\.TXT$' , '^READ\.ME$' , '^README$' , '^README\.md$'
] ,
2016-09-20 03:28:21 +00:00
} ,
2016-10-06 05:22:59 +00:00
yearEstPatterns : [
//
2016-12-07 01:58:56 +00:00
// Patterns should produce the year in the first submatch.
// The extracted year may be YY or YYYY
2016-10-06 05:22:59 +00:00
//
'[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[0-9]{2})?[0-9]{2})' , // m/d/yyyy, mm-dd-yyyy, etc.
2016-10-13 04:07:22 +00:00
"\\B('[1789][0-9])\\b" , // eslint-disable-line quotes
2017-01-12 05:47:00 +00:00
'[0-3]?[0-9][\\-\\/\\.](?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|january|february|march|april|may|june|july|august|september|october|november|december)[\\-\\/\\.]((?:[0-9]{2})?[0-9]{2})' ,
2017-01-23 04:30:49 +00:00
'(?:jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|january|february|march|april|may|june|july|august|september|october|november|december),?\\s[0-9]+(?:st|nd|rd|th)?,?\\s((?:[0-9]{2})?[0-9]{2})' , // November 29th, 1997
2016-10-06 05:22:59 +00:00
// :TODO: DD/MMM/YY, DD/MMMM/YY, DD/MMM/YYYY, etc.
] ,
2016-10-25 03:49:45 +00:00
web : {
path : '/f/' ,
routePath : '/f/[a-zA-Z0-9]+$' ,
expireMinutes : 1440 , // 1 day
} ,
2016-12-07 01:58:56 +00:00
//
// File area storage location tag/value pairs.
// Non-absolute paths are relative to |areaStoragePrefix|.
//
storageTags : {
sys _msg _attach : 'msg_attach' ,
} ,
2016-09-20 03:28:21 +00:00
areas : {
2017-01-02 04:53:04 +00:00
system _message _attachment : {
2016-12-07 01:58:56 +00:00
name : 'Message attachments' ,
desc : 'File attachments to messages' ,
storageTags : 'sys_msg_attach' , // may be string or array of strings
2016-09-20 03:28:21 +00:00
}
}
} ,
2016-06-20 03:09:45 +00:00
eventScheduler : {
events : {
trimMessageAreas : {
// may optionally use [or ]@watch:/path/to/file
2016-06-22 03:37:47 +00:00
schedule : 'every 24 hours' ,
2016-06-20 03:09:45 +00:00
// action:
// - @method:path/to/module.js:theMethodName
// (path is relative to engima base dir)
//
// - @execute:/path/to/something/executable.sh
//
2016-06-22 03:37:47 +00:00
action : '@method:core/message_area.js:trimMessageAreasScheduledEvent' ,
2016-06-20 03:09:45 +00:00
}
}
} ,
2015-07-21 04:56:48 +00:00
2015-08-05 04:35:59 +00:00
misc : {
2016-08-22 01:59:21 +00:00
preAuthIdleLogoutSeconds : 60 * 3 , // 2m
idleLogoutSeconds : 60 * 6 , // 6m
2015-08-05 04:35:59 +00:00
} ,
2015-07-21 04:56:48 +00:00
logging : {
2016-09-04 23:46:28 +00:00
level : 'debug' ,
rotatingFile : { // set to 'disabled' or false to disable
type : 'rotating-file' ,
fileName : 'enigma-bbs.log' ,
period : '1d' ,
count : 3 ,
level : 'debug' ,
}
// :TODO: syslog - https://github.com/mcavage/node-bunyan-syslog
2016-08-22 01:59:21 +00:00
} ,
debug : {
assertsEnabled : false ,
2015-07-21 04:56:48 +00:00
}
2015-04-19 08:13:13 +00:00
} ;
}