1 /*jslint sloppy: true, continue: true, forin: true, regexp: true, undef: false, node: true, nomen: true, plusplus: true, maxerr: 50, indent: 4 */
19 this.init = function (objs
) {
32 starttls
= objs
.starttls
;
33 kiwi
= require('./kiwi.js');
42 * Some process changes
44 this.setTitle = function () {
45 process
.title
= 'kiwiirc';
48 this.changeUser = function () {
49 if (typeof kiwi
.config
.group
!== 'undefined' && kiwi
.config
.group
!== '') {
51 process
.setgid(kiwi
.config
.group
);
53 console
.log('Failed to set gid: ' + err
);
58 if (typeof kiwi
.config
.user
!== 'undefined' && kiwi
.config
.user
!== '') {
60 process
.setuid(kiwi
.config
.user
);
62 console
.log('Failed to set uid: ' + e
);
81 RPL_WHOISSERVER
: '312',
82 RPL_WHOISOPERATOR
: '313',
84 RPL_ENDOFWHOIS
: '318',
85 RPL_WHOISCHANNELS
: '319',
92 RPL_ENDOFNAMES
: '366',
94 RPL_ENDOFBANLIST
: '368',
96 RPL_WHOISMODES
: '379',
97 ERR_NOSUCHNICK
: '401',
98 ERR_CANNOTSENDTOCHAN
: '404',
99 ERR_TOOMANYCHANNELS
: '405',
100 ERR_NICKNAMEINUSE
: '433',
101 ERR_USERNOTINCHANNEL
: '441',
102 ERR_NOTONCHANNEL
: '442',
103 ERR_NOTREGISTERED
: '451',
104 ERR_LINKCHANNEL
: '470',
105 ERR_CHANNELISFULL
: '471',
106 ERR_INVITEONLYCHAN
: '473',
107 ERR_BANNEDFROMCHAN
: '474',
108 ERR_BADCHANNELKEY
: '475',
109 ERR_CHANOPRIVSNEEDED
: '482',
115 this.parseIRCMessage = function (websocket
, ircSocket
, data
) {
116 /*global ircSocketDataHandler */
117 var msg
, regex
, opts
, options
, opt
, i
, j
, matches
, nick
, users
, chan
, channel
,
118 params
, nicklist
, caps
, rtn
, obj
, tmp
, namespace, whois_end
= false;
119 //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;
120 //regex = /^(?::(\S+) )?(\S+)(?: (?!:)(.+?))?(?: :(.+))?$/i;
121 regex
= /^(?::(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@?([a-z0-9\.\-:\/]+)?) )?(\S+)(?: (?!:)(.+?))?(?: :(.+))?$/i;
123 msg
= regex
.exec(data
);
129 hostname
: msg
[4] || '',
131 params
: msg
[6] || '',
132 trailing
: (msg
[7]) ? msg
[7].trim() : ''
135 switch (msg
.command
.toUpperCase()) {
137 websocket
.sendServerLine('PONG ' + msg
.trailing
);
139 case ircNumerics
.RPL_WELCOME
:
140 if (ircSocket
.IRC
.CAP
.negotiating
) {
141 ircSocket
.IRC
.CAP
.negotiating
= false;
142 ircSocket
.IRC
.CAP
.enabled
= [];
143 ircSocket
.IRC
.CAP
.requested
= [];
144 ircSocket
.IRC
.registered
= true;
146 //regex = /([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@?([a-z0-9\.\-:\/]+)/i;
147 //matches = regex.exec(msg.trailing);
148 nick
= msg
.params
.split(' ')[0];
149 websocket
.sendClientEvent('connect', {connected
: true, host
: null, nick
: nick
});
151 case ircNumerics
.RPL_ISUPPORT
:
152 opts
= msg
.params
.split(" ");
154 for (i
= 0; i
< opts
.length
; i
++) {
155 opt
= opts
[i
].split("=", 2);
156 opt
[0] = opt
[0].toUpperCase();
157 ircSocket
.IRC
.options
[opt
[0]] = (typeof opt
[1] !== 'undefined') ? opt
[1] : true;
158 if (_
.include(['NETWORK', 'PREFIX', 'CHANTYPES', 'NAMESX'], opt
[0])) {
159 if (opt
[0] === 'PREFIX') {
160 regex
= /\(([^)]*)\)(.*)/;
161 matches
= regex
.exec(opt
[1]);
162 if ((matches
) && (matches
.length
=== 3)) {
163 ircSocket
.IRC
.options
[opt
[0]] = [];
164 for (j
= 0; j
< matches
[2].length
; j
++) {
165 //ircSocket.IRC.options[opt[0]][matches[2].charAt(j)] = matches[1].charAt(j);
166 ircSocket
.IRC
.options
[opt
[0]].push({symbol
: matches
[2].charAt(j
), mode
: matches
[1].charAt(j
)});
171 if (opt
[0] === 'NAMESX') {
172 websocket
.sendServerLine('PROTOCTL NAMESX');
177 websocket
.sendClientEvent('options', {server
: '', "options": ircSocket
.IRC
.options
});
180 case ircNumerics
.RPL_ENDOFWHOIS
:
182 case ircNumerics
.RPL_WHOISUSER
:
183 case ircNumerics
.RPL_WHOISSERVER
:
184 case ircNumerics
.RPL_WHOISOPERATOR
:
185 case ircNumerics
.RPL_WHOISCHANNELS
:
186 case ircNumerics
.RPL_WHOISMODES
:
187 websocket
.sendClientEvent('whois', {server
: '', nick
: msg
.params
.split(" ", 3)[1], "msg": msg
.trailing
, end
: whois_end
});
190 case ircNumerics
.RPL_LISTSTART
:
192 websocket
.sendClientEvent('list_start', {server
: ''});
193 websocket
.kiwi
.buffer
.list
= [];
196 case ircNumerics
.RPL_LISTEND
:
198 if (websocket
.kiwi
.buffer
.list
.length
> 0) {
199 websocket
.kiwi
.buffer
.list
= _
.sortBy(websocket
.kiwi
.buffer
.list
, function (channel
) {
200 return channel
.num_users
;
202 websocket
.sendClientEvent('list_channel', {chans
: websocket
.kiwi
.buffer
.list
});
203 websocket
.kiwi
.buffer
.list
= [];
205 websocket
.sendClientEvent('list_end', {server
: ''});
209 case ircNumerics
.RPL_LIST
:
211 var parts
, channel
, num_users
, modes
, topic
;
213 parts
= msg
.params
.split(' ');
215 num_users
= parts
[2];
216 topic
= msg
.trailing
;
218 //websocket.sendClientEvent('list_channel', {
219 websocket
.kiwi
.buffer
.list
.push({
224 num_users
: parseInt(num_users
, 10)
227 if (websocket
.kiwi
.buffer
.list
.length
> 200) {
228 websocket
.kiwi
.buffer
.list
= _
.sortBy(websocket
.kiwi
.buffer
.list
, function (channel
) {
229 return channel
.num_users
;
231 websocket
.sendClientEvent('list_channel', {chans
: websocket
.kiwi
.buffer
.list
});
232 websocket
.kiwi
.buffer
.list
= [];
238 case ircNumerics
.RPL_WHOISIDLE
:
239 params
= msg
.params
.split(" ", 4);
240 rtn
= {server
: '', nick
: params
[1], idle
: params
[2]};
242 rtn
.logon
= params
[3];
244 websocket
.sendClientEvent('whois', rtn
);
246 case ircNumerics
.RPL_MOTD
:
247 websocket
.sendClientEvent('motd', {server
: '', "msg": msg
.trailing
});
249 case ircNumerics
.RPL_NAMEREPLY
:
250 params
= msg
.params
.split(" ");
253 users
= msg
.trailing
.split(" ");
256 _
.each(users
, function (user
) {
257 var j
, k
, modes
= [];
258 for (j
= 0; j
< user
.length
; j
++) {
259 for (k
= 0; k
< ircSocket
.IRC
.options
.PREFIX
.length
; k
++) {
260 if (user
.charAt(j
) === ircSocket
.IRC
.options
.PREFIX
[k
].symbol
) {
261 modes
.push(ircSocket
.IRC
.options
.PREFIX
[k
].mode
);
265 nicklist
.push({nick
: user
, modes
: modes
});
267 websocket
.sendClientEvent('userlist', {server
: '', 'users': nicklist
, channel
: chan
});
273 websocket
.sendClientEvent('userlist', {server
: '', "users": nicklist
, channel
: chan
});
278 case ircNumerics
.RPL_ENDOFNAMES
:
279 websocket
.sendClientEvent('userlist_end', {server
: '', channel
: msg
.params
.split(" ")[1]});
281 case ircNumerics
.ERR_LINKCHANNEL
:
282 params
= msg
.params
.split(" ");
283 websocket
.sendClientEvent('channel_redirect', {from: params
[1], to
: params
[2]});
285 case ircNumerics
.ERR_NOSUCHNICK
:
286 websocket
.sendClientEvent('irc_error', {error
: 'no_such_nick', nick
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
288 case ircNumerics
.RPL_BANLIST
:
289 params
= msg
.params
.split(" ");
291 websocket
.sendClientEvent('banlist', {server
: '', channel
: params
[1], banned
: params
[2], banned_by
: params
[3], banned_at
: params
[4]});
293 case ircNumerics
.RPL_ENDOFBANLIST
:
294 websocket
.sendClientEvent('banlist_end', {server
: '', channel
: msg
.params
.split(" ")[1]});
297 // Some BNC's send malformed JOIN causing the channel to be as a
298 // parameter instead of trailing.
299 if (typeof msg
.trailing
=== 'string' && msg
.trailing
!== '') {
300 channel
= msg
.trailing
;
301 } else if (typeof msg
.params
=== 'string' && msg
.params
!== '') {
302 channel
= msg
.params
;
305 websocket
.sendClientEvent('join', {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: channel
});
306 if (msg
.nick
=== ircSocket
.IRC
.nick
) {
307 websocket
.sendServerLine('NAMES ' + msg
.trailing
);
311 websocket
.sendClientEvent('part', {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: msg
.params
.trim(), message
: msg
.trailing
});
314 params
= msg
.params
.split(" ");
315 websocket
.sendClientEvent('kick', {kicked
: params
[1], nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: params
[0].trim(), message
: msg
.trailing
});
318 websocket
.sendClientEvent('quit', {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, message
: msg
.trailing
});
321 if ((msg
.trailing
.charAt(0) === String
.fromCharCode(1)) && (msg
.trailing
.charAt(msg
.trailing
.length
- 1) === String
.fromCharCode(1))) {
322 // It's a CTCP response
323 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)});
325 websocket
.sendClientEvent('notice', {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, target
: msg
.params
.trim(), msg
: msg
.trailing
});
329 websocket
.sendClientEvent('nick', {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, newnick
: msg
.trailing
});
332 obj
= {nick
: msg
.nick
, channel
: msg
.params
, topic
: msg
.trailing
};
333 websocket
.sendClientEvent('topic', obj
);
335 case ircNumerics
.RPL_TOPIC
:
336 obj
= {nick
: '', channel
: msg
.params
.split(" ")[1], topic
: msg
.trailing
};
337 websocket
.sendClientEvent('topic', obj
);
339 case ircNumerics
.RPL_NOTOPIC
:
340 obj
= {nick
: '', channel
: msg
.params
.split(" ")[1], topic
: ''};
341 websocket
.sendClientEvent('topic', obj
);
344 opts
= msg
.params
.split(" ");
345 params
= {nick
: msg
.nick
};
346 switch (opts
.length
) {
348 params
.effected_nick
= opts
[0];
349 params
.mode
= msg
.trailing
;
352 params
.channel
= opts
[0];
353 params
.mode
= opts
[1];
356 params
.channel
= opts
[0];
357 params
.mode
= opts
[1];
358 params
.effected_nick
= opts
[2];
361 websocket
.sendClientEvent('mode', params
);
364 if ((msg
.trailing
.charAt(0) === String
.fromCharCode(1)) && (msg
.trailing
.charAt(msg
.trailing
.length
- 1) === String
.fromCharCode(1))) {
365 // It's a CTCP request
366 if (msg
.trailing
.substr(1, 6) === 'ACTION') {
367 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)});
368 } else if (msg
.trailing
.substr(1, 4) === 'KIWI') {
369 tmp
= msg
.trailing
.substr(6, msg
.trailing
.length
- 2);
370 namespace = tmp
.split(' ', 1)[0];
371 websocket
.sendClientEvent('kiwi', {namespace: namespace, data
: tmp
.substr(namespace.length
+ 1)});
373 } else if (msg
.trailing
.substr(1, 7) === 'VERSION') {
374 ircSocket
.write('NOTICE ' + msg
.nick
+ ' :' + String
.fromCharCode(1) + 'VERSION KiwiIRC' + String
.fromCharCode(1) + '\r\n');
376 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)});
379 obj
= {nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: msg
.params
.trim(), msg
: msg
.trailing
};
380 websocket
.sendClientEvent('msg', obj
);
384 caps
= kiwi
.config
.cap_options
;
385 options
= msg
.trailing
.split(" ");
386 switch (_
.last(msg
.params
.split(" "))) {
389 _
.each(_
.intersect(caps
, options
), function (cap
) {
394 ircSocket
.IRC
.CAP
.requested
.push(cap
);
396 if (opts
.length
> 0) {
397 websocket
.sendServerLine('CAP REQ :' + opts
);
399 websocket
.sendServerLine('CAP END');
402 /*if (_.include(options, 'tls')) {
403 websocket.sendServerLine('STARTTLS');
404 ircSocket.IRC.CAP.requested.push('tls');
408 _
.each(options
, function (cap
) {
409 ircSocket
.IRC
.CAP
.enabled
.push(cap
);
411 if (_
.last(msg
.params
.split(" ")) !== '*') {
412 ircSocket
.IRC
.CAP
.requested
= [];
413 ircSocket
.IRC
.CAP
.negotiating
= false;
414 websocket
.sendServerLine('CAP END');
418 ircSocket
.IRC
.CAP
.requested
= [];
419 ircSocket
.IRC
.CAP
.negotiating
= false;
420 websocket
.sendServerLine('CAP END');
424 /*case ircNumerics.RPL_STARTTLS:
427 listeners = ircSocket.listeners('data');
428 ircSocket.removeAllListeners('data');
429 ssl_socket = starttls(ircSocket, {}, function () {
430 ssl_socket.on("data", function (data) {
431 ircSocketDataHandler(data, websocket, ssl_socket);
433 ircSocket = ssl_socket;
435 _.each(listeners, function (listener) {
436 ircSocket.addListener('data', listener);
439 //console.log(ircSocket);
444 case ircNumerics
.ERR_CANNOTSENDTOCHAN
:
445 websocket
.sendClientEvent('irc_error', {error
: 'cannot_send_to_chan', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
447 case ircNumerics
.ERR_TOOMANYCHANNELS
:
448 websocket
.sendClientEvent('irc_error', {error
: 'too_many_channels', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
450 case ircNumerics
.ERR_USERNOTINCHANNEL
:
451 params
= msg
.params
.split(" ");
452 websocket
.sendClientEvent('irc_error', {error
: 'user_not_in_channel', nick
: params
[0], channel
: params
[1], reason
: msg
.trainling
});
454 case ircNumerics
.ERR_NOTONCHANNEL
:
455 websocket
.sendClientEvent('irc_error', {error
: 'not_on_channel', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
457 case ircNumerics
.ERR_CHANNELISFULL
:
458 websocket
.sendClientEvent('irc_error', {error
: 'channel_is_full', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
460 case ircNumerics
.ERR_INVITEONLYCHAN
:
461 websocket
.sendClientEvent('irc_error', {error
: 'invite_only_channel', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
463 case ircNumerics
.ERR_BANNEDFROMCHAN
:
464 websocket
.sendClientEvent('irc_error', {error
: 'banned_from_channel', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
466 case ircNumerics
.ERR_BADCHANNELKEY
:
467 websocket
.sendClientEvent('irc_error', {error
: 'bad_channel_key', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
469 case ircNumerics
.ERR_CHANOPRIVSNEEDED
:
470 websocket
.sendClientEvent('irc_error', {error
: 'chanop_privs_needed', channel
: msg
.params
.split(" ")[1], reason
: msg
.trailing
});
472 case ircNumerics
.ERR_NICKNAMEINUSE
:
473 websocket
.sendClientEvent('irc_error', {error
: 'nickname_in_use', nick
: _
.last(msg
.params
.split(" ")), reason
: msg
.trailing
});
477 websocket
.sendClientEvent('irc_error', {error
: 'error', reason
: msg
.trailing
});
478 websocket
.disconnect();
480 case ircNumerics
.ERR_NOTREGISTERED
:
481 if (ircSocket
.IRC
.registered
) {
482 console
.log('Kiwi thinks user is registered, but the IRC server thinks differently');
486 console
.log("Unknown command (" + String(msg
.command
).toUpperCase() + ")");
489 console
.log("Malformed IRC line: " + data
);
499 * NOTE: Some IRC servers or BNC's out there incorrectly use
500 * only \n as a line splitter.
502 this.ircSocketDataHandler = function (data
, websocket
, ircSocket
) {
504 if ((ircSocket
.holdLast
) && (ircSocket
.held
!== '')) {
505 data
= ircSocket
.held
+ data
;
506 ircSocket
.holdLast
= false;
509 if (data
.substr(-1) !== '\n') {
510 ircSocket
.holdLast
= true;
512 data
= data
.split("\n");
513 for (i
= 0; i
< data
.length
; i
++) {
515 if ((ircSocket
.holdLast
) && (i
=== data
.length
- 1)) {
516 ircSocket
.held
= data
[i
];
520 // We have a complete line of data, parse it!
521 kiwi
.parseIRCMessage(websocket
, ircSocket
, data
[i
].replace(/^\r+|\r+$/, ''));
530 this.httpHandler = function (request
, response
) {
531 var uri
, uri_parts
, subs
, useragent
, agent
, server_set
, server
, nick
, debug
, touchscreen
, hash
,
532 min
= {}, public_http_path
, port
, ssl
, host
, obj
, args
,
533 secure
= (typeof request
.client
.encrypted
=== 'object');
536 if (kiwi
.config
.handle_http
) {
537 // Run through any plugins..
538 args
= {request
: request
, response
: response
};
539 obj
= kiwi
.kiwi_mod
.run('http', args
);
543 response
= args
.response
;
545 uri
= url
.parse(request
.url
, true);
546 uri_parts
= uri
.pathname
.split('/');
548 subs
= uri
.pathname
.substr(0, 4);
549 public_http_path
= kiwi
.kiwi_root
+ '/' + kiwi
.config
.public_http
;
550 if (uri
.pathname
=== '/js/all.js') {
551 if (kiwi
.cache
.alljs
=== '') {
553 min
.underscore
= fs
.readFileSync(public_http_path
+ 'js/underscore.min.js');
554 min
.util
= fs
.readFileSync(public_http_path
+ 'js/util.js');
555 min
.gateway
= fs
.readFileSync(public_http_path
+ 'js/gateway.js');
556 min
.front
= fs
.readFileSync(public_http_path
+ 'js/front.js');
557 min
.front_events
= fs
.readFileSync(public_http_path
+ 'js/front.events.js');
558 min
.front_ui
= fs
.readFileSync(public_http_path
+ 'js/front.ui.js');
559 min
.iscroll
= fs
.readFileSync(public_http_path
+ 'js/iscroll.js');
560 min
.ast
= jsp
.parse(min
.underscore
+ min
.util
+ min
.gateway
+ min
.front
+ min
.front_events
+ min
.front_ui
+ min
.iscroll
);
561 min
.ast
= pro
.ast_mangle(min
.ast
);
562 min
.ast
= pro
.ast_squeeze(min
.ast
);
563 min
.final_code
= pro
.gen_code(min
.ast
);
564 kiwi
.cache
.alljs
= min
.final_code
;
565 hash
= crypto
.createHash('md5').update(kiwi
.cache
.alljs
);
566 kiwi
.cache
.alljs_hash
= hash
.digest('base64');
568 if (request
.headers
['if-none-match'] === kiwi
.cache
.alljs_hash
) {
569 response
.statusCode
= 304;
571 response
.setHeader('Content-type', 'application/javascript');
572 response
.setHeader('ETag', kiwi
.cache
.alljs_hash
);
573 response
.write(kiwi
.cache
.alljs
);
576 } else if ((subs
=== '/js/') || (subs
=== '/css') || (subs
=== '/img')) {
577 request
.addListener('end', function () {
578 kiwi
.fileServer
.serve(request
, response
);
580 } else if (uri
.pathname
=== '/' || uri_parts
[1] === 'client') {
581 useragent
= (typeof request
.headers
=== 'string') ? request
.headers
['user-agent'] : '';
582 if (useragent
.match(/android/i) !== -1) {
585 } else if (useragent
.match(/iphone/) !== -1) {
588 } else if (useragent
.match(/ipad/) !== -1) {
591 } else if (useragent
.match(/ipod/) !== -1) {
601 debug
= (typeof uri
.query
.debug
!== 'undefined');
603 ssl
= (typeof request
.socket
.pair
!== 'undefined');
604 port
= ssl
? 6697 : 6667;
605 if (uri_parts
[1] !== 'client') {
607 server_set
= ((typeof uri
.query
.server
!== 'undefined') && (uri
.query
.server
!== ''));
608 server
= uri
.query
.server
|| 'irc.anonnet.org';
609 nick
= uri
.query
.nick
|| '';
612 server
= 'irc.anonnet.org';
616 server_set
= ((typeof uri_parts
[2] !== 'undefined') && (uri_parts
[2] !== ''));
617 server
= server_set
? uri_parts
[2] : 'irc.anonnet.org';
618 if (server
.search(/:/) > 0) {
619 port
= server
.substring(server
.search(/:/) + 1);
620 server
= server
.substring(0, server
.search(/:/));
621 if (port
[0] == '+') {
622 port
= port
.substring(1);
628 nick
= uri
.query
.nick
|| '';
631 response
.setHeader('X-Generated-By', 'KiwiIRC');
632 hash
= crypto
.createHash('md5').update(touchscreen
? 't' : 'f')
633 .update(debug
? 't' : 'f')
634 .update(server_set
? 't' : 'f')
635 .update(secure
? 't' : 'f')
637 .update(port
.toString())
638 .update(ssl
? 't' : 'f')
641 .update(JSON
.stringify(kiwi
.config
))
643 if (kiwi
.cache
.html
[hash
]) {
644 if (request
.headers
['if-none-match'] === kiwi
.cache
.html
[hash
].hash
) {
645 response
.statusCode
= 304;
647 response
.setHeader('Etag', kiwi
.cache
.html
[hash
].hash
);
648 response
.setHeader('Content-type', 'text/html');
649 response
.write(kiwi
.cache
.html
[hash
].html
);
653 fs
.readFile(public_http_path
+ 'index.html.jade', 'utf8', function (err
, str
) {
657 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
});
658 hash2
= crypto
.createHash('md5').update(html
).digest('base64');
659 kiwi
.cache
.html
[hash
] = {"html": html
, "hash": hash2
};
660 if (request
.headers
['if-none-match'] === hash2
) {
661 response
.statusCode
= 304;
663 response
.setHeader('Etag', hash2
);
664 response
.setHeader('Content-type', 'text/html');
665 response
.write(html
);
668 response
.statusCode
= 500;
673 response
.statusCode
= 500;
678 } else if (uri
.pathname
.substr(0, 10) === '/socket.io') {
681 response
.statusCode
= 404;
687 // console.log('ERROR app.httpHandler()');
695 this.websocketListen = function (ports
, host
, handler
, key
, cert
) {
696 if (kiwi
.httpServers
.length
> 0) {
697 _
.each(kiwi
.httpServers
, function (hs
) {
700 kiwi
.httpsServers
= [];
703 _
.each(ports
, function (port
) {
705 if (port
.secure
=== true) {
706 hs
= https
.createServer({key
: fs
.readFileSync(__dirname
+ '/' + key
), cert
: fs
.readFileSync(__dirname
+ '/' + cert
)}, handler
);
707 kiwi
.io
.push(ws
.listen(hs
, {secure
: true}));
708 hs
.listen(port
.number
, host
);
709 console
.log("Listening on %s:%d with SSL", host
, port
.number
);
711 hs
= http
.createServer(handler
);
712 kiwi
.io
.push(ws
.listen(hs
, {secure
: false}));
713 hs
.listen(port
.number
, host
);
714 console
.log("Listening on %s:%d without SSL", host
, port
.number
);
717 kiwi
.httpServers
.push(hs
);
720 _
.each(kiwi
.io
, function (io
) {
721 io
.set('log level', 1);
722 io
.enable('browser client minification');
723 io
.enable('browser client etag');
724 io
.set('transports', kiwi
.config
.transports
);
726 io
.of('/kiwi').authorization(function (handshakeData
, callback
) {
727 var address
= handshakeData
.address
.address
;
728 if (typeof kiwi
.connections
[address
] === 'undefined') {
729 kiwi
.connections
[address
] = {count
: 0, sockets
: []};
731 callback(null, true);
732 }).on('connection', kiwi
.websocketConnection
);
741 this.websocketConnection = function (websocket
) {
743 console
.log("New connection!");
744 websocket
.kiwi
= {address
: websocket
.handshake
.address
.address
, buffer
: {list
: []}};
745 con
= kiwi
.connections
[websocket
.kiwi
.address
];
747 if (con
.count
>= kiwi
.config
.max_client_conns
) {
748 websocket
.emit('too_many_connections');
749 websocket
.disconnect();
752 con
.sockets
.push(websocket
);
754 websocket
.sendClientEvent = function (event_name
, data
) {
755 var ev
= kiwi
.kiwi_mod
.run(event_name
, data
, {websocket
: this});
760 data
.event
= event_name
;
761 websocket
.emit('message', data
);
764 websocket
.sendServerLine = function (data
, eol
) {
765 eol
= (typeof eol
=== 'undefined') ? '\r\n' : eol
;
768 websocket
.ircSocket
.write(data
+ eol
);
772 websocket
.on('irc connect', kiwi
.websocketIRCConnect
);
773 websocket
.on('message', kiwi
.websocketMessage
);
774 websocket
.on('disconnect', kiwi
.websocketDisconnect
);
782 this.websocketIRCConnect = function (websocket
, nick
, host
, port
, ssl
, password
, callback
) {
784 //setup IRC connection
786 ircSocket
= net
.createConnection(port
, host
);
788 ircSocket
= tls
.connect(port
, host
);
790 ircSocket
.setEncoding('ascii');
791 ircSocket
.IRC
= {options
: {}, CAP
: {negotiating
: true, requested
: [], enabled
: []}, registered
: false};
792 ircSocket
.on('error', function (e
) {
793 if (ircSocket
.IRC
.registered
) {
794 websocket
.emit('disconnect');
796 websocket
.emit('error', e
.message
);
799 websocket
.ircSocket
= ircSocket
;
800 ircSocket
.holdLast
= false;
803 ircSocket
.on('data', function (data
) {
804 kiwi
.ircSocketDataHandler(data
, websocket
, ircSocket
);
807 ircSocket
.IRC
.nick
= nick
;
808 // Send the login data
809 dns
.reverse(websocket
.kiwi
.address
, function (err
, domains
) {
810 //console.log(domains);
811 websocket
.kiwi
.hostname
= (err
) ? websocket
.kiwi
.address
: _
.first(domains
);
812 if ((kiwi
.config
.webirc
) && (kiwi
.config
.webirc_pass
[host
])) {
813 websocket
.sendServerLine('WEBIRC ' + kiwi
.config
.webirc_pass
[host
] + ' KiwiIRC ' + websocket
.kiwi
.hostname
+ ' ' + websocket
.kiwi
.address
);
816 websocket
.sendServerLine('PASS ' + password
);
818 websocket
.sendServerLine('CAP LS');
819 websocket
.sendServerLine('NICK ' + nick
);
820 websocket
.sendServerLine('USER kiwi_' + nick
.replace(/[^0-9a-zA-Z\-_.]/, '') + ' 0 0 :' + nick
);
822 if ((callback
) && (typeof (callback
) === 'function')) {
830 this.websocketMessage = function (websocket
, msg
, callback
) {
833 msg
.data
= JSON
.parse(msg
.data
);
834 args
= msg
.data
.args
;
835 switch (msg
.data
.method
) {
837 if ((args
.target
) && (args
.msg
)) {
838 obj
= kiwi
.kiwi_mod
.run('msgsend', args
, {websocket
: websocket
});
840 websocket
.sendServerLine('PRIVMSG ' + args
.target
+ ' :' + args
.msg
);
845 if ((args
.target
) && (args
.msg
)) {
846 websocket
.sendServerLine('PRIVMSG ' + args
.target
+ ' :' + String
.fromCharCode(1) + 'ACTION ' + args
.msg
+ String
.fromCharCode(1));
851 if ((args
.target
) && (args
.data
)) {
852 websocket
.sendServerLine('PRIVMSG ' + args
.target
+ ' :' + String
.fromCharCode(1) + 'KIWI ' + args
.data
+ String
.fromCharCode(1));
857 websocket
.sendServerLine(args
.data
);
861 _
.each(args
.channel
.split(","), function (chan
) {
862 websocket
.sendServerLine('JOIN ' + chan
);
868 websocket
.sendServerLine('TOPIC ' + args
.channel
+ ' :' + args
.topic
);
872 websocket
.ircSocket
.end('QUIT :' + args
.message
+ '\r\n');
873 websocket
.sentQUIT
= true;
874 websocket
.ircSocket
.destroySoon();
875 websocket
.disconnect();
878 if ((args
.target
) && (args
.msg
)) {
879 websocket
.sendServerLine('NOTICE ' + args
.target
+ ' :' + args
.msg
);
884 if ((callback
) && (typeof (callback
) === 'function')) {
888 console
.log("Caught error: " + e
);
894 this.websocketDisconnect = function (websocket
) {
897 if ((!websocket
.sentQUIT
) && (websocket
.ircSocket
)) {
899 websocket
.ircSocket
.end('QUIT :' + kiwi
.config
.quit_message
+ '\r\n');
900 websocket
.sentQUIT
= true;
901 websocket
.ircSocket
.destroySoon();
905 con
= kiwi
.connections
[websocket
.kiwi
.address
];
907 con
.sockets
= _
.reject(con
.sockets
, function (sock
) {
908 return sock
=== websocket
;
917 this.rehash = function () {
919 reload_config
= kiwi
.loadConfig();
921 // If loading the new config errored out, dont attempt any changes
922 if (reload_config
=== false) {
926 // We just want the settings that have been changed
927 changes
= reload_config
[1];
929 if (Object
.keys(changes
).length
!== 0) {
930 console
.log('%s config changes: \n', Object
.keys(changes
).length
, changes
);
937 kiwi
.websocketListen(kiwi
.config
.ports
, kiwi
.config
.bind_address
, kiwi
.httpHandler
, kiwi
.config
.ssl_key
, kiwi
.config
.ssl_cert
);
938 delete changes
.ports
;
939 delete changes
.bind_address
;
940 delete changes
.ssl_key
;
941 delete changes
.ssl_cert
;
947 delete changes
.group
;
951 kiwi
.kiwi_mod
.loadModules(kiwi_root
, kiwi
.config
);
952 kiwi
.kiwi_mod
.printMods();
953 delete changes
.module_dir
;
954 delete changes
.modules
;
960 // Also clear the kiwi.cached javascript and HTML
961 if (kiwi
.config
.handle_http
) {
962 kiwi
.cache
= {alljs
: '', html
: []};
973 * KiwiIRC controlling via STDIN
975 this.manageControll = function (data
) {
976 var parts
= data
.toString().trim().split(' '),
981 console
.log('Rehashing...');
982 console
.log(kiwi
.rehash() ? 'Rehash complete' : 'Rehash failed');
986 console
.log('Recoding...');
987 console
.log(kiwi
.recode() ? 'Recode complete' : 'Recode failed');
991 if (parts
[1] === 'reload') {
992 console
.log('Reloading module (' + parts
[2] + ')..');
993 kiwi
.kiwi_mod
.reloadModule(parts
[2]);
998 if (parts
[1] === 'clear') {
999 kiwi
.cache
.html
= {};
1000 kiwi
.cache
.alljs
= '';
1001 console
.log('HTML cache cleared');
1006 for (i
in kiwi
.connections
) {
1007 connections_cnt
= connections_cnt
+ parseInt(kiwi
.connections
[i
].count
, 10);
1009 console
.log(connections_cnt
.toString() + ' connected clients');
1013 console
.log('Unknown command \'' + parts
[0] + '\'');