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