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