No more need to specify a set of available encodings
[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',
9 '004': 'RPL_MYINFO',
10 '005': 'RPL_ISUPPORT',
11 '006': 'RPL_MAPMORE',
12 '007': 'RPL_MAPEND',
13 '250': 'RPL_STATSCONN',
14 '251': 'RPL_LUSERCLIENT',
15 '252': 'RPL_LUSEROP',
16 '253': 'RPL_LUSERUNKNOWN',
17 '254': 'RPL_LUSERCHANNELS',
18 '255': 'RPL_LUSERME',
19 '265': 'RPL_LOCALUSERS',
20 '266': 'RPL_GLOBALUSERS',
21 '301': 'RPL_AWAY',
22 '307': 'RPL_WHOISREGNICK',
23 '311': 'RPL_WHOISUSER',
24 '312': 'RPL_WHOISSERVER',
25 '313': 'RPL_WHOISOPERATOR',
5989f3f4 26 '314': 'RPL_WHOWASUSER',
3ec786bc
JA
27 '317': 'RPL_WHOISIDLE',
28 '318': 'RPL_ENDOFWHOIS',
29 '319': 'RPL_WHOISCHANNELS',
30 '321': 'RPL_LISTSTART',
31 '322': 'RPL_LIST',
32 '323': 'RPL_LISTEND',
33 '331': 'RPL_NOTOPIC',
34 '332': 'RPL_TOPIC',
35 '333': 'RPL_TOPICWHOTIME',
36 '353': 'RPL_NAMEREPLY',
37 '364': 'RPL_LINKS',
38 '365': 'RPL_ENDOFLINKS',
39 '366': 'RPL_ENDOFNAMES',
40 '367': 'RPL_BANLIST',
41 '368': 'RPL_ENDOFBANLIST',
5989f3f4 42 '369': 'RPL_ENDOFWHOWAS',
3ec786bc
JA
43 '372': 'RPL_MOTD',
44 '375': 'RPL_MOTDSTART',
45 '376': 'RPL_ENDOFMOTD',
46 '379': 'RPL_WHOISMODES',
47 '401': 'ERR_NOSUCHNICK',
48 '404': 'ERR_CANNOTSENDTOCHAN',
49 '405': 'ERR_TOOMANYCHANNELS',
5989f3f4 50 '406': 'ERR_WASNOSUCHNICK',
3ec786bc
JA
51 '421': 'ERR_UNKNOWNCOMMAND',
52 '422': 'ERR_NOMOTD',
66c980d3 53 '432': 'ERR_ERRONEUSNICKNAME',
3ec786bc
JA
54 '433': 'ERR_NICKNAMEINUSE',
55 '441': 'ERR_USERNOTINCHANNEL',
56 '442': 'ERR_NOTONCHANNEL',
57 '451': 'ERR_NOTREGISTERED',
58 '464': 'ERR_PASSWDMISMATCH',
59 '470': 'ERR_LINKCHANNEL',
60 '471': 'ERR_CHANNELISFULL',
61 '473': 'ERR_INVITEONLYCHAN',
62 '474': 'ERR_BANNEDFROMCHAN',
63 '475': 'ERR_BADCHANNELKEY',
64 '481': 'ERR_NOPRIVILEGES',
65 '482': 'ERR_CHANOPRIVSNEEDED',
66 '670': 'RPL_STARTTLS',
67 '900': 'RPL_SASLAUTHENTICATED',
68 '903': 'RPL_SASLLOGGEDIN',
69 '904': 'ERR_SASLNOTAUTHORISED',
70 '906': 'ERR_SASLABORTED',
71 '907': 'ERR_SASLALREADYAUTHED'
a8bf3ea4
JA
72};
73
74
3ec786bc 75IrcCommands = function (irc_connection, con_num, client) {
a8bf3ea4
JA
76 this.irc_connection = irc_connection;
77 this.con_num = con_num;
78 this.client = client;
79};
2a8e95d1 80module.exports = IrcCommands;
a8bf3ea4 81
3ec786bc
JA
82IrcCommands.prototype.dispatch = function (command, data) {
83 command += '';
84 if (irc_numerics[command]) {
85 command = irc_numerics[command];
86 }
87 if (handlers[command]) {
88 handlers[command].call(this, data);
89 } else {
90 unknownCommand(command, data);
91 }
92};
2a8e95d1 93
3ec786bc
JA
94IrcCommands.addHandler = function (command, handler) {
95 if (typeof handler !== 'function') {
96 return false;
97 }
98 handlers[command] = handler;
a8bf3ea4
JA
99};
100
3ec786bc
JA
101IrcCommands.addNumeric = function (numeric, handler_name) {
102 irc_numerics[numeric + ''] = handler_name +'';
c08717da
D
103};
104
3ec786bc
JA
105unknownCommand = function (command, data) {
106 // TODO: Do something here, log?
107};
2a8e95d1
D
108
109
3ec786bc 110handlers = {
a49e0dcf
JA
111 'RPL_WELCOME': function (command) {
112 var nick = command.params[0];
113 this.irc_connection.registered = true;
114 this.cap_negotation = false;
d9285da9 115 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' connect', {
1cc056b8
JA
116 nick: nick
117 });
a49e0dcf 118 },
3ec786bc 119
a49e0dcf
JA
120 'RPL_ISUPPORT': function (command) {
121 var options, i, option, matches, j;
122 options = command.params;
123 for (i = 1; i < options.length; i++) {
124 option = options[i].split("=", 2);
125 option[0] = option[0].toUpperCase();
126 this.irc_connection.options[option[0]] = (typeof option[1] !== 'undefined') ? option[1] : true;
127 if (_.include(['NETWORK', 'PREFIX', 'CHANTYPES', 'CHANMODES', 'NAMESX'], option[0])) {
128 if (option[0] === 'PREFIX') {
129 matches = /\(([^)]*)\)(.*)/.exec(option[1]);
130 if ((matches) && (matches.length === 3)) {
131 this.irc_connection.options.PREFIX = [];
132 for (j = 0; j < matches[2].length; j++) {
133 this.irc_connection.options.PREFIX.push({symbol: matches[2].charAt(j), mode: matches[1].charAt(j)});
a8bf3ea4
JA
134 }
135 }
a49e0dcf
JA
136 } else if (option[0] === 'CHANTYPES') {
137 this.irc_connection.options.CHANTYPES = this.irc_connection.options.CHANTYPES.split('');
138 } else if (option[0] === 'CHANMODES') {
139 this.irc_connection.options.CHANMODES = option[1].split(',');
140 } else if ((option[0] === 'NAMESX') && (!_.contains(this.irc_connection.cap.enabled, 'multi-prefix'))) {
141 this.irc_connection.write('PROTOCTL NAMESX');
a8bf3ea4 142 }
a49e0dcf
JA
143 }
144 }
d9285da9 145 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' options', {
1cc056b8
JA
146 options: this.irc_connection.options,
147 cap: this.irc_connection.cap.enabled
148 });
a49e0dcf 149 },
3ec786bc 150
a49e0dcf 151 'RPL_ENDOFWHOIS': function (command) {
d9285da9 152 this.irc_connection.emit('user ' + command.params[1] + ' endofwhois', {
692163ca
JA
153 nick: command.params[1],
154 msg: command.trailing
155 });
a49e0dcf 156 },
3ec786bc 157
f701d5ba
D
158 'RPL_AWAY': function (command) {
159 this.irc_connection.emit('user ' + command.params[1] + ' whoisaway', {
160 nick: command.params[1],
161 reason: command.trailing
162 });
163 },
3ec786bc 164
a49e0dcf 165 'RPL_WHOISUSER': function (command) {
d9285da9 166 this.irc_connection.emit('user ' + command.params[1] + ' whoisuser', {
692163ca
JA
167 nick: command.params[1],
168 ident: command.params[2],
169 host: command.params[3],
170 msg: command.trailing
171 });
a49e0dcf 172 },
3ec786bc 173
a49e0dcf 174 'RPL_WHOISSERVER': function (command) {
d9285da9 175 this.irc_connection.emit('user ' + command.params[1] + ' whoisserver', {
692163ca 176 nick: command.params[1],
5989f3f4
JA
177 irc_server: command.params[2],
178 server_info: command.trailing
692163ca 179 });
a49e0dcf 180 },
3ec786bc 181
a49e0dcf 182 'RPL_WHOISOPERATOR': function (command) {
d9285da9 183 this.irc_connection.emit('user ' + command.params[1] + ' whoisoperator', {
692163ca
JA
184 nick: command.params[1],
185 msg: command.trailing
186 });
a49e0dcf 187 },
3ec786bc 188
a49e0dcf 189 'RPL_WHOISCHANNELS': function (command) {
d9285da9 190 this.irc_connection.emit('user ' + command.params[1] + ' whoischannels', {
692163ca 191 nick: command.params[1],
cefa0900 192 chans: command.trailing
692163ca 193 });
a49e0dcf 194 },
3ec786bc 195
a49e0dcf 196 'RPL_WHOISMODES': function (command) {
d9285da9 197 this.irc_connection.emit('user ' + command.params[1] + ' whoismodes', {
692163ca
JA
198 nick: command.params[1],
199 msg: command.trailing
200 });
a49e0dcf 201 },
3ec786bc 202
a49e0dcf 203 'RPL_WHOISIDLE': function (command) {
d9285da9 204 this.irc_connection.emit('user ' + command.params[1] + ' whoisidle', {
692163ca
JA
205 nick: command.params[1],
206 idle: command.params[2],
207 logon: command.params[3] || undefined
208 });
a49e0dcf 209 },
3ec786bc 210
a49e0dcf 211 'RPL_WHOISREGNICK': function (command) {
d9285da9 212 this.irc_connection.emit('user ' + command.params[1] + ' whoisregnick', {
692163ca
JA
213 nick: command.params[1],
214 msg: command.trailing
215 });
a49e0dcf 216 },
3ec786bc 217
5989f3f4
JA
218 'RPL_WHOWASUSER': function (command) {
219 this.irc_connection.emit('user ' + command.params[1] + ' whowas', {
220 nick: command.params[1],
221 ident: command.params[2],
222 host: command.params[3],
223 real_name: command.trailing
224 });
225 },
226
227 'RPL_ENDOFWHOWAS': function (command) {
228 this.irc_connection.emit('user ' + command.params[1] + ' endofwhowas', {
229 nick: command.params[1]
230 });
231 },
232
233 'ERR_WASNOSUCHNICK': function (command) {
234 this.irc_connection.emit('user ' + command.params[1] + ' wasnosucknick', {
235 nick: command.params[1]
236 });
237 },
238
a49e0dcf 239 'RPL_LISTSTART': function (command) {
d9285da9 240 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' list_start', {});
a49e0dcf 241 },
3ec786bc 242
a49e0dcf 243 'RPL_LISTEND': function (command) {
d9285da9 244 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' list_end', {});
a49e0dcf 245 },
3ec786bc 246
a49e0dcf 247 'RPL_LIST': function (command) {
d9285da9 248 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' list_channel', {
fb1b18bc 249 channel: command.params[1],
25edd441 250 num_users: parseInt(command.params[2], 10),
fb1b18bc
JA
251 topic: command.trailing
252 });
a49e0dcf 253 },
3ec786bc 254
a49e0dcf 255 'RPL_MOTD': function (command) {
d9285da9 256 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' motd', {
fb1b18bc 257 motd: command.trailing + '\n'
1cc056b8 258 });
a49e0dcf 259 },
3ec786bc 260
a49e0dcf 261 'RPL_MOTDSTART': function (command) {
d9285da9 262 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' motd_start', {});
a49e0dcf 263 },
3ec786bc 264
a49e0dcf 265 'RPL_ENDOFMOTD': function (command) {
d9285da9 266 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' motd_end', {});
a49e0dcf 267 },
3ec786bc 268
a49e0dcf
JA
269 'RPL_NAMEREPLY': function (command) {
270 var members = command.trailing.split(' ');
271 var member_list = [];
272 var that = this;
273 var i = 0;
274 _.each(members, function (member) {
275 var j, k, modes = [];
30b4d43a
D
276
277 // Make sure we have some prefixes already
278 if (that.irc_connection.options.PREFIX) {
279 for (j = 0; j < member.length; j++) {
280 for (k = 0; k < that.irc_connection.options.PREFIX.length; k++) {
281 if (member.charAt(j) === that.irc_connection.options.PREFIX[k].symbol) {
282 modes.push(that.irc_connection.options.PREFIX[k].mode);
283 i++;
284 }
a8bf3ea4 285 }
a8bf3ea4 286 }
a49e0dcf 287 }
30b4d43a 288
a49e0dcf 289 member_list.push({nick: member, modes: modes});
a49e0dcf 290 });
e2752476 291
d9285da9 292 this.irc_connection.emit('channel ' + command.params[2] + ' userlist', {
e2752476
D
293 users: member_list,
294 channel: command.params[2]
295 });
a49e0dcf 296 },
e2752476 297
a49e0dcf 298 'RPL_ENDOFNAMES': function (command) {
d9285da9 299 this.irc_connection.emit('channel ' + command.params[1] + ' userlist_end', {
e2752476
D
300 channel: command.params[1]
301 });
a49e0dcf 302 },
e2752476 303
a49e0dcf 304 'RPL_BANLIST': function (command) {
d9285da9 305 this.irc_connection.emit('channel ' + command.params[1] + ' banlist', {
059f4918
JA
306 channel: command.params[1],
307 banned: command.params[2],
308 banned_by: command.params[3],
309 banned_at: command.params[4]
310 });
a49e0dcf 311 },
3ec786bc 312
a49e0dcf 313 'RPL_ENDOFBANLIST': function (command) {
d9285da9 314 this.irc_connection.emit('channel ' + command.params[1] + ' banlist_end', {
aa22caa9 315 channel: command.params[1]
059f4918 316 });
a49e0dcf 317 },
3ec786bc 318
a49e0dcf 319 'RPL_TOPIC': function (command) {
d9285da9 320 this.irc_connection.emit('channel ' + command.params[1] + ' topic', {
059f4918
JA
321 channel: command.params[1],
322 topic: command.trailing
323 });
a49e0dcf 324 },
3ec786bc 325
a49e0dcf 326 'RPL_NOTOPIC': function (command) {
d9285da9 327 this.irc_connection.emit('channel ' + command.params[1] + ' topic', {
059f4918
JA
328 channel: command.params[1],
329 topic: ''
330 });
a49e0dcf 331 },
3ec786bc 332
a49e0dcf 333 'RPL_TOPICWHOTIME': function (command) {
d9285da9 334 this.irc_connection.emit('channel ' + command.params[1] + ' topicsetby', {
059f4918
JA
335 nick: command.params[2],
336 channel: command.params[1],
337 when: command.params[3]
338 });
a49e0dcf 339 },
3ec786bc 340
a49e0dcf
JA
341 'PING': function (command) {
342 this.irc_connection.write('PONG ' + command.trailing);
343 },
e2752476 344
a49e0dcf
JA
345 'JOIN': function (command) {
346 var channel;
347 if (typeof command.trailing === 'string' && command.trailing !== '') {
348 channel = command.trailing;
349 } else if (typeof command.params[0] === 'string' && command.params[0] !== '') {
350 channel = command.params[0];
351 }
3ec786bc 352
d9285da9 353 this.irc_connection.emit('channel ' + channel + ' join', {
e2752476
D
354 nick: command.nick,
355 ident: command.ident,
356 hostname: command.hostname,
357 channel: channel
358 });
a49e0dcf 359 },
e2752476 360
a49e0dcf 361 'PART': function (command) {
d9285da9 362 this.irc_connection.emit('channel ' + command.params[0] + ' part', {
e2752476
D
363 nick: command.nick,
364 ident: command.ident,
365 hostname: command.hostname,
366 channel: command.params[0],
367 message: command.trailing
368 });
a49e0dcf 369 },
e2752476 370
a49e0dcf 371 'KICK': function (command) {
d9285da9 372 this.irc_connection.emit('channel ' + command.params[0] + ' kick', {
e2752476
D
373 kicked: command.params[1],
374 nick: command.nick,
375 ident: command.ident,
376 hostname: command.hostname,
377 channel: command.params[0],
378 message: command.trailing
379 });
a49e0dcf 380 },
e2752476 381
a49e0dcf 382 'QUIT': function (command) {
d9285da9 383 this.irc_connection.emit('user ' + command.nick + ' quit', {
e2752476
D
384 nick: command.nick,
385 ident: command.ident,
386 hostname: command.hostname,
387 message: command.trailing
388 });
a49e0dcf 389 },
e2752476 390
a49e0dcf 391 'NOTICE': function (command) {
e2752476
D
392 var namespace;
393
a49e0dcf
JA
394 if ((command.trailing.charAt(0) === String.fromCharCode(1)) && (command.trailing.charAt(command.trailing.length - 1) === String.fromCharCode(1))) {
395 // It's a CTCP response
e2752476 396 namespace = (command.params[0] == this.irc_connection.nick) ? 'user' : 'channel';
d9285da9 397 this.irc_connection.emit(namespace + ' ' + command.params[0] + ' ctcp_response', {
e2752476
D
398 nick: command.nick,
399 ident: command.ident,
400 hostname: command.hostname,
401 channel: command.params[0],
402 msg: command.trailing.substr(1, command.trailing.length - 2)
403 });
a49e0dcf 404 } else {
0190c2c9
D
405 namespace = (command.params[0] == this.irc_connection.nick || command.params[0] == '*') ?
406 'user' :
407 'channel';
408
d9285da9 409 this.irc_connection.emit(namespace + ' ' + command.params[0] + ' notice', {
0190c2c9
D
410 from_server: command.prefix ? true : false,
411 nick: command.nick || command.prefix || undefined,
e2752476
D
412 ident: command.ident,
413 hostname: command.hostname,
414 target: command.params[0],
415 msg: command.trailing
416 });
a49e0dcf
JA
417 }
418 },
3ec786bc 419
a49e0dcf 420 'NICK': function (command) {
d9285da9 421 this.irc_connection.emit('user ' + command.nick + ' nick', {
692163ca
JA
422 nick: command.nick,
423 ident: command.ident,
424 hostname: command.hostname,
425 newnick: command.trailing || command.params[0]
426 });
a49e0dcf 427 },
3ec786bc 428
a49e0dcf 429 'TOPIC': function (command) {
30b4d43a
D
430 // If we don't have an associated channel, no need to continue
431 if (!command.params[0]) return;
432
433 var channel = command.params[0],
434 topic = command.trailing || '';
435
d9285da9 436 this.irc_connection.emit('channel ' + channel + ' topic', {
f4ec5774
D
437 nick: command.nick,
438 channel: channel,
439 topic: topic
440 });
a49e0dcf 441 },
3ec786bc 442
d9285da9 443 'MODE': function (command) {
30b4d43a
D
444 var chanmodes = this.irc_connection.options.CHANMODES || [],
445 prefixes = this.irc_connection.options.PREFIX || [],
446 always_param = (chanmodes[0] || '').concat((chanmodes[1] || '')),
a49e0dcf 447 modes = [],
a7973dfb 448 has_param, i, j, add, event;
3ec786bc 449
a49e0dcf
JA
450 prefixes = _.reduce(prefixes, function (list, prefix) {
451 list.push(prefix.mode);
452 return list;
453 }, []);
454 always_param = always_param.split('').concat(prefixes);
3ec786bc 455
a49e0dcf
JA
456 has_param = function (mode, add) {
457 if (_.find(always_param, function (m) {
458 return m === mode;
459 })) {
460 return true;
30b4d43a 461 } else if (add && _.find((chanmodes[2] || '').split(''), function (m) {
a49e0dcf
JA
462 return m === mode;
463 })) {
464 return true;
465 } else {
466 return false;
467 }
468 };
3ec786bc 469
a49e0dcf
JA
470 if (!command.params[1]) {
471 command.params[1] = command.trailing;
472 }
3ec786bc 473
a49e0dcf
JA
474 j = 0;
475 for (i = 0; i < command.params[1].length; i++) {
476 switch (command.params[1][i]) {
477 case '+':
478 add = true;
479 break;
480 case '-':
481 add = false;
482 break;
483 default:
484 if (has_param(command.params[1][i], add)) {
485 modes.push({mode: (add ? '+' : '-') + command.params[1][i], param: command.params[2 + j]});
486 j++;
703d2778 487 } else {
a49e0dcf 488 modes.push({mode: (add ? '+' : '-') + command.params[1][i], param: null});
703d2778 489 }
a49e0dcf
JA
490 }
491 }
3ec786bc 492
d9285da9 493 event = (_.contains(this.irc_connection.options.CHANTYPES, command.params[0][0]) ? 'channel ' : 'user ') + command.params[0] + ' mode';
3ec786bc 494
a7973dfb 495 this.irc_connection.emit(event, {
a49e0dcf
JA
496 target: command.params[0],
497 nick: command.nick || command.prefix || '',
498 modes: modes
499 });
500 },
3ec786bc 501
a49e0dcf
JA
502 'PRIVMSG': function (command) {
503 var tmp, namespace;
504 if ((command.trailing.charAt(0) === String.fromCharCode(1)) && (command.trailing.charAt(command.trailing.length - 1) === String.fromCharCode(1))) {
505 //CTCP request
506 if (command.trailing.substr(1, 6) === 'ACTION') {
507 this.client.sendIrcCommand('action', {server: this.con_num, nick: command.nick, ident: command.ident, hostname: command.hostname, channel: command.params[0], msg: command.trailing.substr(7, command.trailing.length - 2)});
508 } else if (command.trailing.substr(1, 4) === 'KIWI') {
509 tmp = command.trailing.substr(6, command.trailing.length - 2);
510 namespace = tmp.split(' ', 1)[0];
511 this.client.sendIrcCommand('kiwi', {server: this.con_num, namespace: namespace, data: tmp.substr(namespace.length + 1)});
512 } else if (command.trailing.substr(1, 7) === 'VERSION') {
513 this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'VERSION KiwiIRC' + String.fromCharCode(1));
b39bb331
D
514 } else if (command.trailing.substr(1, 6) === 'SOURCE') {
515 this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'SOURCE http://www.kiwiirc.com/' + String.fromCharCode(1));
516 } else if (command.trailing.substr(1, 10) === 'CLIENTINFO') {
517 this.irc_connection.write('NOTICE ' + command.nick + ' :' + String.fromCharCode(1) + 'CLIENTINFO SOURCE VERSION TIME' + String.fromCharCode(1));
a49e0dcf 518 } else {
7d969e05 519 namespace = (command.params[0].toLowerCase() == this.irc_connection.nick.toLowerCase()) ? 'user' : 'channel';
d9285da9 520 this.irc_connection.emit(namespace + ' ' + command.nick + ' ctcp_request', {
b39bb331
D
521 nick: command.nick,
522 ident: command.ident,
523 hostname: command.hostname,
524 target: command.params[0],
525 type: (command.trailing.substr(1, command.trailing.length - 2).split(' ') || [null])[0],
526 msg: command.trailing.substr(1, command.trailing.length - 2)
527 });
a49e0dcf
JA
528 }
529 } else {
e2752476 530 // A message to a user (private message) or to a channel?
d9285da9
JA
531 namespace = (command.params[0] === this.irc_connection.nick) ? 'user ' + command.nick : 'channel ' + command.params[0];
532 this.irc_connection.emit(namespace + ' privmsg', {
ffbbc70f
D
533 nick: command.nick,
534 ident: command.ident,
535 hostname: command.hostname,
536 channel: command.params[0],
537 msg: command.trailing
b09157de 538 });
a49e0dcf
JA
539 }
540 },
3ec786bc 541
a49e0dcf
JA
542 'CAP': function (command) {
543 // TODO: capability modifiers
544 // i.e. - for disable, ~ for requires ACK, = for sticky
545 var capabilities = command.trailing.replace(/[\-~=]/, '').split(' ');
546 var request;
259d9b96
JA
547
548 // Which capabilities we want to enable
a49e0dcf 549 var want = ['multi-prefix', 'away-notify'];
259d9b96 550
a49e0dcf
JA
551 if (this.irc_connection.password) {
552 want.push('sasl');
553 }
259d9b96 554
a49e0dcf
JA
555 switch (command.params[1]) {
556 case 'LS':
259d9b96 557 // Compute which of the available capabilities we want and request them
a49e0dcf
JA
558 request = _.intersection(capabilities, want);
559 if (request.length > 0) {
560 this.irc_connection.cap.requested = request;
561 this.irc_connection.write('CAP REQ :' + request.join(' '));
562 } else {
563 this.irc_connection.write('CAP END');
564 this.irc_connection.cap_negotation = false;
a8bf3ea4 565 }
a49e0dcf
JA
566 break;
567 case 'ACK':
568 if (capabilities.length > 0) {
259d9b96 569 // Update list of enabled capabilities
a49e0dcf 570 this.irc_connection.cap.enabled = capabilities;
259d9b96 571 // Update list of capabilities we would like to have but that aren't enabled
a49e0dcf 572 this.irc_connection.cap.requested = _.difference(this.irc_connection.cap.requested, capabilities);
703d2778 573 }
259d9b96 574 if (this.irc_connection.cap.enabled.length > 0) {
a49e0dcf
JA
575 if (_.contains(this.irc_connection.cap.enabled, 'sasl')) {
576 this.irc_connection.sasl = true;
577 this.irc_connection.write('AUTHENTICATE PLAIN');
a8bf3ea4 578 } else {
a49e0dcf
JA
579 this.irc_connection.write('CAP END');
580 this.irc_connection.cap_negotation = false;
a8bf3ea4 581 }
a8bf3ea4 582 }
a49e0dcf
JA
583 break;
584 case 'NAK':
585 if (capabilities.length > 0) {
586 this.irc_connection.cap.requested = _.difference(this.irc_connection.cap.requested, capabilities);
7dfe47c6 587 }
a49e0dcf 588 if (this.irc_connection.cap.requested.length > 0) {
7dfe47c6
JA
589 this.irc_connection.write('CAP END');
590 this.irc_connection.cap_negotation = false;
7dfe47c6 591 }
a49e0dcf
JA
592 break;
593 case 'LIST':
594 // should we do anything here?
595 break;
596 }
597 },
3ec786bc 598
a49e0dcf
JA
599 'AUTHENTICATE': function (command) {
600 var b = new Buffer(this.irc_connection.nick + "\0" + this.irc_connection.nick + "\0" + this.irc_connection.password, 'utf8');
601 var b64 = b.toString('base64');
602 if (command.params[0] === '+') {
603 while (b64.length >= 400) {
604 this.irc_connection.write('AUTHENTICATE ' + b64.slice(0, 399));
605 b64 = b64.slice(399);
a8bf3ea4 606 }
a49e0dcf
JA
607 if (b64.length > 0) {
608 this.irc_connection.write('AUTHENTICATE ' + b64);
609 } else {
610 this.irc_connection.write('AUTHENTICATE +');
611 }
612 } else {
613 this.irc_connection.write('CAP END');
614 this.irc_connection.cap_negotation = false;
615 }
616 },
3ec786bc 617
a49e0dcf 618 'AWAY': function (command) {
d9285da9 619 this.irc_connection.emit('user ' + command.nick + ' away', {
692163ca
JA
620 nick: command.nick,
621 msg: command.trailing
622 });
a49e0dcf 623 },
3ec786bc 624
a49e0dcf
JA
625 'RPL_SASLAUTHENTICATED': function (command) {
626 this.irc_connection.write('CAP END');
627 this.irc_connection.cap_negotation = false;
628 this.irc_connection.sasl = true;
629 },
3ec786bc 630
a49e0dcf
JA
631 'RPL_SASLLOGGEDIN': function (command) {
632 if (this.irc_connection.cap_negotation === false) {
633 this.irc_connection.write('CAP END');
634 }
635 },
3ec786bc 636
a49e0dcf 637 'ERR_SASLNOTAUTHORISED': function (command) {
3ec786bc
JA
638 this.irc_connection.write('CAP END');
639 this.irc_connection.cap_negotation = false;
640 },
641
a49e0dcf
JA
642 'ERR_SASLABORTED': function (command) {
643 this.irc_connection.write('CAP END');
644 this.irc_connection.cap_negotation = false;
645 },
3ec786bc 646
a49e0dcf
JA
647 'ERR_SASLALREADYAUTHED': function (command) {
648 // noop
649 },
3ec786bc 650
a49e0dcf 651 'ERROR': function (command) {
d9285da9 652 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' error', {
1cc056b8
JA
653 reason: command.trailing
654 });
a49e0dcf 655 },
ebe178d6 656 ERR_PASSWDMISMATCH: function (command) {
d9285da9 657 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' password_mismatch', {});
ebe178d6 658 },
3ec786bc 659
a49e0dcf 660 ERR_LINKCHANNEL: function (command) {
d9285da9 661 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' channel_redirect', {
1cc056b8
JA
662 from: command.params[1],
663 to: command.params[2]
664 });
a49e0dcf 665 },
3ec786bc 666
a49e0dcf 667 ERR_NOSUCHNICK: function (command) {
d9285da9 668 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' no_such_nick', {
1cc056b8
JA
669 nick: command.params[1],
670 reason: command.trailing
671 });
a49e0dcf 672 },
3ec786bc 673
a49e0dcf 674 ERR_CANNOTSENDTOCHAN: function (command) {
d9285da9 675 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' cannot_send_to_chan', {
1cc056b8
JA
676 channel: command.params[1],
677 reason: command.trailing
678 });
a49e0dcf 679 },
3ec786bc 680
a49e0dcf 681 ERR_TOOMANYCHANNELS: function (command) {
d9285da9 682 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' too_many_channels', {
1cc056b8
JA
683 channel: command.params[1],
684 reason: command.trailing
685 });
a49e0dcf 686 },
3ec786bc 687
a49e0dcf 688 ERR_USERNOTINCHANNEL: function (command) {
d9285da9 689 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' user_not_in_channel', {
1cc056b8
JA
690 nick: command.params[0],
691 channel: command.params[1],
692 reason: command.trailing
693 });
a49e0dcf 694 },
3ec786bc 695
a49e0dcf 696 ERR_NOTONCHANNEL: function (command) {
d9285da9 697 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' not_on_channel', {
1cc056b8
JA
698 channel: command.params[1],
699 reason: command.trailing
700 });
a49e0dcf 701 },
3ec786bc 702
a49e0dcf 703 ERR_CHANNELISFULL: function (command) {
3ec786bc
JA
704 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' channel_is_full', {
705 channel: command.params[1],
706 reason: command.trailing
707 });
708 },
709
a49e0dcf 710 ERR_INVITEONLYCHAN: function (command) {
d9285da9 711 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' invite_only_channel', {
1cc056b8
JA
712 channel: command.params[1],
713 reason: command.trailing
714 });
a49e0dcf 715 },
3ec786bc 716
a49e0dcf 717 ERR_BANNEDFROMCHAN: function (command) {
d9285da9 718 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' banned_from_channel', {
1cc056b8
JA
719 channel: command.params[1],
720 reason: command.trailing
721 });
a49e0dcf 722 },
3ec786bc 723
a49e0dcf 724 ERR_BADCHANNELKEY: function (command) {
d9285da9 725 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' bad_channel_key', {
1cc056b8
JA
726 channel: command.params[1],
727 reason: command.trailing
728 });
a49e0dcf 729 },
3ec786bc 730
a49e0dcf 731 ERR_CHANOPRIVSNEEDED: function (command) {
d9285da9 732 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' chanop_privs_needed', {
1cc056b8
JA
733 channel: command.params[1],
734 reason: command.trailing
735 });
a49e0dcf 736 },
3ec786bc 737
a49e0dcf 738 ERR_NICKNAMEINUSE: function (command) {
d9285da9 739 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' nickname_in_use', {
1cc056b8
JA
740 nick: command.params[1],
741 reason: command.trailing
742 });
a49e0dcf 743 },
3ec786bc 744
66c980d3
JA
745 ERR_ERRONEUSNICKNAME: function(command) {
746 this.irc_connection.emit('server ' + this.irc_connection.irc_host.hostname + ' erroneus_nickname', {
747 nick: command.params[1],
748 reason: command.trailing
749 });
750 },
751
a49e0dcf 752 ERR_NOTREGISTERED: function (command) {
cea1f25b
D
753 },
754
755 RPL_MAPMORE: function (command) {
756 var params = _.clone(command.params);
757 params.shift();
758 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
759 },
3ec786bc 760
cea1f25b
D
761 RPL_MAPEND: function (command) {
762 var params = _.clone(command.params);
763 params.shift();
764 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
765 },
766
767 RPL_LINKS: function (command) {
768 var params = _.clone(command.params);
769 params.shift();
770 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
771 },
3ec786bc 772
cea1f25b
D
773 RPL_ENDOFLINKS: function (command) {
774 var params = _.clone(command.params);
775 params.shift();
776 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
777 },
778
779 ERR_UNKNOWNCOMMAND: function (command) {
780 var params = _.clone(command.params);
781 params.shift();
782 genericNotice.call(this, command, '`' + params.join(', ') + '` ' + command.trailing);
783 },
784
785 ERR_NOMOTD: function (command) {
786 var params = _.clone(command.params);
787 params.shift();
788 genericNotice.call(this, command, command.trailing);
789 },
790
791 ERR_NOPRIVILEGES: function (command) {
792 var params = _.clone(command.params);
793 params.shift();
794 genericNotice.call(this, command, command.trailing);
28ce8b75
D
795 },
796
797 RPL_STATSCONN: function (command) {
798 var params = _.clone(command.params);
799 params.shift();
800 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
801 },
802
803 RPL_LUSERCLIENT: function (command) {
804 var params = _.clone(command.params);
805 params.shift();
806 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
807 },
808
809 RPL_LUSEROP: function (command) {
810 var params = _.clone(command.params);
811 params.shift();
812 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
813 },
814
815 RPL_LUSERUNKNOWN: function (command) {
816 var params = _.clone(command.params);
817 params.shift();
818 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
819 },
820
821 RPL_LUSERCHANNELS: function (command) {
822 var params = _.clone(command.params);
823 params.shift();
824 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
825 },
826
827 RPL_LUSERME: function (command) {
828 var params = _.clone(command.params);
829 params.shift();
830 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
831 },
832
833 RPL_LOCALUSERS: function (command) {
834 var params = _.clone(command.params);
835 params.shift();
836 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
837 },
3ec786bc 838
28ce8b75
D
839 RPL_GLOBALUSERS: function (command) {
840 var params = _.clone(command.params);
841 params.shift();
842 genericNotice.call(this, command, params.join(', ') + ' ' + command.trailing);
a49e0dcf 843 }
a8bf3ea4 844};
cea1f25b
D
845
846
847
848
849function genericNotice (command, msg, is_error) {
850 // Default to being an error
851 if (typeof is_error !== 'boolean')
852 is_error = true;
853
854 this.client.sendIrcCommand('notice', {
855 server: this.con_num,
0190c2c9 856 from_server: true,
cea1f25b
D
857 nick: command.prefix,
858 ident: '',
859 hostname: '',
860 target: command.params[0],
861 msg: msg,
862 numeric: parseInt(command.command, 10)
863 });
864}