Highlights/activity alerts
[KiwiIRC.git] / server / app.js
index d6a6b7c06c10a17070cfc5865d87ecd43efc4df2..7f544a503f62aaf2e68c25dffa3eeeda38adadc7 100644 (file)
@@ -1,22 +1,22 @@
 /*jslint sloppy: true, continue: true, forin: true, regexp: true, undef: false, node: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */
 /*globals kiwi_root */
 /* Fuck you, git. */
-var tls = null;
-var net = null;
-var http = null;
-var https = null;
-var fs = null;
-var url = null;
-var dns = null;
-var crypto = null;
-var events = require('events');
-var util = require('util');
-var ws = null;
-var jsp = null;
-var pro = null;
-var _ = null;
-var starttls = null;
-var kiwi = null;
+var tls = null,
+    net = null,
+    http = null,
+    https = null,
+    fs = null,
+    url = null,
+    dns = null,
+    crypto = null,
+    events = null,
+    util = null,
+    ws = null,
+    jsp = null,
+    pro = null,
+    _ = null,
+    starttls = null,
+    kiwi = null;
 
 this.init = function (objs) {
     tls = objs.tls;
@@ -35,6 +35,8 @@ this.init = function (objs) {
     _ = objs._;
     starttls = objs.starttls;
     kiwi = require('./kiwi.js');
+
+    util.inherits(this.IRCConnection, events.EventEmitter);
 };
 
 
@@ -117,17 +119,19 @@ var ircNumerics = {
     RPL_STARTTLS:           '670'
 };
 
-
-
 this.bindIRCCommands = function (irc_connection, websocket) {
-    var boundEvents = [];
+    var bound_events = [],
+        bindCommand = function (command, listener) {
+            command = 'irc_' + command;
+            irc_connection.on(command, listener);
+            bound_events.push({"command": command, "listener": listener});
+        };
 
-    irc_connection.on('irc_PING', function (msg) {
+    bindCommand('PING', function (msg) {
         websocket.sendServerLine('PONG ' + msg.trailing);
     });
-    boundEvents.push('irc_PING');
 
-    irc_connection.on('irc_' + ircNumerics.RPL_WELCOME, function (msg) {
+    bindCommand(ircNumerics.RPL_WELCOME, function (msg) {
         if (irc_connection.IRC.CAP.negotiating) {
             irc_connection.IRC.CAP.negotiating = false;
             irc_connection.IRC.CAP.enabled = [];
@@ -137,9 +141,8 @@ this.bindIRCCommands = function (irc_connection, websocket) {
         var nick =  msg.params.split(' ')[0];
         websocket.sendClientEvent('connect', {connected: true, host: null, nick: nick});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_WELCOME);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_ISUPPORT, function (msg) {
+    bindCommand(ircNumerics.RPL_ISUPPORT, function (msg) {
         var opts = msg.params.split(" "),
             opt,
             i,
@@ -170,45 +173,37 @@ this.bindIRCCommands = function (irc_connection, websocket) {
 
         websocket.sendClientEvent('options', {server: '', "options": irc_connection.IRC.options});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_ISUPPORT);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_ENDOFWHOIS, function (msg) {
+    bindCommand(ircNumerics.RPL_ENDOFWHOIS, function (msg) {
         websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: true});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_ENDOFWHOIS);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_WHOISUSER, function (msg) {
+    bindCommand(ircNumerics.RPL_WHOISUSER, function (msg) {
         websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_WHOISUSER);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_WHOISSERVER, function (msg) {
+    bindCommand(ircNumerics.RPL_WHOISSERVER, function (msg) {
         websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_WHOISSERVER);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_WHOISOPERATOR, function (msg) {
+    bindCommand(ircNumerics.RPL_WHOISOPERATOR, function (msg) {
         websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_WHOISOPERATOR);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_WHOISCHANNELS, function (msg) {
+    bindCommand(ircNumerics.RPL_WHOISCHANNELS, function (msg) {
         websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_WHOISCHANNELS);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_WHOISMODES, function (msg) {
+    bindCommand(ircNumerics.RPL_WHOISMODES, function (msg) {
         websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_WHOISMODES);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_LISTSTART, function (msg) {
+    bindCommand(ircNumerics.RPL_LISTSTART, function (msg) {
         websocket.sendClientEvent('list_start', {server: ''});
         websocket.kiwi.buffer.list = [];
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_LISTSTART);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_LISTEND, function (msg) {
+    bindCommand(ircNumerics.RPL_LISTEND, function (msg) {
         if (websocket.kiwi.buffer.list.length > 0) {
             websocket.kiwi.buffer.list = _.sortBy(websocket.kiwi.buffer.list, function (channel) {
                 return channel.num_users;
@@ -218,9 +213,8 @@ this.bindIRCCommands = function (irc_connection, websocket) {
         }
         websocket.sendClientEvent('list_end', {server: ''});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_LISTEND);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_LIST, function (msg) {
+    bindCommand(ircNumerics.RPL_LIST, function (msg) {
         var parts, channel, num_users, topic;
 
         parts = msg.params.split(' ');
@@ -245,9 +239,8 @@ this.bindIRCCommands = function (irc_connection, websocket) {
             websocket.kiwi.buffer.list = [];
         }
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_LIST);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_WHOISIDLE, function (msg) {
+    bindCommand(ircNumerics.RPL_WHOISIDLE, function (msg) {
         var params = msg.params.split(" ", 4),
             rtn = {server: '', nick: params[1], idle: params[2]};
         if (params[3]) {
@@ -255,24 +248,20 @@ this.bindIRCCommands = function (irc_connection, websocket) {
         }
         websocket.sendClientEvent('whois', rtn);
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_WHOISIDLE);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_MOTD, function (msg) {
+    bindCommand(ircNumerics.RPL_MOTD, function (msg) {
         websocket.kiwi.buffer.motd += msg.trailing + '\n';
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_MOTD);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_MOTDSTART, function (msg) {
+    bindCommand(ircNumerics.RPL_MOTDSTART, function (msg) {
         websocket.kiwi.buffer.motd = '';
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_MOTDSTART);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_ENDOFMOTD, function (msg) {
+    bindCommand(ircNumerics.RPL_ENDOFMOTD, function (msg) {
         websocket.sendClientEvent('motd', {server: '', 'msg': websocket.kiwi.buffer.motd});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_ENDOFMOTD);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_NAMEREPLY, function (msg) {
+    bindCommand(ircNumerics.RPL_NAMEREPLY, function (msg) {
         var params = msg.params.split(" "),
             chan = params[2],
             users = msg.trailing.split(" "),
@@ -301,37 +290,31 @@ this.bindIRCCommands = function (irc_connection, websocket) {
             kiwi.log("oops");
         }
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_NAMEREPLY);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_ENDOFNAMES, function (msg) {
+    bindCommand(ircNumerics.RPL_ENDOFNAMES, function (msg) {
         websocket.sendClientEvent('userlist_end', {server: '', channel: msg.params.split(" ")[1]});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_ENDOFNAMES);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_LINKCHANNEL, function (msg) {
+    bindCommand(ircNumerics.ERR_LINKCHANNEL, function (msg) {
         var params = msg.params.split(" ");
         websocket.sendClientEvent('channel_redirect', {from: params[1], to: params[2]});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_LINKCHANNEL);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_NOSUCHNICK, function (msg) {
+    bindCommand(ircNumerics.ERR_NOSUCHNICK, function (msg) {
         websocket.sendClientEvent('irc_error', {error: 'no_such_nick', nick: msg.params.split(" ")[1], reason: msg.trailing});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_NOSUCHNICK);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_BANLIST, function (msg) {
+    bindCommand(ircNumerics.RPL_BANLIST, function (msg) {
         var params = msg.params.split(" ");
         kiwi.log(params);
         websocket.sendClientEvent('banlist', {server: '', channel: params[1], banned: params[2], banned_by: params[3], banned_at: params[4]});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_BANLIST);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_ENDOFBANLIST, function (msg) {
+    bindCommand(ircNumerics.RPL_ENDOFBANLIST, function (msg) {
         websocket.sendClientEvent('banlist_end', {server: '', channel: msg.params.split(" ")[1]});
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_ENDOFBANLIST);
 
-    irc_connection.on('irc_JOIN', function (msg) {
+    bindCommand('JOIN', function (msg) {
         var channel;
 
         // Some BNC's send malformed JOIN causing the channel to be as a
@@ -347,25 +330,21 @@ this.bindIRCCommands = function (irc_connection, websocket) {
             websocket.sendServerLine('NAMES ' + msg.trailing);
         }
     });
-    boundEvents.push('irc_JOIN');
 
-    irc_connection.on('irc_PART', function (msg) {
+    bindCommand('PART', function (msg) {
         websocket.sendClientEvent('part', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), message: msg.trailing});
     });
-    boundEvents.push('irc_PART');
 
-    irc_connection.on('irc_KICK', function (msg) {
+    bindCommand('KICK', function (msg) {
         var params = msg.params.split(" ");
         websocket.sendClientEvent('kick', {kicked: params[1], nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: params[0].trim(), message: msg.trailing});
     });
-    boundEvents.push('irc_KICK');
 
-    irc_connection.on('irc_QUIT', function (msg) {
+    bindCommand('QUIT', function (msg) {
         websocket.sendClientEvent('quit', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, message: msg.trailing});
     });
-    boundEvents.push('irc_QUIT');
 
-    irc_connection.on('irc_NOTICE', function (msg) {
+    bindCommand('NOTICE', function (msg) {
         if ((msg.trailing.charAt(0) === String.fromCharCode(1)) && (msg.trailing.charAt(msg.trailing.length - 1) === String.fromCharCode(1))) {
             // It's a CTCP response
             websocket.sendClientEvent('ctcp_response', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(1, msg.trailing.length - 2)});
@@ -373,32 +352,27 @@ this.bindIRCCommands = function (irc_connection, websocket) {
             websocket.sendClientEvent('notice', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, target: msg.params.trim(), msg: msg.trailing});
         }
     });
-    boundEvents.push('irc_NOTICE');
 
-    irc_connection.on('irc_NICK', function (msg) {
+    bindCommand('NICK', function (msg) {
         websocket.sendClientEvent('nick', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, newnick: msg.trailing});
     });
-    boundEvents.push('irc_NICK');
 
-    irc_connection.on('irc_TOPIC', function (msg) {
+    bindCommand('TOPIC', function (msg) {
         var obj = {nick: msg.nick, channel: msg.params, topic: msg.trailing};
         websocket.sendClientEvent('topic', obj);
     });
-    boundEvents.push('irc_TOPIC');
 
-    irc_connection.on('irc_' + ircNumerics.RPL_TOPIC, function (msg) {
+    bindCommand(ircNumerics.RPL_TOPIC, function (msg) {
         var obj = {nick: '', channel: msg.params.split(" ")[1], topic: msg.trailing};
         websocket.sendClientEvent('topic', obj);
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_TOPIC);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_NOTOPIC, function (msg) {
+    bindCommand(ircNumerics.RPL_NOTOPIC, function (msg) {
         var obj = {nick: '', channel: msg.params.split(" ")[1], topic: ''};
         websocket.sendClientEvent('topic', obj);
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_NOTOPIC);
 
-    irc_connection.on('irc_' + ircNumerics.RPL_TOPICWHOTIME, function (msg) {
+    bindCommand(ircNumerics.RPL_TOPICWHOTIME, function (msg) {
         var parts = msg.params.split(' '),
             nick = parts[2],
             channel = parts[1],
@@ -406,9 +380,8 @@ this.bindIRCCommands = function (irc_connection, websocket) {
             obj = {nick: nick, channel: channel, when: when};
         websocket.sendClientEvent('topicsetby', obj);
     });
-    boundEvents.push('irc_' + ircNumerics.RPL_TOPICWHOTIME);
 
-    irc_connection.on('irc_MODE', function (msg) {
+    bindCommand('MODE', function (msg) {
         var opts = msg.params.split(" "),
             params = {nick: msg.nick};
 
@@ -429,9 +402,8 @@ this.bindIRCCommands = function (irc_connection, websocket) {
         }
         websocket.sendClientEvent('mode', params);
     });
-    boundEvents.push('irc_MODE');
 
-    irc_connection.on('irc_PRIVMSG', function (msg) {
+    bindCommand('PRIVMSG', function (msg) {
         var tmp, namespace, obj;
         if ((msg.trailing.charAt(0) === String.fromCharCode(1)) && (msg.trailing.charAt(msg.trailing.length - 1) === String.fromCharCode(1))) {
             // It's a CTCP request
@@ -452,9 +424,8 @@ this.bindIRCCommands = function (irc_connection, websocket) {
             websocket.sendClientEvent('msg', obj);
         }
     });
-    boundEvents.push('irc_PRIVMSG');
 
-    irc_connection.on('irc_CAP', function (msg) {
+    bindCommand('CAP', function (msg) {
         var caps = kiwi.config.cap_options,
             options = msg.trailing.split(" "),
             opts;
@@ -497,7 +468,6 @@ this.bindIRCCommands = function (irc_connection, websocket) {
             break;
         }
     });
-    boundEvents.push('irc_CAP');
         /*case ircNumerics.RPL_STARTTLS:
             try {
                 IRC = ircSocket.IRC;
@@ -518,72 +488,60 @@ this.bindIRCCommands = function (irc_connection, websocket) {
                 kiwi.log(e);
             }
             break;*/
-    irc_connection.on('irc_' + ircNumerics.ERR_CANNOTSENDTOCHAN, function (msg) {
+    bindCommand(ircNumerics.ERR_CANNOTSENDTOCHAN, function (msg) {
         websocket.sendClientEvent('irc_error', {error: 'cannot_send_to_chan', channel: msg.params.split(" ")[1], reason: msg.trailing});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_CANNOTSENDTOCHAN);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_TOOMANYCHANNELS, function (msg) {
+    bindCommand(ircNumerics.ERR_TOOMANYCHANNELS, function (msg) {
         websocket.sendClientEvent('irc_error', {error: 'too_many_channels', channel: msg.params.split(" ")[1], reason: msg.trailing});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_TOOMANYCHANNELS);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_USERNOTINCHANNEL, function (msg) {
+    bindCommand(ircNumerics.ERR_USERNOTINCHANNEL, function (msg) {
         var params = msg.params.split(" ");
         websocket.sendClientEvent('irc_error', {error: 'user_not_in_channel', nick: params[0], channel: params[1], reason: msg.trainling});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_USERNOTINCHANNEL);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_NOTONCHANNEL, function (msg) {
+    bindCommand(ircNumerics.ERR_NOTONCHANNEL, function (msg) {
         websocket.sendClientEvent('irc_error', {error: 'not_on_channel', channel: msg.params.split(" ")[1], reason: msg.trailing});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_NOTONCHANNEL);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_CHANNELISFULL, function (msg) {
+    bindCommand(ircNumerics.ERR_CHANNELISFULL, function (msg) {
         websocket.sendClientEvent('irc_error', {error: 'channel_is_full', channel: msg.params.split(" ")[1], reason: msg.trailing});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_CHANNELISFULL);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_INVITEONLYCHAN, function (msg) {
+    bindCommand(ircNumerics.ERR_INVITEONLYCHAN, function (msg) {
         websocket.sendClientEvent('irc_error', {error: 'invite_only_channel', channel: msg.params.split(" ")[1], reason: msg.trailing});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_INVITEONLYCHAN);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_BANNEDFROMCHAN, function (msg) {
+    bindCommand(ircNumerics.ERR_BANNEDFROMCHAN, function (msg) {
         websocket.sendClientEvent('irc_error', {error: 'banned_from_channel', channel: msg.params.split(" ")[1], reason: msg.trailing});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_BANNEDFROMCHAN);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_BADCHANNELKEY, function (msg) {
+    bindCommand(ircNumerics.ERR_BADCHANNELKEY, function (msg) {
         websocket.sendClientEvent('irc_error', {error: 'bad_channel_key', channel: msg.params.split(" ")[1], reason: msg.trailing});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_BADCHANNELKEY);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_CHANOPRIVSNEEDED, function (msg) {
+    bindCommand(ircNumerics.ERR_CHANOPRIVSNEEDED, function (msg) {
         websocket.sendClientEvent('irc_error', {error: 'chanop_privs_needed', channel: msg.params.split(" ")[1], reason: msg.trailing});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_CHANOPRIVSNEEDED);
 
-    irc_connection.on('irc_' + ircNumerics.ERR_NICKNAMEINUSE, function (msg) {
+    bindCommand(ircNumerics.ERR_NICKNAMEINUSE, function (msg) {
         websocket.sendClientEvent('irc_error', {error: 'nickname_in_use', nick: _.last(msg.params.split(" ")), reason: msg.trailing});
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_NICKNAMEINUSE);
 
-    irc_connection.on('irc_ERROR', function (msg) {
+    bindCommand('ERROR', function (msg) {
         irc_connection.end();
         websocket.sendClientEvent('irc_error', {error: 'error', reason: msg.trailing});
         websocket.disconnect();
     });
-    boundEvents.push('irc_ERROR');
 
-    irc_connection.on('irc_' + ircNumerics.ERR_NOTREGISTERED, function (msg) {
+    bindCommand(ircNumerics.ERR_NOTREGISTERED, function (msg) {
         if (irc_connection.IRC.registered) {
             kiwi.log('Kiwi thinks user is registered, but the IRC server thinks differently');
         }
     });
-    boundEvents.push('irc_' + ircNumerics.ERR_NOTREGISTERED);
 
-    return boundEvents;
+    return bound_events;
 };
 
 this.rebindIRCCommands = function () {
@@ -866,11 +824,14 @@ this.websocketConnection = function (websocket) {
             websocket.emit('message', data);
         };
 
-        websocket.sendServerLine = function (data, eol) {
-            eol = (typeof eol === 'undefined') ? '\r\n' : eol;
+        websocket.sendServerLine = function (data, eol, callback) {
+            if ((arguments.length < 3) && (typeof eol === 'function')) {
+                callback = eol;
+            }
+            eol = (typeof eol !== 'string') ? '\r\n' : eol;
 
             try {
-                websocket.ircConnection.write(data + eol);
+                websocket.ircConnection.write(data + eol, 'utf-8', callback);
             } catch (e) { }
         };
 
@@ -888,7 +849,7 @@ this.IRCConnection = function (websocket, nick, host, port, ssl, password, callb
         that = this,
         regex,
         onConnectHandler,
-        boundEvents;
+        bound_events;
 
     events.EventEmitter.call(this);
 
@@ -919,7 +880,7 @@ this.IRCConnection = function (websocket, nick, host, port, ssl, password, callb
         ircSocket = tls.connect(port, host, {}, onConnectHandler);
     }
 
-    ircSocket.setEncoding('ascii');
+    ircSocket.setEncoding('utf-8');
     this.IRC = {options: {}, CAP: {negotiating: true, requested: [], enabled: []}, registered: false};
 
     this.on('error', function (e) {
@@ -1004,6 +965,10 @@ this.IRCConnection = function (websocket, nick, host, port, ssl, password, callb
         that.emit('error', {message: 'Connection timed out'});
     });
 
+    ircSocket.on('drain', function () {
+        that.emit('drain');
+    });
+
     this.write = function (data, encoding, callback) {
         ircSocket.write(data, encoding, callback);
     };
@@ -1017,46 +982,52 @@ this.IRCConnection = function (websocket, nick, host, port, ssl, password, callb
         ircSocket.destroySoon();
     };
 
-    boundEvents = kiwi.bindIRCCommands(this, websocket);
+    bound_events = kiwi.bindIRCCommands(this, websocket);
 
     this.rebindIRCCommands = function () {
-        _.each(boundEvents, function (event) {
-            that.removeAllListeners(event);
+        _.each(bound_events, function (event) {
+            that.removeListener(event.command, event.listener);
         });
-        boundEvents = kiwi.bindIRCCommands(that, websocket);
+        bound_events = kiwi.bindIRCCommands(that, websocket);
     };
 };
-util.inherits(this.IRCConnection, events.EventEmitter);
 
-//this.IRCConnection.prototype = events.EventEmitter;
 
 
 this.websocketMessage = function (websocket, msg, callback) {
     var args, obj, channels, keys;
-    try {
-        msg.data = JSON.parse(msg.data);
+    //try {
+        if ((callback) && (typeof (callback) !== 'function')) {
+            callback = null;
+        }
+        try {
+            msg.data = JSON.parse(msg.data);
+        } catch (e) {
+            kiwi.log('[app.websocketMessage] JSON parsing error ' + msg.data);
+            return;
+        }
         args = msg.data.args;
         switch (msg.data.method) {
         case 'privmsg':
             if ((args.target) && (args.msg)) {
                 obj = kiwi.kiwi_mod.run('msgsend', args, {websocket: websocket});
                 if (obj !== null) {
-                    websocket.sendServerLine('PRIVMSG ' + args.target + ' :' + args.msg);
+                    websocket.sendServerLine('PRIVMSG ' + args.target + ' :' + args.msg, callback);
                 }
             }
             break;
         case 'ctcp':
             if ((args.target) && (args.type)) {
                 if (args.request) {
-                    websocket.sendServerLine('PRIVMSG ' + args.target + ' :' + String.fromCharCode(1) + args.type.toUpperCase() + ' ' + args.params + String.fromCharCode(1));
+                    websocket.sendServerLine('PRIVMSG ' + args.target + ' :' + String.fromCharCode(1) + args.type.toUpperCase() + ' ' + args.params + String.fromCharCode(1), callback);
                 } else {
-                    websocket.sendServerLine('NOTICE ' + args.target + ' :' + String.fromCharCode(1) + args.type.toUpperCase() + ' ' + args.params + String.fromCharCode(1));
+                    websocket.sendServerLine('NOTICE ' + args.target + ' :' + String.fromCharCode(1) + args.type.toUpperCase() + ' ' + args.params + String.fromCharCode(1), callback);
                 }
             }
             break;
 
         case 'raw':
-            websocket.sendServerLine(args.data);
+            websocket.sendServerLine(args.data, callback);
             break;
 
         case 'join':
@@ -1064,7 +1035,7 @@ this.websocketMessage = function (websocket, msg, callback) {
                 channels = args.channel.split(",");
                 keys = (args.key) ? args.key.split(",") : [];
                 _.each(channels, function (chan, index) {
-                    websocket.sendServerLine('JOIN ' + chan + ' ' + (keys[index] || ''));
+                    websocket.sendServerLine('JOIN ' + chan + ' ' + (keys[index] || ''), callback);
                 });
             }
             break;
@@ -1072,7 +1043,7 @@ this.websocketMessage = function (websocket, msg, callback) {
         case 'part':
             if (args.channel) {
                 _.each(args.channel.split(","), function (chan) {
-                    websocket.sendServerLine('PART ' + chan);
+                    websocket.sendServerLine('PART ' + chan, callback);
                 });
             }
             break;
@@ -1080,16 +1051,16 @@ this.websocketMessage = function (websocket, msg, callback) {
         case 'topic':
             if (args.channel) {
                 if (args.topic) {
-                    websocket.sendServerLine('TOPIC ' + args.channel + ' :' + args.topic);
+                    websocket.sendServerLine('TOPIC ' + args.channel + ' :' + args.topic, callback);
                 } else {
-                    websocket.sendServerLine('TOPIC ' + args.channel);
+                    websocket.sendServerLine('TOPIC ' + args.channel, callback);
                 }
             }
             break;
 
         case 'kick':
             if ((args.channel) && (args.nick)) {
-                websocket.sendServerLine('KICK ' + args.channel + ' ' + args.nick + ':' + args.reason);
+                websocket.sendServerLine('KICK ' + args.channel + ' ' + args.nick + ':' + args.reason, callback);
             }
             break;
 
@@ -1102,35 +1073,32 @@ this.websocketMessage = function (websocket, msg, callback) {
 
         case 'notice':
             if ((args.target) && (args.msg)) {
-                websocket.sendServerLine('NOTICE ' + args.target + ' :' + args.msg);
+                websocket.sendServerLine('NOTICE ' + args.target + ' :' + args.msg, callback);
             }
             break;
 
         case 'mode':
             if ((args.target) && (args.mode)) {
-                websocket.sendServerLine('MODE ' + args.target + ' ' + args.mode + ' ' + args.params);
+                websocket.sendServerLine('MODE ' + args.target + ' ' + args.mode + ' ' + args.params, callback);
             }
             break;
 
         case 'nick':
             if (args.nick) {
-                websocket.sendServerLine('NICK ' + args.nick);
+                websocket.sendServerLine('NICK ' + args.nick, callback);
             }
             break;
 
         case 'kiwi':
             if ((args.target) && (args.data)) {
-                websocket.sendServerLine('PRIVMSG ' + args.target + ': ' + String.fromCharCode(1) + 'KIWI ' + args.data + String.fromCharCode(1));
+                websocket.sendServerLine('PRIVMSG ' + args.target + ': ' + String.fromCharCode(1) + 'KIWI ' + args.data + String.fromCharCode(1), callback);
             }
             break;
         default:
         }
-        if ((callback) && (typeof (callback) === 'function')) {
-            callback();
-        }
-    } catch (e) {
-        kiwi.log("Caught error: " + e);
-    }
+    //} catch (e) {
+    //    kiwi.log("Caught error (app.websocketMessage): " + e);
+    //}
 };
 
 
@@ -1174,10 +1142,7 @@ this.rehash = function () {
         kiwi.log('%s config changes: \n', Object.keys(changes).length, changes);
         for (i in changes) {
             switch (i) {
-            case 'ports':
-            case 'bind_address':
-            case 'ssl_key':
-            case 'ssl_cert':
+            case 'servers':
                 kiwi.websocketListen(kiwi.config.servers, kiwi.httpHandler);
                 delete changes.ports;
                 delete changes.bind_address;
@@ -1240,6 +1205,8 @@ this.manageControll = function (data) {
 
             kiwi.log('Reloading module (' + parts[2] + ')..');
             kiwi.kiwi_mod.reloadModule(parts[2]);
+        } else if (parts[1] === 'list') {
+            kiwi.kiwi_mod.printMods();
         }
         break;