X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=server%2Fkiwi.js;h=6e697f75a7b306c780af3cc6f3bd003abb455568;hb=e35f86f072142d04d00ee9e834f2ce2a3c897478;hp=cae8fff8bad76d8e2d0564ba3e743fb12469002a;hpb=897abfc347d8b3fadf64d8ee922a1fbffea6bbee;p=KiwiIRC.git diff --git a/server/kiwi.js b/server/kiwi.js old mode 100644 new mode 100755 index cae8fff..6e697f7 --- a/server/kiwi.js +++ b/server/kiwi.js @@ -1,227 +1,270 @@ -/*jslint continue: true, forin: true, regexp: true, undef: false, node: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */ -"use strict"; -var tls = require('tls'), - net = require('net'), - http = require('http'), - https = require('https'), - fs = require('fs'), - url = require('url'), - dns = require('dns'), - crypto = require('crypto'), - events = require("events"), - util = require('util'), - ws = require('socket.io'), - jsp = require("uglify-js").parser, - pro = require("uglify-js").uglify, - _ = require('./lib/underscore.min.js'), - starttls = require('./lib/starttls.js'), - app = require(__dirname + '/app.js'); - - -// Libraries may need to know kiwi.js path as __dirname -// only gives that librarys path. Set it here for usage later. -this.kiwi_root = __dirname; - - - -// How to handle log output -this.log = function(str, level) { - level = level || 0; - console.log(str); -} +var fs = require('fs'), + _ = require('lodash'), + WebListener = require('./weblistener.js'), + config = require('./configuration.js'), + rehash = require('./rehash.js'), + modules = require('./modules.js'), + Identd = require('./identd.js'); -/* - * Configuration and rehashing routines - */ -var config_filename = 'config.json', - config_dirs = ['/etc/kiwiirc/', this.kiwi_root + '/']; - -this.config = {}; -this.loadConfig = function () { - var i, j, - nconf = {}, - cconf = {}, - found_config = false; - - for (i in config_dirs) { - try { - if (fs.lstatSync(config_dirs[i] + config_filename).isDirectory() === false) { - found_config = true; - nconf = JSON.parse(fs.readFileSync(config_dirs[i] + config_filename, 'ascii')); - for (j in nconf) { - // If this has changed from the previous config, mark it as changed - if (!_.isEqual(this.config[j], nconf[j])) { - cconf[j] = nconf[j]; - } - - this.config[j] = nconf[j]; - } - - this.log('Loaded config file ' + config_dirs[i] + config_filename); - break; - } - } catch (e) { - switch (e.code) { - case 'ENOENT': // No file/dir - break; - default: - this.log('An error occured parsing the config file ' + config_dirs[i] + config_filename + ': ' + e.message); - return false; - } - continue; - } - } - if (Object.keys(this.config).length === 0) { - if (!found_config) { - this.log('Couldn\'t find a config file!'); - } - return false; - } - return [nconf, cconf]; -}; +process.chdir(__dirname + '/../'); +config.loadConfig(); -// Reloads the config during runtime -this.rehash = function () { - return app.rehash(); -} -// Reloads app.js during runtime for any recoding -this.recode = function () { - if (typeof require.cache[this.kiwi_root + '/app.js'] !== 'undefined'){ - delete require.cache[this.kiwi_root + '/app.js']; - } +// If we're not running in the forground and we have a log file.. switch +// console.log to output to a file +if (process.argv.indexOf('-f') === -1 && global.config && global.config.log) { + (function () { + var log_file_name = global.config.log; - app = null; - app = require(__dirname + '/app.js'); + if (log_file_name[0] !== '/') { + log_file_name = __dirname + '/../' + log_file_name; + } - var objs = {tls:tls, net:net, http:http, https:https, fs:fs, url:url, dns:dns, crypto:crypto, events:events, util:util, ws:ws, jsp:jsp, pro:pro, _:_, starttls:starttls}; - app.init(objs); - return true; -} + console.log = function() { + var logfile = fs.openSync(log_file_name, 'a'), + out; + out = Array.prototype.join.apply(arguments, [' ']); + // Make sure we out somthing to log and we have an open file + if (!out || !logfile) return; + out += '\n'; + fs.writeSync(logfile, out, null); + fs.closeSync(logfile); + }; + })(); +} -/* - * Before we continue we need the config loaded - */ -if (!this.loadConfig()) { - process.exit(0); + + +// Make sure we have a valid config file and at least 1 server +if (!global.config || Object.keys(global.config).length === 0) { + console.log('Couldn\'t find a valid config.js file (Did you copy the config.example.js file yet?)'); + process.exit(1); } +if ((!global.config.servers) || (global.config.servers.length < 1)) { + console.log('No servers defined in config file'); + process.exit(2); +} +// Create a plugin interface +global.modules = new modules.Publisher(); +// Register as the active interface +modules.registerPublisher(global.modules); -/* - * HTTP file serving - */ -if (this.config.handle_http) { - this.fileServer = new (require('node-static').Server)(__dirname + this.config.public_http); - this.jade = require('jade'); - this.cache = {alljs: '', html: []}; -} -this.httpServers = []; -this.httpHandler = function (request, response) { - return app.httpHandler(request, response); +// Load any modules in the config +if (global.config.module_dir) { + (global.config.modules || []).forEach(function (module_name) { + if (modules.load(global.config.module_dir + module_name + '.js')) { + console.log('Module ' + module_name + ' loaded successfuly'); + } else { + console.log('Module ' + module_name + ' failed to load'); + } + }); } +// Holder for all the connected clients +global.clients = { + clients: Object.create(null), + addresses: Object.create(null), + add: function (client) { + this.clients[client.hash] = client; + if (typeof this.addresses[client.real_address] === 'undefined') { + this.addresses[client.real_address] = Object.create(null); + } + this.addresses[client.real_address][client.hash] = client; + }, + + remove: function (client) { + if (typeof this.clients[client.hash] !== 'undefined') { + delete this.clients[client.hash]; + delete this.addresses[client.real_address][client.hash]; + if (Object.keys(this.addresses[client.real_address]).length < 1) { + delete this.addresses[client.real_address]; + } + } + }, -/* - * Websocket handling - */ -this.connections = {}; -this.io = []; -this.websocketListen = function (servers, handler) { - return app.websocketListen(servers, handler); -} -this.websocketConnection = function (websocket) { - return app.websocketConnection(websocket); -} -this.websocketDisconnect = function () { - return app.websocketDisconnect(this); -} -this.websocketMessage = function (msg, callback) { - return app.websocketMessage(this, msg, callback); -} -this.websocketIRCConnect = function (nick, host, port, ssl, callback) { - return app.websocketIRCConnect(this, nick, host, port, ssl, callback); -} + numOnAddress: function (addr) { + if (typeof this.addresses[addr] !== 'undefined') { + return Object.keys(this.addresses[addr]).length; + } else { + return 0; + } + } +}; + +global.servers = { + servers: Object.create(null), + + addConnection: function (connection) { + var host = connection.irc_host.hostname; + if (!this.servers[host]) { + this.servers[host] = []; + } + this.servers[host].push(connection); + }, + + removeConnection: function (connection) { + var host = connection.irc_host.hostname + if (this.servers[host]) { + this.servers[host] = _.without(this.servers[host], connection); + if (this.servers[host].length === 0) { + delete this.servers[host]; + } + } + }, + + numOnHost: function (host) { + if (this.servers[host]) { + return this.servers[host].length; + } else { + return 0; + } + } +}; /* - * IRC handling + * Identd server */ -this.parseIRCMessage = function (websocket, ircSocket, data) { - return app.parseIRCMessage(websocket, ircSocket, data); -} -this.ircSocketDataHandler = function (data, websocket, ircSocket) { - return app.ircSocketDataHandler(data, websocket, ircSocket); -} -this.IRCConnection = function (websocket, nick, host, port, ssl, password, callback) { - return app.IRCConnection.call(this, websocket, nick, host, port, ssl, password, callback); +if (global.config.identd && global.config.identd.enabled) { + new Identd({ + bind_addr: global.config.identd.address, + bind_port: global.config.identd.port + }).start(); } -util.inherits(this.IRCConnection, events.EventEmitter); -this.bindIRCCommands = function (irc_connection, websocket) { - return app.bindIRCCommands.call(this, irc_connection, websocket); -} - - - /* - * Load up main application source + * Web listeners */ -if (!this.recode()) { - process.exit(0); -} +// Start up a weblistener for each found in the config +_.each(global.config.servers, function (server) { + var wl = new WebListener(server, global.config.transports); + + wl.on('connection', function (client) { + clients.add(client); + }); + + wl.on('client_dispose', function (client) { + clients.remove(client); + }); + + wl.on('listening', function () { + console.log('Listening on %s:%s %s SSL', server.address, server.port, (server.ssl ? 'with' : 'without')); + webListenerRunning(); + }); + + wl.on('error', function (err) { + console.log('Error listening on %s:%s: %s', server.address, server.port, err.code); + // TODO: This should probably be refactored. ^JA + webListenerRunning(); + }); +}); + +// Once all the listeners are listening, set the processes UID/GID +var num_listening = 0; +function webListenerRunning() { + num_listening++; + if (num_listening === global.config.servers.length) { + setProcessUid(); + } +} -// Set the process title -app.setTitle(); /* - * Load the modules as set in the config and print them out + * Process settings */ -this.kiwi_mod = require('./lib/kiwi_mod.js'); -this.kiwi_mod.loadModules(this.kiwi_root, this.config); -this.kiwi_mod.printMods(); + +// Set process title +process.title = 'kiwiirc'; + +// Change UID/GID +function setProcessUid() { + if ((global.config.group) && (global.config.group !== '')) { + process.setgid(global.config.group); + } + if ((global.config.user) && (global.config.user !== '')) { + process.setuid(global.config.user); + } +} // Make sure Kiwi doesn't simply quit on an exception -/*process.on('uncaughtException', function (e) { +process.on('uncaughtException', function (e) { console.log('[Uncaught exception] ' + e); -});*/ + console.log(e.stack); +}); + + +process.on('SIGUSR1', function() { + if (config.loadConfig()) { + console.log('New config file loaded'); + } else { + console.log("No new config file was loaded"); + } +}); + -// Start the server up -this.websocketListen(this.config.servers, this.httpHandler); -// Now we're listening on the network, set our UID/GIDs if required -app.changeUser(); -// Listen for controll messages +/* + * Listen for runtime commands + */ + process.stdin.resume(); -process.stdin.on('data', function (data) { app.manageControll(data); }); +process.stdin.on('data', function (buffered) { + var data = buffered.toString().trim(); + + switch (data) { + case 'stats': + console.log('Connected clients: ' + _.size(global.clients.clients).toString()); + console.log('Num. remote hosts: ' + _.size(global.clients.addresses).toString()); + break; + + case 'reconfig': + if (config.loadConfig()) { + console.log('New config file loaded'); + } else { + console.log("No new config file was loaded"); + } + break; + case 'rehash': + (function () { + rehash.rehashAll(); + console.log('Rehashed'); + })(); + break; + + default: + console.log('Unrecognised command: ' + data); + } +});