Stop notices appearing twice in server panel
[KiwiIRC.git] / server / kiwi.js
index 97849302b23ac23c12a49bd7cfb30af480a81889..6e697f75a7b306c780af3cc6f3bd003abb455568 100755 (executable)
@@ -1,22 +1,55 @@
 var fs          = require('fs'),
-    _           = require('underscore'),
+    _           = require('lodash'),
     WebListener = require('./weblistener.js'),
     config      = require('./configuration.js'),
-    rehash      = require('./rehash.js');
+    rehash      = require('./rehash.js'),
+    modules     = require('./modules.js'),
+    Identd      = require('./identd.js');
 
 
 
+process.chdir(__dirname + '/../');
+config.loadConfig();
+
+
+// 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;
+
+        if (log_file_name[0] !== '/') {
+            log_file_name = __dirname + '/../' + log_file_name;
+        }
+
+
+
+        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);
+        };
+    })();
+}
 
 
-config.loadConfig();
 
 // Make sure we have a valid config file and at least 1 server
-if (Object.keys(config.get()).length === 0) {
-    console.log('Couldn\'t find a valid config file!');
+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 ((!config.get().servers) || (config.get().servers.length < 1)) {
+if ((!global.config.servers) || (global.config.servers.length < 1)) {
     console.log('No servers defined in config file');
     process.exit(2);
 }
@@ -24,34 +57,141 @@ if ((!config.get().servers) || (config.get().servers.length < 1)) {
 
 
 
+// Create a plugin interface
+global.modules = new modules.Publisher();
+
+// Register as the active interface
+modules.registerPublisher(global.modules);
+
+// 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];
+            }
+        }
+    },
+
+    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;
+        }
+    }
+};
+
+
+
+
+/*
+ * Identd server
+ */
+if (global.config.identd && global.config.identd.enabled) {
+    new Identd({
+        bind_addr: global.config.identd.address,
+        bind_port: global.config.identd.port
+    }).start();
+}
+
+
+
 
 /*
  * Web listeners
  */
 
-// Holder for all the connected clients
-// TODO: Change from an array to an object. Generate sha1 hash within the client
-// and use that as the key. (Much less work involved in removing a client)
-var clients = [];
 
 // Start up a weblistener for each found in the config
-_.each(config.get().servers, function (server) {
-    var wl = new WebListener(server, config.get().transports);
+_.each(global.config.servers, function (server) {
+    var wl = new WebListener(server, global.config.transports);
+
     wl.on('connection', function (client) {
-        clients.push(client);
+        clients.add(client);
+    });
+
+    wl.on('client_dispose', function (client) {
+        clients.remove(client);
     });
-    wl.on('destroy', function (client) {
-        clients = _.reject(clients, function (c) {
-            if (client === c) {
-                c.dispose();
-                return true;
-            }
 
-            return false;
-        });
+    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();
+    }
+}
 
 
 
@@ -64,14 +204,33 @@ _.each(config.get().servers, function (server) {
 process.title = 'kiwiirc';
 
 // Change UID/GID
-if ((config.get().group) && (config.get().group !== '')) {
-    process.setgid(config.get().group);
-}
-if ((config.get().user) && (config.get().user !== '')) {
-    process.setuid(config.get().user);
+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) {
+    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");
+    }
+});
+
+
+
 
 /*
  * Listen for runtime commands
@@ -83,17 +242,16 @@ process.stdin.on('data', function (buffered) {
 
     switch (data) {
         case 'stats':
-            console.log('Connected clients: ' + _.size(clients).toString());
+            console.log('Connected clients: ' + _.size(global.clients.clients).toString());
+            console.log('Num. remote hosts: ' + _.size(global.clients.addresses).toString());
             break;
 
         case 'reconfig':
-            (function () {
-                if (config.loadConfig()) {
-                    console.log('New config file loaded');
-                } else {
-                    console.log("No new config file was loaded");
-                }
-            })();
+            if (config.loadConfig()) {
+                console.log('New config file loaded');
+            } else {
+                console.log("No new config file was loaded");
+            }
 
             break;