1 /*jslint continue: true, forin: true, regexp: true, confusion: true, undef: false, node: true, sloppy: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */
3 var tls
= require('tls'),
5 http
= require('http'),
6 https
= require('https'),
10 ws
= require('socket.io'),
11 _
= require('./lib/underscore.min.js'),
12 starttls
= require('./lib/starttls.js');
14 // Libraries may need to know kiwi.js path as __dirname
15 // only gives that librarys path. Set it here for usage later.
16 var kiwi_root
= __dirname
;
20 * Find a config file in the following order:
21 * - /etc/kiwi/config.json
25 config_filename
= 'config.json',
26 config_dirs
= ['/etc/kiwiirc/', __dirname
+ '/'];
28 var loadConfig = function () {
32 var found_config
= false;
33 for (i
in config_dirs
) {
35 if (fs
.lstatSync(config_dirs
[i
] + config_filename
).isDirectory() === false) {
37 nconf
= JSON
.parse(fs
.readFileSync(config_dirs
[i
] + config_filename
, 'ascii'));
39 // If this has changed from the previous config, mark it as changed
40 if (!_
.isEqual(config
[j
], nconf
[j
])) {
47 console
.log('Loaded config file ' + config_dirs
[i
] + config_filename
);
52 case 'ENOENT': // No file/dir
55 console
.log('An error occured parsing the config file ' + config_dirs
[i
] + config_filename
+ ': ' + e
.message
);
61 if (Object
.keys(config
).length
=== 0) {
62 if (!found_config
) console
.log('Couldn\'t find a config file!');
65 return [nconf
, cconf
];
68 if (!loadConfig()) process
.exit(0);
72 * Load the modules as set in the config and print them out
74 var kiwi_mod
= require('./lib/kiwi_mod.js');
75 kiwi_mod
.loadModules(kiwi_root
, config
);
82 * Some process changes
84 process
.title
= 'kiwiirc';
85 var changeUser = function () {
86 if (typeof config
.group
!== 'undefined' && config
.group
!== '') {
88 process
.setgid(config
.group
);
90 console
.log('Failed to set gid: ' + err
);
95 if (typeof config
.user
!== 'undefined' && config
.user
!== '') {
97 process
.setuid(config
.user
);
99 console
.log('Failed to set uid: ' + e
);
109 * And now KiwiIRC, the server :)
116 RPL_WHOISUSER
: '311',
117 RPL_WHOISSERVER
: '312',
118 RPL_WHOISOPERATOR
: '313',
119 RPL_WHOISIDLE
: '317',
120 RPL_ENDOFWHOIS
: '318',
121 RPL_WHOISCHANNELS
: '319',
124 RPL_NAMEREPLY
: '353',
125 RPL_ENDOFNAMES
: '366',
127 RPL_WHOISMODES
: '379',
128 ERR_NOSUCHNICK
: '401',
129 ERR_CANNOTSENDTOCHAN
: '404',
130 ERR_TOOMANYCHANNELS
: '405',
131 ERR_NICKNAMEINUSE
: '433',
132 ERR_USERNOTINCHANNEL
: '441',
133 ERR_NOTONCHANNEL
: '442',
134 ERR_LINKCHANNEL
: '470',
135 ERR_CHANNELISFULL
: '471',
136 ERR_INVITEONLYCHAN
: '473',
137 ERR_BANNEDFROMCHAN
: '474',
138 ERR_BADCHANNELKEY
: '475',
139 ERR_CHANOPRIVSNEEDED
: '482',
146 var parseIRCMessage = function (websocket
, ircSocket
, data
) {
147 /*global ircSocketDataHandler */
148 var msg
, regex
, opts
, options
, opt
, i
, j
, matches
, nick
, users
, chan
, channel
, params
, prefix
, prefixes
, nicklist
, caps
, rtn
, obj
;
149 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;
150 msg
= regex
.exec(data
);
156 hostname
: msg
[4] || '',
158 params
: msg
[6] || '',
159 trailing
: (msg
[7]) ? msg
[7].trim() : ''
161 switch (msg
.command
.toUpperCase()) {
163 websocket
.sendServerLine('PONG ' + msg
.trailing
);
165 case ircNumerics
.RPL_WELCOME
:
166 if (ircSocket
.IRC
.CAP
.negotiating
) {
167 ircSocket
.IRC
.CAP
.negotiating
= false;
168 ircSocket
.IRC
.CAP
.enabled
= [];
169 ircSocket
.IRC
.CAP
.requested
= [];
170 ircSocket
.IRC
.registered
= true;
172 websocket
.sendClientEvent('connect', {connected
: true, host
: null});
174 case ircNumerics
.RPL_ISUPPORT
:
175 opts
= msg
.params
.split(" ");
177 for (i
= 0; i
< opts
.length
; i
++) {
178 opt
= opts
[i
].split("=", 2);
179 opt
[0] = opt
[0].toUpperCase();
180 ircSocket
.IRC
.options
[opt
[0]] = opt
[1] || true;
181 if (_
.include(['NETWORK', 'PREFIX', 'CHANTYPES'], opt
[0])) {
182 if (opt
[0] === 'PREFIX') {
183 regex
= /\(([^)]*)\)(.*)/;
184 matches
= regex
.exec(opt
[1]);
185 if ((matches
) && (matches
.length
=== 3)) {
186 ircSocket
.IRC
.options
[opt
[0]] = [];
187 for (j
= 0; j
< matches
[2].length
; j
++) {
188 //ircSocket.IRC.options[opt[0]][matches[2].charAt(j)] = matches[1].charAt(j);
189 ircSocket
.IRC
.options
[opt
[0]].push({symbol
: matches
[2].charAt(j
), mode
: matches
[1].charAt(j
)});
190 //console.log({symbol: matches[2].charAt(j), mode: matches[1].charAt(j)});
192 console
.log(ircSocket
.IRC
.options
);
197 websocket
.sendClientEvent('options', {server
: '', "options": ircSocket
.IRC
.options
});
199 case ircNumerics
.RPL_WHOISUSER
:
200 case ircNumerics
.RPL_WHOISSERVER
:
201 case ircNumerics
.RPL_WHOISOPERATOR
:
202 case ircNumerics
.RPL_ENDOFWHOIS
:
203 case ircNumerics
.RPL_WHOISCHANNELS
:
204 case ircNumerics
.RPL_WHOISMODES
:
205 websocket
.sendClientEvent('whois', {server
: '', nick
: msg
.params
.split(" ", 3)[1], "msg": msg
.trailing
});
207 case ircNumerics
.RPL_WHOISIDLE
:
208 params
= msg
.params
.split(" ", 4);
209 rtn
= {server
: '', nick
: params
[1], idle
: params
[2]};
211 rtn
.logon
= params
[3];
213 websocket
.sendClientEvent('whois', rtn
);
215 case ircNumerics
.RPL_MOTD
:
216 websocket
.sendClientEvent('motd', {server
: '', "msg": msg
.trailing
});
218 case ircNumerics
.RPL_NAMEREPLY
:
219 params
= msg
.params
.split(" ");
222 users
= msg
.trailing
.split(" ");
223 prefixes
= _
.values(ircSocket
.IRC
.options
.PREFIX
);
226 _
.each(users
, function (user
) {
227 if (_
.include(prefix
, user
.charAt(0))) {
228 prefix
= user
.charAt(0);
229 user
= user
.substring(1);
230 nicklist
[user
] = prefix
;
235 websocket
.sendClientEvent('userlist', {server
: '', 'users': nicklist
, channel
: chan
});
241 websocket
.sendClientEvent('userlist', {server
: '', "users": nicklist
, channel
: chan
});
246 case ircNumerics
.RPL_ENDOFNAMES
:
247 websocket
.sendClientEvent('userlist_end', {server
: '', channel
: msg
.params
.split(" ")[1]});
249 case ircNumerics
.ERR_LINKCHANNEL
:
250 params
= msg
.params
.split(" ");
251 websocket
.sendClientEvent('channel_redirect', {from: params
[1], to
: params
[2]});
253 case ircNumerics
.ERR_NOSUCHNICK
:
254 websocket
.sendClientEvent('irc_error', {error
: 'no_such_nick', nick
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
257 // Some BNC's send malformed JOIN causing the channel to be as a
258 // parameter instead of trailing.
259 if (typeof msg
.trailing
=== 'string' && msg
.trailing
!== '') {
260 channel
= msg
.trailing
;
261 } else if (typeof msg
.params
=== 'string' && msg
.params
!== '') {
262 channel
= msg
.params
;
265 websocket
.sendClientEvent('join', {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: channel
});
266 if (msg
.nick
=== ircSocket
.IRC
.nick
) {
267 websocket
.sendServerLine('NAMES ' + msg
.trailing
);
271 websocket
.sendClientEvent('part', {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: msg
.params
.trim(), message
: msg
.trailing
});
274 params
= msg
.params
.split(" ");
275 websocket
.sendClientEvent('kick', {kicked
: params
[1], nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: params
[0].trim(), message
: msg
.trailing
});
278 websocket
.sendClientEvent('quit', {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, message
: msg
.trailing
});
281 if ((msg
.trailing
.charAt(0) === String
.fromCharCode(1)) && (msg
.trailing
.charAt(msg
.trailing
.length
- 1) === String
.fromCharCode(1))) {
282 // It's a CTCP response
283 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)});
285 websocket
.sendClientEvent('notice', {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: msg
.params
.trim(), msg
: msg
.trailing
});
289 websocket
.sendClientEvent('nick', {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, newnick
: msg
.trailing
});
292 obj
= {nick
: msg
.nick
, channel
: msg
.params
, topic
: msg
.trailing
};
293 websocket
.sendClientEvent('topic', obj
);
295 case ircNumerics
.RPL_TOPIC
:
296 obj
= {nick
: '', channel
: msg
.params
.split(" ")[1], topic
: msg
.trailing
};
297 websocket
.sendClientEvent('topic', obj
);
299 case ircNumerics
.RPL_NOTOPIC
:
300 obj
= {nick
: '', channel
: msg
.params
.split(" ")[1], topic
: ''};
301 websocket
.sendClientEvent('topic', obj
);
304 opts
= msg
.params
.split(" ");
305 params
= {nick
: msg
.nick
};
306 switch (opts
.length
) {
308 params
.effected_nick
= opts
[0];
309 params
.mode
= msg
.trailing
;
312 params
.channel
= opts
[0];
313 params
.mode
= opts
[1];
316 params
.channel
= opts
[0];
317 params
.mode
= opts
[1];
318 params
.effected_nick
= opts
[2];
321 websocket
.sendClientEvent('mode', params
);
324 if ((msg
.trailing
.charAt(0) === String
.fromCharCode(1)) && (msg
.trailing
.charAt(msg
.trailing
.length
- 1) === String
.fromCharCode(1))) {
325 // It's a CTCP request
326 if (msg
.trailing
.substr(1, 6) === 'ACTION') {
327 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)});
328 } else if (msg
.trailing
.substr(1, 7) === 'VERSION') {
329 ircSocket
.write('NOTICE ' + msg
.nick
+ ' :' + String
.fromCharCode(1) + 'VERSION KiwiIRC' + String
.fromCharCode(1) + '\r\n');
331 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)});
334 obj
= {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: msg
.params
.trim(), msg
: msg
.trailing
};
335 websocket
.sendClientEvent('msg', obj
);
339 caps
= config
.cap_options
;
340 options
= msg
.trailing
.split(" ");
341 switch (_
.last(msg
.params
.split(" "))) {
344 _
.each(_
.intersect(caps
, options
), function (cap
) {
349 ircSocket
.IRC
.CAP
.requested
.push(cap
);
351 if (opts
.length
> 0) {
352 websocket
.sendServerLine('CAP REQ :' + opts
);
354 websocket
.sendServerLine('CAP END');
357 /*if (_.include(options, 'tls')) {
358 websocket.sendServerLine('STARTTLS');
359 ircSocket.IRC.CAP.requested.push('tls');
363 _
.each(options
, function (cap
) {
364 ircSocket
.IRC
.CAP
.enabled
.push(cap
);
366 if (_
.last(msg
.params
.split(" ")) !== '*') {
367 ircSocket
.IRC
.CAP
.requested
= [];
368 ircSocket
.IRC
.CAP
.negotiating
= false;
369 websocket
.sendServerLine('CAP END');
373 ircSocket
.IRC
.CAP
.requested
= [];
374 ircSocket
.IRC
.CAP
.negotiating
= false;
375 websocket
.sendServerLine('CAP END');
379 /*case ircNumerics.RPL_STARTTLS:
382 listeners = ircSocket.listeners('data');
383 ircSocket.removeAllListeners('data');
384 ssl_socket = starttls(ircSocket, {}, function () {
385 ssl_socket.on("data", function (data) {
386 ircSocketDataHandler(data, websocket, ssl_socket);
388 ircSocket = ssl_socket;
390 _.each(listeners, function (listener) {
391 ircSocket.addListener('data', listener);
394 //console.log(ircSocket);
399 case ircNumerics
.ERR_CANNOTSENDTOCHAN
:
400 websocket
.sendClientEvent('irc_error', {error
: 'cannot_send_to_chan', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
402 case ircNumerics
.ERR_TOOMANYCHANNELS
:
403 websocket
.sendClientEvent('irc_error', {error
: 'too_many_channels', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
405 case ircNumerics
.ERR_USERNOTINCHANNEL
:
406 params
= msg
.params
.split(" ");
407 websocket
.sendClientEvent('irc_error', {error
: 'user_not_in_channel', nick
: params
[0], channel
: params
[1], reason
: msg
.trainling
});
409 case ircNumerics
.ERR_NOTONCHANNEL
:
410 websocket
.sendClientEvent('irc_error', {error
: 'not_on_channel', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
412 case ircNumerics
.ERR_CHANNELISFULL
:
413 websocket
.sendClientEvent('irc_error', {error
: 'channel_is_full', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
415 case ircNumerics
.ERR_INVITEONLYCHAN
:
416 websocket
.sendClientEvent('irc_error', {error
: 'invite_only_channel', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
418 case ircNumerics
.ERR_BANNEDFROMCHAN
:
419 websocket
.sendClientEvent('irc_error', {error
: 'banned_from_channel', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
421 case ircNumerics
.ERR_BADCHANNELKEY
:
422 websocket
.sendClientEvent('irc_error', {error
: 'bad_channel_key', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
424 case ircNumerics
.ERR_CHANOPRIVSNEEDED
:
425 websocket
.sendClientEvent('irc_error', {error
: 'chanop_privs_needed', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
427 case ircNumerics
.ERR_NICKNAMEINUSE
:
428 websocket
.sendClientEvent('irc_error', {error
: 'nickname_in_use', nick
: _
.last(msg
.params
.split(" ")), reason
: msg
.trailing
});
432 websocket
.sendClientEvent('irc_error', {error
: 'error', reason
: msg
.trailing
});
433 websocket
.disconnect();
436 console
.log("Unknown command (" + String(msg
.command
).toUpperCase() + ")");
439 console
.log("Malformed IRC line");
445 * NOTE: Some IRC servers or BNC's out there incorrectly use
446 * only \n as a line splitter.
448 var ircSocketDataHandler = function (data
, websocket
, ircSocket
) {
450 if ((ircSocket
.holdLast
) && (ircSocket
.held
!== '')) {
451 data
= ircSocket
.held
+ data
;
452 ircSocket
.holdLast
= false;
455 if (data
.substr(-1) !== '\n') {
456 ircSocket
.holdLast
= true;
458 data
= data
.split("\n");
459 for (i
= 0; i
< data
.length
; i
++) {
461 if ((ircSocket
.holdLast
) && (i
=== data
.length
- 1)) {
462 ircSocket
.held
= data
[i
];
466 // We have a complete line of data, parse it!
467 parseIRCMessage(websocket
, ircSocket
, data
[i
].replace(/^\r+|\r+$/, ''));
472 if (config
.handle_http
) {
473 var fileServer
= new (require('node-static').Server
)(__dirname
+ config
.public_http
);
474 var jade
= require('jade');
477 var httpHandler = function (request
, response
) {
478 var uri
, subs
, useragent
, agent
, server_set
, server
, nick
, debug
, touchscreen
;
479 console
.log(request
.url
);
480 if (config
.handle_http
) {
481 uri
= url
.parse(request
.url
);
482 subs
= uri
.pathname
.substr(0, 4);
483 if ((subs
=== '/js/') || (subs
=== '/css') || (subs
=== '/img')) {
484 request
.addListener('end', function () {
485 fileServer
.serve(request
, response
);
487 } else if (uri
.pathname
=== '/') {
488 useragent
= (response
.headers
) ? response
.headers
['user-agent'] : '';
489 if (useragent
.indexOf('android') !== -1) {
492 } else if (useragent
.indexOf('iphone') !== -1) {
495 } else if (useragent
.indexOf('ipad') !== -1) {
498 } else if (useragent
.indexOf('ipod') !== -1) {
506 server_set
= (uri
.query
.server
!== '');
507 server
= uri
.query
.server
|| 'irc.anonnet.org';
508 nick
= uri
.query
.nick
|| '';
509 debug
= (uri
.query
.debug
!== '');
511 server
= 'irc.anonnet.org';
514 response
.setHeader('Connection', 'close');
515 response
.setHeader('X-Generated-By', 'KiwiIRC');
516 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
) {
518 response
.write(html
);
520 response
.statusCode
= 500;
524 } else if (uri
.pathname
.substr(0, 10) === '/socket.io') {
527 response
.statusCode
= 404;
533 var connections
= {};
536 var websocketListen = function (port
, host
, handler
, secure
, key
, cert
) {
541 httpServer
= https
.createServer({key
: fs
.readFileSync(__dirname
+ '/' + key
), cert
: fs
.readFileSync(__dirname
+ '/' + cert
)}, handler
);
542 io
= ws
.listen(httpServer
, {secure
: true});
543 httpServer
.listen(port
, host
);
545 httpServer
= http
.createServer(handler
);
546 io
= ws
.listen(httpServer
, {secure
: false});
547 httpServer
.listen(port
, host
);
549 //console.log(httpServer);
550 io
.set('log level', 1);
551 io
.of('/kiwi').authorization(function (handshakeData
, callback
) {
552 var address
= handshakeData
.address
.address
;
553 handshakeData
.kiwi
= {hostname
: null};
554 dns
.reverse(address
, function (err
, domains
) {
555 console
.log(domains
);
557 handshakeData
.kiwi
.hostname
= err
;
559 handshakeData
.kiwi
.hostname
= _
.first(domains
);
562 if (typeof connections
[address
] === 'undefined') {
563 connections
[address
] = {count
: 0, sockets
: []};
565 callback(null, true);
566 }).on('connection', function (websocket
) {
568 websocket
.kiwi
= {address
: websocket
.handshake
.address
.address
, hostname
: websocket
.handshake
.kiwi
.hostname
};
569 con
= connections
[websocket
.kiwi
.address
];
570 if (con
.count
>= config
.max_client_conns
) {
571 websocket
.emit('too_many_connections');
572 websocket
.disconnect();
575 con
.sockets
.push(websocket
);
577 websocket
.sendClientEvent = function (event_name
, data
) {
578 kiwi_mod
.run(event_name
, data
, {websocket
: this});
579 data
.event
= event_name
;
580 websocket
.emit('message', data
);
583 websocket
.sendServerLine = function (data
, eol
) {
584 eol
= (typeof eol
=== 'undefined') ? '\r\n' : eol
;
585 websocket
.ircSocket
.write(data
+ eol
);
588 websocket
.on('irc connect', function (nick
, host
, port
, ssl
, callback
) {
589 var ircSocket
, login
;
590 //setup IRC connection
592 ircSocket
= net
.createConnection(port
, host
);
594 ircSocket
= tls
.connect(port
, host
);
596 ircSocket
.setEncoding('ascii');
597 ircSocket
.IRC
= {options
: {}, CAP
: {negotiating
: true, requested
: [], enabled
: []}, registered
: false};
598 ircSocket
.on('error', function (e
) {
599 if (ircSocket
.IRC
.registered
) {
600 websocket
.emit('disconnect');
602 websocket
.emit('error', e
.message
);
605 websocket
.ircSocket
= ircSocket
;
606 ircSocket
.holdLast
= false;
609 ircSocket
.on('data', function (data
) {
610 ircSocketDataHandler(data
, websocket
, ircSocket
);
613 ircSocket
.IRC
.nick
= nick
;
614 // Send the login data
615 if ((config
.webirc
) && (config
.webirc_pass
[host
])) {
616 if ((websocket
.kiwi
.hostname
=== null) || (typeof websocket
.kiwi
.hostname
=== Error
)) {
617 websocket
.sendServerLine('WEBIRC ' + config
.webirc_pass
[host
] + ' KiwiIRC ' + websocket
.kiwi
.address
+ ' ' + websocket
.kiwi
.address
);
619 websocket
.sendServerLine('WEBIRC ' + config
.webirc_pass
[host
] + ' KiwiIRC ' + websocket
.kiwi
.hostname
+ ' ' + websocket
.kiwi
.address
);
622 websocket
.sendServerLine('CAP LS');
623 websocket
.sendServerLine('NICK ' + nick
);
624 websocket
.sendServerLine('USER ' + nick
.replace(/[^0-9a-zA-Z\-_.]/, '') + '_kiwi 0 0 :' + nick
);
626 if ((callback
) && (typeof (callback
) === 'function')) {
630 websocket
.on('message', function (msg
, callback
) {
633 msg
.data
= JSON
.parse(msg
.data
);
634 args
= msg
.data
.args
;
635 switch (msg
.data
.method
) {
637 if ((args
.target
) && (args
.msg
)) {
638 obj
= kiwi_mod
.run('msgsend', args
, {websocket
: websocket
});
640 websocket
.sendServerLine('PRIVMSG ' + args
.target
+ ' :' + args
.msg
);
645 if ((args
.target
) && (args
.msg
)) {
646 websocket
.sendServerLine('PRIVMSG ' + args
.target
+ ' :\ 1' + String
.fromCharCode(1) + 'ACTION ' + args
.msg
+ String
.fromCharCode(1));
650 websocket
.sendServerLine(args
.data
);
654 _
.each(args
.channel
.split(","), function (chan
) {
655 websocket
.sendServerLine('JOIN ' + chan
);
661 websocket
.sendServerLine('TOPIC ' + args
.channel
+ ' :' + args
.topic
);
665 websocket
.ircSocket
.end('QUIT :' + args
.message
+ '\r\n');
666 websocket
.sentQUIT
= true;
667 websocket
.ircSocket
.destroySoon();
668 websocket
.disconnect();
671 if ((args
.target
) && (args
.msg
)) {
672 websocket
.sendServerLine('NOTICE ' + args
.target
+ ' :' + args
.msg
);
677 if ((callback
) && (typeof (callback
) === 'function')) {
681 console
.log("Caught error: " + e
);
686 websocket
.on('disconnect', function () {
687 if ((!websocket
.sentQUIT
) && (websocket
.ircSocket
)) {
689 websocket
.ircSocket
.end('QUIT :' + config
.quit_message
+ '\r\n');
690 websocket
.sentQUIT
= true;
691 websocket
.ircSocket
.destroySoon();
695 con
= connections
[websocket
.kiwi
.address
];
697 con
.sockets
= _
.reject(con
.sockets
, function (sock
) {
698 return sock
=== websocket
;
705 websocketListen(config
.port
, config
.bind_address
, httpHandler
, config
.listen_ssl
, config
.ssl_key
, config
.ssl_cert
);
707 // Now we're listening on the network, set our UID/GIDs if required
713 var rehash = function () {
715 var reload_config
= loadConfig();
717 // If loading the new config errored out, dont attempt any changes
718 if (reload_config
=== false) return false;
720 // We just want the settings that have been changed
721 changes
= reload_config
[1];
723 if (Object
.keys(changes
).length
!== 0) {
724 console
.log('%s config changes: \n', Object
.keys(changes
).length
, changes
);
732 websocketListen(config
.port
, config
.bind_address
, httpHandler
, config
.listen_ssl
, config
.ssl_key
, config
.ssl_cert
);
733 delete changes
['port'];
734 delete changes
['bind_address'];
735 delete changes
['listen_ssl'];
736 delete changes
['ssl_key'];
737 delete changes
['ssl_cert'];
742 delete changes
['user'];
743 delete changes
['group'];
747 kiwi_mod
.loadModules(kiwi_root
, config
);
748 kiwi_mod
.printMods();
749 delete changes
['module_dir'];
750 delete changes
['modules'];
762 * KiwiIRC controlling via STDIN
764 process
.stdin
.resume();
765 process
.stdin
.on('data', function (chunk
) {
767 command
= chunk
.toString().trim();
770 console
.log('Rehashing...');
771 console
.log(rehash() ? 'Rehash complete' : 'Rehash failed');
775 console
.log('Unknown command \'' + command
+ '\'');