Additional errors
[KiwiIRC.git] / node / kiwi.js
CommitLineData
f4f7781b
JA
1/*jslint regexp: true, confusion: true, undef: false, node: true, sloppy: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */
2
68ad40c6 3var tls = require('tls'),
c6b5e668 4 net = require('net'),
68ad40c6 5 http = require('http'),
09365139 6 https = require('https'),
1eaf214f 7 fs = require('fs'),
7d1c3ee9 8 url = require('url'),
c6b5e668 9 ws = require('socket.io'),
4136eb38
JA
10 _ = require('./lib/underscore.min.js'),
11 starttls = require('./lib/starttls.js');
c6b5e668 12
4a30a583 13
14/*
15 * Find a config file in the following order:
16 * - /etc/kiwi/config.json
17 * - ./config.json
18 */
19var config = null, config_filename = 'config.json';
20var config_dirs = ['/etc/kiwiirc/', __dirname + '/'];
21for(var i in config_dirs){
22 try {
23 if(fs.lstatSync(config_dirs[i] + config_filename).isDirectory() === false){
24 config = JSON.parse(fs.readFileSync(config_dirs[i] + config_filename, 'ascii'));
25 console.log('Using config file ' + config_dirs[i] + config_filename);
26 break;
27 }
28 } catch(e){
29 continue;
30 }
31}
32
33if(config === null){
34 console.log('Couldn\'t find a config file!');
35 process.exit(0);
36}
37
38
39
40
41/*
42 * Some process changes
43 */
44process.title = 'kiwiirc';
45function changeUser(){
46 if(typeof config.group !== 'undefined' && config.group !== ''){
47 try {
48 process.setgid(config.group);
49 }
50 catch (err) {
51 console.log('Failed to set gid: ' + err);
52 process.exit();
53 }
54 }
55
56 if(typeof config.user !== 'undefined' && config.user !== ''){
57 try {
58 process.setuid(config.user);
59 }
60 catch (err) {
61 console.log('Failed to set uid: ' + err);
62 process.exit();
63 }
64 }
65}
66
67
68/*
69 * And now KiwiIRC, the server :)
70 */
1eaf214f 71
c6b5e668 72var ircNumerics = {
81122538
JA
73 RPL_WELCOME: '001',
74 RPL_ISUPPORT: '005',
75 RPL_WHOISUSER: '311',
76 RPL_WHOISSERVER: '312',
77 RPL_WHOISOPERATOR: '313',
78 RPL_WHOISIDLE: '317',
79 RPL_ENDOFWHOIS: '318',
80 RPL_WHOISCHANNELS: '319',
818ef327 81 RPL_NOTOPIC: '331',
81122538
JA
82 RPL_TOPIC: '332',
83 RPL_NAMEREPLY: '353',
84 RPL_ENDOFNAMES: '366',
85 RPL_MOTD: '372',
86 RPL_WHOISMODES: '379',
87 ERR_NOSUCHNICK: '401',
88 ERR_CANNOTSENDTOCHAN: '404',
89 ERR_TOOMANYCHANNELS: '405',
90 ERR_USERNOTINCHANNEL: '441',
91 ERR_NOTONCHANNEL: '442',
92 ERR_LINKCHANNEL: '470',
93 ERR_CHANNELISFULL: '471',
94 ERR_INVITEONLYCHAN: '473',
95 ERR_BANNEDFROMCHAN: '474',
96 ERR_BADCHANNELKEY: '475',
818ef327
JA
97 ERR_LINKCHANNEL: '470',
98 ERR_CHANOPRIVSNEEDED: '482',
81122538 99 RPL_STARTTLS: '670'
c6b5e668
JA
100};
101
102
103var parseIRCMessage = function (websocket, ircSocket, data) {
4136eb38 104 /*global ircSocketDataHandler */
0c5e2aaa 105 var msg, regex, opts, options, opt, i, j, matches, nick, users, chan, params, prefix, prefixes, nicklist, caps, rtn;
c6b5e668
JA
106 regex = /^(?::(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@([a-z0-9\.\-:]+)) )?([a-z0-9]+)(?:(?: ([^:]+))?(?: :(.+))?)$/i;
107 msg = regex.exec(data);
108 if (msg) {
109 msg = {
110 prefix: msg[1],
111 nick: msg[2],
112 ident: msg[3],
113 hostname: msg[4],
114 command: msg[5],
115 params: msg[6] || '',
116 trailing: (msg[7]) ? msg[7].trim() : ''
117 };
118 switch (msg.command.toUpperCase()) {
119 case 'PING':
120 ircSocket.write('PONG ' + msg.trailing + '\r\n');
121 break;
bad1ea63 122 case ircNumerics.RPL_WELCOME:
4136eb38
JA
123 if (ircSocket.IRC.CAP.negotiating) {
124 ircSocket.IRC.CAP.negotiating = false;
125 ircSocket.IRC.CAP.enabled = [];
126 ircSocket.IRC.CAP.requested = [];
127 }
c6b5e668
JA
128 websocket.emit('message', {event: 'connect', connected: true, host: null});
129 break;
bad1ea63 130 case ircNumerics.RPL_ISUPPORT:
c6b5e668
JA
131 opts = msg.params.split(" ");
132 options = [];
133 for (i = 0; i < opts.length; i++) {
134 opt = opts[i].split("=", 2);
135 opt[0] = opt[0].toUpperCase();
bad1ea63 136 ircSocket.IRC.options[opt[0]] = opt[1] || true;
c6b5e668
JA
137 if (_.include(['NETWORK', 'PREFIX', 'CHANTYPES'], opt[0])) {
138 if (opt[0] === 'PREFIX') {
139 regex = /\(([^)]*)\)(.*)/;
140 matches = regex.exec(opt[1]);
141 if ((matches) && (matches.length === 3)) {
020ec63b 142 ircSocket.IRC.options[opt[0]] = [];
c6b5e668 143 for (j = 0; j < matches[2].length; j++) {
020ec63b 144 //ircSocket.IRC.options[opt[0]][matches[2].charAt(j)] = matches[1].charAt(j);
145 ircSocket.IRC.options[opt[0]].push({symbol: matches[2].charAt(j), mode: matches[1].charAt(j)});
146 //console.log({symbol: matches[2].charAt(j), mode: matches[1].charAt(j)});
c6b5e668 147 }
020ec63b 148 console.log(ircSocket.IRC.options);
c6b5e668
JA
149 }
150 }
151 }
152 }
0417bcfa 153 websocket.emit('message', {event: 'options', server: '', "options": ircSocket.IRC.options});
c6b5e668
JA
154 break;
155 case ircNumerics.RPL_WHOISUSER:
156 case ircNumerics.RPL_WHOISSERVER:
157 case ircNumerics.RPL_WHOISOPERATOR:
c6b5e668
JA
158 case ircNumerics.RPL_ENDOFWHOIS:
159 case ircNumerics.RPL_WHOISCHANNELS:
160 case ircNumerics.RPL_WHOISMODES:
161 websocket.emit('message', {event: 'whois', server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing});
162 break;
eedf3f38 163 case ircNumerics.RPL_WHOISIDLE:
0c5e2aaa
JA
164 params = msg.params.split(" ", 4);
165 rtn = {event: 'whois', server: '', nick: params[1], idle: params[2]};
166 if (params[3]) {
167 rtn.logon = params[3];
168 }
169 websocket.emit('message', rtn);
170 break;
c6b5e668
JA
171 case ircNumerics.RPL_MOTD:
172 websocket.emit('message', {event: 'motd', server: '', "msg": msg.trailing});
173 break;
174 case ircNumerics.RPL_NAMEREPLY:
175 params = msg.params.split(" ");
176 nick = params[0];
177 chan = params[2];
178 users = msg.trailing.split(" ");
bad1ea63
JA
179 prefixes = _.values(ircSocket.IRC.options.PREFIX);
180 nicklist = {};
181 i = 0;
c6b5e668
JA
182 _.each(users, function (user) {
183 if (_.include(prefix, user.charAt(0))) {
184 prefix = user.charAt(0);
185 user = user.substring(1);
186 nicklist[user] = prefix;
bad1ea63
JA
187 } else {
188 nicklist[user] = '';
c6b5e668 189 }
bad1ea63 190 if (i++ >= 50) {
c6b5e668
JA
191 websocket.emit('message', {event: 'userlist', server: '', "users": nicklist, channel: chan});
192 nicklist = {};
193 i = 0;
194 }
c6b5e668 195 });
bad1ea63 196 if (i > 0) {
c6b5e668 197 websocket.emit('message', {event: 'userlist', server: '', "users": nicklist, channel: chan});
bad1ea63
JA
198 } else {
199 console.log("oops");
c6b5e668
JA
200 }
201 break;
bad1ea63
JA
202 case ircNumerics.RPL_ENDOFNAMES:
203 websocket.emit('message', {event: 'userlist_end', server: '', channel: msg.params.split(" ")[1]});
204 break;
205 case ircNumerics.ERR_LINKCHANNEL:
206 params = msg.params.split(" ");
207 websocket.emit('message', {event: 'channel_redirect', from: params[1], to: params[2]});
208 break;
209 case ircNumerics.ERR_NOSUCHNICK:
818ef327 210 websocket.emit('message', {event: 'irc_error', error: 'no_suck_nick', nick: msg.params.split(" ")[1], reason: msg.trailing});
bad1ea63
JA
211 break;
212 case 'JOIN':
213 websocket.emit('message', {event: 'join', nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.trailing});
214 if (msg.nick === ircSocket.IRC.nick) {
215 ircSocket.write('NAMES ' + msg.trailing + '\r\n');
216 }
217 break;
218 case 'PART':
219 websocket.emit('message', {event: 'part', nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), message: msg.trailing});
220 break;
221 case 'KICK':
222 params = msg.params.split(" ");
223 websocket.emit('message', {event: 'kick', kicked: params[1], nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: params[0].trim(), message: msg.trailing});
224 break;
225 case 'QUIT':
226 websocket.emit('message', {event: 'quit', nick: msg.nick, ident: msg.ident, hostname: msg.hostname, message: msg.trailing});
227 break;
228 case 'NOTICE':
423a590e
JA
229 if ((msg.trailing.charAt(0) === '\001') && (msg.trailing.charAt(msg.trailing.length - 1) === '\001')) {
230 // It's a CTCP response
231 websocket.emit('message', {event: 'ctcp_response', nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(1, msg.trailing.length - 2)});
232 } else {
233 websocket.emit('message', {event: 'notice', nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing});
234 }
bad1ea63
JA
235 break;
236 case 'NICK':
237 websocket.emit('message', {event: 'nick', nick: msg.nick, ident: msg.ident, hostname: msg.hostname, newnick: msg.trailing});
238 break;
239 case 'TOPIC':
240 websocket.emit('message', {event: 'topic', nick: msg.nick, channel: msg.params, topic: msg.trailing});
241 break;
242 case ircNumerics.RPL_TOPIC:
243 websocket.emit('message', {event: 'topic', nick: '', channel: msg.params.split(" ")[1], topic: msg.trailing});
244 break;
818ef327
JA
245 case ircNumerics.RPL_NOTOPIC:
246 websocket.emit('message', {event: 'topic', nick: '', channel: msg.params.split(" ")[1], topic:''});
247 break;
bad1ea63
JA
248 case 'MODE':
249 opts = msg.params.split(" ");
250 params = {event: 'mode', nick: msg.nick};
251 switch (opts.length) {
252 case 1:
253 params.effected_nick = opts[0];
254 params.mode = msg.trailing;
255 break;
256 case 2:
257 params.channel = opts[0];
258 params.mode = opts[1];
259 break;
260 default:
261 params.channel = opts[0];
262 params.mode = opts[1];
263 params.effected_nick = opts[2];
264 break;
265 }
266 websocket.emit('message', params);
267 break;
268 case 'PRIVMSG':
423a590e
JA
269 if ((msg.trailing.charAt(0) === '\001') && (msg.trailing.charAt(msg.trailing.length - 1) === '\001')) {
270 // It's a CTCP request
271 if (msg.trailing.substr(1, 6) === 'ACTION') {
020ec63b 272 websocket.emit('message', {event: 'action', nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(7, msg.trailing.length - 2)});
423a590e 273 } else if (msg.trailing.substr(1, 7) === 'VERSION') {
d8568ae2 274 ircSocket.write('NOTICE ' + msg.nick + ' :\001VERSION KiwiIRC\001\r\n');
423a590e
JA
275 } else {
276 websocket.emit('message', {event: 'ctcp_request', nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(1, msg.trailing.length - 2)});
277 }
278 } else {
279 websocket.emit('message', {event: 'msg', nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing});
280 }
c6b5e668 281 break;
4136eb38 282 case 'CAP':
1eaf214f 283 caps = config.cap_options;
4136eb38
JA
284 options = msg.trailing.split(" ");
285 switch (_.first(msg.params.split(" "))) {
286 case 'LS':
287 opts = '';
288 _.each(_.intersect(caps, options), function (cap) {
289 if (opts !== '') {
290 opts += " ";
291 }
292 opts += cap;
293 ircSocket.IRC.CAP.requested.push(cap);
294 });
295 if (opts.length > 0) {
296 ircSocket.write('CAP REQ :' + opts + '\r\n');
297 } else {
298 ircSocket.write('CAP END\r\n');
299 }
300 // TLS is special
301 /*if (_.include(options, 'tls')) {
302 ircSocket.write('STARTTLS\r\n');
303 ircSocket.IRC.CAP.requested.push('tls');
304 }*/
305 break;
306 case 'ACK':
307 _.each(options, function (cap) {
308 ircSocket.IRC.CAP.enabled.push(cap);
309 });
310 if (_.last(msg.params.split(" ")) !== '*') {
311 ircSocket.IRC.CAP.requested = [];
312 ircSocket.IRC.CAP.negotiating = false;
313 ircSocket.write('CAP END\r\n');
314 }
315 break;
316 case 'NAK':
317 ircSocket.IRC.CAP.requested = [];
318 ircSocket.IRC.CAP.negotiating = false;
319 ircSocket.write('CAP END\r\n');
320 break;
321 }
322 break;
323 /*case ircNumerics.RPL_STARTTLS:
324 try {
325 IRC = ircSocket.IRC;
326 listeners = ircSocket.listeners('data');
327 ircSocket.removeAllListeners('data');
328 ssl_socket = starttls(ircSocket, {}, function () {
329 ssl_socket.on("data", function (data) {
330 ircSocketDataHandler(data, websocket, ssl_socket);
331 });
332 ircSocket = ssl_socket;
333 ircSocket.IRC = IRC;
334 _.each(listeners, function (listener) {
335 ircSocket.addListener('data', listener);
336 });
337 });
338 //console.log(ircSocket);
339 } catch (e) {
340 console.log(e);
341 }
342 break;*/
81122538
JA
343 case ircNumerics.ERR_CANNOTSENDTOCHAN:
344 websocket.emit('message', {event: 'irc_error', error: 'cannot_send_to_chan', channel: msg.params.split(" ")[1], reason: msg.trailing});
345 break;
346 case ircNumerics.ERR_TOOMANYCHANNELS:
347 websocket.emit('message', {event: 'irc_error', error: 'too_many_channels', channel: msg.params.split(" ")[1], reason: msg.trailing});
348 break;
349 case ircNumerics.ERR_USERNOTINCHANNEL:
350 params = msg.params.split(" ");
351 websocket.emit('message', {event: 'irc_error', error: 'user_not_in_channel', nick: params[0], channel: params[1], reason: msg.trainling});
352 break;
353 case ircNumerics.ERR_NOTONCHANNEL:
354 websocket.emit('message', {event: 'irc_error', error: 'not_on_channel', channel: msg.params.split(" ")[1], reason: msg.trailing});
355 break;
356 case ircNumerics.ERR_CHANNELISFULL:
357 websocket.emit('message', {event: 'irc_error', error: 'channel_is_full', channel: msg.params.split(" ")[1], reason: msg.trailing});
358 break;
359 case ircNumerics.ERR_INVITEONLYCHAN:
360 websocket.emit('message', {event: 'irc_error', error: 'invite_only_channel', channel: msg.params.split(" ")[1], reason: msg.trailing});
361 break;
362 case ircNumerics.ERR_BANNEDFROMCHAN:
363 websocket.emit('message', {event: 'irc_error', error: 'banned_from_channel', channel: msg.params.split(" ")[1], reason: msg.trailing});
364 break;
365 case ircNumerics.ERR_BADCHANNELKEY:
366 websocket.emit('message', {event: 'irc_error', error: 'bad_channel_key', channel: msg.params.split(" ")[1], reason: msg.trailing});
367 break;
818ef327
JA
368 case ircNumerics.ERR_CHANOPRIVSNEEDED:
369 websocket.emit('message', {event: 'irc_error', error: 'chanop_privs_needed', channel: msg.params.split(" ")[1], reason: msg.trailing});
370 break;
c6b5e668
JA
371 }
372 } else {
373 console.log("Unknown command.\r\n");
374 }
375};
68ad40c6 376
4136eb38
JA
377var ircSocketDataHandler = function (data, websocket, ircSocket) {
378 var i;
1eaf214f
JA
379 if ((ircSocket.holdLast) && (ircSocket.held !== '')) {
380 data = ircSocket.held + data;
381 ircSocket.holdLast = false;
382 ircSocket.held = '';
383 }
0417bcfa 384 if (data.substr(-2) === '\r\n') {
1eaf214f
JA
385 ircSocket.holdLast = true;
386 }
387 data = data.split("\r\n");
4136eb38
JA
388 for (i = 0; i < data.length; i++) {
389 if (data[i]) {
ca5ea123 390 if ((ircSocket.holdLast) && (i === data.length - 1)) {
1eaf214f
JA
391 ircSocket.held = data[i];
392 break;
393 }
4136eb38
JA
394 console.log("->" + data[i]);
395 parseIRCMessage(websocket, ircSocket, data[i]);
396 }
397 }
398};
399
ceec0f93 400if (config.handle_http) {
0c5e2aaa
JA
401 var fileServer = new (require('node-static').Server)(__dirname + config.public_http);
402 var jade = require('jade');
ceec0f93 403}
7d1c3ee9
JA
404
405var httpHandler = function (request, response) {
406 var uri, subs, useragent, agent, server_set, server, nick, debug, touchscreen;
407 if (config.handle_http) {
408 uri = url.parse(request.url);
409 subs = uri.pathname.substr(0, 4);
410 if ((subs === '/js/') || (subs === '/css') || (subs === '/img')) {
411 request.addListener('end', function () {
412 fileServer.serve(request, response);
413 });
414 } else if (uri.pathname === '/') {
415 useragent = (response.headers) ? response.headers['user-agent']: '';
416 if (useragent.indexOf('android') !== -1) {
417 agent = 'android';
418 touchscreen = true;
419 } else if (useragent.indexOf('iphone') !== -1) {
420 agent = 'iphone';
421 touchscreen = true;
422 } else if (useragent.indexOf('ipad') !== -1) {
423 agent = 'ipad';
424 touchscreen = true;
425 } else if (useragent.indexOf('ipod') !== -1) {
426 agent = 'ipod';
427 touchscreen = true;
428 } else {
429 agent = 'normal';
430 touchscreen = false;
431 }
432 if (uri.query) {
433 server_set = (uri.query.server !== '');
434 server = uri.query.server || 'irc.anonnet.org';
435 nick = uri.query.nick || '';
436 debug = (uri.query.debug !== '');
437 } else {
438 server = 'irc.anonnet.org';
439 nick = '';
440 }
441 response.setHeader('Connection', 'close');
442 response.setHeader('X-Generated-By', 'KiwiIRC');
443 jade.renderFile(__dirname + '/client/index.html.jade', { locals: { "touchscreen": touchscreen, "debug": debug, "server_set": server_set, "server": server, "nick": nick, "agent": agent, "config": config }}, function (err, html) {
444 if (!err) {
445 response.write(html);
446 } else {
447 response.statusCode = 500;
448 }
449 response.end();
450 });
451 } else if (uri.pathname.substr(0, 10) === '/socket.io') {
452 // Do nothing!
453 } else {
454 response.statusCode = 404;
455 response.end();
456 }
457 }
458};
459
68ad40c6 460//setup websocket listener
1eaf214f 461if (config.listen_ssl) {
7d1c3ee9 462 var httpServer = https.createServer({key: fs.readFileSync(__dirname + '/' + config.ssl_key), cert: fs.readFileSync(__dirname + '/' + config.ssl_cert)}, httpHandler);
09365139
JA
463 var io = ws.listen(httpServer, {secure: true});
464 httpServer.listen(config.port, config.bind_address);
1eaf214f 465} else {
7d1c3ee9 466 var httpServer = http.createServer(httpHandler);
09365139
JA
467 var io = ws.listen(httpServer, {secure: false});
468 httpServer.listen(config.port, config.bind_address);
1eaf214f 469}
4a30a583 470
471// Now we're listening on the network, set our UID/GIDs if required
472changeUser();
473
7d1c3ee9 474io.of('/kiwi').on('connection', function (websocket) {
c6b5e668 475 websocket.on('irc connect', function (nick, host, port, ssl, callback) {
4136eb38 476 var ircSocket;
68ad40c6 477 //setup IRC connection
c6b5e668
JA
478 if (!ssl) {
479 ircSocket = net.createConnection(port, host);
480 } else {
481 ircSocket = tls.connect(port, host);
68ad40c6
JA
482 }
483 ircSocket.setEncoding('ascii');
4136eb38 484 ircSocket.IRC = {options: {}, CAP: {negotiating: true, requested: [], enabled: []}};
bad1ea63 485 websocket.ircSocket = ircSocket;
0417bcfa
JA
486 ircSocket.holdLast = false;
487 ircSocket.held = '';
68ad40c6 488
c6b5e668 489 ircSocket.on('data', function (data) {
4136eb38 490 ircSocketDataHandler(data, websocket, ircSocket);
68ad40c6
JA
491 });
492
bad1ea63 493 ircSocket.IRC.nick = nick;
68ad40c6 494 // Send the login data
4136eb38 495 ircSocket.write('CAP LS\r\n');
c6b5e668
JA
496 ircSocket.write('NICK ' + nick + '\r\n');
497 ircSocket.write('USER ' + nick + '_kiwi 0 0 :' + nick + '\r\n');
68ad40c6 498
c6b5e668 499 if ((callback) && (typeof (callback) === 'function')) {
68ad40c6
JA
500 callback();
501 }
502 });
c6b5e668 503 websocket.on('message', function (msg, callback) {
bad1ea63 504 var args;
f4f7781b
JA
505 try {
506 msg.data = JSON.parse(msg.data);
507 args = msg.data.args;
508 switch (msg.data.method) {
509 case 'msg':
510 if ((args.target) && (args.msg)) {
511 websocket.ircSocket.write('PRIVMSG ' + args.target + ' :' + args.msg + '\r\n');
512 }
513 break;
514 case 'action':
515 if ((args.target) && (args.msg)) {
516 websocket.ircSocket.write('PRIVMSG ' + args.target + ' :\ 1ACTION ' + args.msg + '\ 1\r\n');
517 }
518 break;
519 case 'raw':
520 websocket.ircSocket.write(args.data + '\r\n');
521 break;
522 case 'join':
523 if (args.channel) {
524 _.each(args.channel.split(","), function (chan) {
525 websocket.ircSocket.write('JOIN ' + chan + '\r\n');
526 });
527 }
528 break;
529 case 'quit':
530 websocket.ircSocket.end('QUIT :' + args.message + '\r\n');
531 websocket.sentQUIT = true;
532 websocket.ircSocket.destroySoon();
533 websocket.disconnect();
534 break;
e36e767b
D
535 case 'notice':
536 if ((args.target) && (args.msg)) {
537 websocket.ircSocket.write('NOTICE ' + args.target + ' :' + args.msg + '\r\n');
538 }
539 break;
f4f7781b 540 default:
bad1ea63 541 }
f4f7781b
JA
542 if ((callback) && (typeof (callback) === 'function')) {
543 callback();
bad1ea63 544 }
f4f7781b
JA
545 } catch (e) {
546 console.log("Caught error: " + e);
bad1ea63 547 }
f4f7781b
JA
548 });
549 websocket.on('disconnect', function () {
550 if ((!websocket.sentQUIT) && (websocket.ircSocket)) {
02a9f7e8 551 websocket.ircSocket.end('QUIT :' + config.quit_message + '\r\n');
f4f7781b
JA
552 websocket.sentQUIT = true;
553 websocket.ircSocket.destroySoon();
68ad40c6
JA
554 }
555 });
556});
c6b5e668 557