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