MAking Wallops a specific message type
[KiwiIRC.git] / server / irc / commands.js
CommitLineData
3ec786bc
JA
1var _ = require('lodash'),
2 irc_numerics,
3 IrcCommands,
4 handlers,
5 unknownCommand;
6
7irc_numerics = {
8 '001': 'RPL_WELCOME',
f26a988a
CC
9 '002': 'RPL_YOURHOST',
10 '003': 'RPL_CREATED',
11 '004': 'RPL_MYINFO',
3ec786bc
JA
12 '005': 'RPL_ISUPPORT',
13 '006': 'RPL_MAPMORE',
14 '007': 'RPL_MAPEND',
f26a988a 15 '008': 'RPL_SNOMASK',
94e2cb82
CC
16 '015': 'RPL_MAP',
17 '017': 'RPL_MAPEND',
f26a988a
CC
18 '200': 'RPL_TRACELINK',
19 '201': 'RPL_TRACECONNECTING',
20 '202': 'RPL_TRACEHANDSHAKE',
21 '203': 'RPL_TRACEUNKNOWN',
22 '204': 'RPL_TRACEOPERATOR',
23 '205': 'RPL_TRACEUSER',
24 '206': 'RPL_TRACESERVER',
25 '207': 'RPL_TRACESERVICE',
26 '208': 'RPL_TRACENEWTYPE',
27 '209': 'RPL_TRACECLASS',
28 '210': 'RPL_TRACERECONNECT',
29 '211': 'RPL_STATSLINKINFO',
30 '212': 'RPL_STATSCOMMANDS',
31 '213': 'RPL_STATSCLINE',
32 '214': 'RPL_STATSNLINE',
33 '215': 'RPL_STATSILINE',
34 '216': 'RPL_STATSKLINE',
35 '217': 'RPL_STATSPLINE',
36 '218': 'RPL_STATSYLINE',
37 '219': 'RPL_ENDOFSTATS',
38 '220': 'RPL_STATSBLINE',
39 '221': 'RPL_UMODEIS',
40 '222': 'RPL_SQLINE_NICK',
41 '223': 'RPL_STATS_E',
42 '224': 'RPL_STATS_D',
43 '229': 'RPL_SPAMFILTER',
44 '231': 'RPL_SERVICEINFO',
45 '232': 'RPL_ENDOFSERVICES',
46 '233': 'RPL_SERVICE',
47 '234': 'RPL_SERVLIST',
48 '235': 'RPL_SERVLISTEND',
49 '241': 'RPL_STATSLLINE',
50 '242': 'RPL_STATSUPTIME',
51 '243': 'RPL_STATSOLINE',
52 '244': 'RPL_STATSHLINE',
53 '245': 'RPL_STATSSLINE',
54 '246': 'RPL_STATSGLINE',
55 '247': 'RPL_STATSXLINE',
56 '248': 'RPL_STATSULINE',
57 '249': 'RPL_STATSDEBUG',
3ec786bc
JA
58 '250': 'RPL_STATSCONN',
59 '251': 'RPL_LUSERCLIENT',
60 '252': 'RPL_LUSEROP',
61 '253': 'RPL_LUSERUNKNOWN',
62 '254': 'RPL_LUSERCHANNELS',
63 '255': 'RPL_LUSERME',
f26a988a
CC
64 '256': 'RPL_ADMINME',
65 '257': 'RPL_ADMINLOC1',
66 '258': 'RPL_ADMINLOC2',
67 '259': 'RPL_ADMINEMAIL',
3ec786bc
JA
68 '265': 'RPL_LOCALUSERS',
69 '266': 'RPL_GLOBALUSERS',
f26a988a
CC
70 '290': 'RPL_HELPHDR',
71 '291': 'RPL_HELPOP',
72 '292': 'RPL_HELPTLR',
3ec786bc 73 '301': 'RPL_AWAY',
f26a988a 74 '304': 'RPL_ZIPSTATS',
3ec786bc 75 '307': 'RPL_WHOISREGNICK',
94e2cb82 76 '310': 'RPL_WHOISHELPOP',
3ec786bc
JA
77 '311': 'RPL_WHOISUSER',
78 '312': 'RPL_WHOISSERVER',
79 '313': 'RPL_WHOISOPERATOR',
5989f3f4 80 '314': 'RPL_WHOWASUSER',
3ab39028 81 '315': 'RPL_ENDOFWHO',
3ec786bc
JA
82 '317': 'RPL_WHOISIDLE',
83 '318': 'RPL_ENDOFWHOIS',
84 '319': 'RPL_WHOISCHANNELS',
f26a988a 85 '320': 'RPL_WHOISSPECIAL',
3ec786bc
JA
86 '321': 'RPL_LISTSTART',
87 '322': 'RPL_LIST',
88 '323': 'RPL_LISTEND',
72db27e4
D
89 '324': 'RPL_CHANNELMODEIS',
90 '328': 'RPL_CHANNEL_URL',
91 '329': 'RPL_CREATIONTIME',
3d753975 92 '330': 'RPL_WHOISACCOUNT',
3ec786bc
JA
93 '331': 'RPL_NOTOPIC',
94 '332': 'RPL_TOPIC',
95 '333': 'RPL_TOPICWHOTIME',
951cd81e 96 '335': 'RPL_WHOISBOT',
f26a988a 97 '338': 'RPL_WHOISACTUALLY',
9a1da609 98 '341': 'RPL_INVITING',
3ab39028 99 '352': 'RPL_WHOREPLY',
3ec786bc
JA
100 '353': 'RPL_NAMEREPLY',
101 '364': 'RPL_LINKS',
102 '365': 'RPL_ENDOFLINKS',
103 '366': 'RPL_ENDOFNAMES',
104 '367': 'RPL_BANLIST',
105 '368': 'RPL_ENDOFBANLIST',
5989f3f4 106 '369': 'RPL_ENDOFWHOWAS',
f26a988a 107 '371': 'RPL_INFO',
3ec786bc 108 '372': 'RPL_MOTD',
f26a988a 109 '374': 'RPL_ENDINFO',
3ec786bc
JA
110 '375': 'RPL_MOTDSTART',
111 '376': 'RPL_ENDOFMOTD',
3d753975 112 '378': 'RPL_WHOISHOST',
3ec786bc 113 '379': 'RPL_WHOISMODES',
f26a988a 114 '381': 'RPL_NOWOPER',
b4f0a085 115 '396': 'RPL_HOSTCLOACKING',
3ec786bc 116 '401': 'ERR_NOSUCHNICK',
f26a988a 117 '402': 'ERR_NOSUCHSERVER',
3ec786bc
JA
118 '404': 'ERR_CANNOTSENDTOCHAN',
119 '405': 'ERR_TOOMANYCHANNELS',
5989f3f4 120 '406': 'ERR_WASNOSUCHNICK',
3ec786bc
JA
121 '421': 'ERR_UNKNOWNCOMMAND',
122 '422': 'ERR_NOMOTD',
f26a988a 123 '423': 'ERR_NOADMININFO',
66c980d3 124 '432': 'ERR_ERRONEUSNICKNAME',
3ec786bc
JA
125 '433': 'ERR_NICKNAMEINUSE',
126 '441': 'ERR_USERNOTINCHANNEL',
127 '442': 'ERR_NOTONCHANNEL',
3d753975 128 '443': 'ERR_USERONCHANNEL',
3ec786bc 129 '451': 'ERR_NOTREGISTERED',
514955a3 130 '461': 'ERR_NOTENOUGHPARAMS',
3ec786bc
JA
131 '464': 'ERR_PASSWDMISMATCH',
132 '470': 'ERR_LINKCHANNEL',
133 '471': 'ERR_CHANNELISFULL',
134 '473': 'ERR_INVITEONLYCHAN',
135 '474': 'ERR_BANNEDFROMCHAN',
136 '475': 'ERR_BADCHANNELKEY',
137 '481': 'ERR_NOPRIVILEGES',
138 '482': 'ERR_CHANOPRIVSNEEDED',
139 '670': 'RPL_STARTTLS',
3d753975 140 '671': 'RPL_WHOISSECURE',
3ec786bc
JA
141 '900': 'RPL_SASLAUTHENTICATED',
142 '903': 'RPL_SASLLOGGEDIN',
143 '904': 'ERR_SASLNOTAUTHORISED',
144 '906': 'ERR_SASLABORTED',
94e2cb82 145 '907': 'ERR_SASLALREADYAUTHED',
514955a3 146 'WALLOPS': 'RPL_WALLOPS'
a8bf3ea4
JA
147};
148
149
cced0091 150IrcCommands = function (irc_connection) {
a8bf3ea4 151 this.irc_connection = irc_connection;
a8bf3ea4 152};
2a8e95d1 153module.exports = IrcCommands;
a8bf3ea4 154
3ec786bc
JA
155IrcCommands.prototype.dispatch = function (command, data) {
156 command += '';
157 if (irc_numerics[command]) {
158 command = irc_numerics[command];
159 }
160 if (handlers[command]) {
161 handlers[command].call(this, data);
162 } else {
3d753975 163 unknownCommand.call(this, command, data);
3ec786bc
JA
164 }
165};
2a8e95d1 166
3ec786bc
JA
167IrcCommands.addHandler = function (command, handler) {
168 if (typeof handler !== 'function') {
169 return false;
170 }
171 handlers[command] = handler;
a8bf3ea4
JA
172};
173
3ec786bc
JA
174IrcCommands.addNumeric = function (numeric, handler_name) {
175 irc_numerics[numeric + ''] = handler_name +'';
c08717da
D
176};
177
3ec786bc 178unknownCommand = function (command, data) {
3d753975
D
179 var params = _.clone(data.params);
180
181 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' unknown_command', {
182 command: command,
17dfa698 183 params: params
3d753975 184 });
17dfa698 185};
2a8e95d1
D
186
187
3ec786bc 188handlers = {
a49e0dcf
JA
189 'RPL_WELCOME': function (command) {
190 var nick = command.params[0];
191 this.irc_connection.registered = true;
992e681c 192 this.cap_negotiation = false;
d9285da9 193 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' connect', {
1cc056b8
JA
194 nick: nick
195 });
a49e0dcf 196 },
3ec786bc 197
a49e0dcf
JA
198 'RPL_ISUPPORT': function (command) {
199 var options, i, option, matches, j;
200 options = command.params;
201 for (i = 1; i < options.length; i++) {
202 option = options[i].split("=", 2);
203 option[0] = option[0].toUpperCase();
204 this.irc_connection.options[option[0]] = (typeof option[1] !== 'undefined') ? option[1] : true;
205 if (_.include(['NETWORK', 'PREFIX', 'CHANTYPES', 'CHANMODES', 'NAMESX'], option[0])) {
206 if (option[0] === 'PREFIX') {
207 matches = /\(([^)]*)\)(.*)/.exec(option[1]);
208 if ((matches) && (matches.length === 3)) {
209 this.irc_connection.options.PREFIX = [];
210 for (j = 0; j < matches[2].length; j++) {
211 this.irc_connection.options.PREFIX.push({symbol: matches[2].charAt(j), mode: matches[1].charAt(j)});
a8bf3ea4
JA
212 }
213 }
a49e0dcf
JA
214 } else if (option[0] === 'CHANTYPES') {
215 this.irc_connection.options.CHANTYPES = this.irc_connection.options.CHANTYPES.split('');
216 } else if (option[0] === 'CHANMODES') {
217 this.irc_connection.options.CHANMODES = option[1].split(',');
218 } else if ((option[0] === 'NAMESX') && (!_.contains(this.irc_connection.cap.enabled, 'multi-prefix'))) {
219 this.irc_connection.write('PROTOCTL NAMESX');
a8bf3ea4 220 }
a49e0dcf
JA
221 }
222 }
d9285da9 223 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' options', {
1cc056b8
JA
224 options: this.irc_connection.options,
225 cap: this.irc_connection.cap.enabled
226 });
a49e0dcf 227 },
3ec786bc 228
a49e0dcf 229 'RPL_ENDOFWHOIS': function (command) {
d9285da9 230 this.irc_connection.emit('user ' + command.params[1] + ' endofwhois', {
692163ca 231 nick: command.params[1],
17dfa698 232 msg: command.params[command.params.length - 1]
692163ca 233 });
a49e0dcf 234 },
3ec786bc 235
f701d5ba
D
236 'RPL_AWAY': function (command) {
237 this.irc_connection.emit('user ' + command.params[1] + ' whoisaway', {
238 nick: command.params[1],
17dfa698 239 reason: command.params[command.params.length - 1]
f701d5ba
D
240 });
241 },
3ec786bc 242
a49e0dcf 243 'RPL_WHOISUSER': function (command) {
d9285da9 244 this.irc_connection.emit('user ' + command.params[1] + ' whoisuser', {
692163ca
JA
245 nick: command.params[1],
246 ident: command.params[2],
247 host: command.params[3],
17dfa698 248 msg: command.params[command.params.length - 1]
692163ca 249 });
a49e0dcf 250 },
3ec786bc 251
951cd81e
K
252 'RPL_WHOISHELPOP': function (command) {
253 this.irc_connection.emit('user ' + command.params[1] + ' whoishelpop', {
254 nick: command.params[1],
255 msg: command.params[command.params.length - 1]
256 });
257 },
258
259 'RPL_WHOISBOT': function (command) {
260 this.irc_connection.emit('user ' + command.params[1] + ' whoisbot', {
261 nick: command.params[1],
262 msg: command.params[command.params.length - 1]
263 });
264 },
265
a49e0dcf 266 'RPL_WHOISSERVER': function (command) {
d9285da9 267 this.irc_connection.emit('user ' + command.params[1] + ' whoisserver', {
692163ca 268 nick: command.params[1],
5989f3f4 269 irc_server: command.params[2],
17dfa698 270 server_info: command.params[command.params.length - 1]
692163ca 271 });
a49e0dcf 272 },
3ec786bc 273
a49e0dcf 274 'RPL_WHOISOPERATOR': function (command) {
d9285da9 275 this.irc_connection.emit('user ' + command.params[1] + ' whoisoperator', {
692163ca 276 nick: command.params[1],
17dfa698 277 msg: command.params[command.params.length - 1]
692163ca 278 });
a49e0dcf 279 },
3ec786bc 280
a49e0dcf 281 'RPL_WHOISCHANNELS': function (command) {
d9285da9 282 this.irc_connection.emit('user ' + command.params[1] + ' whoischannels', {
692163ca 283 nick: command.params[1],
17dfa698 284 chans: command.params[command.params.length - 1]
692163ca 285 });
a49e0dcf 286 },
3ec786bc 287
a49e0dcf 288 'RPL_WHOISMODES': function (command) {
d9285da9 289 this.irc_connection.emit('user ' + command.params[1] + ' whoismodes', {
692163ca 290 nick: command.params[1],
17dfa698 291 msg: command.params[command.params.length - 1]
692163ca 292 });
a49e0dcf 293 },
3ec786bc 294
a49e0dcf 295 'RPL_WHOISIDLE': function (command) {
d9285da9 296 this.irc_connection.emit('user ' + command.params[1] + ' whoisidle', {
692163ca
JA
297 nick: command.params[1],
298 idle: command.params[2],
299 logon: command.params[3] || undefined
300 });
a49e0dcf 301 },
3ec786bc 302
a49e0dcf 303 'RPL_WHOISREGNICK': function (command) {
d9285da9 304 this.irc_connection.emit('user ' + command.params[1] + ' whoisregnick', {
692163ca 305 nick: command.params[1],
17dfa698 306 msg: command.params[command.params.length - 1]
692163ca 307 });
a49e0dcf 308 },
3ec786bc 309
3d753975
D
310 'RPL_WHOISHOST': function (command) {
311 this.irc_connection.emit('user ' + command.params[1] + ' whoishost', {
312 nick: command.params[1],
17dfa698 313 msg: command.params[command.params.length - 1]
3d753975
D
314 });
315 },
316
317 'RPL_WHOISSECURE': function (command) {
318 this.irc_connection.emit('user ' + command.params[1] + ' whoissecure', {
319 nick: command.params[1]
320 });
321 },
322
323 'RPL_WHOISACCOUNT': function (command) {
324 this.irc_connection.emit('user ' + command.params[1] + ' whoisaccount', {
325 nick: command.params[1],
326 account: command.params[2]
327 });
328 },
329
5989f3f4
JA
330 'RPL_WHOWASUSER': function (command) {
331 this.irc_connection.emit('user ' + command.params[1] + ' whowas', {
332 nick: command.params[1],
333 ident: command.params[2],
334 host: command.params[3],
17dfa698 335 real_name: command.params[command.params.length - 1]
5989f3f4
JA
336 });
337 },
338
339 'RPL_ENDOFWHOWAS': function (command) {
340 this.irc_connection.emit('user ' + command.params[1] + ' endofwhowas', {
341 nick: command.params[1]
342 });
343 },
344
345 'ERR_WASNOSUCHNICK': function (command) {
346 this.irc_connection.emit('user ' + command.params[1] + ' wasnosucknick', {
347 nick: command.params[1]
348 });
349 },
350
a49e0dcf 351 'RPL_LISTSTART': function (command) {
d9285da9 352 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' list_start', {});
a49e0dcf 353 },
3ec786bc 354
a49e0dcf 355 'RPL_LISTEND': function (command) {
d9285da9 356 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' list_end', {});
a49e0dcf 357 },
3ec786bc 358
a49e0dcf 359 'RPL_LIST': function (command) {
d9285da9 360 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' list_channel', {
fb1b18bc 361 channel: command.params[1],
25edd441 362 num_users: parseInt(command.params[2], 10),
5301ab32 363 topic: command.params[3] || ''
fb1b18bc 364 });
a49e0dcf 365 },
3ec786bc 366
72db27e4
D
367 'RPL_CHANNELMODEIS': function (command) {
368 var channel = command.params[1],
369 modes = parseModeList.call(this, command.params[2], command.params.slice(3));
370
371 this.irc_connection.emit('channel ' + channel + ' info', {
372 channel: channel,
373 modes: modes
374 });
375 },
376
377 'RPL_CREATIONTIME': function (command) {
378 var channel = command.params[1];
379
380 this.irc_connection.emit('channel ' + channel + ' info', {
381 channel: channel,
382 created_at: parseInt(command.params[2], 10)
383 });
384 },
385
386 'RPL_CHANNEL_URL': function (command) {
387 var channel = command.params[1];
388
389 this.irc_connection.emit('channel ' + channel + ' info', {
390 channel: channel,
17dfa698 391 url: command.params[command.params.length - 1]
72db27e4
D
392 });
393 },
394
a49e0dcf 395 'RPL_MOTD': function (command) {
d9285da9 396 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' motd', {
17dfa698 397 motd: command.params[command.params.length - 1] + '\n'
1cc056b8 398 });
a49e0dcf 399 },
3ec786bc 400
a49e0dcf 401 'RPL_MOTDSTART': function (command) {
d9285da9 402 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' motd_start', {});
a49e0dcf 403 },
3ec786bc 404
a49e0dcf 405 'RPL_ENDOFMOTD': function (command) {
d9285da9 406 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' motd_end', {});
a49e0dcf 407 },
3ec786bc 408
a49e0dcf 409 'RPL_NAMEREPLY': function (command) {
17dfa698 410 var members = command.params[command.params.length - 1].split(' ');
a49e0dcf
JA
411 var member_list = [];
412 var that = this;
a49e0dcf 413 _.each(members, function (member) {
b8d390b0
JA
414 var i = 0,
415 j = 0,
416 modes = [];
30b4d43a
D
417
418 // Make sure we have some prefixes already
419 if (that.irc_connection.options.PREFIX) {
b8d390b0
JA
420 for (j = 0; j < that.irc_connection.options.PREFIX.length; j++) {
421 if (member.charAt(i) === that.irc_connection.options.PREFIX[j].symbol) {
422 modes.push(that.irc_connection.options.PREFIX[j].mode);
423 i++;
a8bf3ea4 424 }
a8bf3ea4 425 }
a49e0dcf 426 }
30b4d43a 427
a49e0dcf 428 member_list.push({nick: member, modes: modes});
a49e0dcf 429 });
e2752476 430
d9285da9 431 this.irc_connection.emit('channel ' + command.params[2] + ' userlist', {
e2752476
D
432 users: member_list,
433 channel: command.params[2]
434 });
a49e0dcf 435 },
e2752476 436
a49e0dcf 437 'RPL_ENDOFNAMES': function (command) {
d9285da9 438 this.irc_connection.emit('channel ' + command.params[1] + ' userlist_end', {
e2752476
D
439 channel: command.params[1]
440 });
a49e0dcf 441 },
e2752476 442
3ab39028
D
443 'RPL_WHOREPLY': function (command) {
444 // For the time being, NOOP this command so they don't get passed
445 // down to the client. Waste of bandwidth since we do not use it yet
446 // TODO: Impliment RPL_WHOREPLY
447 },
448
449 'RPL_ENDOFWHO': function (command) {
450 // For the time being, NOOP this command so they don't get passed
451 // down to the client. Waste of bandwidth since we do not use it yet
452 // TODO: Impliment RPL_ENDOFWHO
453 },
454
a49e0dcf 455 'RPL_BANLIST': function (command) {
d9285da9 456 this.irc_connection.emit('channel ' + command.params[1] + ' banlist', {
059f4918
JA
457 channel: command.params[1],
458 banned: command.params[2],
459 banned_by: command.params[3],
460 banned_at: command.params[4]
461 });
a49e0dcf 462 },
3ec786bc 463
a49e0dcf 464 'RPL_ENDOFBANLIST': function (command) {
d9285da9 465 this.irc_connection.emit('channel ' + command.params[1] + ' banlist_end', {
aa22caa9 466 channel: command.params[1]
059f4918 467 });
a49e0dcf 468 },
3ec786bc 469
a49e0dcf 470 'RPL_TOPIC': function (command) {
d9285da9 471 this.irc_connection.emit('channel ' + command.params[1] + ' topic', {
059f4918 472 channel: command.params[1],
17dfa698 473 topic: command.params[command.params.length - 1]
059f4918 474 });
a49e0dcf 475 },
3ec786bc 476
a49e0dcf 477 'RPL_NOTOPIC': function (command) {
d9285da9 478 this.irc_connection.emit('channel ' + command.params[1] + ' topic', {
059f4918
JA
479 channel: command.params[1],
480 topic: ''
481 });
a49e0dcf 482 },
3ec786bc 483
a49e0dcf 484 'RPL_TOPICWHOTIME': function (command) {
d9285da9 485 this.irc_connection.emit('channel ' + command.params[1] + ' topicsetby', {
059f4918
JA
486 nick: command.params[2],
487 channel: command.params[1],
488 when: command.params[3]
489 });
a49e0dcf 490 },
3ec786bc 491
9a1da609
D
492 'RPL_INVITING': function (command) {
493 this.irc_connection.emit('channel ' + command.params[1] + ' invited', {
494 nick: command.params[0],
495 channel: command.params[1]
496 });
497 },
498
a49e0dcf 499 'PING': function (command) {
17dfa698 500 this.irc_connection.write('PONG ' + command.params[command.params.length - 1]);
a49e0dcf 501 },
e2752476 502
a49e0dcf 503 'JOIN': function (command) {
d10c74a6 504 var channel, time;
17dfa698 505 if (typeof command.params[0] === 'string' && command.params[0] !== '') {
a49e0dcf
JA
506 channel = command.params[0];
507 }
3ec786bc 508
50263ea0
D
509 // Check if we have a server-time
510 time = getServerTime.call(this, command);
d10c74a6 511
d9285da9 512 this.irc_connection.emit('channel ' + channel + ' join', {
e2752476
D
513 nick: command.nick,
514 ident: command.ident,
515 hostname: command.hostname,
d10c74a6
JA
516 channel: channel,
517 time: time
e2752476 518 });
a49e0dcf 519 },
e2752476 520
a49e0dcf 521 'PART': function (command) {
81198d7e 522 var time, channel, message;
d10c74a6 523
50263ea0
D
524 // Check if we have a server-time
525 time = getServerTime.call(this, command);
d10c74a6 526
17dfa698
JA
527 channel = command.params[0];
528 if (command.params.length > 1) {
529 message = command.params[command.params.length - 1];
81198d7e
D
530 }
531
f96620b7 532 this.irc_connection.emit('channel ' + channel + ' part', {
e2752476
D
533 nick: command.nick,
534 ident: command.ident,
535 hostname: command.hostname,
81198d7e
D
536 channel: channel,
537 message: message,
d10c74a6 538 time: time
e2752476 539 });
a49e0dcf 540 },
e2752476 541
a49e0dcf 542 'KICK': function (command) {
d10c74a6
JA
543 var time;
544
50263ea0
D
545 // Check if we have a server-time
546 time = getServerTime.call(this, command);
d10c74a6 547
d9285da9 548 this.irc_connection.emit('channel ' + command.params[0] + ' kick', {
e2752476
D
549 kicked: command.params[1],
550 nick: command.nick,
551 ident: command.ident,
552 hostname: command.hostname,
553 channel: command.params[0],
17dfa698 554 message: command.params[command.params.length - 1],
d10c74a6 555 time: time
e2752476 556 });
a49e0dcf 557 },
e2752476 558
a49e0dcf 559 'QUIT': function (command) {
d10c74a6
JA
560 var time;
561
50263ea0
D
562 // Check if we have a server-time
563 time = getServerTime.call(this, command);
d10c74a6 564
d9285da9 565 this.irc_connection.emit('user ' + command.nick + ' quit', {
e2752476
D
566 nick: command.nick,
567 ident: command.ident,
568 hostname: command.hostname,
17dfa698 569 message: command.params[command.params.length - 1],
d10c74a6 570 time: time
e2752476 571 });
a49e0dcf 572 },
e2752476 573
a49e0dcf 574 'NOTICE': function (command) {
d10c74a6 575 var namespace,
17dfa698
JA
576 time,
577 msg;
d10c74a6 578
50263ea0
D
579 // Check if we have a server-time
580 time = getServerTime.call(this, command);
d10c74a6 581
17dfa698
JA
582 msg = command.params[command.params.length - 1];
583 if ((msg.charAt(0) === String.fromCharCode(1)) && (msg.charAt(msg.length - 1) === String.fromCharCode(1))) {
a49e0dcf 584 // It's a CTCP response
17dfa698 585 namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ? 'user' : 'channel';
d9285da9 586 this.irc_connection.emit(namespace + ' ' + command.params[0] + ' ctcp_response', {
e2752476
D
587 nick: command.nick,
588 ident: command.ident,
589 hostname: command.hostname,
c0576204 590 target: command.params[0],
17dfa698 591 msg: msg.substring(1, msg.length - 1),
d10c74a6 592 time: time
e2752476 593 });
a49e0dcf 594 } else {
17dfa698 595 namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase() || command.params[0] === '*') ?
0190c2c9
D
596 'user' :
597 'channel';
598
d9285da9 599 this.irc_connection.emit(namespace + ' ' + command.params[0] + ' notice', {
0190c2c9
D
600 from_server: command.prefix ? true : false,
601 nick: command.nick || command.prefix || undefined,
e2752476
D
602 ident: command.ident,
603 hostname: command.hostname,
604 target: command.params[0],
17dfa698 605 msg: msg,
d10c74a6 606 time: time
e2752476 607 });
a49e0dcf
JA
608 }
609 },
3ec786bc 610
a49e0dcf 611 'NICK': function (command) {
d10c74a6
JA
612 var time;
613
50263ea0
D
614 // Check if we have a server-time
615 time = getServerTime.call(this, command);
d10c74a6 616
d9285da9 617 this.irc_connection.emit('user ' + command.nick + ' nick', {
692163ca
JA
618 nick: command.nick,
619 ident: command.ident,
620 hostname: command.hostname,
17dfa698 621 newnick: command.params[0],
d10c74a6 622 time: time
692163ca 623 });
a49e0dcf 624 },
3ec786bc 625
a49e0dcf 626 'TOPIC': function (command) {
d10c74a6
JA
627 var time;
628
30b4d43a 629 // If we don't have an associated channel, no need to continue
17dfa698
JA
630 if (!command.params[0]) {
631 return;
632 }
30b4d43a 633
50263ea0
D
634 // Check if we have a server-time
635 time = getServerTime.call(this, command);
d10c74a6 636
30b4d43a 637 var channel = command.params[0],
17dfa698 638 topic = command.params[command.params.length - 1] || '';
30b4d43a 639
d9285da9 640 this.irc_connection.emit('channel ' + channel + ' topic', {
f4ec5774
D
641 nick: command.nick,
642 channel: channel,
d10c74a6
JA
643 topic: topic,
644 time: time
f4ec5774 645 });
a49e0dcf 646 },
3ec786bc 647
d9285da9 648 'MODE': function (command) {
72db27e4 649 var modes = [], event, time;
d10c74a6 650
50263ea0
D
651 // Check if we have a server-time
652 time = getServerTime.call(this, command);
3ec786bc 653
72db27e4 654 // Get a JSON representation of the modes
17dfa698 655 modes = parseModeList.call(this, command.params[1], command.params.slice(2));
d9285da9 656 event = (_.contains(this.irc_connection.options.CHANTYPES, command.params[0][0]) ? 'channel ' : 'user ') + command.params[0] + ' mode';
3ec786bc 657
a7973dfb 658 this.irc_connection.emit(event, {
a49e0dcf
JA
659 target: command.params[0],
660 nick: command.nick || command.prefix || '',
d10c74a6
JA
661 modes: modes,
662 time: time
a49e0dcf
JA
663 });
664 },
3ec786bc 665
a49e0dcf 666 'PRIVMSG': function (command) {
9b95f5f1 667 var tmp, namespace, time, msg, version_string, client_info;
d10c74a6 668
50263ea0
D
669 // Check if we have a server-time
670 time = getServerTime.call(this, command);
d10c74a6 671
17dfa698
JA
672 msg = command.params[command.params.length - 1];
673 if ((msg.charAt(0) === String.fromCharCode(1)) && (msg.charAt(msg.length - 1) === String.fromCharCode(1))) {
a49e0dcf 674 //CTCP request
17dfa698 675 if (msg.substr(1, 6) === 'ACTION') {
cd5b1558
D
676 namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ?
677 'user ' + command.nick :
678 'channel ' + command.params[0];
679
680 this.irc_connection.emit(namespace + ' action', {
d10c74a6
JA
681 nick: command.nick,
682 ident: command.ident,
683 hostname: command.hostname,
c0576204 684 target: command.params[0],
17dfa698 685 msg: msg.substring(8, msg.length - 1),
d10c74a6
JA
686 time: time
687 });
cd5b1558 688
17dfa698 689 } else if (msg.substr(1, 7) === 'VERSION') {
9b95f5f1
D
690 client_info = this.irc_connection.state.client.client_info;
691 version_string = global.build_version;
692
693 // If the client build_version differs from the server, add this to the version_string
694 if (client_info && client_info.build_version !== global.build_version) {
695 version_string += ', client build: ' + client_info.build_version;
696 }
697
698 version_string = 'KiwiIRC (' + version_string + ')';
9b95f5f1 699 this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'VERSION ' + version_string + String.fromCharCode(1));
cd5b1558 700
17dfa698 701 } else if (msg.substr(1, 6) === 'SOURCE') {
b39bb331 702 this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'SOURCE http://www.kiwiirc.com/' + String.fromCharCode(1));
cd5b1558 703
17dfa698 704 } else if (msg.substr(1, 10) === 'CLIENTINFO') {
b39bb331 705 this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'CLIENTINFO SOURCE VERSION TIME' + String.fromCharCode(1));
cd5b1558 706
a49e0dcf 707 } else {
c0576204
D
708 namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ?
709 'user ' + command.nick :
710 'channel ' + command.params[0];
711
712 this.irc_connection.emit(namespace + ' ctcp_request', {
b39bb331
D
713 nick: command.nick,
714 ident: command.ident,
715 hostname: command.hostname,
716 target: command.params[0],
17dfa698
JA
717 type: (msg.substring(1, msg.length - 1).split(' ') || [null])[0],
718 msg: msg.substring(1, msg.length - 1),
d10c74a6 719 time: time
b39bb331 720 });
a49e0dcf
JA
721 }
722 } else {
e2752476 723 // A message to a user (private message) or to a channel?
17dfa698 724 namespace = (command.params[0].toLowerCase() === this.irc_connection.nick.toLowerCase()) ? 'user ' + command.nick : 'channel ' + command.params[0];
d9285da9 725 this.irc_connection.emit(namespace + ' privmsg', {
ffbbc70f
D
726 nick: command.nick,
727 ident: command.ident,
728 hostname: command.hostname,
c0576204 729 target: command.params[0],
17dfa698 730 msg: msg,
d10c74a6 731 time: time
b09157de 732 });
a49e0dcf
JA
733 }
734 },
3ec786bc 735
a49e0dcf
JA
736 'CAP': function (command) {
737 // TODO: capability modifiers
738 // i.e. - for disable, ~ for requires ACK, = for sticky
17dfa698 739 var capabilities = command.params[command.params.length - 1].replace(/(?:^| )[\-~=]/, '').split(' ');
a49e0dcf 740 var request;
259d9b96
JA
741
742 // Which capabilities we want to enable
50263ea0 743 var want = ['multi-prefix', 'away-notify', 'server-time', 'znc.in/server-time-iso', 'znc.in/server-time'];
259d9b96 744
a49e0dcf
JA
745 if (this.irc_connection.password) {
746 want.push('sasl');
747 }
259d9b96 748
a49e0dcf
JA
749 switch (command.params[1]) {
750 case 'LS':
259d9b96 751 // Compute which of the available capabilities we want and request them
a49e0dcf
JA
752 request = _.intersection(capabilities, want);
753 if (request.length > 0) {
754 this.irc_connection.cap.requested = request;
755 this.irc_connection.write('CAP REQ :' + request.join(' '));
756 } else {
757 this.irc_connection.write('CAP END');
992e681c 758 this.irc_connection.cap_negotiation = false;
a8bf3ea4 759 }
a49e0dcf
JA
760 break;
761 case 'ACK':
762 if (capabilities.length > 0) {
259d9b96 763 // Update list of enabled capabilities
a49e0dcf 764 this.irc_connection.cap.enabled = capabilities;
259d9b96 765 // Update list of capabilities we would like to have but that aren't enabled
a49e0dcf 766 this.irc_connection.cap.requested = _.difference(this.irc_connection.cap.requested, capabilities);
703d2778 767 }
259d9b96 768 if (this.irc_connection.cap.enabled.length > 0) {
a49e0dcf
JA
769 if (_.contains(this.irc_connection.cap.enabled, 'sasl')) {
770 this.irc_connection.sasl = true;
771 this.irc_connection.write('AUTHENTICATE PLAIN');
a8bf3ea4 772 } else {
a49e0dcf 773 this.irc_connection.write('CAP END');
992e681c 774 this.irc_connection.cap_negotiation = false;
a8bf3ea4 775 }
a8bf3ea4 776 }
a49e0dcf
JA
777 break;
778 case 'NAK':
779 if (capabilities.length > 0) {
780 this.irc_connection.cap.requested = _.difference(this.irc_connection.cap.requested, capabilities);
7dfe47c6 781 }
a49e0dcf 782 if (this.irc_connection.cap.requested.length > 0) {
7dfe47c6 783 this.irc_connection.write('CAP END');
992e681c 784 this.irc_connection.cap_negotiation = false;
7dfe47c6 785 }
a49e0dcf
JA
786 break;
787 case 'LIST':
788 // should we do anything here?
789 break;
790 }
791 },
3ec786bc 792
a49e0dcf
JA
793 'AUTHENTICATE': function (command) {
794 var b = new Buffer(this.irc_connection.nick + "\0" + this.irc_connection.nick + "\0" + this.irc_connection.password, 'utf8');
795 var b64 = b.toString('base64');
796 if (command.params[0] === '+') {
797 while (b64.length >= 400) {
798 this.irc_connection.write('AUTHENTICATE ' + b64.slice(0, 399));
799 b64 = b64.slice(399);
a8bf3ea4 800 }
a49e0dcf
JA
801 if (b64.length > 0) {
802 this.irc_connection.write('AUTHENTICATE ' + b64);
803 } else {
804 this.irc_connection.write('AUTHENTICATE +');
805 }
806 } else {
807 this.irc_connection.write('CAP END');
992e681c 808 this.irc_connection.cap_negotiation = false;
a49e0dcf
JA
809 }
810 },
3ec786bc 811
a49e0dcf 812 'AWAY': function (command) {
d10c74a6
JA
813 var time;
814
50263ea0
D
815 // Check if we have a server-time
816 time = getServerTime.call(this, command);
d10c74a6 817
d9285da9 818 this.irc_connection.emit('user ' + command.nick + ' away', {
692163ca 819 nick: command.nick,
17dfa698 820 msg: command.params[command.params.length - 1],
d10c74a6 821 time: time
692163ca 822 });
a49e0dcf 823 },
3ec786bc 824
a49e0dcf
JA
825 'RPL_SASLAUTHENTICATED': function (command) {
826 this.irc_connection.write('CAP END');
992e681c 827 this.irc_connection.cap_negotiation = false;
a49e0dcf
JA
828 this.irc_connection.sasl = true;
829 },
3ec786bc 830
a49e0dcf 831 'RPL_SASLLOGGEDIN': function (command) {
992e681c 832 if (this.irc_connection.cap_negotiation === true) {
a49e0dcf 833 this.irc_connection.write('CAP END');
992e681c 834 this.irc_connection.cap_negotiation = false;
a49e0dcf
JA
835 }
836 },
3ec786bc 837
a49e0dcf 838 'ERR_SASLNOTAUTHORISED': function (command) {
3ec786bc 839 this.irc_connection.write('CAP END');
992e681c 840 this.irc_connection.cap_negotiation = false;
3ec786bc
JA
841 },
842
a49e0dcf
JA
843 'ERR_SASLABORTED': function (command) {
844 this.irc_connection.write('CAP END');
992e681c 845 this.irc_connection.cap_negotiation = false;
a49e0dcf 846 },
3ec786bc 847
a49e0dcf
JA
848 'ERR_SASLALREADYAUTHED': function (command) {
849 // noop
850 },
3ec786bc 851
a49e0dcf 852 'ERROR': function (command) {
d9285da9 853 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' error', {
17dfa698 854 reason: command.params[command.params.length - 1]
1cc056b8 855 });
a49e0dcf 856 },
ebe178d6 857 ERR_PASSWDMISMATCH: function (command) {
d9285da9 858 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' password_mismatch', {});
ebe178d6 859 },
3ec786bc 860
a49e0dcf 861 ERR_LINKCHANNEL: function (command) {
d9285da9 862 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' channel_redirect', {
1cc056b8
JA
863 from: command.params[1],
864 to: command.params[2]
865 });
a49e0dcf 866 },
3ec786bc 867
a49e0dcf 868 ERR_NOSUCHNICK: function (command) {
d9285da9 869 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' no_such_nick', {
1cc056b8 870 nick: command.params[1],
17dfa698 871 reason: command.params[command.params.length - 1]
1cc056b8 872 });
a49e0dcf 873 },
3ec786bc 874
a49e0dcf 875 ERR_CANNOTSENDTOCHAN: function (command) {
1dad9adf 876 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' cannot_send_to_channel', {
1cc056b8 877 channel: command.params[1],
17dfa698 878 reason: command.params[command.params.length - 1]
1cc056b8 879 });
a49e0dcf 880 },
3ec786bc 881
a49e0dcf 882 ERR_TOOMANYCHANNELS: function (command) {
d9285da9 883 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' too_many_channels', {
1cc056b8 884 channel: command.params[1],
17dfa698 885 reason: command.params[command.params.length - 1]
1cc056b8 886 });
a49e0dcf 887 },
3ec786bc 888
a49e0dcf 889 ERR_USERNOTINCHANNEL: function (command) {
d9285da9 890 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' user_not_in_channel', {
1cc056b8
JA
891 nick: command.params[0],
892 channel: command.params[1],
17dfa698 893 reason: command.params[command.params.length - 1]
1cc056b8 894 });
a49e0dcf 895 },
3ec786bc 896
a49e0dcf 897 ERR_NOTONCHANNEL: function (command) {
d9285da9 898 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' not_on_channel', {
1cc056b8 899 channel: command.params[1],
17dfa698 900 reason: command.params[command.params.length - 1]
1cc056b8 901 });
a49e0dcf 902 },
3ec786bc 903
3d753975
D
904 ERR_USERONCHANNEL: function (command) {
905 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' user_on_channel', {
906 nick: command.params[1],
907 channel: command.params[2]
908 });
909 },
910
a49e0dcf 911 ERR_CHANNELISFULL: function (command) {
3ec786bc
JA
912 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' channel_is_full', {
913 channel: command.params[1],
17dfa698 914 reason: command.params[command.params.length - 1]
3ec786bc
JA
915 });
916 },
917
a49e0dcf 918 ERR_INVITEONLYCHAN: function (command) {
d9285da9 919 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' invite_only_channel', {
1cc056b8 920 channel: command.params[1],
17dfa698 921 reason: command.params[command.params.length - 1]
1cc056b8 922 });
a49e0dcf 923 },
3ec786bc 924
a49e0dcf 925 ERR_BANNEDFROMCHAN: function (command) {
d9285da9 926 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' banned_from_channel', {
1cc056b8 927 channel: command.params[1],
17dfa698 928 reason: command.params[command.params.length - 1]
1cc056b8 929 });
a49e0dcf 930 },
3ec786bc 931
a49e0dcf 932 ERR_BADCHANNELKEY: function (command) {
d9285da9 933 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' bad_channel_key', {
1cc056b8 934 channel: command.params[1],
17dfa698 935 reason: command.params[command.params.length - 1]
1cc056b8 936 });
a49e0dcf 937 },
3ec786bc 938
a49e0dcf 939 ERR_CHANOPRIVSNEEDED: function (command) {
d9285da9 940 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' chanop_privs_needed', {
1cc056b8 941 channel: command.params[1],
17dfa698 942 reason: command.params[command.params.length - 1]
1cc056b8 943 });
a49e0dcf 944 },
3ec786bc 945
a49e0dcf 946 ERR_NICKNAMEINUSE: function (command) {
d9285da9 947 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' nickname_in_use', {
1cc056b8 948 nick: command.params[1],
17dfa698 949 reason: command.params[command.params.length - 1]
1cc056b8 950 });
a49e0dcf 951 },
3ec786bc 952
66c980d3
JA
953 ERR_ERRONEUSNICKNAME: function(command) {
954 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' erroneus_nickname', {
955 nick: command.params[1],
17dfa698 956 reason: command.params[command.params.length - 1]
66c980d3
JA
957 });
958 },
959
a49e0dcf 960 ERR_NOTREGISTERED: function (command) {
cea1f25b
D
961 },
962
963 RPL_MAPMORE: function (command) {
964 var params = _.clone(command.params);
965 params.shift();
17dfa698 966 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
cea1f25b 967 },
3ec786bc 968
cea1f25b
D
969 RPL_MAPEND: function (command) {
970 var params = _.clone(command.params);
971 params.shift();
17dfa698 972 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
cea1f25b
D
973 },
974
975 RPL_LINKS: function (command) {
976 var params = _.clone(command.params);
977 params.shift();
17dfa698 978 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
cea1f25b 979 },
3ec786bc 980
cea1f25b
D
981 RPL_ENDOFLINKS: function (command) {
982 var params = _.clone(command.params);
983 params.shift();
17dfa698 984 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
cea1f25b
D
985 },
986
987 ERR_UNKNOWNCOMMAND: function (command) {
988 var params = _.clone(command.params);
989 params.shift();
17dfa698 990 genericNotice.call(this, command, '`' + params.slice(0, -1).join(', ') + '` ' + command.params[command.params.length - 1]);
cea1f25b
D
991 },
992
993 ERR_NOMOTD: function (command) {
994 var params = _.clone(command.params);
995 params.shift();
17dfa698 996 genericNotice.call(this, command, command.params[command.params.length - 1]);
cea1f25b
D
997 },
998
999 ERR_NOPRIVILEGES: function (command) {
1000 var params = _.clone(command.params);
1001 params.shift();
17dfa698 1002 genericNotice.call(this, command, command.params[command.params.length - 1]);
28ce8b75
D
1003 },
1004
1005 RPL_STATSCONN: function (command) {
1006 var params = _.clone(command.params);
1007 params.shift();
17dfa698 1008 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
28ce8b75
D
1009 },
1010
1011 RPL_LUSERCLIENT: function (command) {
1012 var params = _.clone(command.params);
1013 params.shift();
17dfa698 1014 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
28ce8b75
D
1015 },
1016
1017 RPL_LUSEROP: function (command) {
1018 var params = _.clone(command.params);
1019 params.shift();
17dfa698 1020 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
28ce8b75
D
1021 },
1022
1023 RPL_LUSERUNKNOWN: function (command) {
1024 var params = _.clone(command.params);
1025 params.shift();
17dfa698 1026 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
28ce8b75
D
1027 },
1028
1029 RPL_LUSERCHANNELS: function (command) {
1030 var params = _.clone(command.params);
1031 params.shift();
17dfa698 1032 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
28ce8b75
D
1033 },
1034
1035 RPL_LUSERME: function (command) {
1036 var params = _.clone(command.params);
1037 params.shift();
17dfa698 1038 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
28ce8b75
D
1039 },
1040
1041 RPL_LOCALUSERS: function (command) {
1042 var params = _.clone(command.params);
1043 params.shift();
17dfa698 1044 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
28ce8b75 1045 },
3ec786bc 1046
28ce8b75
D
1047 RPL_GLOBALUSERS: function (command) {
1048 var params = _.clone(command.params);
1049 params.shift();
17dfa698 1050 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
b4f0a085 1051 },
cd5b1558 1052
b4f0a085 1053 RPL_HOSTCLOACKING: function (command) {
55084113 1054 genericNotice.call(this, command, command.params[1] + ' ' + command.params[command.params.length - 1]);
cd5b1558 1055 },
94e2cb82
CC
1056
1057 RPL_YOURHOST: function (command) {
1058 var params = _.clone(command.params);
1059 params.shift();
1060 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1061 },
1062
1063 RPL_CREATED: function (command) {
1064 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' server', {
1065 motd: command.params[command.params.length - 1] + '\n'
1066 });
1067 },
1068
1069 RPL_MYINFO: function (command) {
1070 var params = _.clone(command.params);
1071 params.shift();
1072 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1073 },
1074
1075 RPL_SNOMASK: function (command) {
1076 var params = _.clone(command.params);
1077 params.shift();
1078 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1079 },
1080
1081 RPL_NOWOPER: function (command) {
1082 var params = _.clone(command.params);
1083 params.shift();
1084 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1085 },
1086
1087 RPL_TRACELINK: function (command) {
1088 var params = _.clone(command.params);
1089 params.shift();
1090 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1091 },
1092
1093 RPL_TRACECONNECTING: function (command) {
1094 var params = _.clone(command.params);
1095 params.shift();
1096 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1097 },
1098
1099 RPL_TRACEHANDSHAKE: function (command) {
1100 var params = _.clone(command.params);
1101 params.shift();
1102 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1103 },
1104
1105 RPL_TRACEUNKNOWN: function (command) {
1106 var params = _.clone(command.params);
1107 params.shift();
1108 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1109 },
1110
1111 RPL_TRACEOPERATOR: function (command) {
1112 var params = _.clone(command.params);
1113 params.shift();
1114 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1115 },
1116
1117 RPL_TRACEUSER: function (command) {
1118 var params = _.clone(command.params);
1119 params.shift();
1120 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1121 },
1122
1123 RPL_TRACESERVER: function (command) {
1124 var params = _.clone(command.params);
1125 params.shift();
1126 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1127 },
1128
1129 RPL_TRACESERVICE: function (command) {
1130 var params = _.clone(command.params);
1131 params.shift();
1132 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1133 },
1134
1135 RPL_TRACENEWTYPE: function (command) {
1136 var params = _.clone(command.params);
1137 params.shift();
1138 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1139 },
1140
1141 RPL_TRACECLASS: function (command) {
1142 var params = _.clone(command.params);
1143 params.shift();
1144 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1145 },
1146
1147 RPL_TRACERECONNECT: function (command) {
1148 var params = _.clone(command.params);
1149 params.shift();
1150 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1151 },
1152
1153 RPL_STATSLINKINFO: function (command) {
1154 var params = _.clone(command.params);
1155 params.shift();
1156 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1157 },
1158
1159 RPL_STATSCOMMANDS: function (command) {
1160 var params = _.clone(command.params);
1161 params.shift();
1162 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1163 },
1164
1165 RPL_STATSCLINE: function (command) {
1166 var params = _.clone(command.params);
1167 params.shift();
1168 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1169 },
1170
1171 RPL_STATSNLINE: function (command) {
1172 var params = _.clone(command.params);
1173 params.shift();
1174 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1175 },
1176
1177 RPL_STATSILINE: function (command) {
1178 var params = _.clone(command.params);
1179 params.shift();
1180 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1181 },
1182
1183 RPL_STATSKLINE: function (command) {
1184 var params = _.clone(command.params);
1185 params.shift();
1186 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1187 },
1188
1189 RPL_STATSPLINE: function (command) {
1190 var params = _.clone(command.params);
1191 params.shift();
1192 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1193 },
1194
1195 RPL_STATSYLINE: function (command) {
1196 var params = _.clone(command.params);
1197 params.shift();
1198 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1199 },
1200
1201 RPL_ENDOFSTATS: function (command) {
1202 var params = _.clone(command.params);
1203 params.shift();
1204 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1205 },
1206
1207 RPL_STATSBLINE: function (command) {
1208 var params = _.clone(command.params);
1209 params.shift();
1210 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1211 },
1212
1213 RPL_UMODEIS: function (command) {
1214 var params = _.clone(command.params);
1215 params.shift();
1216 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1217 },
1218
1219 RPL_SQLINE_NICK: function (command) {
1220 var params = _.clone(command.params);
1221 params.shift();
1222 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1223 },
1224
1225 RPL_STATS_E: function (command) {
1226 var params = _.clone(command.params);
1227 params.shift();
1228 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1229 },
1230
1231 RPL_STATS_D: function (command) {
1232 var params = _.clone(command.params);
1233 params.shift();
1234 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1235 },
1236
1237 RPL_SPAMFILTER: function (command) {
1238 var params = _.clone(command.params);
1239 params.shift();
1240 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1241 },
1242
1243 RPL_SERVICEINFO: function (command) {
1244 var params = _.clone(command.params);
1245 params.shift();
1246 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1247 },
1248
1249 RPL_ENDOFSERVICES: function (command) {
1250 var params = _.clone(command.params);
1251 params.shift();
1252 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1253 },
1254
1255 RPL_SERVICE: function (command) {
1256 var params = _.clone(command.params);
1257 params.shift();
1258 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1259 },
1260
1261 RPL_SERVLIST: function (command) {
1262 var params = _.clone(command.params);
1263 params.shift();
1264 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1265 },
1266
1267 RPL_SERVLISTEND: function (command) {
1268 var params = _.clone(command.params);
1269 params.shift();
1270 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1271 },
1272
1273 RPL_STATSLLINE: function (command) {
1274 var params = _.clone(command.params);
1275 params.shift();
1276 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1277 },
1278
1279 RPL_STATSUPTIME: function (command) {
1280 var params = _.clone(command.params);
1281 params.shift();
1282 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1283 },
1284
1285 RPL_STATSOLINE: function (command) {
1286 var params = _.clone(command.params);
1287 params.shift();
1288 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1289 },
1290
1291 RPL_STATSHLINE: function (command) {
1292 var params = _.clone(command.params);
1293 params.shift();
1294 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1295 },
1296
1297 RPL_STATSSLINE: function (command) {
1298 var params = _.clone(command.params);
1299 params.shift();
1300 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1301 },
1302
1303 RPL_STATSGLINE: function (command) {
1304 var params = _.clone(command.params);
1305 params.shift();
1306 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1307 },
1308
1309 RPL_STATSXLINE: function (command) {
1310 var params = _.clone(command.params);
1311 params.shift();
1312 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1313 },
1314
1315 RPL_STATSULINE: function (command) {
1316 var params = _.clone(command.params);
1317 params.shift();
1318 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1319 },
1320
1321 RPL_STATSDEBUG: function (command) {
1322 var params = _.clone(command.params);
1323 params.shift();
1324 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1325 },
1326
1327 RPL_ADMINME: function (command) {
1328 var params = _.clone(command.params);
1329 params.shift();
1330 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1331 },
1332
1333 RPL_ADMINLOC1: function (command) {
1334 var params = _.clone(command.params);
1335 params.shift();
1336 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1337 },
1338
1339 RPL_ADMINLOC2: function (command) {
1340 var params = _.clone(command.params);
1341 params.shift();
1342 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1343 },
1344
1345 RPL_ADMINEMAIL: function (command) {
1346 var params = _.clone(command.params);
1347 params.shift();
1348 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1349 },
1350
1351 RPL_HELPHDR: function (command) {
1352 var params = _.clone(command.params);
1353 params.shift();
1354 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1355 },
1356
1357 RPL_HELPOP: function (command) {
1358 var params = _.clone(command.params);
1359 params.shift();
1360 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1361 },
1362
1363 RPL_HELPTLR: function (command) {
1364 var params = _.clone(command.params);
1365 params.shift();
1366 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1367 },
1368
1369 RPL_ZIPSTATS: function (command) {
1370 var params = _.clone(command.params);
1371 params.shift();
1372 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1373 },
1374
1375 RPL_WHOISSPECIAL: function (command) {
1376 this.irc_connection.emit('user ' + command.params[1] + ' whoisswhois', {
1377 nick: command.params[1],
1378 msg: command.params[command.params.length - 1]
1379 });
1380 },
1381
1382 'RPL_WHOISACTUALLY': function (command) {
1383 this.irc_connection.emit('user ' + command.params[1] + ' whoishost', {
1384 nick: command.params[1],
1385 msg: command.params[command.params.length - 1]
1386 });
1387 },
1388
1389 RPL_INFO: function (command) {
1390 var params = _.clone(command.params);
1391 params.shift();
1392 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1393 },
1394
1395 RPL_ENDINFO: function (command) {
1396 var params = _.clone(command.params);
1397 params.shift();
1398 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1399 },
1400
1401 RPL_NOSUCHSERVER: function (command) {
1402 var params = _.clone(command.params);
1403 params.shift();
1404 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1405 },
1406
1407 ERR_NOADMININFO: function (command) {
1408 var params = _.clone(command.params);
1409 params.shift();
1410 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1411 },
1412
1413 ERR_NOTENOUGHPARAMS: function (command) {
1414 var params = _.clone(command.params);
1415 params.shift();
1416 genericNotice.call(this, command, params.slice(0, -1).join(', ') + ' ' + command.params[command.params.length - 1]);
1417 },
1418
1419 RPL_WALLOPS: function (command) {
44f18d27 1420 this.irc_connection.emit('user ' + this.irc_connection.nick + ' wallops', {
876a7b29 1421 from_server: false,
44f18d27 1422 nick: command.nick,
876a7b29
CC
1423 ident: command.ident,
1424 hostname: command.hostname,
8f67d91a 1425 target: this.irc_connection.nick,
94e2cb82
CC
1426 msg: command.params[command.params.length - 1]
1427 });
1428 },
a8bf3ea4 1429};
cea1f25b
D
1430
1431
1432
1433
1434function genericNotice (command, msg, is_error) {
1435 // Default to being an error
1436 if (typeof is_error !== 'boolean')
1437 is_error = true;
1438
cd5b1558 1439 this.irc_connection.emit('user ' + command.prefix + ' notice', {
514955a3
CC
1440 from_server: true,
1441 nick: command.prefix,
cea1f25b
D
1442 ident: '',
1443 hostname: '',
1444 target: command.params[0],
1445 msg: msg,
1446 numeric: parseInt(command.command, 10)
1447 });
aef6e2a7 1448}
0d14bcf8 1449
50263ea0 1450
72db27e4
D
1451/**
1452 * Convert a mode string such as '+k pass', or '-i' to a readable
1453 * format.
1454 * [ { mode: '+k', param: 'pass' } ]
1455 * [ { mode: '-i', param: null } ]
1456 */
1457function parseModeList(mode_string, mode_params) {
1458 var chanmodes = this.irc_connection.options.CHANMODES || [],
1459 prefixes = this.irc_connection.options.PREFIX || [],
1460 always_param = (chanmodes[0] || '').concat((chanmodes[1] || '')),
1461 modes = [],
1462 has_param, i, j, add;
1463
1464 prefixes = _.reduce(prefixes, function (list, prefix) {
1465 list.push(prefix.mode);
1466 return list;
1467 }, []);
1468 always_param = always_param.split('').concat(prefixes);
1469
1470 has_param = function (mode, add) {
1471 if (_.find(always_param, function (m) {
1472 return m === mode;
1473 })) {
1474 return true;
1475 } else if (add && _.find((chanmodes[2] || '').split(''), function (m) {
1476 return m === mode;
1477 })) {
1478 return true;
1479 } else {
1480 return false;
1481 }
1482 };
1483
1484 j = 0;
1485 for (i = 0; i < mode_string.length; i++) {
1486 switch (mode_string[i]) {
1487 case '+':
1488 add = true;
1489 break;
1490 case '-':
1491 add = false;
1492 break;
1493 default:
1494 if (has_param(mode_string[i], add)) {
1495 modes.push({mode: (add ? '+' : '-') + mode_string[i], param: mode_params[j]});
1496 j++;
1497 } else {
1498 modes.push({mode: (add ? '+' : '-') + mode_string[i], param: null});
1499 }
1500 }
1501 }
1502
1503 return modes;
1504}
1505
1506
50263ea0
D
1507function getServerTime(command) {
1508 var time;
1509
1510 // No tags? No times.
1511 if (!command.tags || command.tags.length === 0) {
1512 return time;
1513 }
1514
1515 if (capContainsAny.call(this, ['server-time', 'znc.in/server-time', 'znc.in/server-time-iso'])) {
1516 time = _.find(command.tags, function (tag) {
1517 return tag.tag === 'time';
1518 });
1519
1520 time = time ? time.value : undefined;
1521
1522 // Convert the time value to a unixtimestamp
1523 if (typeof time === 'string') {
1524 if (time.indexOf('T') > -1) {
75d92a37 1525 time = parseISO8601(time);
50263ea0
D
1526
1527 } else if(time.match(/^[0-9.]+$/)) {
1528 // A string formatted unix timestamp
1529 time = new Date(time * 1000);
1530 }
1531
1532 time = time.getTime();
1533
1534 } else if (typeof time === 'number') {
1535 time = new Date(time * 1000);
1536 time = time.getTime();
1537 }
1538 }
1539
1540 return time;
1541}
1542
1543
0d14bcf8
JA
1544function capContainsAny (caps) {
1545 var intersection;
50263ea0 1546 if (!caps instanceof Array) {
0d14bcf8
JA
1547 caps = [caps];
1548 }
1549 intersection = _.intersection(this.irc_connection.cap.enabled, caps);
1550 return intersection.length > 0;
1551}
50263ea0
D
1552
1553
1554// Code based on http://anentropic.wordpress.com/2009/06/25/javascript-iso8601-parser-and-pretty-dates/#comment-154
1555function parseISO8601(str) {
1556 if (Date.prototype.toISOString) {
1557 return new Date(str);
1558 } else {
1559 var parts = str.split('T'),
1560 dateParts = parts[0].split('-'),
1561 timeParts = parts[1].split('Z'),
1562 timeSubParts = timeParts[0].split(':'),
1563 timeSecParts = timeSubParts[2].split('.'),
1564 timeHours = Number(timeSubParts[0]),
1565 _date = new Date();
1566
1567 _date.setUTCFullYear(Number(dateParts[0]));
1568 _date.setUTCDate(1);
1569 _date.setUTCMonth(Number(dateParts[1])-1);
1570 _date.setUTCDate(Number(dateParts[2]));
1571 _date.setUTCHours(Number(timeHours));
1572 _date.setUTCMinutes(Number(timeSubParts[1]));
1573 _date.setUTCSeconds(Number(timeSecParts[0]));
1574 if (timeSecParts[1]) {
1575 _date.setUTCMilliseconds(Number(timeSecParts[1]));
1576 }
1577
1578 return _date;
1579 }
1580}