From 138d8d17d3398fabfd4a6707b51dbb79a70d68d7 Mon Sep 17 00:00:00 2001 From: Darren Date: Sat, 12 Jul 2014 16:15:53 +0100 Subject: [PATCH] Tidying + splitting up IRC commands --- server/irc/commands.js | 1411 ++------------------------- server/irc/commands/channel.js | 224 +++++ server/irc/commands/messaging.js | 129 +++ server/irc/commands/misc.js | 700 +++++++++++++ server/irc/commands/registration.js | 160 +++ server/irc/commands/user.js | 146 +++ server/irc/connection.js | 4 +- 7 files changed, 1447 insertions(+), 1327 deletions(-) create mode 100644 server/irc/commands/channel.js create mode 100644 server/irc/commands/messaging.js create mode 100644 server/irc/commands/misc.js create mode 100644 server/irc/commands/registration.js create mode 100644 server/irc/commands/user.js diff --git a/server/irc/commands.js b/server/irc/commands.js index 20cb1d1..9516718 100644 --- a/server/irc/commands.js +++ b/server/irc/commands.js @@ -1,7 +1,5 @@ var _ = require('lodash'), irc_numerics, - IrcCommands, - handlers, unknownCommand; irc_numerics = { @@ -153,1328 +151,60 @@ irc_numerics = { }; -IrcCommands = function (irc_connection) { +function IrcCommandsHandler (irc_connection) { this.irc_connection = irc_connection; -}; -module.exports = IrcCommands; + this.handlers = []; + + require('./commands/registration')(this); + require('./commands/channel')(this); + require('./commands/user')(this); + require('./commands/messaging')(this); + require('./commands/misc')(this); +} + + +IrcCommandsHandler.prototype.dispatch = function (irc_command) { + var command_name = irc_command.command; -IrcCommands.prototype.dispatch = function (command, data) { - command += ''; - if (irc_numerics[command]) { - command = irc_numerics[command]; + // Check if we have a numeric->command name- mapping for this command + if (irc_numerics[irc_command.command.toUpperCase()]) { + command_name = irc_numerics[irc_command.command.toUpperCase()]; } - if (handlers[command]) { - handlers[command].call(this, data); + + if (this.handlers[command_name]) { + this.handlers[command_name].call(this, irc_command); } else { - unknownCommand.call(this, command, data); + this.emitUnknownCommand(irc_command); } }; -IrcCommands.addHandler = function (command, handler) { + +IrcCommandsHandler.prototype.addHandler = function (command, handler) { if (typeof handler !== 'function') { return false; } - handlers[command] = handler; + this.handlers[command] = handler; }; -IrcCommands.addNumeric = function (numeric, handler_name) { + +IrcCommandsHandler.prototype.addNumeric = function (numeric, handler_name) { irc_numerics[numeric + ''] = handler_name +''; }; -unknownCommand = function (command, data) { - var params = _.clone(data.params); +IrcCommandsHandler.prototype.emitUnknownCommand = function (command) { this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' unknown_command', { - command: command, - params: params + command: command.command, + params: command.params }); }; -handlers = { - 'RPL_WELCOME': function (command) { - var nick = command.params[0]; - this.irc_connection.registered = true; - this.cap_negotiation = false; - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' connect', { - nick: nick - }); - }, - - 'RPL_ISUPPORT': function (command) { - var options, i, option, matches, j; - options = command.params; - for (i = 1; i < options.length; i++) { - option = options[i].split("=", 2); - option[0] = option[0].toUpperCase(); - this.irc_connection.options[option[0]] = (typeof option[1] !== 'undefined') ? option[1] : true; - if (_.include(['NETWORK', 'PREFIX', 'CHANTYPES', 'CHANMODES', 'NAMESX'], option[0])) { - if (option[0] === 'PREFIX') { - matches = /\(([^)]*)\)(.*)/.exec(option[1]); - if ((matches) && (matches.length === 3)) { - this.irc_connection.options.PREFIX = []; - for (j = 0; j < matches[2].length; j++) { - this.irc_connection.options.PREFIX.push({symbol: matches[2].charAt(j), mode: matches[1].charAt(j)}); - } - } - } else if (option[0] === 'CHANTYPES') { - this.irc_connection.options.CHANTYPES = this.irc_connection.options.CHANTYPES.split(''); - } else if (option[0] === 'CHANMODES') { - this.irc_connection.options.CHANMODES = option[1].split(','); - } else if ((option[0] === 'NAMESX') && (!_.contains(this.irc_connection.cap.enabled, 'multi-prefix'))) { - this.irc_connection.write('PROTOCTL NAMESX'); - } - } - } - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' options', { - options: this.irc_connection.options, - cap: this.irc_connection.cap.enabled - }); - }, - - 'RPL_ENDOFWHOIS': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' endofwhois', { - nick: command.params[1], - msg: command.params[command.params.length - 1] - }); - }, - - 'RPL_AWAY': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoisaway', { - nick: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISUSER': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoisuser', { - nick: command.params[1], - ident: command.params[2], - host: command.params[3], - msg: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISHELPOP': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoishelpop', { - nick: command.params[1], - msg: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISBOT': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoisbot', { - nick: command.params[1], - msg: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISSERVER': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoisserver', { - nick: command.params[1], - irc_server: command.params[2], - server_info: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISOPERATOR': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoisoperator', { - nick: command.params[1], - msg: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISCHANNELS': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoischannels', { - nick: command.params[1], - chans: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISMODES': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoismodes', { - nick: command.params[1], - msg: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISIDLE': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoisidle', { - nick: command.params[1], - idle: command.params[2], - logon: command.params[3] || undefined - }); - }, - - 'RPL_WHOISREGNICK': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoisregnick', { - nick: command.params[1], - msg: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISHOST': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoishost', { - nick: command.params[1], - msg: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISSECURE': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoissecure', { - nick: command.params[1] - }); - }, - - 'RPL_WHOISACCOUNT': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoisaccount', { - nick: command.params[1], - account: command.params[2] - }); - }, - - 'RPL_WHOWASUSER': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whowas', { - nick: command.params[1], - ident: command.params[2], - host: command.params[3], - real_name: command.params[command.params.length - 1] - }); - }, - - 'RPL_ENDOFWHOWAS': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' endofwhowas', { - nick: command.params[1] - }); - }, - - 'ERR_WASNOSUCHNICK': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' wasnosucknick', { - nick: command.params[1] - }); - }, - - 'RPL_LISTSTART': function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' list_start', {}); - }, - - 'RPL_LISTEND': function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' list_end', {}); - }, - - 'RPL_LIST': function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' list_channel', { - channel: command.params[1], - num_users: parseInt(command.params[2], 10), - topic: command.params[3] || '' - }); - }, - - 'RPL_CHANNELMODEIS': function (command) { - var channel = command.params[1], - modes = parseModeList.call(this, command.params[2], command.params.slice(3)); - - this.irc_connection.emit('channel ' + channel + ' info', { - channel: channel, - modes: modes - }); - }, - - 'RPL_CREATIONTIME': function (command) { - var channel = command.params[1]; - - this.irc_connection.emit('channel ' + channel + ' info', { - channel: channel, - created_at: parseInt(command.params[2], 10) - }); - }, - - 'RPL_CHANNEL_URL': function (command) { - var channel = command.params[1]; - - this.irc_connection.emit('channel ' + channel + ' info', { - channel: channel, - url: command.params[command.params.length - 1] - }); - }, - - 'RPL_MOTD': function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' motd', { - motd: command.params[command.params.length - 1] + '\n' - }); - }, - - 'RPL_MOTDSTART': function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' motd_start', {}); - }, - - 'RPL_ENDOFMOTD': function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' motd_end', {}); - }, - - 'RPL_NAMEREPLY': function (command) { - var members = command.params[command.params.length - 1].split(' '); - var member_list = []; - var that = this; - _.each(members, function (member) { - var i = 0, - j = 0, - modes = []; - - // Make sure we have some prefixes already - if (that.irc_connection.options.PREFIX) { - for (j = 0; j < that.irc_connection.options.PREFIX.length; j++) { - if (member.charAt(i) === that.irc_connection.options.PREFIX[j].symbol) { - modes.push(that.irc_connection.options.PREFIX[j].mode); - i++; - } - } - } - - member_list.push({nick: member, modes: modes}); - }); - - this.irc_connection.emit('channel ' + command.params[2] + ' userlist', { - users: member_list, - channel: command.params[2] - }); - }, - - 'RPL_ENDOFNAMES': function (command) { - this.irc_connection.emit('channel ' + command.params[1] + ' userlist_end', { - channel: command.params[1] - }); - }, - - 'RPL_WHOREPLY': function (command) { - // For the time being, NOOP this command so they don't get passed - // down to the client. Waste of bandwidth since we do not use it yet - // TODO: Impliment RPL_WHOREPLY - }, - - 'RPL_ENDOFWHO': function (command) { - // For the time being, NOOP this command so they don't get passed - // down to the client. Waste of bandwidth since we do not use it yet - // TODO: Impliment RPL_ENDOFWHO - }, - - 'RPL_BANLIST': function (command) { - this.irc_connection.emit('channel ' + command.params[1] + ' banlist', { - channel: command.params[1], - banned: command.params[2], - banned_by: command.params[3], - banned_at: command.params[4] - }); - }, - - 'RPL_ENDOFBANLIST': function (command) { - this.irc_connection.emit('channel ' + command.params[1] + ' banlist_end', { - channel: command.params[1] - }); - }, - - 'RPL_TOPIC': function (command) { - this.irc_connection.emit('channel ' + command.params[1] + ' topic', { - channel: command.params[1], - topic: command.params[command.params.length - 1] - }); - }, - - 'RPL_NOTOPIC': function (command) { - this.irc_connection.emit('channel ' + command.params[1] + ' topic', { - channel: command.params[1], - topic: '' - }); - }, - - 'RPL_TOPICWHOTIME': function (command) { - this.irc_connection.emit('channel ' + command.params[1] + ' topicsetby', { - nick: command.params[2], - channel: command.params[1], - when: command.params[3] - }); - }, - - 'RPL_INVITING': function (command) { - this.irc_connection.emit('channel ' + command.params[1] + ' invited', { - nick: command.params[0], - channel: command.params[1] - }); - }, - - 'PING': function (command) { - this.irc_connection.write('PONG ' + command.params[command.params.length - 1]); - }, - - 'JOIN': function (command) { - var channel, time; - if (typeof command.params[0] === 'string' && command.params[0] !== '') { - channel = command.params[0]; - } - - // Check if we have a server-time - time = getServerTime.call(this, command); - - this.irc_connection.emit('channel ' + channel + ' join', { - nick: command.nick, - ident: command.ident, - hostname: command.hostname, - channel: channel, - time: time - }); - }, - - 'PART': function (command) { - var time, channel, message; - - // Check if we have a server-time - time = getServerTime.call(this, command); - - channel = command.params[0]; - if (command.params.length > 1) { - message = command.params[command.params.length - 1]; - } - - this.irc_connection.emit('channel ' + channel + ' part', { - nick: command.nick, - ident: command.ident, - hostname: command.hostname, - channel: channel, - message: message, - time: time - }); - }, - - 'KICK': function (command) { - var time; - - // Check if we have a server-time - time = getServerTime.call(this, command); - - this.irc_connection.emit('channel ' + command.params[0] + ' kick', { - kicked: command.params[1], - nick: command.nick, - ident: command.ident, - hostname: command.hostname, - channel: command.params[0], - message: command.params[command.params.length - 1], - time: time - }); - }, - - 'QUIT': function (command) { - var time; - - // Check if we have a server-time - time = getServerTime.call(this, command); - - this.irc_connection.emit('user ' + command.nick + ' quit', { - nick: command.nick, - ident: command.ident, - hostname: command.hostname, - message: command.params[command.params.length - 1], - time: time - }); - }, - - 'NOTICE': function (command) { - var namespace, - time, - msg; - - // Check if we have a server-time - time = getServerTime.call(this, command); - - msg = command.params[command.params.length - 1]; - if ((msg.charAt(0) === String.fromCharCode(1)) && (msg.charAt(msg.length - 1) === String.fromCharCode(1))) { - // It's a CTCP response - namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ? 'user' : 'channel'; - this.irc_connection.emit(namespace + ' ' + command.params[0] + ' ctcp_response', { - nick: command.nick, - ident: command.ident, - hostname: command.hostname, - target: command.params[0], - msg: msg.substring(1, msg.length - 1), - time: time - }); - } else { - namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase() || command.params[0] === '*') ? - 'user' : - 'channel'; - - this.irc_connection.emit(namespace + ' ' + command.params[0] + ' notice', { - from_server: command.prefix ? true : false, - nick: command.nick || command.prefix || undefined, - ident: command.ident, - hostname: command.hostname, - target: command.params[0], - msg: msg, - time: time - }); - } - }, - - 'NICK': function (command) { - var time; - - // Check if we have a server-time - time = getServerTime.call(this, command); - - this.irc_connection.emit('user ' + command.nick + ' nick', { - nick: command.nick, - ident: command.ident, - hostname: command.hostname, - newnick: command.params[0], - time: time - }); - }, - - 'TOPIC': function (command) { - var time; - - // If we don't have an associated channel, no need to continue - if (!command.params[0]) { - return; - } - - // Check if we have a server-time - time = getServerTime.call(this, command); - - var channel = command.params[0], - topic = command.params[command.params.length - 1] || ''; - - this.irc_connection.emit('channel ' + channel + ' topic', { - nick: command.nick, - channel: channel, - topic: topic, - time: time - }); - }, - - 'MODE': function (command) { - var modes = [], event, time; - - // Check if we have a server-time - time = getServerTime.call(this, command); - - // Get a JSON representation of the modes - modes = parseModeList.call(this, command.params[1], command.params.slice(2)); - event = (_.contains(this.irc_connection.options.CHANTYPES, command.params[0][0]) ? 'channel ' : 'user ') + command.params[0] + ' mode'; - - this.irc_connection.emit(event, { - target: command.params[0], - nick: command.nick || command.prefix || '', - modes: modes, - time: time - }); - }, - - 'PRIVMSG': function (command) { - var tmp, namespace, time, msg, version_string, client_info; - - // Check if we have a server-time - time = getServerTime.call(this, command); - - msg = command.params[command.params.length - 1]; - if ((msg.charAt(0) === String.fromCharCode(1)) && (msg.charAt(msg.length - 1) === String.fromCharCode(1))) { - //CTCP request - if (msg.substr(1, 6) === 'ACTION') { - namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ? - 'user ' + command.nick : - 'channel ' + command.params[0]; - - this.irc_connection.emit(namespace + ' action', { - nick: command.nick, - ident: command.ident, - hostname: command.hostname, - target: command.params[0], - msg: msg.substring(8, msg.length - 1), - time: time - }); - - } else if (msg.substr(1, 7) === 'VERSION') { - client_info = this.irc_connection.state.client.client_info; - version_string = global.build_version; - - // If the client build_version differs from the server, add this to the version_string - if (client_info && client_info.build_version !== global.build_version) { - version_string += ', client build: ' + client_info.build_version; - } - - version_string = 'KiwiIRC (' + version_string + ')'; - this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'VERSION ' + version_string + String.fromCharCode(1)); - - } else if (msg.substr(1, 6) === 'SOURCE') { - this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'SOURCE http://www.kiwiirc.com/' + String.fromCharCode(1)); - - } else if (msg.substr(1, 10) === 'CLIENTINFO') { - this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'CLIENTINFO SOURCE VERSION TIME' + String.fromCharCode(1)); - - } else { - namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ? - 'user ' + command.nick : - 'channel ' + command.params[0]; - - this.irc_connection.emit(namespace + ' ctcp_request', { - nick: command.nick, - ident: command.ident, - hostname: command.hostname, - target: command.params[0], - type: (msg.substring(1, msg.length - 1).split(' ') || [null])[0], - msg: msg.substring(1, msg.length - 1), - time: time - }); - } - } else { - // A message to a user (private message) or to a channel? - namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ? 'user ' + command.nick : 'channel ' + command.params[0]; - this.irc_connection.emit(namespace + ' privmsg', { - nick: command.nick, - ident: command.ident, - hostname: command.hostname, - target: command.params[0], - msg: msg, - time: time - }); - } - }, - - 'CAP': function (command) { - // TODO: capability modifiers - // i.e. - for disable, ~ for requires ACK, = for sticky - var capabilities = command.params[command.params.length - 1].replace(/(?:^| )[\-~=]/, '').split(' '); - var request; - - // Which capabilities we want to enable - var want = ['multi-prefix', 'away-notify', 'server-time', 'znc.in/server-time-iso', 'znc.in/server-time']; - - if (this.irc_connection.password) { - want.push('sasl'); - } - - switch (command.params[1]) { - case 'LS': - // Compute which of the available capabilities we want and request them - request = _.intersection(capabilities, want); - if (request.length > 0) { - this.irc_connection.cap.requested = request; - this.irc_connection.write('CAP REQ :' + request.join(' ')); - } else { - this.irc_connection.write('CAP END'); - this.irc_connection.cap_negotiation = false; - } - break; - case 'ACK': - if (capabilities.length > 0) { - // Update list of enabled capabilities - this.irc_connection.cap.enabled = capabilities; - // Update list of capabilities we would like to have but that aren't enabled - this.irc_connection.cap.requested = _.difference(this.irc_connection.cap.requested, capabilities); - } - if (this.irc_connection.cap.enabled.length > 0) { - if (_.contains(this.irc_connection.cap.enabled, 'sasl')) { - this.irc_connection.sasl = true; - this.irc_connection.write('AUTHENTICATE PLAIN'); - } else { - this.irc_connection.write('CAP END'); - this.irc_connection.cap_negotiation = false; - } - } - break; - case 'NAK': - if (capabilities.length > 0) { - this.irc_connection.cap.requested = _.difference(this.irc_connection.cap.requested, capabilities); - } - if (this.irc_connection.cap.requested.length > 0) { - this.irc_connection.write('CAP END'); - this.irc_connection.cap_negotiation = false; - } - break; - case 'LIST': - // should we do anything here? - break; - } - }, - - 'AUTHENTICATE': function (command) { - var b = new Buffer(this.irc_connection.nick + "\0" + this.irc_connection.nick + "\0" + this.irc_connection.password, 'utf8'); - var b64 = b.toString('base64'); - if (command.params[0] === '+') { - while (b64.length >= 400) { - this.irc_connection.write('AUTHENTICATE ' + b64.slice(0, 399)); - b64 = b64.slice(399); - } - if (b64.length > 0) { - this.irc_connection.write('AUTHENTICATE ' + b64); - } else { - this.irc_connection.write('AUTHENTICATE +'); - } - } else { - this.irc_connection.write('CAP END'); - this.irc_connection.cap_negotiation = false; - } - }, - - 'AWAY': function (command) { - var time; - - // Check if we have a server-time - time = getServerTime.call(this, command); - - this.irc_connection.emit('user ' + command.nick + ' away', { - nick: command.nick, - msg: command.params[command.params.length - 1], - time: time - }); - }, - - 'RPL_SASLAUTHENTICATED': function (command) { - this.irc_connection.write('CAP END'); - this.irc_connection.cap_negotiation = false; - this.irc_connection.sasl = true; - }, - - 'RPL_SASLLOGGEDIN': function (command) { - if (this.irc_connection.cap_negotiation === true) { - this.irc_connection.write('CAP END'); - this.irc_connection.cap_negotiation = false; - } - }, - - 'ERR_SASLNOTAUTHORISED': function (command) { - this.irc_connection.write('CAP END'); - this.irc_connection.cap_negotiation = false; - }, - - 'ERR_SASLABORTED': function (command) { - this.irc_connection.write('CAP END'); - this.irc_connection.cap_negotiation = false; - }, - - 'ERR_SASLALREADYAUTHED': function (command) { - // noop - }, - - 'ERROR': function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' error', { - reason: command.params[command.params.length - 1] - }); - }, - ERR_PASSWDMISMATCH: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' password_mismatch', {}); - }, - - ERR_LINKCHANNEL: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' channel_redirect', { - from: command.params[1], - to: command.params[2] - }); - }, - - ERR_NOSUCHNICK: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' no_such_nick', { - nick: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_CANNOTSENDTOCHAN: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' cannot_send_to_channel', { - channel: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_TOOMANYCHANNELS: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' too_many_channels', { - channel: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_USERNOTINCHANNEL: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' user_not_in_channel', { - nick: command.params[0], - channel: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_NOTONCHANNEL: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' not_on_channel', { - channel: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_USERONCHANNEL: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' user_on_channel', { - nick: command.params[1], - channel: command.params[2] - }); - }, - - ERR_CHANNELISFULL: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' channel_is_full', { - channel: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_INVITEONLYCHAN: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' invite_only_channel', { - channel: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_BANNEDFROMCHAN: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' banned_from_channel', { - channel: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_BADCHANNELKEY: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' bad_channel_key', { - channel: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_CHANOPRIVSNEEDED: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' chanop_privs_needed', { - channel: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_NICKNAMEINUSE: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' nickname_in_use', { - nick: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_ERRONEUSNICKNAME: function(command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' erroneus_nickname', { - nick: command.params[1], - reason: command.params[command.params.length - 1] - }); - }, - - ERR_NOTREGISTERED: function (command) { - }, - - RPL_MAPMORE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_MAPEND: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_LINKS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_ENDOFLINKS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - ERR_UNKNOWNCOMMAND: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, '`' + params.slice(0, -1).join(', ') + '` ' + command.params[command.params.length - 1]); - }, - - ERR_NOMOTD: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, command.params[command.params.length - 1]); - }, - - ERR_NOPRIVILEGES: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, command.params[command.params.length - 1]); - }, - - RPL_STATSCONN: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_LUSERCLIENT: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_LUSEROP: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_LUSERUNKNOWN: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_LUSERCHANNELS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_LUSERME: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_LOCALUSERS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_GLOBALUSERS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_HOSTCLOACKING: function (command) { - genericNotice.call(this, command, command.params[1] + ' ' + command.params[command.params.length - 1]); - }, - - RPL_YOURHOST: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_CREATED: function (command) { - this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' server', { - motd: command.params[command.params.length - 1] + '\n' - }); - }, - - RPL_MYINFO: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_SNOMASK: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_NOWOPER: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACELINK: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACECONNECTING: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACEHANDSHAKE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACEUNKNOWN: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACEOPERATOR: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACEUSER: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACESERVER: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACESERVICE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACENEWTYPE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACECLASS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_TRACERECONNECT: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSLINKINFO: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSCOMMANDS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSCLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSNLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSILINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSKLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSPLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSYLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_ENDOFSTATS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSBLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_UMODEIS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_SQLINE_NICK: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATS_E: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATS_D: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_SPAMFILTER: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_SERVICEINFO: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_ENDOFSERVICES: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_SERVICE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_SERVLIST: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_SERVLISTEND: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSLLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSUPTIME: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSOLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSHLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSSLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSGLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSXLINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSULINE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_STATSDEBUG: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_ADMINME: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_ADMINLOC1: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_ADMINLOC2: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_ADMINEMAIL: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_HELPHDR: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_HELPOP: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_HELPTLR: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_ZIPSTATS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_WHOISSPECIAL: function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoisswhois', { - nick: command.params[1], - msg: command.params[command.params.length - 1] - }); - }, - - 'RPL_WHOISACTUALLY': function (command) { - this.irc_connection.emit('user ' + command.params[1] + ' whoishost', { - nick: command.params[1], - msg: command.params[command.params.length - 1] - }); - }, - - RPL_INFO: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_ENDINFO: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_NOSUCHSERVER: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - ERR_NOADMININFO: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - ERR_NOTENOUGHPARAMS: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - RPL_WALLOPS: function (command) { - this.irc_connection.emit('user ' + this.irc_connection.nick + ' wallops', { - from_server: false, - nick: command.nick, - ident: command.ident, - hostname: command.hostname, - msg: command.params[command.params.length - 1] - }); - }, - - ERR_NOOPERHOST: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - ERR_UNKNOWNMODE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - ERR_CANTKILLSERVER: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - ERR_ISCHANSERVICE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - - ERR_ISREALSERVICE: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, - ERR_CANNOTDOCOMMAND: function (command) { - var params = _.clone(command.params); - params.shift(); - genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); - }, -}; - - - - -function genericNotice (command, msg, is_error) { +IrcCommandsHandler.prototype.emitGenericNotice = function (command, msg, is_error) { // Default to being an error - if (typeof is_error !== 'boolean') + if (typeof is_error !== 'boolean') { is_error = true; + } this.irc_connection.emit('user ' + command.prefix + ' notice', { from_server: true, @@ -1485,7 +215,12 @@ function genericNotice (command, msg, is_error) { msg: msg, numeric: parseInt(command.command, 10) }); -} +}; + + +IrcCommandsHandler.prototype.emit = function() { + return this.irc_connection.emit.apply(this.irc_connection, arguments); +}; /** @@ -1494,7 +229,7 @@ function genericNotice (command, msg, is_error) { * [ { mode: '+k', param: 'pass' } ] * [ { mode: '-i', param: null } ] */ -function parseModeList(mode_string, mode_params) { +IrcCommandsHandler.prototype.parseModeList = function (mode_string, mode_params) { var chanmodes = this.irc_connection.options.CHANMODES || [], prefixes = this.irc_connection.options.PREFIX || [], always_param = (chanmodes[0] || '').concat((chanmodes[1] || '')), @@ -1544,41 +279,63 @@ function parseModeList(mode_string, mode_params) { } -function getServerTime(command) { + + + +function IrcCommand(command, data) { + this.command = command += ''; + this.params = _.clone(data.params); + this.tags = _.clone(data.tags); + + this.prefix = data.prefix; + this.nick = data.nick; + this.ident = data.ident; + this.hostname = data.hostname; +} + + +IrcCommand.prototype.getServerTime = function() { var time; // No tags? No times. - if (!command.tags || command.tags.length === 0) { - return time; + if (!this.tags || this.tags.length === 0) { + return; } - if (capContainsAny.call(this, ['server-time', 'znc.in/server-time', 'znc.in/server-time-iso'])) { - time = _.find(command.tags, function (tag) { - return tag.tag === 'time'; - }); - - time = time ? time.value : undefined; - - // Convert the time value to a unixtimestamp - if (typeof time === 'string') { - if (time.indexOf('T') > -1) { - time = parseISO8601(time); + time = _.find(this.tags, function (tag) { + return tag.tag === 'time'; + }); - } else if(time.match(/^[0-9.]+$/)) { - // A string formatted unix timestamp - time = new Date(time * 1000); - } + if (time) { + time = time.value; + } - time = time.getTime(); + // Convert the time value to a unixtimestamp + if (typeof time === 'string') { + if (time.indexOf('T') > -1) { + time = parseISO8601(time); - } else if (typeof time === 'number') { + } else if(time.match(/^[0-9.]+$/)) { + // A string formatted unix timestamp time = new Date(time * 1000); - time = time.getTime(); } + + time = time.getTime(); + + } else if (typeof time === 'number') { + time = new Date(time * 1000); + time = time.getTime(); } return time; -} +}; + + + + + + + function capContainsAny (caps) { @@ -1618,3 +375,7 @@ function parseISO8601(str) { return _date; } } + + +module.exports.Handler = IrcCommandsHandler; +module.exports.Command = IrcCommand; diff --git a/server/irc/commands/channel.js b/server/irc/commands/channel.js new file mode 100644 index 0000000..ac35bed --- /dev/null +++ b/server/irc/commands/channel.js @@ -0,0 +1,224 @@ +var _ = require('lodash'); + +module.exports = function AddCommandHandlers(command_controller) { + _.each(handlers, function(handler, handler_command) { + command_controller.addHandler(handler_command, handler); + }); +}; + + +var handlers = { + RPL_CHANNELMODEIS: function (command) { + var channel = command.params[1], + modes = parseModeList.call(this, command.params[2], command.params.slice(3)); + + this.emit('channel ' + channel + ' info', { + channel: channel, + modes: modes + }); + }, + + + RPL_CREATIONTIME: function (command) { + var channel = command.params[1]; + + this.emit('channel ' + channel + ' info', { + channel: channel, + created_at: parseInt(command.params[2], 10) + }); + }, + + + RPL_CHANNEL_URL: function (command) { + var channel = command.params[1]; + + this.emit('channel ' + channel + ' info', { + channel: channel, + url: command.params[command.params.length - 1] + }); + }, + + + RPL_NAMEREPLY: function (command) { + var members = command.params[command.params.length - 1].split(' '); + var member_list = []; + var that = this; + _.each(members, function (member) { + var i = 0, + j = 0, + modes = []; + + // Make sure we have some prefixes already + if (that.irc_connection.options.PREFIX) { + for (j = 0; j < that.irc_connection.options.PREFIX.length; j++) { + if (member.charAt(i) === that.irc_connection.options.PREFIX[j].symbol) { + modes.push(that.irc_connection.options.PREFIX[j].mode); + i++; + } + } + } + + member_list.push({nick: member, modes: modes}); + }); + + this.emit('channel ' + command.params[2] + ' userlist', { + users: member_list, + channel: command.params[2] + }); + }, + + + RPL_ENDOFNAMES: function (command) { + this.emit('channel ' + command.params[1] + ' userlist_end', { + channel: command.params[1] + }); + }, + + + RPL_BANLIST: function (command) { + this.emit('channel ' + command.params[1] + ' banlist', { + channel: command.params[1], + banned: command.params[2], + banned_by: command.params[3], + banned_at: command.params[4] + }); + }, + + + RPL_ENDOFBANLIST: function (command) { + this.emit('channel ' + command.params[1] + ' banlist_end', { + channel: command.params[1] + }); + }, + + + RPL_TOPIC: function (command) { + this.emit('channel ' + command.params[1] + ' topic', { + channel: command.params[1], + topic: command.params[command.params.length - 1] + }); + }, + + + RPL_NOTOPIC: function (command) { + this.emit('channel ' + command.params[1] + ' topic', { + channel: command.params[1], + topic: '' + }); + }, + + + RPL_TOPICWHOTIME: function (command) { + this.emit('channel ' + command.params[1] + ' topicsetby', { + nick: command.params[2], + channel: command.params[1], + when: command.params[3] + }); + }, + + + JOIN: function (command) { + var channel, time; + if (typeof command.params[0] === 'string' && command.params[0] !== '') { + channel = command.params[0]; + } + + // Check if we have a server-time + time = command.getServerTime(); + + this.emit('channel ' + channel + ' join', { + nick: command.nick, + ident: command.ident, + hostname: command.hostname, + channel: channel, + time: time + }); + }, + + + PART: function (command) { + var time, channel, message; + + // Check if we have a server-time + time = command.getServerTime(); + + channel = command.params[0]; + if (command.params.length > 1) { + message = command.params[command.params.length - 1]; + } + + this.emit('channel ' + channel + ' part', { + nick: command.nick, + ident: command.ident, + hostname: command.hostname, + channel: channel, + message: message, + time: time + }); + }, + + + KICK: function (command) { + var time; + + // Check if we have a server-time + time = command.getServerTime(); + + this.emit('channel ' + command.params[0] + ' kick', { + kicked: command.params[1], + nick: command.nick, + ident: command.ident, + hostname: command.hostname, + channel: command.params[0], + message: command.params[command.params.length - 1], + time: time + }); + }, + + + QUIT: function (command) { + var time; + + // Check if we have a server-time + time = command.getServerTime(); + + this.emit('user ' + command.nick + ' quit', { + nick: command.nick, + ident: command.ident, + hostname: command.hostname, + message: command.params[command.params.length - 1], + time: time + }); + }, + + + TOPIC: function (command) { + var time; + + // If we don't have an associated channel, no need to continue + if (!command.params[0]) { + return; + } + + // Check if we have a server-time + time = command.getServerTime(); + + var channel = command.params[0], + topic = command.params[command.params.length - 1] || ''; + + this.emit('channel ' + channel + ' topic', { + nick: command.nick, + channel: channel, + topic: topic, + time: time + }); + }, + + + RPL_INVITING: function (command) { + this.emit('channel ' + command.params[1] + ' invited', { + nick: command.params[0], + channel: command.params[1] + }); + }, +}; \ No newline at end of file diff --git a/server/irc/commands/messaging.js b/server/irc/commands/messaging.js new file mode 100644 index 0000000..6a44a9b --- /dev/null +++ b/server/irc/commands/messaging.js @@ -0,0 +1,129 @@ +var _ = require('lodash'); + +module.exports = function AddCommandHandlers(command_controller) { + _.each(handlers, function(handler, handler_command) { + command_controller.addHandler(handler_command, handler); + }); +}; + + +var handlers = { + NOTICE: function (command) { + var namespace, + time, + msg; + + // Check if we have a server-time + time = command.getServerTime(); + + msg = command.params[command.params.length - 1]; + if ((msg.charAt(0) === String.fromCharCode(1)) && (msg.charAt(msg.length - 1) === String.fromCharCode(1))) { + // It's a CTCP response + namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ? 'user' : 'channel'; + this.emit(namespace + ' ' + command.params[0] + ' ctcp_response', { + nick: command.nick, + ident: command.ident, + hostname: command.hostname, + target: command.params[0], + msg: msg.substring(1, msg.length - 1), + time: time + }); + } else { + namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase() || command.params[0] === '*') ? + 'user' : + 'channel'; + + this.emit(namespace + ' ' + command.params[0] + ' notice', { + from_server: command.prefix ? true : false, + nick: command.nick || command.prefix || undefined, + ident: command.ident, + hostname: command.hostname, + target: command.params[0], + msg: msg, + time: time + }); + } + }, + + + PRIVMSG: function (command) { + var tmp, namespace, time, msg, version_string, client_info; + + // Check if we have a server-time + time = command.getServerTime(); + + msg = command.params[command.params.length - 1]; + if ((msg.charAt(0) === String.fromCharCode(1)) && (msg.charAt(msg.length - 1) === String.fromCharCode(1))) { + //CTCP request + if (msg.substr(1, 6) === 'ACTION') { + namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ? + 'user ' + command.nick : + 'channel ' + command.params[0]; + + this.emit(namespace + ' action', { + nick: command.nick, + ident: command.ident, + hostname: command.hostname, + target: command.params[0], + msg: msg.substring(8, msg.length - 1), + time: time + }); + + } else if (msg.substr(1, 7) === 'VERSION') { + client_info = this.irc_connection.state.client.client_info; + version_string = global.build_version; + + // If the client build_version differs from the server, add this to the version_string + if (client_info && client_info.build_version !== global.build_version) { + version_string += ', client build: ' + client_info.build_version; + } + + version_string = 'KiwiIRC (' + version_string + ')'; + this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'VERSION ' + version_string + String.fromCharCode(1)); + + } else if (msg.substr(1, 6) === 'SOURCE') { + this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'SOURCE http://www.kiwiirc.com/' + String.fromCharCode(1)); + + } else if (msg.substr(1, 10) === 'CLIENTINFO') { + this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'CLIENTINFO SOURCE VERSION TIME' + String.fromCharCode(1)); + + } else { + namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ? + 'user ' + command.nick : + 'channel ' + command.params[0]; + + this.emit(namespace + ' ctcp_request', { + nick: command.nick, + ident: command.ident, + hostname: command.hostname, + target: command.params[0], + type: (msg.substring(1, msg.length - 1).split(' ') || [null])[0], + msg: msg.substring(1, msg.length - 1), + time: time + }); + } + } else { + // A message to a user (private message) or to a channel? + namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ? 'user ' + command.nick : 'channel ' + command.params[0]; + this.emit(namespace + ' privmsg', { + nick: command.nick, + ident: command.ident, + hostname: command.hostname, + target: command.params[0], + msg: msg, + time: time + }); + } + }, + + + RPL_WALLOPS: function (command) { + this.emit('user ' + this.irc_connection.nick + ' wallops', { + from_server: false, + nick: command.nick, + ident: command.ident, + hostname: command.hostname, + msg: command.params[command.params.length - 1] + }); + }, +}; \ No newline at end of file diff --git a/server/irc/commands/misc.js b/server/irc/commands/misc.js new file mode 100644 index 0000000..60feb3e --- /dev/null +++ b/server/irc/commands/misc.js @@ -0,0 +1,700 @@ +var _ = require('lodash'); + +module.exports = function AddCommandHandlers(command_controller) { + _.each(handlers, function(handler, handler_command) { + command_controller.addHandler(handler_command, handler); + }); +}; + + +var handlers = { + RPL_LISTSTART: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' list_start', {}); + }, + + RPL_LISTEND: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' list_end', {}); + }, + + RPL_LIST: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' list_channel', { + channel: command.params[1], + num_users: parseInt(command.params[2], 10), + topic: command.params[3] || '' + }); + }, + + + + RPL_MOTD: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' motd', { + motd: command.params[command.params.length - 1] + '\n' + }); + }, + + RPL_MOTDSTART: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' motd_start', {}); + }, + + RPL_ENDOFMOTD: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' motd_end', {}); + }, + + + + RPL_WHOREPLY: function (command) { + // For the time being, NOOP this command so they don't get passed + // down to the client. Waste of bandwidth since we do not use it yet + // TODO: Impliment RPL_WHOREPLY + }, + + RPL_ENDOFWHO: function (command) { + // For the time being, NOOP this command so they don't get passed + // down to the client. Waste of bandwidth since we do not use it yet + // TODO: Impliment RPL_ENDOFWHO + }, + + + PING: function (command) { + this.irc_connection.write('PONG ' + command.params[command.params.length - 1]); + }, + + + NICK: function (command) { + var time; + + // Check if we have a server-time + time = command.getServerTime(); + + this.emit('user ' + command.nick + ' nick', { + nick: command.nick, + ident: command.ident, + hostname: command.hostname, + newnick: command.params[0], + time: time + }); + }, + + + MODE: function (command) { + var modes = [], event, time; + + // Check if we have a server-time + time = command.getServerTime(); + + // Get a JSON representation of the modes + modes = this.parseModeList(command.params[1], command.params.slice(2)); + event = (_.contains(this.irc_connection.options.CHANTYPES, command.params[0][0]) ? 'channel ' : 'user ') + command.params[0] + ' mode'; + + this.emit(event, { + target: command.params[0], + nick: command.nick || command.prefix || '', + modes: modes, + time: time + }); + }, + + + ERROR: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' error', { + reason: command.params[command.params.length - 1] + }); + }, + + ERR_PASSWDMISMATCH: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' password_mismatch', {}); + }, + + ERR_LINKCHANNEL: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' channel_redirect', { + from: command.params[1], + to: command.params[2] + }); + }, + + ERR_NOSUCHNICK: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' no_such_nick', { + nick: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_CANNOTSENDTOCHAN: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' cannot_send_to_channel', { + channel: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_TOOMANYCHANNELS: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' too_many_channels', { + channel: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_USERNOTINCHANNEL: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' user_not_in_channel', { + nick: command.params[0], + channel: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_NOTONCHANNEL: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' not_on_channel', { + channel: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_USERONCHANNEL: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' user_on_channel', { + nick: command.params[1], + channel: command.params[2] + }); + }, + + ERR_CHANNELISFULL: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' channel_is_full', { + channel: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_INVITEONLYCHAN: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' invite_only_channel', { + channel: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_BANNEDFROMCHAN: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' banned_from_channel', { + channel: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_BADCHANNELKEY: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' bad_channel_key', { + channel: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_CHANOPRIVSNEEDED: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' chanop_privs_needed', { + channel: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_NICKNAMEINUSE: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' nickname_in_use', { + nick: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_ERRONEUSNICKNAME: function(command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' erroneus_nickname', { + nick: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + ERR_NOTREGISTERED: function (command) { + }, + + RPL_MAPMORE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_MAPEND: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_LINKS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_ENDOFLINKS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + ERR_UNKNOWNCOMMAND: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, '`' + params.slice(0, -1).join(', ') + '` ' + command.params[command.params.length - 1]); + }, + + ERR_NOMOTD: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, command.params[command.params.length - 1]); + }, + + ERR_NOPRIVILEGES: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, command.params[command.params.length - 1]); + }, + + RPL_STATSCONN: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_LUSERCLIENT: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_LUSEROP: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_LUSERUNKNOWN: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_LUSERCHANNELS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_LUSERME: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_LOCALUSERS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_GLOBALUSERS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_HOSTCLOACKING: function (command) { + this.emitGenericNotice(command, command.params[1] + ' ' + command.params[command.params.length - 1]); + }, + + RPL_YOURHOST: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_CREATED: function (command) { + this.emit('server ' + this.irc_connection.irc_host.hostname + ' server', { + motd: command.params[command.params.length - 1] + '\n' + }); + }, + + RPL_MYINFO: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_SNOMASK: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_NOWOPER: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACELINK: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACECONNECTING: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACEHANDSHAKE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACEUNKNOWN: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACEOPERATOR: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACEUSER: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACESERVER: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACESERVICE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACENEWTYPE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACECLASS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_TRACERECONNECT: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSLINKINFO: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSCOMMANDS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSCLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSNLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSILINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSKLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSPLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSYLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_ENDOFSTATS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSBLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_UMODEIS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_SQLINE_NICK: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATS_E: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATS_D: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_SPAMFILTER: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_SERVICEINFO: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_ENDOFSERVICES: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_SERVICE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_SERVLIST: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_SERVLISTEND: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSLLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSUPTIME: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSOLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSHLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSSLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSGLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSXLINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSULINE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_STATSDEBUG: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_ADMINME: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_ADMINLOC1: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_ADMINLOC2: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_ADMINEMAIL: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_HELPHDR: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_HELPOP: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_HELPTLR: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_ZIPSTATS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_WHOISSPECIAL: function (command) { + this.emit('user ' + command.params[1] + ' whoisswhois', { + nick: command.params[1], + msg: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISACTUALLY: function (command) { + this.emit('user ' + command.params[1] + ' whoishost', { + nick: command.params[1], + msg: command.params[command.params.length - 1] + }); + }, + + RPL_INFO: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_ENDINFO: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + RPL_NOSUCHSERVER: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + ERR_NOADMININFO: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + ERR_NOTENOUGHPARAMS: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + ERR_NOOPERHOST: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + ERR_UNKNOWNMODE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + ERR_CANTKILLSERVER: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + ERR_ISCHANSERVICE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + + ERR_ISREALSERVICE: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + }, + ERR_CANNOTDOCOMMAND: function (command) { + var params = _.clone(command.params); + params.shift(); + this.emitGenericNotice(command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]); + } +}; \ No newline at end of file diff --git a/server/irc/commands/registration.js b/server/irc/commands/registration.js new file mode 100644 index 0000000..a19d87a --- /dev/null +++ b/server/irc/commands/registration.js @@ -0,0 +1,160 @@ +var _ = require('lodash'); + +module.exports = function AddCommandHandlers(command_controller) { + _.each(handlers, function(handler, handler_command) { + command_controller.addHandler(handler_command, handler); + }); +}; + + +var handlers = { + RPL_WELCOME: function (command) { + var nick = command.params[0]; + this.irc_connection.registered = true; + this.cap_negotiation = false; + this.emit('server ' + this.irc_connection.irc_host.hostname + ' connect', { + nick: nick + }); + }, + + + RPL_ISUPPORT: function (command) { + var options, i, option, matches, j; + options = command.params; + for (i = 1; i < options.length; i++) { + option = options[i].split("=", 2); + option[0] = option[0].toUpperCase(); + this.irc_connection.options[option[0]] = (typeof option[1] !== 'undefined') ? option[1] : true; + if (_.include(['NETWORK', 'PREFIX', 'CHANTYPES', 'CHANMODES', 'NAMESX'], option[0])) { + if (option[0] === 'PREFIX') { + matches = /\(([^)]*)\)(.*)/.exec(option[1]); + if ((matches) && (matches.length === 3)) { + this.irc_connection.options.PREFIX = []; + for (j = 0; j < matches[2].length; j++) { + this.irc_connection.options.PREFIX.push({symbol: matches[2].charAt(j), mode: matches[1].charAt(j)}); + } + } + } else if (option[0] === 'CHANTYPES') { + this.irc_connection.options.CHANTYPES = this.irc_connection.options.CHANTYPES.split(''); + } else if (option[0] === 'CHANMODES') { + this.irc_connection.options.CHANMODES = option[1].split(','); + } else if ((option[0] === 'NAMESX') && (!_.contains(this.irc_connection.cap.enabled, 'multi-prefix'))) { + this.irc_connection.write('PROTOCTL NAMESX'); + } + } + } + this.emit('server ' + this.irc_connection.irc_host.hostname + ' options', { + options: this.irc_connection.options, + cap: this.irc_connection.cap.enabled + }); + }, + + + CAP: function (command) { + // TODO: capability modifiers + // i.e. - for disable, ~ for requires ACK, = for sticky + var capabilities = command.params[command.params.length - 1].replace(/(?:^| )[\-~=]/, '').split(' '); + var request; + + // Which capabilities we want to enable + var want = ['multi-prefix', 'away-notify', 'server-time', 'znc.in/server-time-iso', 'znc.in/server-time']; + + if (this.irc_connection.password) { + want.push('sasl'); + } + + switch (command.params[1]) { + case 'LS': + // Compute which of the available capabilities we want and request them + request = _.intersection(capabilities, want); + if (request.length > 0) { + this.irc_connection.cap.requested = request; + this.irc_connection.write('CAP REQ :' + request.join(' ')); + } else { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotiation = false; + } + break; + case 'ACK': + if (capabilities.length > 0) { + // Update list of enabled capabilities + this.irc_connection.cap.enabled = capabilities; + // Update list of capabilities we would like to have but that aren't enabled + this.irc_connection.cap.requested = _.difference(this.irc_connection.cap.requested, capabilities); + } + if (this.irc_connection.cap.enabled.length > 0) { + if (_.contains(this.irc_connection.cap.enabled, 'sasl')) { + this.irc_connection.sasl = true; + this.irc_connection.write('AUTHENTICATE PLAIN'); + } else { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotiation = false; + } + } + break; + case 'NAK': + if (capabilities.length > 0) { + this.irc_connection.cap.requested = _.difference(this.irc_connection.cap.requested, capabilities); + } + if (this.irc_connection.cap.requested.length > 0) { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotiation = false; + } + break; + case 'LIST': + // should we do anything here? + break; + } + }, + + + AUTHENTICATE: function (command) { + var b = new Buffer(this.irc_connection.nick + "\0" + this.irc_connection.nick + "\0" + this.irc_connection.password, 'utf8'); + var b64 = b.toString('base64'); + if (command.params[0] === '+') { + while (b64.length >= 400) { + this.irc_connection.write('AUTHENTICATE ' + b64.slice(0, 399)); + b64 = b64.slice(399); + } + if (b64.length > 0) { + this.irc_connection.write('AUTHENTICATE ' + b64); + } else { + this.irc_connection.write('AUTHENTICATE +'); + } + } else { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotiation = false; + } + }, + + + RPL_SASLAUTHENTICATED: function (command) { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotiation = false; + this.irc_connection.sasl = true; + }, + + + RPL_SASLLOGGEDIN: function (command) { + if (this.irc_connection.cap_negotiation === true) { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotiation = false; + } + }, + + ERR_SASLNOTAUTHORISED: function (command) { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotiation = false; + }, + + + ERR_SASLABORTED: function (command) { + this.irc_connection.write('CAP END'); + this.irc_connection.cap_negotiation = false; + }, + + + ERR_SASLALREADYAUTHED: function (command) { + // noop + } +}; \ No newline at end of file diff --git a/server/irc/commands/user.js b/server/irc/commands/user.js new file mode 100644 index 0000000..f67319f --- /dev/null +++ b/server/irc/commands/user.js @@ -0,0 +1,146 @@ +var _ = require('lodash'); + +module.exports = function AddCommandHandlers(command_controller) { + _.each(handlers, function(handler, handler_command) { + command_controller.addHandler(handler_command, handler); + }); +}; + + +var handlers = { + AWAY: function (command) { + var time; + + // Check if we have a server-time + time = command.getServerTime(); + + this.emit('user ' + command.nick + ' away', { + nick: command.nick, + msg: command.params[command.params.length - 1], + time: time + }); + }, + + + RPL_ENDOFWHOIS: function (command) { + this.emit('user ' + command.params[1] + ' endofwhois', { + nick: command.params[1], + msg: command.params[command.params.length - 1] + }); + }, + + RPL_AWAY: function (command) { + this.emit('user ' + command.params[1] + ' whoisaway', { + nick: command.params[1], + reason: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISUSER: function (command) { + this.emit('user ' + command.params[1] + ' whoisuser', { + nick: command.params[1], + ident: command.params[2], + host: command.params[3], + msg: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISHELPOP: function (command) { + this.emit('user ' + command.params[1] + ' whoishelpop', { + nick: command.params[1], + msg: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISBOT: function (command) { + this.emit('user ' + command.params[1] + ' whoisbot', { + nick: command.params[1], + msg: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISSERVER: function (command) { + this.emit('user ' + command.params[1] + ' whoisserver', { + nick: command.params[1], + irc_server: command.params[2], + server_info: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISOPERATOR: function (command) { + this.emit('user ' + command.params[1] + ' whoisoperator', { + nick: command.params[1], + msg: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISCHANNELS: function (command) { + this.emit('user ' + command.params[1] + ' whoischannels', { + nick: command.params[1], + chans: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISMODES: function (command) { + this.emit('user ' + command.params[1] + ' whoismodes', { + nick: command.params[1], + msg: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISIDLE: function (command) { + this.emit('user ' + command.params[1] + ' whoisidle', { + nick: command.params[1], + idle: command.params[2], + logon: command.params[3] || undefined + }); + }, + + RPL_WHOISREGNICK: function (command) { + this.emit('user ' + command.params[1] + ' whoisregnick', { + nick: command.params[1], + msg: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISHOST: function (command) { + this.emit('user ' + command.params[1] + ' whoishost', { + nick: command.params[1], + msg: command.params[command.params.length - 1] + }); + }, + + RPL_WHOISSECURE: function (command) { + this.emit('user ' + command.params[1] + ' whoissecure', { + nick: command.params[1] + }); + }, + + RPL_WHOISACCOUNT: function (command) { + this.emit('user ' + command.params[1] + ' whoisaccount', { + nick: command.params[1], + account: command.params[2] + }); + }, + + RPL_WHOWASUSER: function (command) { + this.emit('user ' + command.params[1] + ' whowas', { + nick: command.params[1], + ident: command.params[2], + host: command.params[3], + real_name: command.params[command.params.length - 1] + }); + }, + + RPL_ENDOFWHOWAS: function (command) { + this.emit('user ' + command.params[1] + ' endofwhowas', { + nick: command.params[1] + }); + }, + + ERR_WASNOSUCHNICK: function (command) { + this.emit('user ' + command.params[1] + ' wasnosucknick', { + nick: command.params[1] + }); + }, +}; \ No newline at end of file diff --git a/server/irc/connection.js b/server/irc/connection.js index 69eb684..62b5bf3 100644 --- a/server/irc/connection.js +++ b/server/irc/connection.js @@ -74,7 +74,7 @@ var IrcConnection = function (hostname, port, ssl, nick, user, options, state, c this.con_num = con_num; // IRC protocol handling - this.irc_commands = new IrcCommands(this); + this.irc_commands = new IrcCommands.Handler(this); // IrcServer object this.server = new IrcServer(this, hostname, port); @@ -834,5 +834,5 @@ function parseIrcLine(buffer_line) { msg_obj.params.push(msg[8].trim()); } - this.irc_commands.dispatch(msg_obj.command.toUpperCase(), msg_obj); + this.irc_commands.dispatch(new IrcCommands.Command(msg_obj.command.toUpperCase(), msg_obj)); } -- 2.25.1