<commit_before>/* jslint node: true */
'use strict';

var miscUtil			= require('./misc_util.js');

var fs					= require('fs');
var paths				= require('path');
var async				= require('async');
var _					= require('lodash');
var hjson				= require('hjson');
var assert              = require('assert');

exports.init				= init;
exports.getDefaultPath		= getDefaultPath;

function hasMessageConferenceAndArea(config) {
    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
    
    var 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;
}

function init(configPath, cb) {
	async.waterfall(
		[
			function loadUserConfig(callback) {
				if(_.isString(configPath)) {
					fs.readFile(configPath, { encoding : 'utf8' }, function configData(err, data) {
						if(err) {
							callback(err);
						} else {
							try {
								var configJson = hjson.parse(data);
								callback(null, configJson);
							} catch(e) {
								callback(e);							
							}
						}
					});
				} else {
					callback(null, { } );
				}
			},
			function mergeWithDefaultConfig(configJson, callback) {
				var mergedConfig = _.merge(getDefaultConfig(), configJson, function mergeCustomizer(conf1, conf2) {
					//	Arrays should always concat
					if(_.isArray(conf1)) {
						//	:TODO: look for collisions & override dupes
						return conf1.concat(conf2);
					}
				});

				callback(null, mergedConfig);
			},
			function validate(mergedConfig, callback) {
				//
				//	Various sections must now exist in config
				//
                if(hasMessageConferenceAndArea(mergedConfig)) {
                    var msgAreasErr = new Error('Please create at least one message conference and area!');
					msgAreasErr.code = 'EBADCONFIG';
					callback(msgAreasErr);
                } else {
                    callback(null, mergedConfig);
                }
			}
		],
		function complete(err, mergedConfig) {
			exports.config = mergedConfig;
			cb(err);
		}
	);
}

function getDefaultPath() {
	var base = miscUtil.resolvePath('~/');
	if(base) {
		//	e.g. /home/users/joeuser/.config/enigma-bbs/config.hjson
		return paths.join(base, '.config', 'enigma-bbs', 'config.hjson');
	}
}

function getDefaultConfig() {
	return {
		general : {
			boardName		: 'Another Fine ENiGMA½ BBS',

			closedSystem	: false,					//	is the system closed to new users?

			loginAttempts	: 3,

			menuFile		: 'menu.hjson',				//	Override to use something else, e.g. demo.hjson. Can be a full path (defaults to ./mods)
			promptFile		: 'prompt.hjson',			//	Override to use soemthing else, e.g. myprompt.hjson. Can be a full path (defaults to ./mods)
		},

		//	:TODO: see notes below about 'theme' section - move this!
		preLoginTheme : 'luciano_blocktronics',

		users : {
			usernameMin			: 2,
			usernameMax			: 16,	//	Note that FidoNet wants 36 max
			usernamePattern		: '^[A-Za-z0-9~!@#$%^&*()\\-\\_+]+$',

			passwordMin			: 6,
			passwordMax			: <PASSWORD>,

			realNameMax			: 32,
			locationMax			: 32,
			affilsMax			: 32,
			emailMax			: 255,
			webMax				: 255,

			requireActivation	: true,	//	require SysOp activation?
			invalidUsernames	: [],

			groups				: [ 'users', 'sysops' ],		//	built in groups
			defaultGroups		: [ 'users' ],					//	default groups new users belong to

			newUserNames		: [ 'new', 'apply' ],			//	Names reserved for applying

			//	:TODO: Mystic uses TRASHCAN.DAT for this -- is there a reason to support something like that?
			badUserNames		: [ 'sysop', 'admin', 'administrator', 'root', 'all' ],
		},

		//	:TODO: better name for "defaults"... which is redundant here!
		/*
		Concept
		"theme" : {
			"default" : "defaultThemeName", // or "*"
			"preLogin" : "*",
			"passwordChar" : "*",
			...
		}
		*/
		defaults : {
			theme			: 'luciano_blocktronics',
			passwordChar	: '*',		//	TODO: move to user ?
			dateFormat	: {
				short	: 'MM/DD/YYYY',
			},
			timeFormat : {
				short	: 'h:mm a',
			},
			dateTimeFormat : {
				short	: 'MM/DD/YYYY h:mm a',
			}
		},

		menus : {
			cls		: true,	//	Clear screen before each menu by default?
		},	

		paths		: {
			mods				: paths.join(__dirname, './../mods/'),
			servers				: paths.join(__dirname, './servers/'),

			scannerTossers		: paths.join(__dirname, './scanner_tossers/'),
			mailers				: paths.join(__dirname, './mailers/')		,

			art					: paths.join(__dirname, './../mods/art/'),
			themes				: paths.join(__dirname, './../mods/themes/'),
			logs				: paths.join(__dirname, './../logs/'),	//	:TODO: set up based on system, e.g. /var/logs/enigmabbs or such
			db					: paths.join(__dirname, './../db/'),
			dropFiles			: paths.join(__dirname, './../dropfiles/'),	//	+ "/node<x>/
			misc				: paths.join(__dirname, './../misc/'),
		},
		
		servers : {
			telnet : {
				port			: 8888,
				enabled			: true,
				firstMenu		: 'telnetConnected',
			},
			ssh : {
				port				: 8889,
				enabled				: true,

				//<commit_msg>SSH not enabled by default (Req's PK/pass in config)<commit_after>/* jslint node: true */
'use strict';

var miscUtil			= require('./misc_util.js');

var fs					= require('fs');
var paths				= require('path');
var async				= require('async');
var _					= require('lodash');
var hjson				= require('hjson');
var assert              = require('assert');

exports.init				= init;
exports.getDefaultPath		= getDefaultPath;

function hasMessageConferenceAndArea(config) {
    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
    
    var 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;
}

function init(configPath, cb) {
	async.waterfall(
		[
			function loadUserConfig(callback) {
				if(_.isString(configPath)) {
					fs.readFile(configPath, { encoding : 'utf8' }, function configData(err, data) {
						if(err) {
							callback(err);
						} else {
							try {
								var configJson = hjson.parse(data);
								callback(null, configJson);
							} catch(e) {
								callback(e);							
							}
						}
					});
				} else {
					callback(null, { } );
				}
			},
			function mergeWithDefaultConfig(configJson, callback) {
				var mergedConfig = _.merge(getDefaultConfig(), configJson, function mergeCustomizer(conf1, conf2) {
					//	Arrays should always concat
					if(_.isArray(conf1)) {
						//	:TODO: look for collisions & override dupes
						return conf1.concat(conf2);
					}
				});

				callback(null, mergedConfig);
			},
			function validate(mergedConfig, callback) {
				//
				//	Various sections must now exist in config
				//
                if(hasMessageConferenceAndArea(mergedConfig)) {
                    var msgAreasErr = new Error('Please create at least one message conference and area!');
					msgAreasErr.code = 'EBADCONFIG';
					callback(msgAreasErr);
                } else {
                    callback(null, mergedConfig);
                }
			}
		],
		function complete(err, mergedConfig) {
			exports.config = mergedConfig;
			cb(err);
		}
	);
}

function getDefaultPath() {
	var base = miscUtil.resolvePath('~/');
	if(base) {
		//	e.g. /home/users/joeuser/.config/enigma-bbs/config.hjson
		return paths.join(base, '.config', 'enigma-bbs', 'config.hjson');
	}
}

function getDefaultConfig() {
	return {
		general : {
			boardName		: 'Another Fine ENiGMA½ BBS',

			closedSystem	: false,					//	is the system closed to new users?

			loginAttempts	: 3,

			menuFile		: 'menu.hjson',				//	Override to use something else, e.g. demo.hjson. Can be a full path (defaults to ./mods)
			promptFile		: 'prompt.hjson',			//	Override to use soemthing else, e.g. myprompt.hjson. Can be a full path (defaults to ./mods)
		},

		//	:TODO: see notes below about 'theme' section - move this!
		preLoginTheme : 'luciano_blocktronics',

		users : {
			usernameMin			: 2,
			usernameMax			: 16,	//	Note that FidoNet wants 36 max
			usernamePattern		: '^[A-Za-z0-9~!@#$%^&*()\\-\\_+]+$',

			passwordMin			: 6,
			passwordMax			: <PASSWORD>,

			realNameMax			: 32,
			locationMax			: 32,
			affilsMax			: 32,
			emailMax			: 255,
			webMax				: 255,

			requireActivation	: true,	//	require SysOp activation?
			invalidUsernames	: [],

			groups				: [ 'users', 'sysops' ],		//	built in groups
			defaultGroups		: [ 'users' ],					//	default groups new users belong to

			newUserNames		: [ 'new', 'apply' ],			//	Names reserved for applying

			//	:TODO: Mystic uses TRASHCAN.DAT for this -- is there a reason to support something like that?
			badUserNames		: [ 'sysop', 'admin', 'administrator', 'root', 'all' ],
		},

		//	:TODO: better name for "defaults"... which is redundant here!
		/*
		Concept
		"theme" : {
			"default" : "defaultThemeName", // or "*"
			"preLogin" : "*",
			"passwordChar" : "*",
			...
		}
		*/
		defaults : {
			theme			: 'luciano_blocktronics',
			passwordChar	: '*',		//	TODO: move to user ?
			dateFormat	: {
				short	: 'MM/DD/YYYY',
			},
			timeFormat : {
				short	: 'h:mm a',
			},
			dateTimeFormat : {
				short	: 'MM/DD/YYYY h:mm a',
			}
		},

		menus : {
			cls		: true,	//	Clear screen before each menu by default?
		},	

		paths		: {
			mods				: paths.join(__dirname, './../mods/'),
			servers				: paths.join(__dirname, './servers/'),

			scannerTossers		: paths.join(__dirname, './scanner_tossers/'),
			mailers				: paths.join(__dirname, './mailers/')		,

			art					: paths.join(__dirname, './../mods/art/'),
			themes				: paths.join(__dirname, './../mods/themes/'),
			logs				: paths.join(__dirname, './../logs/'),	//	:TODO: set up based on system, e.g. /var/logs/enigmabbs or such
			db					: paths.join(__dirname, './../db/'),
			dropFiles			: paths.join(__dirname, './../dropfiles/'),	//	+ "/node<x>/
			misc				: paths.join(__dirname, './../misc/'),
		},
		
		servers : {
			telnet : {
				port			: 8888,
				enabled			: true,
				firstMenu		: 'telnetConnected',
			},
			ssh : {
				port				: 8889,
				enabled				: false,    //  defualt to false as PK/pass in config.hjson are required

				//
{'fraction_non_alphanumeric': 0.14965617082880928, 'fraction_numerical': 0.0066956207021353605, 'mean_word_length': 2.305322966507177, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3984697', 'n_tokens_mistral': 4613, 'n_tokens_neox': 3734, 'n_words': 845}