X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=server%2Fapp.js;h=7f544a503f62aaf2e68c25dffa3eeeda38adadc7;hb=0fc0605dcb437048daced6ba230b6258d82a9b25;hp=2b6d03067eff2887e8e276fcdafb6855d60b07c9;hpb=5db5955b560df5933f3656a089c1e8b50b727611;p=KiwiIRC.git diff --git a/server/app.js b/server/app.js index 2b6d030..7f544a5 100644 --- a/server/app.js +++ b/server/app.js @@ -1,20 +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 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; @@ -25,12 +27,16 @@ this.init = function (objs) { url = objs.url; dns = objs.dns; crypto = objs.crypto; + events = objs.events; + util = objs.util; ws = objs.ws; jsp = objs.jsp; pro = objs.pro; _ = objs._; starttls = objs.starttls; kiwi = require('./kiwi.js'); + + util.inherits(this.IRCConnection, events.EventEmitter); }; @@ -50,7 +56,7 @@ this.changeUser = function () { try { process.setgid(kiwi.config.group); } catch (err) { - console.log('Failed to set gid: ' + err); + kiwi.log('Failed to set gid: ' + err); process.exit(); } } @@ -59,7 +65,7 @@ this.changeUser = function () { try { process.setuid(kiwi.config.user); } catch (e) { - console.log('Failed to set uid: ' + e); + kiwi.log('Failed to set uid: ' + e); process.exit(); } } @@ -88,11 +94,14 @@ var ircNumerics = { RPL_LISTEND: '323', RPL_NOTOPIC: '331', RPL_TOPIC: '332', + RPL_TOPICWHOTIME: '333', RPL_NAMEREPLY: '353', RPL_ENDOFNAMES: '366', RPL_BANLIST: '367', RPL_ENDOFBANLIST: '368', RPL_MOTD: '372', + RPL_MOTDSTART: '375', + RPL_ENDOFMOTD: '376', RPL_WHOISMODES: '379', ERR_NOSUCHNICK: '401', ERR_CANNOTSENDTOCHAN: '404', @@ -110,317 +119,355 @@ var ircNumerics = { RPL_STARTTLS: '670' }; - - -this.parseIRCMessage = function (websocket, ircSocket, data) { - /*global ircSocketDataHandler */ - var msg, regex, opts, options, opt, i, j, matches, nick, users, chan, channel, - params, nicklist, caps, rtn, obj, tmp, namespace, whois_end = false; - //regex = /^(?::(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@?([a-z0-9\.\-:\/]+)?) )?([a-z0-9]+)(?:(?: ([^:]+))?(?: :(.+))?)$/i; - //regex = /^(?::(\S+) )?(\S+)(?: (?!:)(.+?))?(?: :(.+))?$/i; - regex = /^(?::(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@?([a-z0-9\.\-:\/]+)?) )?(\S+)(?: (?!:)(.+?))?(?: :(.+))?$/i; - - msg = regex.exec(data); - if (msg) { - msg = { - prefix: msg[1], - nick: msg[2], - ident: msg[3], - hostname: msg[4] || '', - command: msg[5], - params: msg[6] || '', - trailing: (msg[7]) ? msg[7].trim() : '' +this.bindIRCCommands = function (irc_connection, websocket) { + var bound_events = [], + bindCommand = function (command, listener) { + command = 'irc_' + command; + irc_connection.on(command, listener); + bound_events.push({"command": command, "listener": listener}); }; - switch (msg.command.toUpperCase()) { - case 'PING': - websocket.sendServerLine('PONG ' + msg.trailing); - break; - case ircNumerics.RPL_WELCOME: - if (ircSocket.IRC.CAP.negotiating) { - ircSocket.IRC.CAP.negotiating = false; - ircSocket.IRC.CAP.enabled = []; - ircSocket.IRC.CAP.requested = []; - ircSocket.IRC.registered = true; - } - //regex = /([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@?([a-z0-9\.\-:\/]+)/i; - //matches = regex.exec(msg.trailing); - nick = msg.params.split(' ')[0]; - websocket.sendClientEvent('connect', {connected: true, host: null, nick: nick}); - break; - case ircNumerics.RPL_ISUPPORT: - opts = msg.params.split(" "); - options = []; - for (i = 0; i < opts.length; i++) { - opt = opts[i].split("=", 2); - opt[0] = opt[0].toUpperCase(); - ircSocket.IRC.options[opt[0]] = (typeof opt[1] !== 'undefined') ? opt[1] : true; - if (_.include(['NETWORK', 'PREFIX', 'CHANTYPES', 'NAMESX'], opt[0])) { - if (opt[0] === 'PREFIX') { - regex = /\(([^)]*)\)(.*)/; - matches = regex.exec(opt[1]); - if ((matches) && (matches.length === 3)) { - ircSocket.IRC.options[opt[0]] = []; - for (j = 0; j < matches[2].length; j++) { - //ircSocket.IRC.options[opt[0]][matches[2].charAt(j)] = matches[1].charAt(j); - ircSocket.IRC.options[opt[0]].push({symbol: matches[2].charAt(j), mode: matches[1].charAt(j)}); - } + bindCommand('PING', function (msg) { + websocket.sendServerLine('PONG ' + msg.trailing); + }); + + bindCommand(ircNumerics.RPL_WELCOME, function (msg) { + if (irc_connection.IRC.CAP.negotiating) { + irc_connection.IRC.CAP.negotiating = false; + irc_connection.IRC.CAP.enabled = []; + irc_connection.IRC.CAP.requested = []; + irc_connection.IRC.registered = true; + } + var nick = msg.params.split(' ')[0]; + websocket.sendClientEvent('connect', {connected: true, host: null, nick: nick}); + }); + bindCommand(ircNumerics.RPL_ISUPPORT, function (msg) { + var opts = msg.params.split(" "), + opt, + i, + j, + regex, + matches; + for (i = 0; i < opts.length; i++) { + opt = opts[i].split("=", 2); + opt[0] = opt[0].toUpperCase(); + irc_connection.IRC.options[opt[0]] = (typeof opt[1] !== 'undefined') ? opt[1] : true; + if (_.include(['NETWORK', 'PREFIX', 'CHANTYPES', 'NAMESX'], opt[0])) { + if (opt[0] === 'PREFIX') { + regex = /\(([^)]*)\)(.*)/; + matches = regex.exec(opt[1]); + if ((matches) && (matches.length === 3)) { + irc_connection.IRC.options[opt[0]] = []; + for (j = 0; j < matches[2].length; j++) { + irc_connection.IRC.options[opt[0]].push({symbol: matches[2].charAt(j), mode: matches[1].charAt(j)}); } - } - if (opt[0] === 'NAMESX') { - websocket.sendServerLine('PROTOCTL NAMESX'); + } } + if (opt[0] === 'NAMESX') { + websocket.sendServerLine('PROTOCTL NAMESX'); + } } + } - websocket.sendClientEvent('options', {server: '', "options": ircSocket.IRC.options}); - break; + websocket.sendClientEvent('options', {server: '', "options": irc_connection.IRC.options}); + }); - case ircNumerics.RPL_ENDOFWHOIS: - whois_end = true; - case ircNumerics.RPL_WHOISUSER: - case ircNumerics.RPL_WHOISSERVER: - case ircNumerics.RPL_WHOISOPERATOR: - case ircNumerics.RPL_WHOISCHANNELS: - case ircNumerics.RPL_WHOISMODES: - websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: whois_end}); - break; + bindCommand(ircNumerics.RPL_ENDOFWHOIS, function (msg) { + websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: true}); + }); - case ircNumerics.RPL_LISTSTART: - (function () { - websocket.sendClientEvent('list_start', {server: ''}); - websocket.kiwi.buffer.list = []; - }()); - break; - case ircNumerics.RPL_LISTEND: - (function () { - if (websocket.kiwi.buffer.list.length > 0) { - websocket.kiwi.buffer.list = _.sortBy(websocket.kiwi.buffer.list, function (channel) { - return channel.num_users; - }); - websocket.sendClientEvent('list_channel', {chans: websocket.kiwi.buffer.list}); - websocket.kiwi.buffer.list = []; - } - websocket.sendClientEvent('list_end', {server: ''}); - }()); - break; + bindCommand(ircNumerics.RPL_WHOISUSER, function (msg) { + websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false}); + }); - case ircNumerics.RPL_LIST: - (function () { - var parts, channel, num_users, modes, topic; - - parts = msg.params.split(' '); - channel = parts[1]; - num_users = parts[2]; - topic = msg.trailing; - - //websocket.sendClientEvent('list_channel', { - websocket.kiwi.buffer.list.push({ - server: '', - channel: channel, - topic: topic, - //modes: modes, - num_users: parseInt(num_users, 10) - }); + bindCommand(ircNumerics.RPL_WHOISSERVER, function (msg) { + websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false}); + }); - if (websocket.kiwi.buffer.list.length > 200) { - websocket.kiwi.buffer.list = _.sortBy(websocket.kiwi.buffer.list, function (channel) { - return channel.num_users; - }); - websocket.sendClientEvent('list_channel', {chans: websocket.kiwi.buffer.list}); - websocket.kiwi.buffer.list = []; - } + bindCommand(ircNumerics.RPL_WHOISOPERATOR, function (msg) { + websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false}); + }); - }()); - break; + bindCommand(ircNumerics.RPL_WHOISCHANNELS, function (msg) { + websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false}); + }); - case ircNumerics.RPL_WHOISIDLE: - params = msg.params.split(" ", 4); + bindCommand(ircNumerics.RPL_WHOISMODES, function (msg) { + websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false}); + }); + + bindCommand(ircNumerics.RPL_LISTSTART, function (msg) { + websocket.sendClientEvent('list_start', {server: ''}); + websocket.kiwi.buffer.list = []; + }); + + 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; + }); + websocket.sendClientEvent('list_channel', {chans: websocket.kiwi.buffer.list}); + websocket.kiwi.buffer.list = []; + } + websocket.sendClientEvent('list_end', {server: ''}); + }); + + bindCommand(ircNumerics.RPL_LIST, function (msg) { + var parts, channel, num_users, topic; + + parts = msg.params.split(' '); + channel = parts[1]; + num_users = parts[2]; + topic = msg.trailing; + + //websocket.sendClientEvent('list_channel', { + websocket.kiwi.buffer.list.push({ + server: '', + channel: channel, + topic: topic, + //modes: modes, + num_users: parseInt(num_users, 10) + }); + + if (websocket.kiwi.buffer.list.length > 200) { + websocket.kiwi.buffer.list = _.sortBy(websocket.kiwi.buffer.list, function (channel) { + return channel.num_users; + }); + websocket.sendClientEvent('list_channel', {chans: websocket.kiwi.buffer.list}); + websocket.kiwi.buffer.list = []; + } + }); + + bindCommand(ircNumerics.RPL_WHOISIDLE, function (msg) { + var params = msg.params.split(" ", 4), rtn = {server: '', nick: params[1], idle: params[2]}; - if (params[3]) { - rtn.logon = params[3]; - } - websocket.sendClientEvent('whois', rtn); - break; - case ircNumerics.RPL_MOTD: - websocket.sendClientEvent('motd', {server: '', "msg": msg.trailing}); - break; - case ircNumerics.RPL_NAMEREPLY: - params = msg.params.split(" "); - nick = params[0]; - chan = params[2]; - users = msg.trailing.split(" "); - nicklist = []; + if (params[3]) { + rtn.logon = params[3]; + } + websocket.sendClientEvent('whois', rtn); + }); + + bindCommand(ircNumerics.RPL_MOTD, function (msg) { + websocket.kiwi.buffer.motd += msg.trailing + '\n'; + }); + + bindCommand(ircNumerics.RPL_MOTDSTART, function (msg) { + websocket.kiwi.buffer.motd = ''; + }); + + bindCommand(ircNumerics.RPL_ENDOFMOTD, function (msg) { + websocket.sendClientEvent('motd', {server: '', 'msg': websocket.kiwi.buffer.motd}); + }); + + bindCommand(ircNumerics.RPL_NAMEREPLY, function (msg) { + var params = msg.params.split(" "), + chan = params[2], + users = msg.trailing.split(" "), + nicklist = [], i = 0; - _.each(users, function (user) { - var j, k, modes = []; - for (j = 0; j < user.length; j++) { - for (k = 0; k < ircSocket.IRC.options.PREFIX.length; k++) { - if (user.charAt(j) === ircSocket.IRC.options.PREFIX[k].symbol) { - modes.push(ircSocket.IRC.options.PREFIX[k].mode); - } + + _.each(users, function (user) { + var j, k, modes = []; + for (j = 0; j < user.length; j++) { + for (k = 0; k < irc_connection.IRC.options.PREFIX.length; k++) { + if (user.charAt(j) === irc_connection.IRC.options.PREFIX[k].symbol) { + modes.push(irc_connection.IRC.options.PREFIX[k].mode); } } - nicklist.push({nick: user, modes: modes}); - if (i++ >= 50) { - websocket.sendClientEvent('userlist', {server: '', 'users': nicklist, channel: chan}); - nicklist = []; - i = 0; - } - }); - if (i > 0) { - websocket.sendClientEvent('userlist', {server: '', "users": nicklist, channel: chan}); - } else { - console.log("oops"); } - break; - case ircNumerics.RPL_ENDOFNAMES: - websocket.sendClientEvent('userlist_end', {server: '', channel: msg.params.split(" ")[1]}); - break; - case ircNumerics.ERR_LINKCHANNEL: - params = msg.params.split(" "); - websocket.sendClientEvent('channel_redirect', {from: params[1], to: params[2]}); - break; - case ircNumerics.ERR_NOSUCHNICK: - websocket.sendClientEvent('irc_error', {error: 'no_such_nick', nick: msg.params.split(" ")[1], reason: msg.trailing}); - break; - case ircNumerics.RPL_BANLIST: - params = msg.params.split(" "); - console.log(params); - websocket.sendClientEvent('banlist', {server: '', channel: params[1], banned: params[2], banned_by: params[3], banned_at: params[4]}); - break; - case ircNumerics.RPL_ENDOFBANLIST: - websocket.sendClientEvent('banlist_end', {server: '', channel: msg.params.split(" ")[1]}); - break; - case 'JOIN': - // Some BNC's send malformed JOIN causing the channel to be as a - // parameter instead of trailing. - if (typeof msg.trailing === 'string' && msg.trailing !== '') { - channel = msg.trailing; - } else if (typeof msg.params === 'string' && msg.params !== '') { - channel = msg.params; + nicklist.push({nick: user, modes: modes}); + if (i++ >= 50) { + websocket.sendClientEvent('userlist', {server: '', 'users': nicklist, channel: chan}); + nicklist = []; + i = 0; } + }); + if (i > 0) { + websocket.sendClientEvent('userlist', {server: '', "users": nicklist, channel: chan}); + } else { + kiwi.log("oops"); + } + }); - websocket.sendClientEvent('join', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: channel}); - if (msg.nick === ircSocket.IRC.nick) { - websocket.sendServerLine('NAMES ' + msg.trailing); - } - break; - case 'PART': - websocket.sendClientEvent('part', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), message: msg.trailing}); + bindCommand(ircNumerics.RPL_ENDOFNAMES, function (msg) { + websocket.sendClientEvent('userlist_end', {server: '', channel: msg.params.split(" ")[1]}); + }); + + bindCommand(ircNumerics.ERR_LINKCHANNEL, function (msg) { + var params = msg.params.split(" "); + websocket.sendClientEvent('channel_redirect', {from: params[1], to: params[2]}); + }); + + bindCommand(ircNumerics.ERR_NOSUCHNICK, function (msg) { + websocket.sendClientEvent('irc_error', {error: 'no_such_nick', nick: msg.params.split(" ")[1], reason: msg.trailing}); + }); + + 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]}); + }); + + bindCommand(ircNumerics.RPL_ENDOFBANLIST, function (msg) { + websocket.sendClientEvent('banlist_end', {server: '', channel: msg.params.split(" ")[1]}); + }); + + bindCommand('JOIN', function (msg) { + var channel; + + // Some BNC's send malformed JOIN causing the channel to be as a + // parameter instead of trailing. + if (typeof msg.trailing === 'string' && msg.trailing !== '') { + channel = msg.trailing; + } else if (typeof msg.params === 'string' && msg.params !== '') { + channel = msg.params; + } + + websocket.sendClientEvent('join', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: channel}); + if (msg.nick === irc_connection.IRC.nick) { + websocket.sendServerLine('NAMES ' + msg.trailing); + } + }); + + bindCommand('PART', function (msg) { + websocket.sendClientEvent('part', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), message: msg.trailing}); + }); + + 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}); + }); + + bindCommand('QUIT', function (msg) { + websocket.sendClientEvent('quit', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, message: msg.trailing}); + }); + + 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)}); + } else { + websocket.sendClientEvent('notice', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, target: msg.params.trim(), msg: msg.trailing}); + } + }); + + bindCommand('NICK', function (msg) { + websocket.sendClientEvent('nick', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, newnick: msg.trailing}); + }); + + bindCommand('TOPIC', function (msg) { + var obj = {nick: msg.nick, channel: msg.params, topic: msg.trailing}; + websocket.sendClientEvent('topic', obj); + }); + + bindCommand(ircNumerics.RPL_TOPIC, function (msg) { + var obj = {nick: '', channel: msg.params.split(" ")[1], topic: msg.trailing}; + websocket.sendClientEvent('topic', obj); + }); + + bindCommand(ircNumerics.RPL_NOTOPIC, function (msg) { + var obj = {nick: '', channel: msg.params.split(" ")[1], topic: ''}; + websocket.sendClientEvent('topic', obj); + }); + + bindCommand(ircNumerics.RPL_TOPICWHOTIME, function (msg) { + var parts = msg.params.split(' '), + nick = parts[2], + channel = parts[1], + when = parts[3], + obj = {nick: nick, channel: channel, when: when}; + websocket.sendClientEvent('topicsetby', obj); + }); + + bindCommand('MODE', function (msg) { + var opts = msg.params.split(" "), + params = {nick: msg.nick}; + + switch (opts.length) { + case 1: + params.effected_nick = opts[0]; + params.mode = msg.trailing; break; - case 'KICK': - 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}); + case 2: + params.channel = opts[0]; + params.mode = opts[1]; break; - case 'QUIT': - websocket.sendClientEvent('quit', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, message: msg.trailing}); + default: + params.channel = opts[0]; + params.mode = opts[1]; + params.effected_nick = opts[2]; break; - case 'NOTICE': - 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)}); + } + websocket.sendClientEvent('mode', params); + }); + + 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 + if (msg.trailing.substr(1, 6) === 'ACTION') { + websocket.sendClientEvent('action', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(7, msg.trailing.length - 2)}); + } else if (msg.trailing.substr(1, 4) === 'KIWI') { + tmp = msg.trailing.substr(6, msg.trailing.length - 2); + namespace = tmp.split(' ', 1)[0]; + websocket.sendClientEvent('kiwi', {namespace: namespace, data: tmp.substr(namespace.length + 1)}); + + } else if (msg.trailing.substr(1, 7) === 'VERSION') { + irc_connection.write('NOTICE ' + msg.nick + ' :' + String.fromCharCode(1) + 'VERSION KiwiIRC' + String.fromCharCode(1) + '\r\n'); } else { - websocket.sendClientEvent('notice', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, target: msg.params.trim(), msg: msg.trailing}); - } - break; - case 'NICK': - websocket.sendClientEvent('nick', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, newnick: msg.trailing}); - break; - case 'TOPIC': - obj = {nick: msg.nick, channel: msg.params, topic: msg.trailing}; - websocket.sendClientEvent('topic', obj); - break; - case ircNumerics.RPL_TOPIC: - obj = {nick: '', channel: msg.params.split(" ")[1], topic: msg.trailing}; - websocket.sendClientEvent('topic', obj); - break; - case ircNumerics.RPL_NOTOPIC: - obj = {nick: '', channel: msg.params.split(" ")[1], topic: ''}; - websocket.sendClientEvent('topic', obj); - break; - case 'MODE': - opts = msg.params.split(" "); - params = {nick: msg.nick}; - switch (opts.length) { - case 1: - params.effected_nick = opts[0]; - params.mode = msg.trailing; - break; - case 2: - params.channel = opts[0]; - params.mode = opts[1]; - break; - default: - params.channel = opts[0]; - params.mode = opts[1]; - params.effected_nick = opts[2]; - break; + websocket.sendClientEvent('ctcp_request', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(1, msg.trailing.length - 2)}); } - websocket.sendClientEvent('mode', params); - break; - case 'PRIVMSG': - if ((msg.trailing.charAt(0) === String.fromCharCode(1)) && (msg.trailing.charAt(msg.trailing.length - 1) === String.fromCharCode(1))) { - // It's a CTCP request - if (msg.trailing.substr(1, 6) === 'ACTION') { - websocket.sendClientEvent('action', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(7, msg.trailing.length - 2)}); - } else if (msg.trailing.substr(1, 4) === 'KIWI') { - tmp = msg.trailing.substr(6, msg.trailing.length - 2); - namespace = tmp.split(' ', 1)[0]; - websocket.sendClientEvent('kiwi', {namespace: namespace, data: tmp.substr(namespace.length + 1)}); - - } else if (msg.trailing.substr(1, 7) === 'VERSION') { - ircSocket.write('NOTICE ' + msg.nick + ' :' + String.fromCharCode(1) + 'VERSION KiwiIRC' + String.fromCharCode(1) + '\r\n'); - } else { - websocket.sendClientEvent('ctcp_request', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(1, msg.trailing.length - 2)}); + } else { + obj = {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing}; + websocket.sendClientEvent('msg', obj); + } + }); + + bindCommand('CAP', function (msg) { + var caps = kiwi.config.cap_options, + options = msg.trailing.split(" "), + opts; + + switch (_.last(msg.params.split(" "))) { + case 'LS': + opts = ''; + _.each(_.intersect(caps, options), function (cap) { + if (opts !== '') { + opts += " "; } + opts += cap; + irc_connection.IRC.CAP.requested.push(cap); + }); + if (opts.length > 0) { + websocket.sendServerLine('CAP REQ :' + opts); } else { - obj = {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing}; - websocket.sendClientEvent('msg', obj); + websocket.sendServerLine('CAP END'); } + // TLS is special + /*if (_.include(options, 'tls')) { + websocket.sendServerLine('STARTTLS'); + ircSocket.IRC.CAP.requested.push('tls'); + }*/ break; - case 'CAP': - caps = kiwi.config.cap_options; - options = msg.trailing.split(" "); - switch (_.last(msg.params.split(" "))) { - case 'LS': - opts = ''; - _.each(_.intersect(caps, options), function (cap) { - if (opts !== '') { - opts += " "; - } - opts += cap; - ircSocket.IRC.CAP.requested.push(cap); - }); - if (opts.length > 0) { - websocket.sendServerLine('CAP REQ :' + opts); - } else { - websocket.sendServerLine('CAP END'); - } - // TLS is special - /*if (_.include(options, 'tls')) { - websocket.sendServerLine('STARTTLS'); - ircSocket.IRC.CAP.requested.push('tls'); - }*/ - break; - case 'ACK': - _.each(options, function (cap) { - ircSocket.IRC.CAP.enabled.push(cap); - }); - if (_.last(msg.params.split(" ")) !== '*') { - ircSocket.IRC.CAP.requested = []; - ircSocket.IRC.CAP.negotiating = false; - websocket.sendServerLine('CAP END'); - } - break; - case 'NAK': - ircSocket.IRC.CAP.requested = []; - ircSocket.IRC.CAP.negotiating = false; + case 'ACK': + _.each(options, function (cap) { + irc_connection.IRC.CAP.enabled.push(cap); + }); + if (_.last(msg.params.split(" ")) !== '*') { + irc_connection.IRC.CAP.requested = []; + irc_connection.IRC.CAP.negotiating = false; websocket.sendServerLine('CAP END'); - break; } break; + case 'NAK': + irc_connection.IRC.CAP.requested = []; + irc_connection.IRC.CAP.negotiating = false; + websocket.sendServerLine('CAP END'); + break; + } + }); /*case ircNumerics.RPL_STARTTLS: try { IRC = ircSocket.IRC; @@ -436,103 +483,82 @@ this.parseIRCMessage = function (websocket, ircSocket, data) { ircSocket.addListener('data', listener); }); }); - //console.log(ircSocket); + //log(ircSocket); } catch (e) { - console.log(e); + kiwi.log(e); } break;*/ - case ircNumerics.ERR_CANNOTSENDTOCHAN: - websocket.sendClientEvent('irc_error', {error: 'cannot_send_to_chan', channel: msg.params.split(" ")[1], reason: msg.trailing}); - break; - case ircNumerics.ERR_TOOMANYCHANNELS: - websocket.sendClientEvent('irc_error', {error: 'too_many_channels', channel: msg.params.split(" ")[1], reason: msg.trailing}); - break; - case ircNumerics.ERR_USERNOTINCHANNEL: - params = msg.params.split(" "); - websocket.sendClientEvent('irc_error', {error: 'user_not_in_channel', nick: params[0], channel: params[1], reason: msg.trainling}); - break; - case ircNumerics.ERR_NOTONCHANNEL: - websocket.sendClientEvent('irc_error', {error: 'not_on_channel', channel: msg.params.split(" ")[1], reason: msg.trailing}); - break; - case ircNumerics.ERR_CHANNELISFULL: - websocket.sendClientEvent('irc_error', {error: 'channel_is_full', channel: msg.params.split(" ")[1], reason: msg.trailing}); - break; - case ircNumerics.ERR_INVITEONLYCHAN: - websocket.sendClientEvent('irc_error', {error: 'invite_only_channel', channel: msg.params.split(" ")[1], reason: msg.trailing}); - break; - case ircNumerics.ERR_BANNEDFROMCHAN: - websocket.sendClientEvent('irc_error', {error: 'banned_from_channel', channel: msg.params.split(" ")[1], reason: msg.trailing}); - break; - case ircNumerics.ERR_BADCHANNELKEY: - websocket.sendClientEvent('irc_error', {error: 'bad_channel_key', channel: msg.params.split(" ")[1], reason: msg.trailing}); - break; - case ircNumerics.ERR_CHANOPRIVSNEEDED: - websocket.sendClientEvent('irc_error', {error: 'chanop_privs_needed', channel: msg.params.split(" ")[1], reason: msg.trailing}); - break; - case ircNumerics.ERR_NICKNAMEINUSE: - websocket.sendClientEvent('irc_error', {error: 'nickname_in_use', nick: _.last(msg.params.split(" ")), reason: msg.trailing}); - break; - case 'ERROR': - ircSocket.end(); - websocket.sendClientEvent('irc_error', {error: 'error', reason: msg.trailing}); - websocket.disconnect(); - break; - case ircNumerics.ERR_NOTREGISTERED: - if (ircSocket.IRC.registered) { - console.log('Kiwi thinks user is registered, but the IRC server thinks differently'); - } - break; - default: - console.log("Unknown command (" + String(msg.command).toUpperCase() + ")"); - } - } else { - console.log("Malformed IRC line: " + data); - } -}; + bindCommand(ircNumerics.ERR_CANNOTSENDTOCHAN, function (msg) { + websocket.sendClientEvent('irc_error', {error: 'cannot_send_to_chan', channel: msg.params.split(" ")[1], reason: msg.trailing}); + }); + bindCommand(ircNumerics.ERR_TOOMANYCHANNELS, function (msg) { + websocket.sendClientEvent('irc_error', {error: 'too_many_channels', channel: msg.params.split(" ")[1], reason: msg.trailing}); + }); + 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}); + }); + bindCommand(ircNumerics.ERR_NOTONCHANNEL, function (msg) { + websocket.sendClientEvent('irc_error', {error: 'not_on_channel', channel: msg.params.split(" ")[1], reason: msg.trailing}); + }); + bindCommand(ircNumerics.ERR_CHANNELISFULL, function (msg) { + websocket.sendClientEvent('irc_error', {error: 'channel_is_full', channel: msg.params.split(" ")[1], reason: msg.trailing}); + }); + bindCommand(ircNumerics.ERR_INVITEONLYCHAN, function (msg) { + websocket.sendClientEvent('irc_error', {error: 'invite_only_channel', channel: msg.params.split(" ")[1], reason: msg.trailing}); + }); -/* - * NOTE: Some IRC servers or BNC's out there incorrectly use - * only \n as a line splitter. - */ -this.ircSocketDataHandler = function (data, websocket, ircSocket) { - var i; - if ((ircSocket.holdLast) && (ircSocket.held !== '')) { - data = ircSocket.held + data; - ircSocket.holdLast = false; - ircSocket.held = ''; - } - if (data.substr(-1) !== '\n') { - ircSocket.holdLast = true; - } - data = data.split("\n"); - for (i = 0; i < data.length; i++) { - if (data[i]) { - if ((ircSocket.holdLast) && (i === data.length - 1)) { - ircSocket.held = data[i]; - break; - } + bindCommand(ircNumerics.ERR_BANNEDFROMCHAN, function (msg) { + websocket.sendClientEvent('irc_error', {error: 'banned_from_channel', channel: msg.params.split(" ")[1], reason: msg.trailing}); + }); - // We have a complete line of data, parse it! - kiwi.parseIRCMessage(websocket, ircSocket, data[i].replace(/^\r+|\r+$/, '')); - } - } -}; + bindCommand(ircNumerics.ERR_BADCHANNELKEY, function (msg) { + websocket.sendClientEvent('irc_error', {error: 'bad_channel_key', channel: msg.params.split(" ")[1], reason: msg.trailing}); + }); + + bindCommand(ircNumerics.ERR_CHANOPRIVSNEEDED, function (msg) { + websocket.sendClientEvent('irc_error', {error: 'chanop_privs_needed', channel: msg.params.split(" ")[1], reason: msg.trailing}); + }); + bindCommand(ircNumerics.ERR_NICKNAMEINUSE, function (msg) { + websocket.sendClientEvent('irc_error', {error: 'nickname_in_use', nick: _.last(msg.params.split(" ")), reason: msg.trailing}); + }); + bindCommand('ERROR', function (msg) { + irc_connection.end(); + websocket.sendClientEvent('irc_error', {error: 'error', reason: msg.trailing}); + websocket.disconnect(); + }); + bindCommand(ircNumerics.ERR_NOTREGISTERED, function (msg) { + if (irc_connection.IRC.registered) { + kiwi.log('Kiwi thinks user is registered, but the IRC server thinks differently'); + } + }); + + return bound_events; +}; + +this.rebindIRCCommands = function () { + _.each(kiwi.connections, function (con) { + _.each(con.sockets, function (sock) { + sock.ircConnection.rebindIRCCommands(); + }); + }); +}; this.httpHandler = function (request, response) { var uri, uri_parts, subs, useragent, agent, server_set, server, nick, debug, touchscreen, hash, - min = {}, public_http_path, port, ssl, host, obj, args, ircuri, pass, target, modifiers, query, + min = {}, public_http_path, port, ssl, obj, args, ircuri, target, modifiers, query, secure = (typeof request.client.encrypted === 'object'); - //try { + try { if (kiwi.config.handle_http) { // Run through any plugins.. args = {request: request, response: response, ssl: secure}; @@ -547,7 +573,7 @@ this.httpHandler = function (request, response) { subs = uri.pathname.substr(0, 4); public_http_path = kiwi.kiwi_root + '/' + kiwi.config.public_http; - + if (typeof uri.query.ircuri !== 'undefined') { ircuri = url.parse(uri.query.ircuri, true); if (ircuri.protocol === 'irc:') { @@ -559,7 +585,7 @@ this.httpHandler = function (request, response) { nick = _.detect(modifiers, function (mod) { return mod === ',isnick'; }); - console.log(request.headers); + kiwi.log(request.headers); response.statusCode = 303; response.setHeader('Location', 'http' + ((secure) ? 's' : '') + '://' + request.headers.host + '/client/' + ircuri.host + '/' + ((!nick) ? target : '')); response.end(); @@ -617,25 +643,25 @@ this.httpHandler = function (request, response) { debug = (typeof uri.query.debug !== 'undefined'); - ssl = (typeof request.socket.pair !== 'undefined'); - port = ssl ? 6697 : 6667; + ssl = secure; // ssl is passed to the client + port = ssl ? kiwi.config.client_defaults.port_ssl : kiwi.config.client_defaults.port; if (uri_parts[1] !== 'client') { if (uri.query) { server_set = ((typeof uri.query.server !== 'undefined') && (uri.query.server !== '')); - server = uri.query.server || 'irc.anonnet.org'; + server = uri.query.server || kiwi.config.client_defaults.server; nick = uri.query.nick || ''; } else { server_set = false; - server = 'irc.anonnet.org'; + server = kiwi.config.client_defaults.server; nick = ''; } } else { server_set = ((typeof uri_parts[2] !== 'undefined') && (uri_parts[2] !== '')); - server = server_set ? uri_parts[2] : 'irc.anonnet.org'; + server = server_set ? uri_parts[2] : kiwi.config.client_defaults.server; if (server.search(/:/) > 0) { port = server.substring(server.search(/:/) + 1); server = server.substring(0, server.search(/:/)); - if (port[0] == '+') { + if (port[0] === '+') { port = port.substring(1); ssl = true; } else { @@ -646,12 +672,12 @@ this.httpHandler = function (request, response) { } // Set the default nick if one isn't provided - if (nick == '') { + if (nick === '') { nick = 'kiwi_?'; } // Set any random numbers if needed - nick = nick.replace('?', Math.floor(Math.random()*100000).toString()); + nick = nick.replace('?', Math.floor(Math.random() * 100000).toString()); response.setHeader('X-Generated-By', 'KiwiIRC'); hash = crypto.createHash('md5').update(touchscreen ? 't' : 'f') @@ -691,10 +717,10 @@ this.httpHandler = function (request, response) { } } catch (e) { response.statusCode = 500; - console.log(e); + kiwi.log(e); } } else { - console.log(err); + kiwi.log(err); response.statusCode = 500; } response.end(); @@ -708,10 +734,10 @@ this.httpHandler = function (request, response) { } } - //} catch (e) { - // console.log('ERROR app.httpHandler()'); - // console.log(e); - //} + } catch (e) { + kiwi.log('ERROR app.httpHandler()'); + kiwi.log(e); + } }; @@ -742,13 +768,13 @@ this.websocketListen = function (servers, handler) { hs = https.createServer(opts, handler); kiwi.io.push(ws.listen(hs, {secure: true})); hs.listen(server.port, server.address); - console.log("Listening on %s:%d with SSL", server.address, server.port); + kiwi.log('Listening on ' + server.address + ':' + server.port.toString() + ' with SSL'); } else { // Start some plain-text server up hs = http.createServer(handler); kiwi.io.push(ws.listen(hs, {secure: false})); hs.listen(server.port, server.address); - console.log("Listening on %s:%d without SSL", server.address, server.port); + kiwi.log('Listening on ' + server.address + ':' + server.port.toString() + ' without SSL'); } kiwi.httpServers.push(hs); @@ -777,7 +803,7 @@ this.websocketListen = function (servers, handler) { this.websocketConnection = function (websocket) { var con; - console.log("New connection!"); + kiwi.log("New connection!"); websocket.kiwi = {address: websocket.handshake.address.address, buffer: {list: []}}; con = kiwi.connections[websocket.kiwi.address]; @@ -798,98 +824,210 @@ 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.ircSocket.write(data + eol); + websocket.ircConnection.write(data + eol, 'utf-8', callback); } catch (e) { } }; - websocket.on('irc connect', kiwi.websocketIRCConnect); + websocket.on('irc connect', function (nick, host, port, ssl, password, callback) { + websocket.ircConnection = new kiwi.IRCConnection(this, nick, host, port, ssl, password, callback); + }); websocket.on('message', kiwi.websocketMessage); websocket.on('disconnect', kiwi.websocketDisconnect); } }; +this.IRCConnection = function (websocket, nick, host, port, ssl, password, callback) { + var ircSocket, + that = this, + regex, + onConnectHandler, + bound_events; + events.EventEmitter.call(this); + onConnectHandler = function () { + that.IRC.nick = nick; + // Send the login data + dns.reverse(websocket.kiwi.address, function (err, domains) { + websocket.kiwi.hostname = (err) ? websocket.kiwi.address : _.first(domains); + if ((kiwi.config.webirc) && (kiwi.config.webirc_pass[host])) { + websocket.sendServerLine('WEBIRC ' + kiwi.config.webirc_pass[host] + ' KiwiIRC ' + websocket.kiwi.hostname + ' ' + websocket.kiwi.address); + } + if (password) { + websocket.sendServerLine('PASS ' + password); + } + websocket.sendServerLine('CAP LS'); + websocket.sendServerLine('NICK ' + nick); + websocket.sendServerLine('USER kiwi_' + nick.replace(/[^0-9a-zA-Z\-_.]/, '') + ' 0 0 :' + nick); + + that.connected = true; + that.emit('connect'); + }); + }; -this.websocketIRCConnect = function (websocket, nick, host, port, ssl, password, callback) { - var ircSocket; - //setup IRC connection if (!ssl) { ircSocket = net.createConnection(port, host); + ircSocket.on('connect', onConnectHandler); } else { - ircSocket = tls.connect(port, host); + ircSocket = tls.connect(port, host, {}, onConnectHandler); } - ircSocket.setEncoding('ascii'); - ircSocket.IRC = {options: {}, CAP: {negotiating: true, requested: [], enabled: []}, registered: false}; - ircSocket.on('error', function (e) { - if (ircSocket.IRC.registered) { + + ircSocket.setEncoding('utf-8'); + this.IRC = {options: {}, CAP: {negotiating: true, requested: [], enabled: []}, registered: false}; + + this.on('error', function (e) { + if (that.IRC.registered) { websocket.emit('disconnect'); } else { websocket.emit('error', e.message); } }); - websocket.ircSocket = ircSocket; - ircSocket.holdLast = false; - ircSocket.held = ''; - ircSocket.on('data', function (data) { - kiwi.ircSocketDataHandler(data, websocket, ircSocket); + ircSocket.on('error', function (e) { + that.connected = false; + that.emit('error', e); + that.destroySoon(); }); - ircSocket.IRC.nick = nick; - // Send the login data - dns.reverse(websocket.kiwi.address, function (err, domains) { - //console.log(domains); - websocket.kiwi.hostname = (err) ? websocket.kiwi.address : _.first(domains); - if ((kiwi.config.webirc) && (kiwi.config.webirc_pass[host])) { - websocket.sendServerLine('WEBIRC ' + kiwi.config.webirc_pass[host] + ' KiwiIRC ' + websocket.kiwi.hostname + ' ' + websocket.kiwi.address); + if (typeof callback === 'function') { + this.on('connect', callback); + } + + regex = /^(?::(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@?([a-z0-9\.\-:\/]+)?) )?(\S+)(?: (?!:)(.+?))?(?: :(.+))?$/i; + ircSocket.holdLast = false; + ircSocket.held = ''; + ircSocket.on('data', function (data) { + var i, msg; + if ((ircSocket.holdLast) && (ircSocket.held !== '')) { + data = ircSocket.held + data; + ircSocket.holdLast = false; + ircSocket.held = ''; } - if (password) { - websocket.sendServerLine('PASS ' + password); + if (data.substr(-1) !== '\n') { + ircSocket.holdLast = true; } - websocket.sendServerLine('CAP LS'); - websocket.sendServerLine('NICK ' + nick); - websocket.sendServerLine('USER kiwi_' + nick.replace(/[^0-9a-zA-Z\-_.]/, '') + ' 0 0 :' + nick); + data = data.split("\n"); + for (i = 0; i < data.length; i++) { + if (data[i]) { + if ((ircSocket.holdLast) && (i === data.length - 1)) { + ircSocket.held = data[i]; + break; + } - if ((callback) && (typeof (callback) === 'function')) { - callback(); + // We have a complete line of data, parse it! + msg = regex.exec(data[i].replace(/^\r+|\r+$/, '')); + if (msg) { + msg = { + prefix: msg[1], + nick: msg[2], + ident: msg[3], + hostname: msg[4] || '', + command: msg[5], + params: msg[6] || '', + trailing: (msg[7]) ? msg[7].trim() : '' + }; + that.emit('irc_' + msg.command.toUpperCase(), msg); + if (that.listeners('irc_' + msg.command.toUpperCase()).length < 1) { + kiwi.log("Unknown command (" + String(msg.command).toUpperCase() + ")"); + } + } else { + kiwi.log("Malformed IRC line: " + data[i].replace(/^\r+|\r+$/, '')); + } + } } }); + + if (callback) { + ircSocket.on('connect', callback); + } + + ircSocket.on('end', function () { + that.connected = false; + that.emit('disconnect', false); + }); + + ircSocket.on('close', function (had_error) { + that.connected = false; + that.emit('disconnect', had_error); + }); + + ircSocket.on('timeout', function () { + ircSocket.destroy(); + that.connected = false; + 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); + }; + + this.end = function (data, encoding, callback) { + that.connected = false; + ircSocket.end(data, encoding, callback); + }; + + this.destroySoon = function () { + ircSocket.destroySoon(); + }; + + bound_events = kiwi.bindIRCCommands(this, websocket); + + this.rebindIRCCommands = function () { + _.each(bound_events, function (event) { + that.removeListener(event.command, event.listener); + }); + bound_events = kiwi.bindIRCCommands(that, websocket); + }; }; 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': @@ -897,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; @@ -905,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; @@ -913,57 +1051,54 @@ 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; case 'quit': - websocket.ircSocket.end('QUIT :' + args.message + '\r\n'); + websocket.ircConnection.end('QUIT :' + args.message + '\r\n'); websocket.sentQUIT = true; - websocket.ircSocket.destroySoon(); + websocket.ircConnection.destroySoon(); websocket.disconnect(); break; 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) { - console.log("Caught error: " + e); - } + //} catch (e) { + // kiwi.log("Caught error (app.websocketMessage): " + e); + //} }; @@ -971,11 +1106,11 @@ this.websocketMessage = function (websocket, msg, callback) { this.websocketDisconnect = function (websocket) { var con; - if ((!websocket.sentQUIT) && (websocket.ircSocket)) { + if ((!websocket.sentQUIT) && (websocket.ircConnection.connected)) { try { - websocket.ircSocket.end('QUIT :' + kiwi.config.quit_message + '\r\n'); + websocket.ircConnection.end('QUIT :' + kiwi.config.quit_message + '\r\n'); websocket.sentQUIT = true; - websocket.ircSocket.destroySoon(); + websocket.ircConnection.destroySoon(); } catch (e) { } } @@ -1004,13 +1139,10 @@ this.rehash = function () { changes = reload_config[1]; if (Object.keys(changes).length !== 0) { - console.log('%s config changes: \n', Object.keys(changes).length, changes); + 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; @@ -1055,19 +1187,26 @@ this.manageControll = function (data) { i; switch (parts[0]) { case 'rehash': - console.log('Rehashing...'); - console.log(kiwi.rehash() ? 'Rehash complete' : 'Rehash failed'); + kiwi.log('Rehashing...'); + kiwi.log(kiwi.rehash() ? 'Rehash complete' : 'Rehash failed'); break; case 'recode': - console.log('Recoding...'); - console.log(kiwi.recode() ? 'Recode complete' : 'Recode failed'); + kiwi.log('Recoding...'); + kiwi.log(kiwi.recode() ? 'Recode complete' : 'Recode failed'); break; case 'mod': if (parts[1] === 'reload') { - console.log('Reloading module (' + parts[2] + ')..'); + if (!parts[2]) { + kiwi.log('Usage: mod reload module_name'); + return; + } + + kiwi.log('Reloading module (' + parts[2] + ')..'); kiwi.kiwi_mod.reloadModule(parts[2]); + } else if (parts[1] === 'list') { + kiwi.kiwi_mod.printMods(); } break; @@ -1075,7 +1214,7 @@ this.manageControll = function (data) { if (parts[1] === 'clear') { kiwi.cache.html = {}; kiwi.cache.alljs = ''; - console.log('HTML cache cleared'); + kiwi.log('HTML cache cleared'); } break; @@ -1083,10 +1222,10 @@ this.manageControll = function (data) { for (i in kiwi.connections) { connections_cnt = connections_cnt + parseInt(kiwi.connections[i].count, 10); } - console.log(connections_cnt.toString() + ' connected clients'); + kiwi.log(connections_cnt.toString() + ' connected clients'); break; default: - console.log('Unknown command \'' + parts[0] + '\''); + kiwi.log('Unknown command \'' + parts[0] + '\''); } };