Merge branch 'backbone_ui' of github.com:prawnsalad/KiwiIRC into backbone_ui
[KiwiIRC.git] / server / app.js
CommitLineData
80c584e7 1/*jslint sloppy: true, continue: true, forin: true, regexp: true, undef: false, node: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */
2fc64ec2 2/*globals kiwi_root */
f236196a 3/* Fuck you, git. */
55c7f2af
D
4var tls = null,
5 net = null,
6 http = null,
7 https = null,
8 fs = null,
9 url = null,
10 dns = null,
11 crypto = null,
12 events = null,
13 util = null,
14 ws = null,
15 jsp = null,
16 pro = null,
17 _ = null,
18 starttls = null,
19 kiwi = null;
fd779420
D
20
21this.init = function (objs) {
1fce4b40
JA
22 tls = objs.tls;
23 net = objs.net;
24 http = objs.http;
25 https = objs.https;
26 fs = objs.fs;
27 url = objs.url;
28 dns = objs.dns;
29 crypto = objs.crypto;
897abfc3 30 events = objs.events;
bf371d45 31 util = objs.util;
1fce4b40
JA
32 ws = objs.ws;
33 jsp = objs.jsp;
34 pro = objs.pro;
35 _ = objs._;
36 starttls = objs.starttls;
37 kiwi = require('./kiwi.js');
55c7f2af
D
38
39 util.inherits(this.IRCConnection, events.EventEmitter);
2fc64ec2 40};
fd779420
D
41
42
43
44
45
46
47/*
48 * Some process changes
49 */
50this.setTitle = function () {
1fce4b40 51 process.title = 'kiwiirc';
2fc64ec2 52};
fd779420
D
53
54this.changeUser = function () {
55 if (typeof kiwi.config.group !== 'undefined' && kiwi.config.group !== '') {
56 try {
57 process.setgid(kiwi.config.group);
58 } catch (err) {
9970e1d7 59 kiwi.log('Failed to set gid: ' + err);
fd779420
D
60 process.exit();
61 }
62 }
63
64 if (typeof kiwi.config.user !== 'undefined' && kiwi.config.user !== '') {
65 try {
66 process.setuid(kiwi.config.user);
67 } catch (e) {
9970e1d7 68 kiwi.log('Failed to set uid: ' + e);
fd779420
D
69 process.exit();
70 }
71 }
72};
73
74
75
76
77
78
79
80
81
82var ircNumerics = {
83 RPL_WELCOME: '001',
337c9866 84 RPL_MYINFO: '004',
fd779420
D
85 RPL_ISUPPORT: '005',
86 RPL_WHOISUSER: '311',
87 RPL_WHOISSERVER: '312',
88 RPL_WHOISOPERATOR: '313',
89 RPL_WHOISIDLE: '317',
90 RPL_ENDOFWHOIS: '318',
91 RPL_WHOISCHANNELS: '319',
b10afa2d
D
92 RPL_LISTSTART: '321',
93 RPL_LIST: '322',
94 RPL_LISTEND: '323',
fd779420
D
95 RPL_NOTOPIC: '331',
96 RPL_TOPIC: '332',
2a4e22fa 97 RPL_TOPICWHOTIME: '333',
fd779420
D
98 RPL_NAMEREPLY: '353',
99 RPL_ENDOFNAMES: '366',
936eabe7
JA
100 RPL_BANLIST: '367',
101 RPL_ENDOFBANLIST: '368',
fd779420 102 RPL_MOTD: '372',
2955d2b2
D
103 RPL_MOTDSTART: '375',
104 RPL_ENDOFMOTD: '376',
fd779420
D
105 RPL_WHOISMODES: '379',
106 ERR_NOSUCHNICK: '401',
107 ERR_CANNOTSENDTOCHAN: '404',
108 ERR_TOOMANYCHANNELS: '405',
109 ERR_NICKNAMEINUSE: '433',
110 ERR_USERNOTINCHANNEL: '441',
111 ERR_NOTONCHANNEL: '442',
112 ERR_NOTREGISTERED: '451',
113 ERR_LINKCHANNEL: '470',
114 ERR_CHANNELISFULL: '471',
115 ERR_INVITEONLYCHAN: '473',
116 ERR_BANNEDFROMCHAN: '474',
117 ERR_BADCHANNELKEY: '475',
118 ERR_CHANOPRIVSNEEDED: '482',
119 RPL_STARTTLS: '670'
120};
121
bf371d45 122this.bindIRCCommands = function (irc_connection, websocket) {
f4492289
JA
123 var bound_events = [],
124 bindCommand = function (command, listener) {
125 command = 'irc_' + command;
126 irc_connection.on(command, listener);
127 bound_events.push({"command": command, "listener": listener});
128 };
897abfc3 129
f4492289 130 bindCommand('PING', function (msg) {
bf371d45
JA
131 websocket.sendServerLine('PONG ' + msg.trailing);
132 });
8343584e 133
f4492289 134 bindCommand(ircNumerics.RPL_WELCOME, function (msg) {
bf371d45
JA
135 if (irc_connection.IRC.CAP.negotiating) {
136 irc_connection.IRC.CAP.negotiating = false;
137 irc_connection.IRC.CAP.enabled = [];
138 irc_connection.IRC.CAP.requested = [];
139 irc_connection.IRC.registered = true;
140 }
141 var nick = msg.params.split(' ')[0];
142 websocket.sendClientEvent('connect', {connected: true, host: null, nick: nick});
143 });
337c9866 144
f4492289 145 bindCommand(ircNumerics.RPL_ISUPPORT, function (msg) {
bf371d45 146 var opts = msg.params.split(" "),
bf371d45
JA
147 opt,
148 i,
897abfc3 149 j,
bf371d45
JA
150 regex,
151 matches;
152 for (i = 0; i < opts.length; i++) {
153 opt = opts[i].split("=", 2);
154 opt[0] = opt[0].toUpperCase();
155 irc_connection.IRC.options[opt[0]] = (typeof opt[1] !== 'undefined') ? opt[1] : true;
156 if (_.include(['NETWORK', 'PREFIX', 'CHANTYPES', 'NAMESX'], opt[0])) {
157 if (opt[0] === 'PREFIX') {
158 regex = /\(([^)]*)\)(.*)/;
159 matches = regex.exec(opt[1]);
160 if ((matches) && (matches.length === 3)) {
161 irc_connection.IRC.options[opt[0]] = [];
162 for (j = 0; j < matches[2].length; j++) {
163 irc_connection.IRC.options[opt[0]].push({symbol: matches[2].charAt(j), mode: matches[1].charAt(j)});
fd779420 164 }
bf371d45 165
fd779420 166 }
bf371d45
JA
167 }
168 if (opt[0] === 'NAMESX') {
169 websocket.sendServerLine('PROTOCTL NAMESX');
fd779420
D
170 }
171 }
bf371d45 172 }
337c9866 173
bf371d45 174 websocket.sendClientEvent('options', {server: '', "options": irc_connection.IRC.options});
897abfc3 175 });
a146029e 176
f4492289 177 bindCommand(ircNumerics.RPL_ENDOFWHOIS, function (msg) {
bf371d45
JA
178 websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: true});
179 });
897abfc3 180
f4492289 181 bindCommand(ircNumerics.RPL_WHOISUSER, function (msg) {
bf371d45
JA
182 websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false});
183 });
897abfc3 184
f4492289 185 bindCommand(ircNumerics.RPL_WHOISSERVER, function (msg) {
bf371d45
JA
186 websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false});
187 });
897abfc3 188
f4492289 189 bindCommand(ircNumerics.RPL_WHOISOPERATOR, function (msg) {
bf371d45
JA
190 websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false});
191 });
897abfc3 192
f4492289 193 bindCommand(ircNumerics.RPL_WHOISCHANNELS, function (msg) {
bf371d45
JA
194 websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false});
195 });
897abfc3 196
f4492289 197 bindCommand(ircNumerics.RPL_WHOISMODES, function (msg) {
bf371d45
JA
198 websocket.sendClientEvent('whois', {server: '', nick: msg.params.split(" ", 3)[1], "msg": msg.trailing, end: false});
199 });
b10afa2d 200
f4492289 201 bindCommand(ircNumerics.RPL_LISTSTART, function (msg) {
bf371d45
JA
202 websocket.sendClientEvent('list_start', {server: ''});
203 websocket.kiwi.buffer.list = [];
204 });
897abfc3 205
f4492289 206 bindCommand(ircNumerics.RPL_LISTEND, function (msg) {
bf371d45
JA
207 if (websocket.kiwi.buffer.list.length > 0) {
208 websocket.kiwi.buffer.list = _.sortBy(websocket.kiwi.buffer.list, function (channel) {
209 return channel.num_users;
210 });
211 websocket.sendClientEvent('list_channel', {chans: websocket.kiwi.buffer.list});
212 websocket.kiwi.buffer.list = [];
213 }
214 websocket.sendClientEvent('list_end', {server: ''});
215 });
8343584e 216
f4492289 217 bindCommand(ircNumerics.RPL_LIST, function (msg) {
897abfc3 218 var parts, channel, num_users, topic;
bf371d45
JA
219
220 parts = msg.params.split(' ');
221 channel = parts[1];
222 num_users = parts[2];
223 topic = msg.trailing;
224
225 //websocket.sendClientEvent('list_channel', {
226 websocket.kiwi.buffer.list.push({
227 server: '',
228 channel: channel,
229 topic: topic,
230 //modes: modes,
231 num_users: parseInt(num_users, 10)
232 });
8343584e 233
bf371d45
JA
234 if (websocket.kiwi.buffer.list.length > 200) {
235 websocket.kiwi.buffer.list = _.sortBy(websocket.kiwi.buffer.list, function (channel) {
236 return channel.num_users;
237 });
238 websocket.sendClientEvent('list_channel', {chans: websocket.kiwi.buffer.list});
239 websocket.kiwi.buffer.list = [];
240 }
241 });
8343584e 242
f4492289 243 bindCommand(ircNumerics.RPL_WHOISIDLE, function (msg) {
897abfc3
JA
244 var params = msg.params.split(" ", 4),
245 rtn = {server: '', nick: params[1], idle: params[2]};
bf371d45
JA
246 if (params[3]) {
247 rtn.logon = params[3];
248 }
249 websocket.sendClientEvent('whois', rtn);
250 });
b10afa2d 251
f4492289 252 bindCommand(ircNumerics.RPL_MOTD, function (msg) {
bf371d45
JA
253 websocket.kiwi.buffer.motd += msg.trailing + '\n';
254 });
255
f4492289 256 bindCommand(ircNumerics.RPL_MOTDSTART, function (msg) {
bf371d45
JA
257 websocket.kiwi.buffer.motd = '';
258 });
259
f4492289 260 bindCommand(ircNumerics.RPL_ENDOFMOTD, function (msg) {
bf371d45
JA
261 websocket.sendClientEvent('motd', {server: '', 'msg': websocket.kiwi.buffer.motd});
262 });
263
f4492289 264 bindCommand(ircNumerics.RPL_NAMEREPLY, function (msg) {
897abfc3
JA
265 var params = msg.params.split(" "),
266 chan = params[2],
267 users = msg.trailing.split(" "),
268 nicklist = [],
269 i = 0;
270
bf371d45
JA
271 _.each(users, function (user) {
272 var j, k, modes = [];
273 for (j = 0; j < user.length; j++) {
897abfc3
JA
274 for (k = 0; k < irc_connection.IRC.options.PREFIX.length; k++) {
275 if (user.charAt(j) === irc_connection.IRC.options.PREFIX[k].symbol) {
276 modes.push(irc_connection.IRC.options.PREFIX[k].mode);
8343584e 277 }
fd779420 278 }
fd779420 279 }
bf371d45
JA
280 nicklist.push({nick: user, modes: modes});
281 if (i++ >= 50) {
282 websocket.sendClientEvent('userlist', {server: '', 'users': nicklist, channel: chan});
283 nicklist = [];
284 i = 0;
fd779420 285 }
bf371d45
JA
286 });
287 if (i > 0) {
288 websocket.sendClientEvent('userlist', {server: '', "users": nicklist, channel: chan});
289 } else {
290 kiwi.log("oops");
291 }
292 });
fd779420 293
f4492289 294 bindCommand(ircNumerics.RPL_ENDOFNAMES, function (msg) {
bf371d45
JA
295 websocket.sendClientEvent('userlist_end', {server: '', channel: msg.params.split(" ")[1]});
296 });
297
f4492289 298 bindCommand(ircNumerics.ERR_LINKCHANNEL, function (msg) {
897abfc3 299 var params = msg.params.split(" ");
bf371d45
JA
300 websocket.sendClientEvent('channel_redirect', {from: params[1], to: params[2]});
301 });
302
f4492289 303 bindCommand(ircNumerics.ERR_NOSUCHNICK, function (msg) {
bf371d45
JA
304 websocket.sendClientEvent('irc_error', {error: 'no_such_nick', nick: msg.params.split(" ")[1], reason: msg.trailing});
305 });
306
f4492289 307 bindCommand(ircNumerics.RPL_BANLIST, function (msg) {
897abfc3 308 var params = msg.params.split(" ");
bf371d45
JA
309 kiwi.log(params);
310 websocket.sendClientEvent('banlist', {server: '', channel: params[1], banned: params[2], banned_by: params[3], banned_at: params[4]});
311 });
312
f4492289 313 bindCommand(ircNumerics.RPL_ENDOFBANLIST, function (msg) {
bf371d45
JA
314 websocket.sendClientEvent('banlist_end', {server: '', channel: msg.params.split(" ")[1]});
315 });
316
f4492289 317 bindCommand('JOIN', function (msg) {
897abfc3
JA
318 var channel;
319
bf371d45
JA
320 // Some BNC's send malformed JOIN causing the channel to be as a
321 // parameter instead of trailing.
322 if (typeof msg.trailing === 'string' && msg.trailing !== '') {
323 channel = msg.trailing;
324 } else if (typeof msg.params === 'string' && msg.params !== '') {
325 channel = msg.params;
326 }
327
328 websocket.sendClientEvent('join', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: channel});
897abfc3 329 if (msg.nick === irc_connection.IRC.nick) {
bf371d45
JA
330 websocket.sendServerLine('NAMES ' + msg.trailing);
331 }
332 });
333
f4492289 334 bindCommand('PART', function (msg) {
897abfc3 335 websocket.sendClientEvent('part', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), message: msg.trailing});
bf371d45
JA
336 });
337
f4492289 338 bindCommand('KICK', function (msg) {
897abfc3 339 var params = msg.params.split(" ");
bf371d45
JA
340 websocket.sendClientEvent('kick', {kicked: params[1], nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: params[0].trim(), message: msg.trailing});
341 });
342
f4492289 343 bindCommand('QUIT', function (msg) {
bf371d45
JA
344 websocket.sendClientEvent('quit', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, message: msg.trailing});
345 });
346
f4492289 347 bindCommand('NOTICE', function (msg) {
bf371d45
JA
348 if ((msg.trailing.charAt(0) === String.fromCharCode(1)) && (msg.trailing.charAt(msg.trailing.length - 1) === String.fromCharCode(1))) {
349 // It's a CTCP response
350 websocket.sendClientEvent('ctcp_response', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(1, msg.trailing.length - 2)});
351 } else {
352 websocket.sendClientEvent('notice', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, target: msg.params.trim(), msg: msg.trailing});
353 }
354 });
355
f4492289 356 bindCommand('NICK', function (msg) {
bf371d45
JA
357 websocket.sendClientEvent('nick', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, newnick: msg.trailing});
358 });
359
f4492289 360 bindCommand('TOPIC', function (msg) {
897abfc3 361 var obj = {nick: msg.nick, channel: msg.params, topic: msg.trailing};
bf371d45
JA
362 websocket.sendClientEvent('topic', obj);
363 });
364
f4492289 365 bindCommand(ircNumerics.RPL_TOPIC, function (msg) {
897abfc3 366 var obj = {nick: '', channel: msg.params.split(" ")[1], topic: msg.trailing};
bf371d45
JA
367 websocket.sendClientEvent('topic', obj);
368 });
369
f4492289 370 bindCommand(ircNumerics.RPL_NOTOPIC, function (msg) {
897abfc3
JA
371 var obj = {nick: '', channel: msg.params.split(" ")[1], topic: ''};
372 websocket.sendClientEvent('topic', obj);
bf371d45
JA
373 });
374
f4492289 375 bindCommand(ircNumerics.RPL_TOPICWHOTIME, function (msg) {
bf371d45
JA
376 var parts = msg.params.split(' '),
377 nick = parts[2],
378 channel = parts[1],
897abfc3 379 when = parts[3],
bf371d45 380 obj = {nick: nick, channel: channel, when: when};
897abfc3 381 websocket.sendClientEvent('topicsetby', obj);
bf371d45
JA
382 });
383
f4492289 384 bindCommand('MODE', function (msg) {
897abfc3
JA
385 var opts = msg.params.split(" "),
386 params = {nick: msg.nick};
387
bf371d45
JA
388 switch (opts.length) {
389 case 1:
390 params.effected_nick = opts[0];
391 params.mode = msg.trailing;
fd779420 392 break;
bf371d45
JA
393 case 2:
394 params.channel = opts[0];
395 params.mode = opts[1];
2a4e22fa 396 break;
bf371d45
JA
397 default:
398 params.channel = opts[0];
399 params.mode = opts[1];
400 params.effected_nick = opts[2];
fd779420 401 break;
bf371d45
JA
402 }
403 websocket.sendClientEvent('mode', params);
404 });
405
f4492289 406 bindCommand('PRIVMSG', function (msg) {
897abfc3 407 var tmp, namespace, obj;
bf371d45
JA
408 if ((msg.trailing.charAt(0) === String.fromCharCode(1)) && (msg.trailing.charAt(msg.trailing.length - 1) === String.fromCharCode(1))) {
409 // It's a CTCP request
410 if (msg.trailing.substr(1, 6) === 'ACTION') {
411 websocket.sendClientEvent('action', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(7, msg.trailing.length - 2)});
412 } else if (msg.trailing.substr(1, 4) === 'KIWI') {
413 tmp = msg.trailing.substr(6, msg.trailing.length - 2);
414 namespace = tmp.split(' ', 1)[0];
415 websocket.sendClientEvent('kiwi', {namespace: namespace, data: tmp.substr(namespace.length + 1)});
416
417 } else if (msg.trailing.substr(1, 7) === 'VERSION') {
897abfc3 418 irc_connection.write('NOTICE ' + msg.nick + ' :' + String.fromCharCode(1) + 'VERSION KiwiIRC' + String.fromCharCode(1) + '\r\n');
fd779420 419 } else {
bf371d45 420 websocket.sendClientEvent('ctcp_request', {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing.substr(1, msg.trailing.length - 2)});
fd779420 421 }
bf371d45
JA
422 } else {
423 obj = {nick: msg.nick, ident: msg.ident, hostname: msg.hostname, channel: msg.params.trim(), msg: msg.trailing};
424 websocket.sendClientEvent('msg', obj);
425 }
426 });
427
f4492289 428 bindCommand('CAP', function (msg) {
897abfc3
JA
429 var caps = kiwi.config.cap_options,
430 options = msg.trailing.split(" "),
431 opts;
432
bf371d45
JA
433 switch (_.last(msg.params.split(" "))) {
434 case 'LS':
435 opts = '';
436 _.each(_.intersect(caps, options), function (cap) {
437 if (opts !== '') {
438 opts += " ";
fd779420 439 }
bf371d45 440 opts += cap;
897abfc3 441 irc_connection.IRC.CAP.requested.push(cap);
bf371d45
JA
442 });
443 if (opts.length > 0) {
444 websocket.sendServerLine('CAP REQ :' + opts);
445 } else {
446 websocket.sendServerLine('CAP END');
447 }
448 // TLS is special
449 /*if (_.include(options, 'tls')) {
450 websocket.sendServerLine('STARTTLS');
451 ircSocket.IRC.CAP.requested.push('tls');
452 }*/
453 break;
454 case 'ACK':
455 _.each(options, function (cap) {
897abfc3 456 irc_connection.IRC.CAP.enabled.push(cap);
bf371d45
JA
457 });
458 if (_.last(msg.params.split(" ")) !== '*') {
897abfc3
JA
459 irc_connection.IRC.CAP.requested = [];
460 irc_connection.IRC.CAP.negotiating = false;
fd779420 461 websocket.sendServerLine('CAP END');
fd779420
D
462 }
463 break;
bf371d45 464 case 'NAK':
897abfc3
JA
465 irc_connection.IRC.CAP.requested = [];
466 irc_connection.IRC.CAP.negotiating = false;
bf371d45
JA
467 websocket.sendServerLine('CAP END');
468 break;
469 }
470 });
fd779420
D
471 /*case ircNumerics.RPL_STARTTLS:
472 try {
473 IRC = ircSocket.IRC;
474 listeners = ircSocket.listeners('data');
475 ircSocket.removeAllListeners('data');
476 ssl_socket = starttls(ircSocket, {}, function () {
477 ssl_socket.on("data", function (data) {
478 ircSocketDataHandler(data, websocket, ssl_socket);
479 });
480 ircSocket = ssl_socket;
481 ircSocket.IRC = IRC;
482 _.each(listeners, function (listener) {
483 ircSocket.addListener('data', listener);
484 });
485 });
9970e1d7 486 //log(ircSocket);
fd779420 487 } catch (e) {
9970e1d7 488 kiwi.log(e);
fd779420
D
489 }
490 break;*/
f4492289 491 bindCommand(ircNumerics.ERR_CANNOTSENDTOCHAN, function (msg) {
897abfc3 492 websocket.sendClientEvent('irc_error', {error: 'cannot_send_to_chan', channel: msg.params.split(" ")[1], reason: msg.trailing});
bf371d45 493 });
fd779420 494
f4492289 495 bindCommand(ircNumerics.ERR_TOOMANYCHANNELS, function (msg) {
bf371d45
JA
496 websocket.sendClientEvent('irc_error', {error: 'too_many_channels', channel: msg.params.split(" ")[1], reason: msg.trailing});
497 });
fd779420 498
f4492289 499 bindCommand(ircNumerics.ERR_USERNOTINCHANNEL, function (msg) {
897abfc3 500 var params = msg.params.split(" ");
bf371d45
JA
501 websocket.sendClientEvent('irc_error', {error: 'user_not_in_channel', nick: params[0], channel: params[1], reason: msg.trainling});
502 });
fd779420 503
f4492289 504 bindCommand(ircNumerics.ERR_NOTONCHANNEL, function (msg) {
bf371d45
JA
505 websocket.sendClientEvent('irc_error', {error: 'not_on_channel', channel: msg.params.split(" ")[1], reason: msg.trailing});
506 });
fd779420 507
f4492289 508 bindCommand(ircNumerics.ERR_CHANNELISFULL, function (msg) {
bf371d45
JA
509 websocket.sendClientEvent('irc_error', {error: 'channel_is_full', channel: msg.params.split(" ")[1], reason: msg.trailing});
510 });
fd779420 511
f4492289 512 bindCommand(ircNumerics.ERR_INVITEONLYCHAN, function (msg) {
bf371d45
JA
513 websocket.sendClientEvent('irc_error', {error: 'invite_only_channel', channel: msg.params.split(" ")[1], reason: msg.trailing});
514 });
fd779420 515
f4492289 516 bindCommand(ircNumerics.ERR_BANNEDFROMCHAN, function (msg) {
bf371d45
JA
517 websocket.sendClientEvent('irc_error', {error: 'banned_from_channel', channel: msg.params.split(" ")[1], reason: msg.trailing});
518 });
fd779420 519
f4492289 520 bindCommand(ircNumerics.ERR_BADCHANNELKEY, function (msg) {
bf371d45
JA
521 websocket.sendClientEvent('irc_error', {error: 'bad_channel_key', channel: msg.params.split(" ")[1], reason: msg.trailing});
522 });
fd779420 523
f4492289 524 bindCommand(ircNumerics.ERR_CHANOPRIVSNEEDED, function (msg) {
bf371d45
JA
525 websocket.sendClientEvent('irc_error', {error: 'chanop_privs_needed', channel: msg.params.split(" ")[1], reason: msg.trailing});
526 });
fd779420 527
f4492289 528 bindCommand(ircNumerics.ERR_NICKNAMEINUSE, function (msg) {
bf371d45
JA
529 websocket.sendClientEvent('irc_error', {error: 'nickname_in_use', nick: _.last(msg.params.split(" ")), reason: msg.trailing});
530 });
531
f4492289 532 bindCommand('ERROR', function (msg) {
897abfc3 533 irc_connection.end();
bf371d45
JA
534 websocket.sendClientEvent('irc_error', {error: 'error', reason: msg.trailing});
535 websocket.disconnect();
536 });
fd779420 537
f4492289 538 bindCommand(ircNumerics.ERR_NOTREGISTERED, function (msg) {
897abfc3 539 if (irc_connection.IRC.registered) {
bf371d45
JA
540 kiwi.log('Kiwi thinks user is registered, but the IRC server thinks differently');
541 }
542 });
f52d8543 543
55c7f2af 544 return bound_events;
f52d8543
JA
545};
546
547this.rebindIRCCommands = function () {
548 _.each(kiwi.connections, function (con) {
549 _.each(con.sockets, function (sock) {
550 sock.ircConnection.rebindIRCCommands();
551 });
552 });
bf371d45 553};
fd779420
D
554
555
16400250
JA
556this.httpHandler = function (request, response, serverconf) {
557 var uri, uri_parts, subs, useragent, agent, server_set, serverconf, nick, debug, touchscreen, hash,
897abfc3 558 min = {}, public_http_path, port, ssl, obj, args, ircuri, target, modifiers, query,
16400250 559 secure = serverconf.secure || false;
0622748f 560
480d2fea 561 try {
0622748f 562 if (kiwi.config.handle_http) {
f97fe3d2 563 // Run through any plugins..
3010bd6c 564 args = {request: request, response: response, ssl: secure};
f97fe3d2
D
565 obj = kiwi.kiwi_mod.run('http', args);
566 if (obj === null) {
567 return;
568 }
569 response = args.response;
570
0622748f
D
571 uri = url.parse(request.url, true);
572 uri_parts = uri.pathname.split('/');
573
574 subs = uri.pathname.substr(0, 4);
5638a960 575 public_http_path = kiwi.kiwi_root + '/' + kiwi.config.public_http;
897abfc3 576
cbcccdbd
D
577 if (typeof uri.query.ircuri !== 'undefined') {
578 ircuri = url.parse(uri.query.ircuri, true);
579 if (ircuri.protocol === 'irc:') {
580 uri_parts = /^\/([^,\?]*)((,[^,\?]*)*)?$/.exec(ircuri.pathname);
581 target = uri_parts[1];
582 modifiers = (typeof uri_parts[2] !== 'undefined') ? uri_parts[2].split(',') : [];
583 query = ircuri.query;
584
585 nick = _.detect(modifiers, function (mod) {
586 return mod === ',isnick';
587 });
9970e1d7 588 kiwi.log(request.headers);
cbcccdbd
D
589 response.statusCode = 303;
590 response.setHeader('Location', 'http' + ((secure) ? 's' : '') + '://' + request.headers.host + '/client/' + ircuri.host + '/' + ((!nick) ? target : ''));
591 response.end();
592 }
593 } else if (uri.pathname === '/js/all.js') {
0622748f 594 if (kiwi.cache.alljs === '') {
0622748f 595
1fce4b40 596 min.underscore = fs.readFileSync(public_http_path + 'js/underscore.min.js');
0622748f 597 min.util = fs.readFileSync(public_http_path + 'js/util.js');
54a2a4e1 598 min.backbone = fs.readFileSync(public_http_path + 'js/backbone-git.js');
0622748f 599 min.gateway = fs.readFileSync(public_http_path + 'js/gateway.js');
f7a9a13c
JA
600 min.model = fs.readFileSync(public_http_path + 'js/model.js');
601 min.view = fs.readFileSync(public_http_path + 'js/view.js');
0622748f 602 min.front = fs.readFileSync(public_http_path + 'js/front.js');
b41a381f
D
603 min.front_events = fs.readFileSync(public_http_path + 'js/front.events.js');
604 min.front_ui = fs.readFileSync(public_http_path + 'js/front.ui.js');
0622748f 605 min.iscroll = fs.readFileSync(public_http_path + 'js/iscroll.js');
f7a9a13c 606 min.ast = jsp.parse(min.underscore + min.util + min.backbone + min.gateway + min.model + min.view + min.front + min.front_events + min.front_ui + min.iscroll);
0622748f
D
607 min.ast = pro.ast_mangle(min.ast);
608 min.ast = pro.ast_squeeze(min.ast);
609 min.final_code = pro.gen_code(min.ast);
610 kiwi.cache.alljs = min.final_code;
611 hash = crypto.createHash('md5').update(kiwi.cache.alljs);
612 kiwi.cache.alljs_hash = hash.digest('base64');
613 }
614 if (request.headers['if-none-match'] === kiwi.cache.alljs_hash) {
615 response.statusCode = 304;
616 } else {
617 response.setHeader('Content-type', 'application/javascript');
618 response.setHeader('ETag', kiwi.cache.alljs_hash);
16400250
JA
619 if ((secure) && (serverconf.hsts)) {
620 response.setHeader("Strict-Transport-Security", "max-age=604 800");
621 }
0622748f
D
622 response.write(kiwi.cache.alljs);
623 }
624 response.end();
625 } else if ((subs === '/js/') || (subs === '/css') || (subs === '/img')) {
626 request.addListener('end', function () {
627 kiwi.fileServer.serve(request, response);
628 });
629 } else if (uri.pathname === '/' || uri_parts[1] === 'client') {
630 useragent = (typeof request.headers === 'string') ? request.headers['user-agent'] : '';
631 if (useragent.match(/android/i) !== -1) {
632 agent = 'android';
633 touchscreen = true;
634 } else if (useragent.match(/iphone/) !== -1) {
635 agent = 'iphone';
636 touchscreen = true;
637 } else if (useragent.match(/ipad/) !== -1) {
638 agent = 'ipad';
639 touchscreen = true;
640 } else if (useragent.match(/ipod/) !== -1) {
641 agent = 'ipod';
642 touchscreen = true;
643 } else {
644 agent = 'normal';
645 touchscreen = false;
646 }
fd779420
D
647 agent = 'normal';
648 touchscreen = false;
91726016 649
0622748f 650 debug = (typeof uri.query.debug !== 'undefined');
8343584e 651
d3b6433a
D
652 ssl = secure; // ssl is passed to the client
653 port = ssl ? kiwi.config.client_defaults.port_ssl : kiwi.config.client_defaults.port;
0622748f
D
654 if (uri_parts[1] !== 'client') {
655 if (uri.query) {
656 server_set = ((typeof uri.query.server !== 'undefined') && (uri.query.server !== ''));
d3b6433a 657 server = uri.query.server || kiwi.config.client_defaults.server;
0622748f
D
658 nick = uri.query.nick || '';
659 } else {
660 server_set = false;
d3b6433a 661 server = kiwi.config.client_defaults.server;
0622748f
D
662 nick = '';
663 }
8397d902 664 } else {
0622748f 665 server_set = ((typeof uri_parts[2] !== 'undefined') && (uri_parts[2] !== ''));
d3b6433a 666 server = server_set ? uri_parts[2] : kiwi.config.client_defaults.server;
c512925c
D
667 if (server.search(/:/) > 0) {
668 port = server.substring(server.search(/:/) + 1);
669 server = server.substring(0, server.search(/:/));
897abfc3 670 if (port[0] === '+') {
c512925c
D
671 port = port.substring(1);
672 ssl = true;
4023f19f
D
673 } else {
674 ssl = false;
c512925c
D
675 }
676 }
0622748f 677 nick = uri.query.nick || '';
8397d902 678 }
8397d902 679
5c845ac2 680 // Set the default nick if one isn't provided
897abfc3 681 if (nick === '') {
5c845ac2
D
682 nick = 'kiwi_?';
683 }
684
685 // Set any random numbers if needed
897abfc3 686 nick = nick.replace('?', Math.floor(Math.random() * 100000).toString());
5c845ac2 687
0622748f
D
688 response.setHeader('X-Generated-By', 'KiwiIRC');
689 hash = crypto.createHash('md5').update(touchscreen ? 't' : 'f')
690 .update(debug ? 't' : 'f')
691 .update(server_set ? 't' : 'f')
692 .update(secure ? 't' : 'f')
693 .update(server)
e8d7c021 694 .update(port.toString())
c512925c 695 .update(ssl ? 't' : 'f')
0622748f
D
696 .update(nick)
697 .update(agent)
698 .update(JSON.stringify(kiwi.config))
699 .digest('base64');
700 if (kiwi.cache.html[hash]) {
701 if (request.headers['if-none-match'] === kiwi.cache.html[hash].hash) {
702 response.statusCode = 304;
fd779420 703 } else {
0622748f
D
704 response.setHeader('Etag', kiwi.cache.html[hash].hash);
705 response.setHeader('Content-type', 'text/html');
16400250
JA
706 if ((secure) && (serverconf.hsts)) {
707 response.setHeader("Strict-Transport-Security", "max-age=604 800");
708 }
0622748f 709 response.write(kiwi.cache.html[hash].html);
fd779420
D
710 }
711 response.end();
0622748f 712 } else {
5638a960 713 fs.readFile(public_http_path + 'index.html.jade', 'utf8', function (err, str) {
0622748f
D
714 var html, hash2;
715 if (!err) {
9dc939b7 716 try {
c512925c 717 html = kiwi.jade.compile(str)({ "touchscreen": touchscreen, "debug": debug, "secure": secure, "server_set": server_set, "server": server, "port": port, "ssl": ssl, "nick": nick, "agent": agent, "config": kiwi.config });
9dc939b7
JA
718 hash2 = crypto.createHash('md5').update(html).digest('base64');
719 kiwi.cache.html[hash] = {"html": html, "hash": hash2};
720 if (request.headers['if-none-match'] === hash2) {
721 response.statusCode = 304;
722 } else {
723 response.setHeader('Etag', hash2);
724 response.setHeader('Content-type', 'text/html');
16400250
JA
725 if ((secure) && (serverconf.hsts)) {
726 response.setHeader("Strict-Transport-Security", "max-age=604 800");
727 }
9dc939b7
JA
728 response.write(html);
729 }
730 } catch (e) {
731 response.statusCode = 500;
9970e1d7 732 kiwi.log(e);
0622748f
D
733 }
734 } else {
9970e1d7 735 kiwi.log(err);
0622748f
D
736 response.statusCode = 500;
737 }
738 response.end();
739 });
740 }
741 } else if (uri.pathname.substr(0, 10) === '/socket.io') {
742 return;
743 } else {
744 response.statusCode = 404;
745 response.end();
fd779420 746 }
fd779420 747 }
0622748f 748
480d2fea 749 } catch (e) {
9970e1d7
D
750 kiwi.log('ERROR app.httpHandler()');
751 kiwi.log(e);
480d2fea 752 }
fd779420
D
753};
754
755
756
757
d423ee18 758this.websocketListen = function (servers, handler) {
9f6954dc
JA
759 if (kiwi.httpServers.length > 0) {
760 _.each(kiwi.httpServers, function (hs) {
761 hs.close();
762 });
763 kiwi.httpsServers = [];
fd779420 764 }
8343584e 765
d423ee18
D
766 _.each(servers, function (server) {
767 var hs, opts;
6170d584 768 if (server.secure === true) {
d423ee18
D
769 // Start some SSL server up
770 opts = {
771 key: fs.readFileSync(__dirname + '/' + server.ssl_key),
772 cert: fs.readFileSync(__dirname + '/' + server.ssl_cert)
773 };
774
775 // Do we have an intermediate certificate?
776 if (typeof server.ssl_ca !== 'undefined') {
777 opts.ca = fs.readFileSync(__dirname + '/' + server.ssl_ca);
778 }
779
16400250
JA
780 hs = https.createServer(opts, function (request, response) {
781 handler(request, response, server);
782 });
9f6954dc 783 kiwi.io.push(ws.listen(hs, {secure: true}));
d423ee18 784 hs.listen(server.port, server.address);
deadd643 785 kiwi.log('Listening on ' + server.address + ':' + server.port.toString() + ' with SSL');
9f6954dc 786 } else {
d423ee18 787 // Start some plain-text server up
16400250
JA
788 hs = http.createServer(function (request, response) {
789 handler(request, response, server);
790 });
9f6954dc 791 kiwi.io.push(ws.listen(hs, {secure: false}));
d423ee18 792 hs.listen(server.port, server.address);
deadd643 793 kiwi.log('Listening on ' + server.address + ':' + server.port.toString() + ' without SSL');
fd779420 794 }
f97fe3d2
D
795
796 kiwi.httpServers.push(hs);
9f6954dc 797 });
8343584e 798
9f6954dc
JA
799 _.each(kiwi.io, function (io) {
800 io.set('log level', 1);
801 io.enable('browser client minification');
802 io.enable('browser client etag');
803 io.set('transports', kiwi.config.transports);
1fce4b40 804
9f6954dc
JA
805 io.of('/kiwi').authorization(function (handshakeData, callback) {
806 var address = handshakeData.address.address;
807 if (typeof kiwi.connections[address] === 'undefined') {
808 kiwi.connections[address] = {count: 0, sockets: []};
809 }
810 callback(null, true);
811 }).on('connection', kiwi.websocketConnection);
af979742 812 io.of('/kiwi').on('error', console.log);
9f6954dc 813 });
fd779420
D
814};
815
816
817
818
819
820
821this.websocketConnection = function (websocket) {
822 var con;
9970e1d7 823 kiwi.log("New connection!");
5c7ac96f 824 websocket.kiwi = {address: websocket.handshake.address.address, buffer: {list: []}};
fd779420
D
825 con = kiwi.connections[websocket.kiwi.address];
826
827 if (con.count >= kiwi.config.max_client_conns) {
828 websocket.emit('too_many_connections');
829 websocket.disconnect();
830 } else {
831 con.count += 1;
832 con.sockets.push(websocket);
833
834 websocket.sendClientEvent = function (event_name, data) {
4f06269b 835 var ev = kiwi.kiwi_mod.run(event_name, data, {websocket: this});
2fc64ec2
JA
836 if (ev === null) {
837 return;
838 }
4f06269b 839
fd779420
D
840 data.event = event_name;
841 websocket.emit('message', data);
842 };
843
1b2bb94d
JA
844 websocket.sendServerLine = function (data, eol, callback) {
845 if ((arguments.length < 3) && (typeof eol === 'function')) {
846 callback = eol;
1b2bb94d 847 }
fc92c3d7 848 eol = (typeof eol !== 'string') ? '\r\n' : eol;
abb46d2d
D
849
850 try {
1b2bb94d 851 websocket.ircConnection.write(data + eol, 'utf-8', callback);
abb46d2d 852 } catch (e) { }
fd779420
D
853 };
854
897abfc3
JA
855 websocket.on('irc connect', function (nick, host, port, ssl, password, callback) {
856 websocket.ircConnection = new kiwi.IRCConnection(this, nick, host, port, ssl, password, callback);
bf371d45 857 });
fd779420
D
858 websocket.on('message', kiwi.websocketMessage);
859 websocket.on('disconnect', kiwi.websocketDisconnect);
af979742 860 websocket.on('error', console.log);
fd779420
D
861 }
862};
863
864
bf371d45
JA
865this.IRCConnection = function (websocket, nick, host, port, ssl, password, callback) {
866 var ircSocket,
867 that = this,
63eaef31 868 regex,
f52d8543 869 onConnectHandler,
55c7f2af 870 bound_events;
fd779420 871
bf371d45 872 events.EventEmitter.call(this);
f52d8543 873
63eaef31
JA
874 onConnectHandler = function () {
875 that.IRC.nick = nick;
876 // Send the login data
877 dns.reverse(websocket.kiwi.address, function (err, domains) {
878 websocket.kiwi.hostname = (err) ? websocket.kiwi.address : _.first(domains);
879 if ((kiwi.config.webirc) && (kiwi.config.webirc_pass[host])) {
880 websocket.sendServerLine('WEBIRC ' + kiwi.config.webirc_pass[host] + ' KiwiIRC ' + websocket.kiwi.hostname + ' ' + websocket.kiwi.address);
881 }
882 if (password) {
883 websocket.sendServerLine('PASS ' + password);
884 }
885 websocket.sendServerLine('CAP LS');
886 websocket.sendServerLine('NICK ' + nick);
887 websocket.sendServerLine('USER kiwi_' + nick.replace(/[^0-9a-zA-Z\-_.]/, '') + ' 0 0 :' + nick);
888
d62bbe08 889 that.connected = true;
f52d8543 890 that.emit('connect');
63eaef31 891 });
63eaef31 892 };
f52d8543 893
fd779420
D
894 if (!ssl) {
895 ircSocket = net.createConnection(port, host);
63eaef31 896 ircSocket.on('connect', onConnectHandler);
fd779420 897 } else {
63eaef31 898 ircSocket = tls.connect(port, host, {}, onConnectHandler);
fd779420 899 }
897abfc3 900
0a401b0e 901 ircSocket.setEncoding('utf-8');
897abfc3
JA
902 this.IRC = {options: {}, CAP: {negotiating: true, requested: [], enabled: []}, registered: false};
903
bf371d45 904 this.on('error', function (e) {
897abfc3 905 if (that.IRC.registered) {
fd779420
D
906 websocket.emit('disconnect');
907 } else {
908 websocket.emit('error', e.message);
909 }
910 });
897abfc3 911
bf371d45 912 ircSocket.on('error', function (e) {
d62bbe08 913 that.connected = false;
bf371d45 914 that.emit('error', e);
d62bbe08 915 that.destroySoon();
bf371d45 916 });
f52d8543
JA
917
918 if (typeof callback === 'function') {
919 this.on('connect', callback);
920 }
921
bf371d45 922 regex = /^(?::(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@?([a-z0-9\.\-:\/]+)?) )?(\S+)(?: (?!:)(.+?))?(?: :(.+))?$/i;
fd779420
D
923 ircSocket.holdLast = false;
924 ircSocket.held = '';
fd779420 925 ircSocket.on('data', function (data) {
897abfc3 926 var i, msg;
bf371d45
JA
927 if ((ircSocket.holdLast) && (ircSocket.held !== '')) {
928 data = ircSocket.held + data;
929 ircSocket.holdLast = false;
930 ircSocket.held = '';
fd779420 931 }
bf371d45
JA
932 if (data.substr(-1) !== '\n') {
933 ircSocket.holdLast = true;
e8a707e7 934 }
bf371d45
JA
935 data = data.split("\n");
936 for (i = 0; i < data.length; i++) {
937 if (data[i]) {
938 if ((ircSocket.holdLast) && (i === data.length - 1)) {
939 ircSocket.held = data[i];
940 break;
941 }
fd779420 942
bf371d45 943 // We have a complete line of data, parse it!
07e97a5a 944 msg = regex.exec(data[i].replace(/^\r+|\r+$/, ''));
bf371d45
JA
945 if (msg) {
946 msg = {
947 prefix: msg[1],
948 nick: msg[2],
949 ident: msg[3],
950 hostname: msg[4] || '',
951 command: msg[5],
952 params: msg[6] || '',
953 trailing: (msg[7]) ? msg[7].trim() : ''
954 };
955 that.emit('irc_' + msg.command.toUpperCase(), msg);
07e97a5a
JA
956 if (that.listeners('irc_' + msg.command.toUpperCase()).length < 1) {
957 kiwi.log("Unknown command (" + String(msg.command).toUpperCase() + ")");
bf371d45
JA
958 }
959 } else {
07e97a5a 960 kiwi.log("Malformed IRC line: " + data[i].replace(/^\r+|\r+$/, ''));
bf371d45
JA
961 }
962 }
fd779420
D
963 }
964 });
897abfc3 965
bf371d45
JA
966 if (callback) {
967 ircSocket.on('connect', callback);
968 }
897abfc3 969
bf371d45 970 ircSocket.on('end', function () {
d62bbe08 971 that.connected = false;
bf371d45
JA
972 that.emit('disconnect', false);
973 });
897abfc3 974
bf371d45 975 ircSocket.on('close', function (had_error) {
d62bbe08 976 that.connected = false;
bf371d45
JA
977 that.emit('disconnect', had_error);
978 });
897abfc3 979
bf371d45
JA
980 ircSocket.on('timeout', function () {
981 ircSocket.destroy();
d62bbe08 982 that.connected = false;
897abfc3 983 that.emit('error', {message: 'Connection timed out'});
bf371d45 984 });
f52d8543 985
1b2bb94d
JA
986 ircSocket.on('drain', function () {
987 that.emit('drain');
988 });
989
bf371d45
JA
990 this.write = function (data, encoding, callback) {
991 ircSocket.write(data, encoding, callback);
992 };
897abfc3 993
07e97a5a 994 this.end = function (data, encoding, callback) {
d62bbe08 995 that.connected = false;
07e97a5a
JA
996 ircSocket.end(data, encoding, callback);
997 };
998
999 this.destroySoon = function () {
1000 ircSocket.destroySoon();
1001 };
897abfc3 1002
55c7f2af 1003 bound_events = kiwi.bindIRCCommands(this, websocket);
f52d8543
JA
1004
1005 this.rebindIRCCommands = function () {
55c7f2af 1006 _.each(bound_events, function (event) {
f4492289 1007 that.removeListener(event.command, event.listener);
f52d8543 1008 });
55c7f2af 1009 bound_events = kiwi.bindIRCCommands(that, websocket);
f52d8543 1010 };
af979742
JA
1011
1012 that.on('error', console.log);
1013
fd779420
D
1014};
1015
1016
1017
1018this.websocketMessage = function (websocket, msg, callback) {
312d2d7c 1019 var args, obj, channels, keys;
15c6dc28 1020 //try {
1b2bb94d
JA
1021 if ((callback) && (typeof (callback) !== 'function')) {
1022 callback = null;
1023 }
15c6dc28
D
1024 try {
1025 msg.data = JSON.parse(msg.data);
1026 } catch (e) {
1027 kiwi.log('[app.websocketMessage] JSON parsing error ' + msg.data);
1028 return;
1029 }
fd779420
D
1030 args = msg.data.args;
1031 switch (msg.data.method) {
312d2d7c 1032 case 'privmsg':
fd779420
D
1033 if ((args.target) && (args.msg)) {
1034 obj = kiwi.kiwi_mod.run('msgsend', args, {websocket: websocket});
1035 if (obj !== null) {
1b2bb94d 1036 websocket.sendServerLine('PRIVMSG ' + args.target + ' :' + args.msg, callback);
fd779420
D
1037 }
1038 }
1039 break;
312d2d7c
JA
1040 case 'ctcp':
1041 if ((args.target) && (args.type)) {
1042 if (args.request) {
1b2bb94d 1043 websocket.sendServerLine('PRIVMSG ' + args.target + ' :' + String.fromCharCode(1) + args.type.toUpperCase() + ' ' + args.params + String.fromCharCode(1), callback);
312d2d7c 1044 } else {
1b2bb94d 1045 websocket.sendServerLine('NOTICE ' + args.target + ' :' + String.fromCharCode(1) + args.type.toUpperCase() + ' ' + args.params + String.fromCharCode(1), callback);
312d2d7c 1046 }
abb46d2d
D
1047 }
1048 break;
1049
fd779420 1050 case 'raw':
1b2bb94d 1051 websocket.sendServerLine(args.data, callback);
fd779420 1052 break;
312d2d7c 1053
fd779420 1054 case 'join':
312d2d7c
JA
1055 if (args.channel) {
1056 channels = args.channel.split(",");
1057 keys = (args.key) ? args.key.split(",") : [];
1058 _.each(channels, function (chan, index) {
1b2bb94d 1059 websocket.sendServerLine('JOIN ' + chan + ' ' + (keys[index] || ''), callback);
312d2d7c
JA
1060 });
1061 }
1062 break;
1063
1064 case 'part':
fd779420
D
1065 if (args.channel) {
1066 _.each(args.channel.split(","), function (chan) {
1b2bb94d 1067 websocket.sendServerLine('PART ' + chan, callback);
fd779420
D
1068 });
1069 }
1070 break;
312d2d7c 1071
fd779420
D
1072 case 'topic':
1073 if (args.channel) {
312d2d7c 1074 if (args.topic) {
1b2bb94d 1075 websocket.sendServerLine('TOPIC ' + args.channel + ' :' + args.topic, callback);
312d2d7c 1076 } else {
1b2bb94d 1077 websocket.sendServerLine('TOPIC ' + args.channel, callback);
312d2d7c 1078 }
fd779420
D
1079 }
1080 break;
312d2d7c
JA
1081
1082 case 'kick':
1083 if ((args.channel) && (args.nick)) {
1b2bb94d 1084 websocket.sendServerLine('KICK ' + args.channel + ' ' + args.nick + ':' + args.reason, callback);
fd779420
D
1085 }
1086 break;
312d2d7c 1087
fd779420 1088 case 'quit':
07e97a5a 1089 websocket.ircConnection.end('QUIT :' + args.message + '\r\n');
fd779420 1090 websocket.sentQUIT = true;
07e97a5a 1091 websocket.ircConnection.destroySoon();
fd779420
D
1092 websocket.disconnect();
1093 break;
312d2d7c 1094
fd779420
D
1095 case 'notice':
1096 if ((args.target) && (args.msg)) {
1b2bb94d 1097 websocket.sendServerLine('NOTICE ' + args.target + ' :' + args.msg, callback);
fd779420
D
1098 }
1099 break;
312d2d7c
JA
1100
1101 case 'mode':
1102 if ((args.target) && (args.mode)) {
1b2bb94d 1103 websocket.sendServerLine('MODE ' + args.target + ' ' + args.mode + ' ' + args.params, callback);
312d2d7c
JA
1104 }
1105 break;
1106
1107 case 'nick':
1108 if (args.nick) {
1b2bb94d 1109 websocket.sendServerLine('NICK ' + args.nick, callback);
312d2d7c
JA
1110 }
1111 break;
1112
5db5955b
JA
1113 case 'kiwi':
1114 if ((args.target) && (args.data)) {
1b2bb94d 1115 websocket.sendServerLine('PRIVMSG ' + args.target + ': ' + String.fromCharCode(1) + 'KIWI ' + args.data + String.fromCharCode(1), callback);
5db5955b
JA
1116 }
1117 break;
fd779420
D
1118 default:
1119 }
15c6dc28
D
1120 //} catch (e) {
1121 // kiwi.log("Caught error (app.websocketMessage): " + e);
1122 //}
fd779420
D
1123};
1124
1125
1126
1127this.websocketDisconnect = function (websocket) {
1fce4b40 1128 var con;
fd779420 1129
d62bbe08 1130 if ((!websocket.sentQUIT) && (websocket.ircConnection.connected)) {
fd779420 1131 try {
d62bbe08 1132 websocket.ircConnection.end('QUIT :' + kiwi.config.quit_message + '\r\n');
fd779420 1133 websocket.sentQUIT = true;
d62bbe08 1134 websocket.ircConnection.destroySoon();
fd779420
D
1135 } catch (e) {
1136 }
1137 }
1138 con = kiwi.connections[websocket.kiwi.address];
1139 con.count -= 1;
1140 con.sockets = _.reject(con.sockets, function (sock) {
1141 return sock === websocket;
1142 });
1143};
1144
1145
1146
1147
1148
1149
1150this.rehash = function () {
1151 var changes, i,
1152 reload_config = kiwi.loadConfig();
1153
1154 // If loading the new config errored out, dont attempt any changes
1155 if (reload_config === false) {
1156 return false;
1157 }
8343584e 1158
fd779420
D
1159 // We just want the settings that have been changed
1160 changes = reload_config[1];
1161
1162 if (Object.keys(changes).length !== 0) {
9970e1d7 1163 kiwi.log('%s config changes: \n', Object.keys(changes).length, changes);
fd779420
D
1164 for (i in changes) {
1165 switch (i) {
7fd54a2d 1166 case 'servers':
d423ee18 1167 kiwi.websocketListen(kiwi.config.servers, kiwi.httpHandler);
9f6954dc 1168 delete changes.ports;
fd779420 1169 delete changes.bind_address;
fd779420
D
1170 delete changes.ssl_key;
1171 delete changes.ssl_cert;
1172 break;
1173 case 'user':
1174 case 'group':
1175 kiwi.changeUser();
1176 delete changes.user;
1177 delete changes.group;
1178 break;
1179 case 'module_dir':
1180 case 'modules':
1181 kiwi.kiwi_mod.loadModules(kiwi_root, kiwi.config);
1182 kiwi.kiwi_mod.printMods();
1183 delete changes.module_dir;
1184 delete changes.modules;
1185 break;
1186 }
1187 }
1188 }
1189
1190 // Also clear the kiwi.cached javascript and HTML
1191 if (kiwi.config.handle_http) {
1192 kiwi.cache = {alljs: '', html: []};
1193 }
1194
1195 return true;
1196};
1197
1198
1199
1200
1201
1202/*
1203 * KiwiIRC controlling via STDIN
1204 */
87a6abbe 1205this.manageControll = function (data) {
2fc64ec2
JA
1206 var parts = data.toString().trim().split(' '),
1207 connections_cnt = 0,
1208 i;
87a6abbe
D
1209 switch (parts[0]) {
1210 case 'rehash':
9970e1d7
D
1211 kiwi.log('Rehashing...');
1212 kiwi.log(kiwi.rehash() ? 'Rehash complete' : 'Rehash failed');
87a6abbe
D
1213 break;
1214
1fce4b40 1215 case 'recode':
9970e1d7
D
1216 kiwi.log('Recoding...');
1217 kiwi.log(kiwi.recode() ? 'Recode complete' : 'Recode failed');
87a6abbe
D
1218 break;
1219
1220 case 'mod':
1221 if (parts[1] === 'reload') {
9970e1d7
D
1222 if (!parts[2]) {
1223 kiwi.log('Usage: mod reload module_name');
1224 return;
1225 }
1226
1227 kiwi.log('Reloading module (' + parts[2] + ')..');
87a6abbe 1228 kiwi.kiwi_mod.reloadModule(parts[2]);
1b2bb94d
JA
1229 } else if (parts[1] === 'list') {
1230 kiwi.kiwi_mod.printMods();
87a6abbe
D
1231 }
1232 break;
4f06269b 1233
87a6abbe
D
1234 case 'cache':
1235 if (parts[1] === 'clear') {
1236 kiwi.cache.html = {};
604c5174 1237 kiwi.cache.alljs = '';
9970e1d7 1238 kiwi.log('HTML cache cleared');
87a6abbe
D
1239 }
1240 break;
1241
1242 case 'status':
2fc64ec2 1243 for (i in kiwi.connections) {
87a6abbe
D
1244 connections_cnt = connections_cnt + parseInt(kiwi.connections[i].count, 10);
1245 }
9970e1d7 1246 kiwi.log(connections_cnt.toString() + ' connected clients');
87a6abbe 1247 break;
c89b9fdf 1248
87a6abbe 1249 default:
9970e1d7 1250 kiwi.log('Unknown command \'' + parts[0] + '\'');
87a6abbe 1251 }
2fc64ec2 1252};