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