diff --git a/core/config.js b/core/config.js index 7e467709..4a235c5e 100644 --- a/core/config.js +++ b/core/config.js @@ -2,17 +2,17 @@ 'use strict'; // ENiGMA½ -const Errors = require('./enig_error.js').Errors; +const Errors = require('./enig_error.js').Errors; +const DefaultConfig = require('./config_default'); // deps -const paths = require('path'); -const async = require('async'); -const _ = require('lodash'); -const assert = require('assert'); +const paths = require('path'); +const async = require('async'); +const _ = require('lodash'); +const assert = require('assert'); exports.init = init; exports.getDefaultPath = getDefaultPath; -exports.getDefaultConfig = getDefaultConfig; let currentConfiguration = {}; @@ -55,7 +55,7 @@ const ArrayReplaceKeys = [ ]; function mergeValidateAndFinalize(config, cb) { - const defaultConfig = getDefaultConfig(); + const defaultConfig = DefaultConfig(); const arrayReplaceKeyPathsMutable = _.clone(ArrayReplaceKeyPaths); const shouldReplaceArray = (arr, key) => { @@ -164,930 +164,3 @@ function getDefaultPath() { // e.g. /enigma-bbs-install-path/config/ return './config/'; } - -function getDefaultConfig() { - return { - general : { - boardName : 'Another Fine ENiGMA½ BBS', - prettyBoardName : '|08A|07nother |07F|08ine |07E|08NiGMA|07½ B|08BS', - telnetHostname : '', - sshHostname : '', - website : 'https://enigma-bbs.github.io', - description : 'An ENiGMA½ BBS', - - // :TODO: closedSystem prob belongs under users{}? - closedSystem : false, // is the system closed to new users? - - menuFile : 'menu.hjson', // 'oputil.js config new' will set this appropriately in config.hjson; may be full path - promptFile : 'prompt.hjson', // 'oputil.js config new' will set this appropriately in config.hjson; may be full path - achievementFile : 'achievements.hjson', - }, - - users : { - usernameMin : 2, - usernameMax : 16, // Note that FidoNet wants 36 max - usernamePattern : '^[A-Za-z0-9~!@#$%^&*()\\-\\_+ .]+$', - - passwordMin : 6, - passwordMax : 128, - - // - // The bad password list is a text file containing a password per line. - // Entries in this list are not allowed to be used on the system as they - // are known to be too common. - // - // A great resource can be found at https://github.com/danielmiessler/SecLists - // - // Current list source: https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/probable-v2-top12000.txt - // - badPassFile : paths.join(__dirname, '../misc/bad_passwords.txt'), - - realNameMax : 32, - locationMax : 32, - affilsMax : 32, - emailMax : 255, - webMax : 255, - - requireActivation : false, // require SysOp activation? false = auto-activate - - groups : [ 'users', 'sysops' ], // built in groups - defaultGroups : [ 'users' ], // default groups new users belong to - - newUserNames : [ 'new', 'apply' ], // Names reserved for applying - - badUserNames : [ - 'sysop', 'admin', 'administrator', 'root', 'all', - 'areamgr', 'filemgr', 'filefix', 'areafix', 'allfix', - 'server', 'client', 'notme' - ], - - preAuthIdleLogoutSeconds : 60 * 3, // 3m - idleLogoutSeconds : 60 * 6, // 6m - - failedLogin : { - disconnect : 3, // 0=disabled - lockAccount : 9, // 0=disabled; Mark user status as "locked" if >= N - autoUnlockMinutes : 60 * 6, // 0=disabled; Auto unlock after N minutes. - }, - unlockAtEmailPwReset : true, // if true, password reset via email will unlock locked accounts - - twoFactorAuth : { - method : 'googleAuth', - - otp : { - registerEmailText : paths.join(__dirname, '../misc/otp_register_email.template.txt'), - registerEmailHtml : paths.join(__dirname, '../misc/otp_register_email.template.html'), - registerPageTemplate : paths.join(__dirname, '../www/otp_register.template.html'), - } - } - }, - - theme : { - default : 'luciano_blocktronics', - preLogin : 'luciano_blocktronics', - - passwordChar : '*', - dateFormat : { - short : 'MM/DD/YYYY', - long : 'ddd, MMMM Do, YYYY', - }, - timeFormat : { - short : 'h:mm a', - }, - dateTimeFormat : { - short : 'MM/DD/YYYY h:mm a', - long : 'ddd, MMMM Do, YYYY, h:mm a', - } - }, - - menus : { - cls : true, // Clear screen before each menu by default? - }, - - paths : { - config : paths.join(__dirname, './../config/'), - security : paths.join(__dirname, './../config/security'), // certs, keys, etc. - mods : paths.join(__dirname, './../mods/'), - loginServers : paths.join(__dirname, './servers/login/'), - contentServers : paths.join(__dirname, './servers/content/'), - chatServers : paths.join(__dirname, './servers/chat/'), - - scannerTossers : paths.join(__dirname, './scanner_tossers/'), - mailers : paths.join(__dirname, './mailers/') , - - art : paths.join(__dirname, './../art/general/'), - themes : paths.join(__dirname, './../art/themes/'), - logs : paths.join(__dirname, './../logs/'), - db : paths.join(__dirname, './../db/'), - modsDb : paths.join(__dirname, './../db/mods/'), - dropFiles : paths.join(__dirname, './../drop/'), // + "/node/ - misc : paths.join(__dirname, './../misc/'), - }, - - loginServers : { - telnet : { - port : 8888, - enabled : true, - firstMenu : 'telnetConnected', - }, - ssh : { - port : 8889, - enabled : false, // default to false as PK/pass in config.hjson are required - // - // To enable SSH, perform the following steps: - // - // 1 - Generate a Private Key (PK): - // Currently ENiGMA 1/2 requires a PKCS#1 PEM formatted PK. - // To generate a secure PK, issue the following command: - // - // > openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \ - // -pkeyopt rsa_keygen_pubexp:65537 | openssl rsa \ - // -out ./config/security/ssh_private_key.pem -aes128 - // - // (The above is a more modern equivalent of the following): - // > openssl genrsa -aes128 -out ./config/security/ssh_private_key.pem 2048 - // - // 2 - Set 'privateKeyPass' to the password you used in step #1 - // - // 3 - Finally, set 'enabled' to 'true' - // - // Additional reading: - // - https://blog.sleeplessbeastie.eu/2017/12/28/how-to-generate-private-key/ - // - https://gist.github.com/briansmith/2ee42439923d8e65a266994d0f70180b - // - privateKeyPem : paths.join(__dirname, './../config/security/ssh_private_key.pem'), - firstMenu : 'sshConnected', - firstMenuNewUser : 'sshConnectedNewUser', - - // - // SSH details that can affect security. Stronger ciphers are better for example, - // but terminals such as SyncTERM require KEX diffie-hellman-group14-sha1, - // cipher 3des-cbc, etc. - // - // See https://github.com/mscdex/ssh2-streams for the full list of supported - // algorithms. - // - algorithms : { - kex : [ - 'ecdh-sha2-nistp256', - 'ecdh-sha2-nistp384', - 'ecdh-sha2-nistp521', - 'diffie-hellman-group-exchange-sha256', - 'diffie-hellman-group14-sha1', - 'diffie-hellman-group-exchange-sha1', - 'diffie-hellman-group1-sha1', - ], - cipher : [ - 'aes128-ctr', - 'aes192-ctr', - 'aes256-ctr', - 'aes128-gcm', - 'aes128-gcm@openssh.com', - 'aes256-gcm', - 'aes256-gcm@openssh.com', - 'aes256-cbc', - 'aes192-cbc', - 'aes128-cbc', - 'blowfish-cbc', - '3des-cbc', - 'arcfour256', - 'arcfour128', - 'cast128-cbc', - 'arcfour', - ], - hmac : [ - 'hmac-sha2-256', - 'hmac-sha2-512', - 'hmac-sha1', - 'hmac-md5', - 'hmac-sha2-256-96', - 'hmac-sha2-512-96', - 'hmac-ripemd160', - 'hmac-sha1-96', - 'hmac-md5-96', - ], - // note that we disable compression by default due to issues with many clients. YMMV. - compress : [ 'none' ] - }, - }, - webSocket : { - ws : { - // non-secure ws:// - enabled : false, - port : 8810, - }, - wss : { - // secure ws:// - // must provide valid certPem and keyPem - enabled : false, - port : 8811, - certPem : paths.join(__dirname, './../config/https_cert.pem'), - keyPem : paths.join(__dirname, './../config/https_cert_key.pem'), - }, - }, - }, - - contentServers : { - web : { - domain : 'another-fine-enigma-bbs.org', - - staticRoot : paths.join(__dirname, './../www'), - - resetPassword : { - // - // The following templates have these variables available to them: - // - // * %BOARDNAME% : Name of BBS - // * %USERNAME% : Username of whom to reset password - // * %TOKEN% : Reset token - // * %RESET_URL% : In case of email, the link to follow for reset. In case of landing page, - // URL to POST submit reset form. - - // templates for pw reset *email* - resetPassEmailText : paths.join(__dirname, '../misc/reset_password_email.template.txt'), // plain text version - resetPassEmailHtml : paths.join(__dirname, '../misc/reset_password_email.template.html'), // HTML version - - // tempalte for pw reset *landing page* - // - resetPageTemplate : paths.join(__dirname, './../www/reset_password.template.html'), - }, - - http : { - enabled : false, - port : 8080, - }, - https : { - enabled : false, - port : 8443, - certPem : paths.join(__dirname, './../config/https_cert.pem'), - keyPem : paths.join(__dirname, './../config/https_cert_key.pem'), - } - }, - - gopher : { - enabled : false, - port : 8070, - publicHostname : 'another-fine-enigma-bbs.org', - publicPort : 8070, // adjust if behind NAT/etc. - bannerFile : 'gopher_banner.asc', - - // - // Set messageConferences{} to maps of confTag -> [ areaTag1, areaTag2, ... ] - // to export message confs/areas - // - }, - - nntp : { - // internal caching of groups, message lists, etc. - cache : { - maxItems : 200, - maxAge : 1000 * 30, // 30s - }, - - // - // Set publicMessageConferences{} to a map of confTag -> [ areaTag1, areaTag2, ... ] - // in order to export *public* conf/areas that are available to anonymous - // NNTP users. Other conf/areas: Standard ACS rules apply. - // - publicMessageConferences: {}, - - nntp : { - enabled : false, - port : 8119, - }, - - nntps : { - enabled : false, - port : 8563, - certPem : paths.join(__dirname, './../config/nntps_cert.pem'), - keyPem : paths.join(__dirname, './../config/nntps_key.pem'), - } - } - }, - - chatServers : { - mrc: { - enabled : false, - serverHostname : 'mrc.bottomlessabyss.net', - serverPort : 5000, - retryDelay : 10000, - multiplexerPort : 5000, - } - }, - - infoExtractUtils : { - Exiftool2Desc : { - cmd : `${__dirname}/../util/exiftool2desc.js`, // ensure chmod +x - }, - Exiftool : { - cmd : 'exiftool', - args : [ - '-charset', 'utf8', '{filePath}', - // exclude the following: - '--directory', '--filepermissions', '--exiftoolversion', '--filename', '--filesize', - '--filemodifydate', '--fileaccessdate', '--fileinodechangedate', '--createdate', '--modifydate', - '--metadatadate', '--xmptoolkit' - ] - }, - XDMS2Desc : { - // http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html - cmd : 'xdms', - args : [ 'd', '{filePath}' ] - }, - XDMS2LongDesc : { - // http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html - cmd : 'xdms', - args : [ 'f', '{filePath}' ] - }, - }, - - fileTypes : { - // - // File types explicitly known to the system. Here we can configure - // information extraction, archive treatment, etc. - // - // MIME types can be found in mime-db: https://github.com/jshttp/mime-db - // - // Resources for signature/magic bytes: - // * http://www.garykessler.net/library/file_sigs.html - // - // - // :TODO: text/x-ansi -> SAUCE extraction for .ans uploads - // :TODO: textual : bool -- if text, we can view. - // :TODO: asText : { cmd, args[] } -> viewable text - - // - // Audio - // - 'audio/mpeg' : { - desc : 'MP3 Audio', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', - }, - 'application/pdf' : { - desc : 'Adobe PDF', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', - }, - // - // Video - // - 'video/mp4' : { - desc : 'MPEG Video', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', - }, - 'video/x-matroska ' : { - desc : 'Matroska Video', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', - }, - 'video/x-msvideo' : { - desc : 'Audio Video Interleave', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', - }, - // - // Images - // - 'image/jpeg' : { - desc : 'JPEG Image', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', - }, - 'image/png' : { - desc : 'Portable Network Graphic Image', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', - }, - 'image/gif' : { - desc : 'Graphics Interchange Format Image', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', - }, - 'image/webp' : { - desc : 'WebP Image', - shortDescUtil : 'Exiftool2Desc', - longDescUtil : 'Exiftool', - }, - // - // Archives - // - 'application/zip' : { - desc : 'ZIP Archive', - sig : '504b0304', - offset : 0, - archiveHandler : 'InfoZip', - }, - /* - 'application/x-cbr' : { - desc : 'Comic Book Archive', - sig : '504b0304', - }, - */ - 'application/x-arj' : { - desc : 'ARJ Archive', - sig : '60ea', - offset : 0, - archiveHandler : 'Arj', - }, - 'application/x-rar-compressed' : { - desc : 'RAR Archive', - sig : '526172211a07', - offset : 0, - archiveHandler : 'Rar', - }, - 'application/gzip' : { - desc : 'Gzip Archive', - sig : '1f8b', - offset : 0, - archiveHandler : 'TarGz', - }, - // :TODO: application/x-bzip - 'application/x-bzip2' : { - desc : 'BZip2 Archive', - sig : '425a68', - offset : 0, - archiveHandler : '7Zip', - }, - 'application/x-lzh-compressed' : { - desc : 'LHArc Archive', - sig : '2d6c68', - offset : 2, - archiveHandler : 'Lha', - }, - 'application/x-lzx' : { - desc : 'LZX Archive', - sig : '4c5a5800', - offset : 0, - archiveHandler : 'Lzx', - }, - 'application/x-7z-compressed' : { - desc : '7-Zip Archive', - sig : '377abcaf271c', - offset : 0, - archiveHandler : '7Zip', - }, - - // - // Generics that need further mapping - // - 'application/octet-stream' : [ - { - desc : 'Amiga DISKMASHER', - sig : '444d5321', // DMS! - ext : '.dms', - shortDescUtil : 'XDMS2Desc', - longDescUtil : 'XDMS2LongDesc', - }, - { - desc : 'SIO2PC Atari Disk Image', - sig : '9602', // 16bit sum of "NICKATARI" - ext : '.atr', - archiveHandler : 'Atr', - } - ] - }, - - archives : { - archivers : { - '7Zip' : { // p7zip package - compress : { - cmd : '7za', - args : [ 'a', '-tzip', '{archivePath}', '{fileList}' ], - }, - decompress : { - cmd : '7za', - args : [ 'e', '-o{extractPath}', '{archivePath}' ] // :TODO: should be 'x'? - }, - list : { - cmd : '7za', - 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 : { - cmd : '7za', - args : [ 'e', '-o{extractPath}', '{archivePath}', '{fileList}' ], - }, - }, - - InfoZip: { - compress : { - cmd : 'zip', - args : [ '{archivePath}', '{fileList}' ], - }, - decompress : { - cmd : 'unzip', - args : [ '-n', '{archivePath}', '-d', '{extractPath}' ], - }, - list : { - cmd : 'unzip', - args : [ '-l', '{archivePath}' ], - // Annoyingly, dates can be in YYYY-MM-DD or MM-DD-YYYY format - entryMatch : '^\\s*([0-9]+)\\s+[0-9]{2,4}-[0-9]{2}-[0-9]{2,4}\\s+[0-9]{2}:[0-9]{2}\\s+([^\\r\\n]+)$', - }, - extract : { - cmd : 'unzip', - args : [ '-n', '{archivePath}', '{fileList}', '-d', '{extractPath}' ], - } - }, - - Lha : { - // - // 'lha' command can be obtained from: - // * apt-get: lhasa - // - // (compress not currently supported) - // - decompress : { - cmd : 'lha', - args : [ '-efw={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 : [ '-efw={extractPath}', '{archivePath}', '{fileList}' ] - } - }, - - Lzx : { - // - // 'unlzx' command can be obtained from: - // * Debian based: https://launchpad.net/~rzr/+archive/ubuntu/ppa/+build/2486127 (amd64/x86_64) - // * RedHat: https://fedora.pkgs.org/28/rpm-sphere/unlzx-1.1-4.1.x86_64.rpm.html - // * Source: http://xavprods.free.fr/lzx/ - // - decompress : { - cmd : 'unlzx', - // unzlx doesn't have a output dir option, but we'll cwd to the temp output dir first - args : [ '-x', '{archivePath}' ], - }, - list : { - cmd : 'unlzx', - args : [ '-v', '{archivePath}' ], - entryMatch : '^\\s+([0-9]+)\\s+[^\\s]+\\s+[0-9]{2}:[0-9]{2}:[0-9]{2}\\s+[0-9]{1,2}-[a-z]{3}-[0-9]{4}\\s+[a-z\\-]+\\s+\\"([^"]+)\\"$', - } - }, - - Arj : { - // - // 'arj' command can be obtained from: - // * apt-get: arj - // - decompress : { - cmd : 'arj', - args : [ 'x', '{archivePath}', '{extractPath}' ], - }, - list : { - cmd : 'arj', - args : [ 'l', '{archivePath}' ], - entryMatch : '^([^\\s]+)\\s+([0-9]+)\\s+[0-9]+\\s[0-9\\.]+\\s+[0-9]{2}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\:[0-9]{2}\\s+(?:[^\\r\\n]+)$', - entryGroupOrder : { // defaults to { byteSize : 1, fileName : 2 } - fileName : 1, - byteSize : 2, - } - }, - extract : { - cmd : 'arj', - args : [ 'e', '{archivePath}', '{extractPath}', '{fileList}' ], - } - }, - - Rar : { - decompress : { - cmd : 'unrar', - args : [ 'x', '{archivePath}', '{extractPath}' ], - }, - list : { - cmd : 'unrar', - args : [ 'l', '{archivePath}' ], - entryMatch : '^\\s+[\\.A-Z]+\\s+([\\d]+)\\s{2}[0-9]{2,4}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\s{2}([^\\r\\n]+)$', - }, - extract : { - cmd : 'unrar', - args : [ 'e', '{archivePath}', '{extractPath}', '{fileList}' ], - } - }, - - TarGz : { - decompress : { - cmd : 'tar', - args : [ '-xf', '{archivePath}', '-C', '{extractPath}', '--strip-components=1' ], - }, - list : { - cmd : 'tar', - args : [ '-tvf', '{archivePath}' ], - entryMatch : '^[drwx\\-]{10}\\s[A-Za-z0-9\\/]+\\s+([0-9]+)\\s[0-9]{4}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\s([^\\r\\n]+)$', - }, - extract : { - cmd : 'tar', - args : [ '-xvf', '{archivePath}', '-C', '{extractPath}', '{fileList}' ], - } - }, - - Atr : { - decompress : { - cmd : 'atr', - args : [ '{archivePath}', 'x', '-a', '-o', '{extractPath}' ] - }, - list : { - cmd : 'atr', - args : [ '{archivePath}', 'ls', '-la1' ], - entryMatch : '^[rwxs-]{5}\\s+([0-9]+)\\s\\([0-9\\s]+\\)\\s([^\\r\\n\\s]*)(?:[^\\r\\n]+)?$', - }, - extract : { - cmd : 'atr', - // note: -l converts Atari 0x9b line feeds to 0x0a; not ideal if we're dealing with a binary of course. - args : [ '{archivePath}', 'x', '-a', '-l', '-o', '{extractPath}', '{fileList}' ] - } - } - }, - }, - - fileTransferProtocols : { - // - // See http://www.synchro.net/docs/sexyz.txt for information on SEXYZ - // - zmodem8kSexyz : { - name : 'ZModem 8k (SEXYZ)', - type : 'external', - sort : 1, - external : { - // :TODO: Look into shipping sexyz binaries or at least hosting them somewhere for common systems - // Linux x86_64 binary: https://l33t.codes/outgoing/sexyz - sendCmd : 'sexyz', - sendArgs : [ '-telnet', '-8', 'sz', '@{fileListPath}' ], - recvCmd : 'sexyz', - recvArgs : [ '-telnet', '-8', 'rz', '{uploadDir}' ], - recvArgsNonBatch : [ '-telnet', '-8', 'rz', '{fileName}' ], - } - }, - - xmodemSexyz : { - name : 'XModem (SEXYZ)', - type : 'external', - sort : 3, - external : { - sendCmd : 'sexyz', - sendArgs : [ '-telnet', 'sX', '@{fileListPath}' ], - recvCmd : 'sexyz', - recvArgsNonBatch : [ '-telnet', 'rC', '{fileName}' ] - } - }, - - ymodemSexyz : { - name : 'YModem (SEXYZ)', - type : 'external', - sort : 4, - external : { - sendCmd : 'sexyz', - sendArgs : [ '-telnet', 'sY', '@{fileListPath}' ], - recvCmd : 'sexyz', - recvArgs : [ '-telnet', 'ry', '{uploadDir}' ], - } - }, - - zmodem8kSz : { - name : 'ZModem 8k', - type : 'external', - sort : 2, - external : { - sendCmd : 'sz', // Avail on Debian/Ubuntu based systems as the package "lrzsz" - sendArgs : [ - // :TODO: try -q - '--zmodem', '--try-8k', '--binary', '--restricted', '{filePaths}' - ], - recvCmd : 'rz', // Avail on Debian/Ubuntu based systems as the package "lrzsz" - recvArgs : [ - '--zmodem', '--binary', '--restricted', '--keep-uppercase', // dumps to CWD which is set to {uploadDir} - ], - processIACs : true, // escape/de-escape IACs (0xff) - } - } - }, - - messageAreaDefaults : { - // - // The following can be override per-area as well - // - maxMessages : 1024, // 0 = unlimited - maxAgeDays : 0, // 0 = unlimited - }, - - messageConferences : { - 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', - maxExternalSentAgeDays : 30, // max external "outbox" item age - }, - - local_bulletin : { - name : 'System Bulletins', - desc : 'Bulletin messages for all users', - } - } - } - }, - - scannerTossers : { - ftn_bso : { - paths : { - outbound : paths.join(__dirname, './../mail/ftn_out/'), - inbound : paths.join(__dirname, './../mail/ftn_in/'), - secInbound : paths.join(__dirname, './../mail/ftn_secin/'), - reject : paths.join(__dirname, './../mail/reject/'), // bad pkt, bundles, TIC attachments that fail any check, etc. - //outboundNetMail : paths.join(__dirname, './../mail/ftn_netmail_out/'), - // set 'retain' to a valid path to keep good pkt files - }, - - // - // 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 - packetMsgEncoding : 'utf8', // default packet encoding. Override per node if desired. - packetAnsiMsgEncoding : 'cp437', // packet encoding for *ANSI ART* messages - - tic : { - secureInOnly : true, // only bring in from secure inbound (|secInbound| path, password protected) - uploadBy : 'ENiGMA TIC', // default upload by username (override @ network) - allowReplace : false, // use "Replaces" TIC field - descPriority : 'diz', // May be diz=.DIZ/etc., or tic=from TIC Ldesc - } - } - }, - - fileBase: { - // areas with an explicit |storageDir| will be stored relative to |areaStoragePrefix|: - areaStoragePrefix : paths.join(__dirname, './../file_base/'), - - maxDescFileByteSize : 471859, // ~1/4 MB - maxDescLongFileByteSize : 524288, // 1/2 MB - - fileNamePatterns: { - // These are NOT case sensitive - // FILE_ID.DIZ - https://en.wikipedia.org/wiki/FILE_ID.DIZ - // Some groups include a FILE_ID.ANS. We try to use that over FILE_ID.DIZ if available. - desc : [ - '^.*FILE_ID\.ANS$', '^.*FILE_ID\.DIZ$', // eslint-disable-line no-useless-escape - '^.*DESC\.SDI$', // eslint-disable-line no-useless-escape - '^.*DESCRIPT\.ION$', // eslint-disable-line no-useless-escape - '^.*FILE\.DES$', // eslint-disable-line no-useless-escape - '^.*FILE\.SDI$', // eslint-disable-line no-useless-escape - '^.*DISK\.ID$' // eslint-disable-line no-useless-escape - ], - - // common README filename - https://en.wikipedia.org/wiki/README - descLong : [ - '^[^/\]*\.NFO$', // eslint-disable-line no-useless-escape - '^.*README\.1ST$', // eslint-disable-line no-useless-escape - '^.*README\.NOW$', // eslint-disable-line no-useless-escape - '^.*README\.TXT$', // eslint-disable-line no-useless-escape - '^.*READ\.ME$', // eslint-disable-line no-useless-escape - '^.*README$', // eslint-disable-line no-useless-escape - '^.*README\.md$', // eslint-disable-line no-useless-escape - '^RELEASE-INFO.ASC$' // eslint-disable-line no-useless-escape - ], - }, - - yearEstPatterns: [ - // - // Patterns should produce the year in the first submatch. - // The extracted year may be YY or YYYY - // - '\\b((?:[1-2][0-9][0-9]{2}))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]\\b', // yyyy-mm-dd, yyyy/mm/dd, ... - '\\b[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[1-2][0-9][0-9]{2}))\\b', // mm/dd/yyyy, mm.dd.yyyy, ... - '\\b((?:[1789][0-9]))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]\\b', // yy-mm-dd, yy-mm-dd, ... - '\\b[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[1789][0-9]))\\b', // mm-dd-yy, mm/dd/yy, ... - //'\\b((?:[1-2][0-9][0-9]{2}))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]|[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[0-9]{2})?[0-9]{2})\\b', // yyyy-mm-dd, m/d/yyyy, mm-dd-yyyy, etc. - //"\\b('[1789][0-9])\\b", // eslint-disable-line quotes - '\\b[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})\\b', - '\\b(?: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})\\b', // November 29th, 1997 - '\\(((?:19|20)[0-9]{2})\\)', // (19xx) or (20xx) -- with parens -- do this before 19xx 20xx such that this has priority - '\\b((?:19|20)[0-9]{2})\\b', // simple 19xx or 20xx with word boundaries - '\\b\'([17-9][0-9])\\b', // '95, '17, ... - // :TODO: DD/MMM/YY, DD/MMMM/YY, DD/MMM/YYYY, etc. - ], - - web : { - path : '/f/', - routePath : '/f/[a-zA-Z0-9]+$', - expireMinutes : 1440, // 1 day - }, - - // - // File area storage location tag/value pairs. - // Non-absolute paths are relative to |areaStoragePrefix|. - // - storageTags : { - sys_msg_attach : 'sys_msg_attach', - sys_temp_download : 'sys_temp_download', - }, - - areas: { - system_message_attachment : { - name : 'System Message Attachments', - desc : 'File attachments to messages', - storageTags : [ 'sys_msg_attach' ], - }, - - system_temporary_download : { - name : 'System Temporary Downloads', - desc : 'Temporary downloadables', - storageTags : [ 'sys_temp_download' ], - } - } - }, - - eventScheduler : { - - events : { - dailyMaintenance : { - schedule : 'at 11:59pm', - action : '@method:core/misc_scheduled_events.js:dailyMaintenanceScheduledEvent', - }, - trimMessageAreas : { - // may optionally use [or ]@watch:/path/to/file - schedule : 'every 24 hours', - - // action: - // - @method:path/to/module.js:theMethodName - // (path is relative to ENiGMA base dir) - // - // - @execute:/path/to/something/executable.sh - // - action : '@method:core/message_area.js:trimMessageAreasScheduledEvent', - }, - - nntpMaintenance : { - schedule : 'every 12 hours', // should generally be < trimMessageAreas interval - action : '@method:core/servers/content/nntp.js:performMaintenanceTask', - }, - - updateFileAreaStats : { - schedule : 'every 1 hours', - action : '@method:core/file_base_area.js:updateAreaStatsScheduledEvent', - }, - - forgotPasswordMaintenance : { - schedule : 'every 24 hours', - action : '@method:core/web_password_reset.js:performMaintenanceTask', - args : [ '24 hours' ] // items older than this will be removed - }, - - twoFactorRegisterTokenMaintenance : { - schedule : 'every 24 hours', - action : '@method:core/user_temp_token.js:temporaryTokenMaintenanceTask', - args : [ - 'auth_factor2_otp_register', - '24 hours', // expire time - ] - }, - - // - // Enable the following entry in your config.hjson to periodically create/update - // DESCRIPT.ION files for your file base - // - /* - updateDescriptIonFiles : { - schedule : 'on the last day of the week', - action : '@method:core/file_base_list_export.js:updateFileBaseDescFilesScheduledEvent', - } - */ - } - }, - - logging : { - 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 - }, - - debug : { - assertsEnabled : false, - }, - - statLog : { - systemEvents : { - loginHistoryMax: -1, // set to -1 for forever - } - }, - }; -} diff --git a/core/config_default.js b/core/config_default.js new file mode 100644 index 00000000..063f3899 --- /dev/null +++ b/core/config_default.js @@ -0,0 +1,928 @@ +const paths = require('path'); + +module.exports = () => { + return { + general : { + boardName : 'Another Fine ENiGMA½ BBS', + prettyBoardName : '|08A|07nother |07F|08ine |07E|08NiGMA|07½ B|08BS', + telnetHostname : '', + sshHostname : '', + website : 'https://enigma-bbs.github.io', + description : 'An ENiGMA½ BBS', + + // :TODO: closedSystem prob belongs under users{}? + closedSystem : false, // is the system closed to new users? + + menuFile : 'menu.hjson', // 'oputil.js config new' will set this appropriately in config.hjson; may be full path + promptFile : 'prompt.hjson', // 'oputil.js config new' will set this appropriately in config.hjson; may be full path + achievementFile : 'achievements.hjson', + }, + + users : { + usernameMin : 2, + usernameMax : 16, // Note that FidoNet wants 36 max + usernamePattern : '^[A-Za-z0-9~!@#$%^&*()\\-\\_+ .]+$', + + passwordMin : 6, + passwordMax : 128, + + // + // The bad password list is a text file containing a password per line. + // Entries in this list are not allowed to be used on the system as they + // are known to be too common. + // + // A great resource can be found at https://github.com/danielmiessler/SecLists + // + // Current list source: https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/probable-v2-top12000.txt + // + badPassFile : paths.join(__dirname, '../misc/bad_passwords.txt'), + + realNameMax : 32, + locationMax : 32, + affilsMax : 32, + emailMax : 255, + webMax : 255, + + requireActivation : false, // require SysOp activation? false = auto-activate + + groups : [ 'users', 'sysops' ], // built in groups + defaultGroups : [ 'users' ], // default groups new users belong to + + newUserNames : [ 'new', 'apply' ], // Names reserved for applying + + badUserNames : [ + 'sysop', 'admin', 'administrator', 'root', 'all', + 'areamgr', 'filemgr', 'filefix', 'areafix', 'allfix', + 'server', 'client', 'notme' + ], + + preAuthIdleLogoutSeconds : 60 * 3, // 3m + idleLogoutSeconds : 60 * 6, // 6m + + failedLogin : { + disconnect : 3, // 0=disabled + lockAccount : 9, // 0=disabled; Mark user status as "locked" if >= N + autoUnlockMinutes : 60 * 6, // 0=disabled; Auto unlock after N minutes. + }, + unlockAtEmailPwReset : true, // if true, password reset via email will unlock locked accounts + + twoFactorAuth : { + method : 'googleAuth', + + otp : { + registerEmailText : paths.join(__dirname, '../misc/otp_register_email.template.txt'), + registerEmailHtml : paths.join(__dirname, '../misc/otp_register_email.template.html'), + registerPageTemplate : paths.join(__dirname, '../www/otp_register.template.html'), + } + } + }, + + theme : { + default : 'luciano_blocktronics', + preLogin : 'luciano_blocktronics', + + passwordChar : '*', + dateFormat : { + short : 'MM/DD/YYYY', + long : 'ddd, MMMM Do, YYYY', + }, + timeFormat : { + short : 'h:mm a', + }, + dateTimeFormat : { + short : 'MM/DD/YYYY h:mm a', + long : 'ddd, MMMM Do, YYYY, h:mm a', + } + }, + + menus : { + cls : true, // Clear screen before each menu by default? + }, + + paths : { + config : paths.join(__dirname, './../config/'), + security : paths.join(__dirname, './../config/security'), // certs, keys, etc. + mods : paths.join(__dirname, './../mods/'), + loginServers : paths.join(__dirname, './servers/login/'), + contentServers : paths.join(__dirname, './servers/content/'), + chatServers : paths.join(__dirname, './servers/chat/'), + + scannerTossers : paths.join(__dirname, './scanner_tossers/'), + mailers : paths.join(__dirname, './mailers/') , + + art : paths.join(__dirname, './../art/general/'), + themes : paths.join(__dirname, './../art/themes/'), + logs : paths.join(__dirname, './../logs/'), + db : paths.join(__dirname, './../db/'), + modsDb : paths.join(__dirname, './../db/mods/'), + dropFiles : paths.join(__dirname, './../drop/'), // + "/node/ + misc : paths.join(__dirname, './../misc/'), + }, + + loginServers : { + telnet : { + port : 8888, + enabled : true, + firstMenu : 'telnetConnected', + }, + ssh : { + port : 8889, + enabled : false, // default to false as PK/pass in config.hjson are required + // + // To enable SSH, perform the following steps: + // + // 1 - Generate a Private Key (PK): + // Currently ENiGMA 1/2 requires a PKCS#1 PEM formatted PK. + // To generate a secure PK, issue the following command: + // + // > openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \ + // -pkeyopt rsa_keygen_pubexp:65537 | openssl rsa \ + // -out ./config/security/ssh_private_key.pem -aes128 + // + // (The above is a more modern equivalent of the following): + // > openssl genrsa -aes128 -out ./config/security/ssh_private_key.pem 2048 + // + // 2 - Set 'privateKeyPass' to the password you used in step #1 + // + // 3 - Finally, set 'enabled' to 'true' + // + // Additional reading: + // - https://blog.sleeplessbeastie.eu/2017/12/28/how-to-generate-private-key/ + // - https://gist.github.com/briansmith/2ee42439923d8e65a266994d0f70180b + // + privateKeyPem : paths.join(__dirname, './../config/security/ssh_private_key.pem'), + firstMenu : 'sshConnected', + firstMenuNewUser : 'sshConnectedNewUser', + + // + // SSH details that can affect security. Stronger ciphers are better for example, + // but terminals such as SyncTERM require KEX diffie-hellman-group14-sha1, + // cipher 3des-cbc, etc. + // + // See https://github.com/mscdex/ssh2-streams for the full list of supported + // algorithms. + // + algorithms : { + kex : [ + 'ecdh-sha2-nistp256', + 'ecdh-sha2-nistp384', + 'ecdh-sha2-nistp521', + 'diffie-hellman-group-exchange-sha256', + 'diffie-hellman-group14-sha1', + 'diffie-hellman-group-exchange-sha1', + 'diffie-hellman-group1-sha1', + ], + cipher : [ + 'aes128-ctr', + 'aes192-ctr', + 'aes256-ctr', + 'aes128-gcm', + 'aes128-gcm@openssh.com', + 'aes256-gcm', + 'aes256-gcm@openssh.com', + 'aes256-cbc', + 'aes192-cbc', + 'aes128-cbc', + 'blowfish-cbc', + '3des-cbc', + 'arcfour256', + 'arcfour128', + 'cast128-cbc', + 'arcfour', + ], + hmac : [ + 'hmac-sha2-256', + 'hmac-sha2-512', + 'hmac-sha1', + 'hmac-md5', + 'hmac-sha2-256-96', + 'hmac-sha2-512-96', + 'hmac-ripemd160', + 'hmac-sha1-96', + 'hmac-md5-96', + ], + // note that we disable compression by default due to issues with many clients. YMMV. + compress : [ 'none' ] + }, + }, + webSocket : { + ws : { + // non-secure ws:// + enabled : false, + port : 8810, + }, + wss : { + // secure ws:// + // must provide valid certPem and keyPem + enabled : false, + port : 8811, + certPem : paths.join(__dirname, './../config/https_cert.pem'), + keyPem : paths.join(__dirname, './../config/https_cert_key.pem'), + }, + }, + }, + + contentServers : { + web : { + domain : 'another-fine-enigma-bbs.org', + + staticRoot : paths.join(__dirname, './../www'), + + resetPassword : { + // + // The following templates have these variables available to them: + // + // * %BOARDNAME% : Name of BBS + // * %USERNAME% : Username of whom to reset password + // * %TOKEN% : Reset token + // * %RESET_URL% : In case of email, the link to follow for reset. In case of landing page, + // URL to POST submit reset form. + + // templates for pw reset *email* + resetPassEmailText : paths.join(__dirname, '../misc/reset_password_email.template.txt'), // plain text version + resetPassEmailHtml : paths.join(__dirname, '../misc/reset_password_email.template.html'), // HTML version + + // tempalte for pw reset *landing page* + // + resetPageTemplate : paths.join(__dirname, './../www/reset_password.template.html'), + }, + + http : { + enabled : false, + port : 8080, + }, + https : { + enabled : false, + port : 8443, + certPem : paths.join(__dirname, './../config/https_cert.pem'), + keyPem : paths.join(__dirname, './../config/https_cert_key.pem'), + } + }, + + gopher : { + enabled : false, + port : 8070, + publicHostname : 'another-fine-enigma-bbs.org', + publicPort : 8070, // adjust if behind NAT/etc. + bannerFile : 'gopher_banner.asc', + + // + // Set messageConferences{} to maps of confTag -> [ areaTag1, areaTag2, ... ] + // to export message confs/areas + // + }, + + nntp : { + // internal caching of groups, message lists, etc. + cache : { + maxItems : 200, + maxAge : 1000 * 30, // 30s + }, + + // + // Set publicMessageConferences{} to a map of confTag -> [ areaTag1, areaTag2, ... ] + // in order to export *public* conf/areas that are available to anonymous + // NNTP users. Other conf/areas: Standard ACS rules apply. + // + publicMessageConferences: {}, + + nntp : { + enabled : false, + port : 8119, + }, + + nntps : { + enabled : false, + port : 8563, + certPem : paths.join(__dirname, './../config/nntps_cert.pem'), + keyPem : paths.join(__dirname, './../config/nntps_key.pem'), + } + } + }, + + chatServers : { + mrc: { + enabled : false, + serverHostname : 'mrc.bottomlessabyss.net', + serverPort : 5000, + retryDelay : 10000, + multiplexerPort : 5000, + } + }, + + infoExtractUtils : { + Exiftool2Desc : { + cmd : `${__dirname}/../util/exiftool2desc.js`, // ensure chmod +x + }, + Exiftool : { + cmd : 'exiftool', + args : [ + '-charset', 'utf8', '{filePath}', + // exclude the following: + '--directory', '--filepermissions', '--exiftoolversion', '--filename', '--filesize', + '--filemodifydate', '--fileaccessdate', '--fileinodechangedate', '--createdate', '--modifydate', + '--metadatadate', '--xmptoolkit' + ] + }, + XDMS2Desc : { + // http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html + cmd : 'xdms', + args : [ 'd', '{filePath}' ] + }, + XDMS2LongDesc : { + // http://manpages.ubuntu.com/manpages/trusty/man1/xdms.1.html + cmd : 'xdms', + args : [ 'f', '{filePath}' ] + }, + }, + + fileTypes : { + // + // File types explicitly known to the system. Here we can configure + // information extraction, archive treatment, etc. + // + // MIME types can be found in mime-db: https://github.com/jshttp/mime-db + // + // Resources for signature/magic bytes: + // * http://www.garykessler.net/library/file_sigs.html + // + // + // :TODO: text/x-ansi -> SAUCE extraction for .ans uploads + // :TODO: textual : bool -- if text, we can view. + // :TODO: asText : { cmd, args[] } -> viewable text + + // + // Audio + // + 'audio/mpeg' : { + desc : 'MP3 Audio', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', + }, + 'application/pdf' : { + desc : 'Adobe PDF', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', + }, + // + // Video + // + 'video/mp4' : { + desc : 'MPEG Video', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', + }, + 'video/x-matroska ' : { + desc : 'Matroska Video', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', + }, + 'video/x-msvideo' : { + desc : 'Audio Video Interleave', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', + }, + // + // Images + // + 'image/jpeg' : { + desc : 'JPEG Image', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', + }, + 'image/png' : { + desc : 'Portable Network Graphic Image', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', + }, + 'image/gif' : { + desc : 'Graphics Interchange Format Image', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', + }, + 'image/webp' : { + desc : 'WebP Image', + shortDescUtil : 'Exiftool2Desc', + longDescUtil : 'Exiftool', + }, + // + // Archives + // + 'application/zip' : { + desc : 'ZIP Archive', + sig : '504b0304', + offset : 0, + archiveHandler : 'InfoZip', + }, + /* + 'application/x-cbr' : { + desc : 'Comic Book Archive', + sig : '504b0304', + }, + */ + 'application/x-arj' : { + desc : 'ARJ Archive', + sig : '60ea', + offset : 0, + archiveHandler : 'Arj', + }, + 'application/x-rar-compressed' : { + desc : 'RAR Archive', + sig : '526172211a07', + offset : 0, + archiveHandler : 'Rar', + }, + 'application/gzip' : { + desc : 'Gzip Archive', + sig : '1f8b', + offset : 0, + archiveHandler : 'TarGz', + }, + // :TODO: application/x-bzip + 'application/x-bzip2' : { + desc : 'BZip2 Archive', + sig : '425a68', + offset : 0, + archiveHandler : '7Zip', + }, + 'application/x-lzh-compressed' : { + desc : 'LHArc Archive', + sig : '2d6c68', + offset : 2, + archiveHandler : 'Lha', + }, + 'application/x-lzx' : { + desc : 'LZX Archive', + sig : '4c5a5800', + offset : 0, + archiveHandler : 'Lzx', + }, + 'application/x-7z-compressed' : { + desc : '7-Zip Archive', + sig : '377abcaf271c', + offset : 0, + archiveHandler : '7Zip', + }, + + // + // Generics that need further mapping + // + 'application/octet-stream' : [ + { + desc : 'Amiga DISKMASHER', + sig : '444d5321', // DMS! + ext : '.dms', + shortDescUtil : 'XDMS2Desc', + longDescUtil : 'XDMS2LongDesc', + }, + { + desc : 'SIO2PC Atari Disk Image', + sig : '9602', // 16bit sum of "NICKATARI" + ext : '.atr', + archiveHandler : 'Atr', + } + ] + }, + + archives : { + archivers : { + '7Zip' : { // p7zip package + compress : { + cmd : '7za', + args : [ 'a', '-tzip', '{archivePath}', '{fileList}' ], + }, + decompress : { + cmd : '7za', + args : [ 'e', '-o{extractPath}', '{archivePath}' ] // :TODO: should be 'x'? + }, + list : { + cmd : '7za', + 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 : { + cmd : '7za', + args : [ 'e', '-o{extractPath}', '{archivePath}', '{fileList}' ], + }, + }, + + InfoZip: { + compress : { + cmd : 'zip', + args : [ '{archivePath}', '{fileList}' ], + }, + decompress : { + cmd : 'unzip', + args : [ '-n', '{archivePath}', '-d', '{extractPath}' ], + }, + list : { + cmd : 'unzip', + args : [ '-l', '{archivePath}' ], + // Annoyingly, dates can be in YYYY-MM-DD or MM-DD-YYYY format + entryMatch : '^\\s*([0-9]+)\\s+[0-9]{2,4}-[0-9]{2}-[0-9]{2,4}\\s+[0-9]{2}:[0-9]{2}\\s+([^\\r\\n]+)$', + }, + extract : { + cmd : 'unzip', + args : [ '-n', '{archivePath}', '{fileList}', '-d', '{extractPath}' ], + } + }, + + Lha : { + // + // 'lha' command can be obtained from: + // * apt-get: lhasa + // + // (compress not currently supported) + // + decompress : { + cmd : 'lha', + args : [ '-efw={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 : [ '-efw={extractPath}', '{archivePath}', '{fileList}' ] + } + }, + + Lzx : { + // + // 'unlzx' command can be obtained from: + // * Debian based: https://launchpad.net/~rzr/+archive/ubuntu/ppa/+build/2486127 (amd64/x86_64) + // * RedHat: https://fedora.pkgs.org/28/rpm-sphere/unlzx-1.1-4.1.x86_64.rpm.html + // * Source: http://xavprods.free.fr/lzx/ + // + decompress : { + cmd : 'unlzx', + // unzlx doesn't have a output dir option, but we'll cwd to the temp output dir first + args : [ '-x', '{archivePath}' ], + }, + list : { + cmd : 'unlzx', + args : [ '-v', '{archivePath}' ], + entryMatch : '^\\s+([0-9]+)\\s+[^\\s]+\\s+[0-9]{2}:[0-9]{2}:[0-9]{2}\\s+[0-9]{1,2}-[a-z]{3}-[0-9]{4}\\s+[a-z\\-]+\\s+\\"([^"]+)\\"$', + } + }, + + Arj : { + // + // 'arj' command can be obtained from: + // * apt-get: arj + // + decompress : { + cmd : 'arj', + args : [ 'x', '{archivePath}', '{extractPath}' ], + }, + list : { + cmd : 'arj', + args : [ 'l', '{archivePath}' ], + entryMatch : '^([^\\s]+)\\s+([0-9]+)\\s+[0-9]+\\s[0-9\\.]+\\s+[0-9]{2}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\:[0-9]{2}\\s+(?:[^\\r\\n]+)$', + entryGroupOrder : { // defaults to { byteSize : 1, fileName : 2 } + fileName : 1, + byteSize : 2, + } + }, + extract : { + cmd : 'arj', + args : [ 'e', '{archivePath}', '{extractPath}', '{fileList}' ], + } + }, + + Rar : { + decompress : { + cmd : 'unrar', + args : [ 'x', '{archivePath}', '{extractPath}' ], + }, + list : { + cmd : 'unrar', + args : [ 'l', '{archivePath}' ], + entryMatch : '^\\s+[\\.A-Z]+\\s+([\\d]+)\\s{2}[0-9]{2,4}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\s{2}([^\\r\\n]+)$', + }, + extract : { + cmd : 'unrar', + args : [ 'e', '{archivePath}', '{extractPath}', '{fileList}' ], + } + }, + + TarGz : { + decompress : { + cmd : 'tar', + args : [ '-xf', '{archivePath}', '-C', '{extractPath}', '--strip-components=1' ], + }, + list : { + cmd : 'tar', + args : [ '-tvf', '{archivePath}' ], + entryMatch : '^[drwx\\-]{10}\\s[A-Za-z0-9\\/]+\\s+([0-9]+)\\s[0-9]{4}\\-[0-9]{2}\\-[0-9]{2}\\s[0-9]{2}\\:[0-9]{2}\\s([^\\r\\n]+)$', + }, + extract : { + cmd : 'tar', + args : [ '-xvf', '{archivePath}', '-C', '{extractPath}', '{fileList}' ], + } + }, + + Atr : { + decompress : { + cmd : 'atr', + args : [ '{archivePath}', 'x', '-a', '-o', '{extractPath}' ] + }, + list : { + cmd : 'atr', + args : [ '{archivePath}', 'ls', '-la1' ], + entryMatch : '^[rwxs-]{5}\\s+([0-9]+)\\s\\([0-9\\s]+\\)\\s([^\\r\\n\\s]*)(?:[^\\r\\n]+)?$', + }, + extract : { + cmd : 'atr', + // note: -l converts Atari 0x9b line feeds to 0x0a; not ideal if we're dealing with a binary of course. + args : [ '{archivePath}', 'x', '-a', '-l', '-o', '{extractPath}', '{fileList}' ] + } + } + }, + }, + + fileTransferProtocols : { + // + // See http://www.synchro.net/docs/sexyz.txt for information on SEXYZ + // + zmodem8kSexyz : { + name : 'ZModem 8k (SEXYZ)', + type : 'external', + sort : 1, + external : { + // :TODO: Look into shipping sexyz binaries or at least hosting them somewhere for common systems + // Linux x86_64 binary: https://l33t.codes/outgoing/sexyz + sendCmd : 'sexyz', + sendArgs : [ '-telnet', '-8', 'sz', '@{fileListPath}' ], + recvCmd : 'sexyz', + recvArgs : [ '-telnet', '-8', 'rz', '{uploadDir}' ], + recvArgsNonBatch : [ '-telnet', '-8', 'rz', '{fileName}' ], + } + }, + + xmodemSexyz : { + name : 'XModem (SEXYZ)', + type : 'external', + sort : 3, + external : { + sendCmd : 'sexyz', + sendArgs : [ '-telnet', 'sX', '@{fileListPath}' ], + recvCmd : 'sexyz', + recvArgsNonBatch : [ '-telnet', 'rC', '{fileName}' ] + } + }, + + ymodemSexyz : { + name : 'YModem (SEXYZ)', + type : 'external', + sort : 4, + external : { + sendCmd : 'sexyz', + sendArgs : [ '-telnet', 'sY', '@{fileListPath}' ], + recvCmd : 'sexyz', + recvArgs : [ '-telnet', 'ry', '{uploadDir}' ], + } + }, + + zmodem8kSz : { + name : 'ZModem 8k', + type : 'external', + sort : 2, + external : { + sendCmd : 'sz', // Avail on Debian/Ubuntu based systems as the package "lrzsz" + sendArgs : [ + // :TODO: try -q + '--zmodem', '--try-8k', '--binary', '--restricted', '{filePaths}' + ], + recvCmd : 'rz', // Avail on Debian/Ubuntu based systems as the package "lrzsz" + recvArgs : [ + '--zmodem', '--binary', '--restricted', '--keep-uppercase', // dumps to CWD which is set to {uploadDir} + ], + processIACs : true, // escape/de-escape IACs (0xff) + } + } + }, + + messageAreaDefaults : { + // + // The following can be override per-area as well + // + maxMessages : 1024, // 0 = unlimited + maxAgeDays : 0, // 0 = unlimited + }, + + messageConferences : { + 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', + maxExternalSentAgeDays : 30, // max external "outbox" item age + }, + + local_bulletin : { + name : 'System Bulletins', + desc : 'Bulletin messages for all users', + } + } + } + }, + + scannerTossers : { + ftn_bso : { + paths : { + outbound : paths.join(__dirname, './../mail/ftn_out/'), + inbound : paths.join(__dirname, './../mail/ftn_in/'), + secInbound : paths.join(__dirname, './../mail/ftn_secin/'), + reject : paths.join(__dirname, './../mail/reject/'), // bad pkt, bundles, TIC attachments that fail any check, etc. + //outboundNetMail : paths.join(__dirname, './../mail/ftn_netmail_out/'), + // set 'retain' to a valid path to keep good pkt files + }, + + // + // 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 + packetMsgEncoding : 'utf8', // default packet encoding. Override per node if desired. + packetAnsiMsgEncoding : 'cp437', // packet encoding for *ANSI ART* messages + + tic : { + secureInOnly : true, // only bring in from secure inbound (|secInbound| path, password protected) + uploadBy : 'ENiGMA TIC', // default upload by username (override @ network) + allowReplace : false, // use "Replaces" TIC field + descPriority : 'diz', // May be diz=.DIZ/etc., or tic=from TIC Ldesc + } + } + }, + + fileBase: { + // areas with an explicit |storageDir| will be stored relative to |areaStoragePrefix|: + areaStoragePrefix : paths.join(__dirname, './../file_base/'), + + maxDescFileByteSize : 471859, // ~1/4 MB + maxDescLongFileByteSize : 524288, // 1/2 MB + + fileNamePatterns: { + // These are NOT case sensitive + // FILE_ID.DIZ - https://en.wikipedia.org/wiki/FILE_ID.DIZ + // Some groups include a FILE_ID.ANS. We try to use that over FILE_ID.DIZ if available. + desc : [ + '^.*FILE_ID\.ANS$', '^.*FILE_ID\.DIZ$', // eslint-disable-line no-useless-escape + '^.*DESC\.SDI$', // eslint-disable-line no-useless-escape + '^.*DESCRIPT\.ION$', // eslint-disable-line no-useless-escape + '^.*FILE\.DES$', // eslint-disable-line no-useless-escape + '^.*FILE\.SDI$', // eslint-disable-line no-useless-escape + '^.*DISK\.ID$' // eslint-disable-line no-useless-escape + ], + + // common README filename - https://en.wikipedia.org/wiki/README + descLong : [ + '^[^/\]*\.NFO$', // eslint-disable-line no-useless-escape + '^.*README\.1ST$', // eslint-disable-line no-useless-escape + '^.*README\.NOW$', // eslint-disable-line no-useless-escape + '^.*README\.TXT$', // eslint-disable-line no-useless-escape + '^.*READ\.ME$', // eslint-disable-line no-useless-escape + '^.*README$', // eslint-disable-line no-useless-escape + '^.*README\.md$', // eslint-disable-line no-useless-escape + '^RELEASE-INFO.ASC$' // eslint-disable-line no-useless-escape + ], + }, + + yearEstPatterns: [ + // + // Patterns should produce the year in the first submatch. + // The extracted year may be YY or YYYY + // + '\\b((?:[1-2][0-9][0-9]{2}))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]\\b', // yyyy-mm-dd, yyyy/mm/dd, ... + '\\b[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[1-2][0-9][0-9]{2}))\\b', // mm/dd/yyyy, mm.dd.yyyy, ... + '\\b((?:[1789][0-9]))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]\\b', // yy-mm-dd, yy-mm-dd, ... + '\\b[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[1789][0-9]))\\b', // mm-dd-yy, mm/dd/yy, ... + //'\\b((?:[1-2][0-9][0-9]{2}))[\\-\\/\\.][0-3]?[0-9][\\-\\/\\.][0-3]?[0-9]|[0-3]?[0-9][\\-\\/\\.][0-3]?[0-9][\\-\\/\\.]((?:[0-9]{2})?[0-9]{2})\\b', // yyyy-mm-dd, m/d/yyyy, mm-dd-yyyy, etc. + //"\\b('[1789][0-9])\\b", // eslint-disable-line quotes + '\\b[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})\\b', + '\\b(?: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})\\b', // November 29th, 1997 + '\\(((?:19|20)[0-9]{2})\\)', // (19xx) or (20xx) -- with parens -- do this before 19xx 20xx such that this has priority + '\\b((?:19|20)[0-9]{2})\\b', // simple 19xx or 20xx with word boundaries + '\\b\'([17-9][0-9])\\b', // '95, '17, ... + // :TODO: DD/MMM/YY, DD/MMMM/YY, DD/MMM/YYYY, etc. + ], + + web : { + path : '/f/', + routePath : '/f/[a-zA-Z0-9]+$', + expireMinutes : 1440, // 1 day + }, + + // + // File area storage location tag/value pairs. + // Non-absolute paths are relative to |areaStoragePrefix|. + // + storageTags : { + sys_msg_attach : 'sys_msg_attach', + sys_temp_download : 'sys_temp_download', + }, + + areas: { + system_message_attachment : { + name : 'System Message Attachments', + desc : 'File attachments to messages', + storageTags : [ 'sys_msg_attach' ], + }, + + system_temporary_download : { + name : 'System Temporary Downloads', + desc : 'Temporary downloadables', + storageTags : [ 'sys_temp_download' ], + } + } + }, + + eventScheduler : { + + events : { + dailyMaintenance : { + schedule : 'at 11:59pm', + action : '@method:core/misc_scheduled_events.js:dailyMaintenanceScheduledEvent', + }, + trimMessageAreas : { + // may optionally use [or ]@watch:/path/to/file + schedule : 'every 24 hours', + + // action: + // - @method:path/to/module.js:theMethodName + // (path is relative to ENiGMA base dir) + // + // - @execute:/path/to/something/executable.sh + // + action : '@method:core/message_area.js:trimMessageAreasScheduledEvent', + }, + + nntpMaintenance : { + schedule : 'every 12 hours', // should generally be < trimMessageAreas interval + action : '@method:core/servers/content/nntp.js:performMaintenanceTask', + }, + + updateFileAreaStats : { + schedule : 'every 1 hours', + action : '@method:core/file_base_area.js:updateAreaStatsScheduledEvent', + }, + + forgotPasswordMaintenance : { + schedule : 'every 24 hours', + action : '@method:core/web_password_reset.js:performMaintenanceTask', + args : [ '24 hours' ] // items older than this will be removed + }, + + twoFactorRegisterTokenMaintenance : { + schedule : 'every 24 hours', + action : '@method:core/user_temp_token.js:temporaryTokenMaintenanceTask', + args : [ + 'auth_factor2_otp_register', + '24 hours', // expire time + ] + }, + + // + // Enable the following entry in your config.hjson to periodically create/update + // DESCRIPT.ION files for your file base + // + /* + updateDescriptIonFiles : { + schedule : 'on the last day of the week', + action : '@method:core/file_base_list_export.js:updateFileBaseDescFilesScheduledEvent', + } + */ + } + }, + + logging : { + 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 + }, + + debug : { + assertsEnabled : false, + }, + + statLog : { + systemEvents : { + loginHistoryMax: -1, // set to -1 for forever + } + }, + }; +}; \ No newline at end of file diff --git a/core/oputil/oputil_config.js b/core/oputil/oputil_config.js index 8a51fdb5..4548a910 100644 --- a/core/oputil/oputil_config.js +++ b/core/oputil/oputil_config.js @@ -159,7 +159,7 @@ function askNewConfigQuestions(cb) { }, function basic(callback) { getAnswers(QUESTIONS.Basic, answers => { - const defaultConfig = require('../../core/config.js').getDefaultConfig(); + const defaultConfig = require('../../core/config_default')(); // start by plopping in values we want directly from config.js const template = hjson.rt.parse(fs.readFileSync(paths.join(__dirname, '../../misc/config_template.in.hjson'), 'utf8'));