f46eed5376c2f5ca45084c19e0b444c01e186ae5
1 /*jslint 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 ws
= require('socket.io'),
7 _
= require('./lib/underscore.min.js'),
8 starttls
= require('./lib/starttls.js');
14 RPL_WHOISSERVER
: '312',
15 RPL_WHOISOPERATOR
: '313',
17 RPL_ENDOFWHOIS
: '318',
18 RPL_WHOISCHANNELS
: '319',
21 RPL_ENDOFNAMES
: '366',
23 RPL_WHOISMODES
: '379',
24 ERR_NOSUCHNICK
: '401',
25 ERR_LINKCHANNEL
: '470',
30 var parseIRCMessage = function (websocket
, ircSocket
, data
) {
31 /*global ircSocketDataHandler */
32 var msg
, regex
, opts
, options
, opt
, i
, j
, matches
, nick
, users
, chan
, params
, prefix
, prefixes
, nicklist
, caps
, IRC
, listeners
, ssl_socket
;
33 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;
34 msg
= regex
.exec(data
);
43 trailing
: (msg
[7]) ? msg
[7].trim() : ''
45 switch (msg
.command
.toUpperCase()) {
47 ircSocket
.write('PONG ' + msg
.trailing
+ '\r\n');
49 case ircNumerics
.RPL_WELCOME
:
50 if (ircSocket
.IRC
.CAP
.negotiating
) {
51 ircSocket
.IRC
.CAP
.negotiating
= false;
52 ircSocket
.IRC
.CAP
.enabled
= [];
53 ircSocket
.IRC
.CAP
.requested
= [];
55 websocket
.emit('message', {event
: 'connect', connected
: true, host
: null});
57 case ircNumerics
.RPL_ISUPPORT
:
58 opts
= msg
.params
.split(" ");
60 for (i
= 0; i
< opts
.length
; i
++) {
61 opt
= opts
[i
].split("=", 2);
62 opt
[0] = opt
[0].toUpperCase();
63 ircSocket
.IRC
.options
[opt
[0]] = opt
[1] || true;
64 if (_
.include(['NETWORK', 'PREFIX', 'CHANTYPES'], opt
[0])) {
65 if (opt
[0] === 'PREFIX') {
66 regex
= /\(([^)]*)\)(.*)/;
67 matches
= regex
.exec(opt
[1]);
68 if ((matches
) && (matches
.length
=== 3)) {
70 for (j
= 0; j
< matches
[2].length
; j
++) {
71 options
[opt
[0]][matches
[2].charAt(j
)] = matches
[1].charAt(j
);
77 websocket
.emit('message', {event
: 'options', server
: '', "options": options
});
79 case ircNumerics
.RPL_WHOISUSER
:
80 case ircNumerics
.RPL_WHOISSERVER
:
81 case ircNumerics
.RPL_WHOISOPERATOR
:
82 case ircNumerics
.RPL_WHOISIDLE
:
83 case ircNumerics
.RPL_ENDOFWHOIS
:
84 case ircNumerics
.RPL_WHOISCHANNELS
:
85 case ircNumerics
.RPL_WHOISMODES
:
86 websocket
.emit('message', {event
: 'whois', server
: '', nick
: msg
.params
.split(" ", 3)[1], "msg": msg
.trailing
});
88 case ircNumerics
.RPL_MOTD
:
89 websocket
.emit('message', {event
: 'motd', server
: '', "msg": msg
.trailing
});
91 case ircNumerics
.RPL_NAMEREPLY
:
92 params
= msg
.params
.split(" ");
95 users
= msg
.trailing
.split(" ");
96 prefixes
= _
.values(ircSocket
.IRC
.options
.PREFIX
);
99 _
.each(users
, function (user
) {
100 if (_
.include(prefix
, user
.charAt(0))) {
101 prefix
= user
.charAt(0);
102 user
= user
.substring(1);
103 nicklist
[user
] = prefix
;
108 websocket
.emit('message', {event
: 'userlist', server
: '', "users": nicklist
, channel
: chan
});
114 websocket
.emit('message', {event
: 'userlist', server
: '', "users": nicklist
, channel
: chan
});
119 case ircNumerics
.RPL_ENDOFNAMES
:
120 websocket
.emit('message', {event
: 'userlist_end', server
: '', channel
: msg
.params
.split(" ")[1]});
122 case ircNumerics
.ERR_LINKCHANNEL
:
123 params
= msg
.params
.split(" ");
124 websocket
.emit('message', {event
: 'channel_redirect', from: params
[1], to
: params
[2]});
126 case ircNumerics
.ERR_NOSUCHNICK
:
130 websocket
.emit('message', {event
: 'join', nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: msg
.trailing
});
131 if (msg
.nick
=== ircSocket
.IRC
.nick
) {
132 ircSocket
.write('NAMES ' + msg
.trailing
+ '\r\n');
136 websocket
.emit('message', {event
: 'part', nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: msg
.params
.trim(), message
: msg
.trailing
});
139 params
= msg
.params
.split(" ");
140 websocket
.emit('message', {event
: 'kick', kicked
: params
[1], nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: params
[0].trim(), message
: msg
.trailing
});
143 websocket
.emit('message', {event
: 'quit', nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, message
: msg
.trailing
});
146 websocket
.emit('message', {event
: 'notice', nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: msg
.params
.trim(), msg
: msg
.trailing
});
149 websocket
.emit('message', {event
: 'nick', nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, newnick
: msg
.trailing
});
152 websocket
.emit('message', {event
: 'topic', nick
: msg
.nick
, channel
: msg
.params
, topic
: msg
.trailing
});
154 case ircNumerics
.RPL_TOPIC
:
155 websocket
.emit('message', {event
: 'topic', nick
: '', channel
: msg
.params
.split(" ")[1], topic
: msg
.trailing
});
158 opts
= msg
.params
.split(" ");
159 params
= {event
: 'mode', nick
: msg
.nick
};
160 switch (opts
.length
) {
162 params
.effected_nick
= opts
[0];
163 params
.mode
= msg
.trailing
;
166 params
.channel
= opts
[0];
167 params
.mode
= opts
[1];
170 params
.channel
= opts
[0];
171 params
.mode
= opts
[1];
172 params
.effected_nick
= opts
[2];
175 websocket
.emit('message', params
);
178 websocket
.emit('message', {event
: 'msg', nick
: msg
.nick
, ident
: msg
.ident
, hostname
: msg
.hostname
, channel
: msg
.params
.trim(), msg
: msg
.trailing
});
182 options
= msg
.trailing
.split(" ");
183 switch (_
.first(msg
.params
.split(" "))) {
186 _
.each(_
.intersect(caps
, options
), function (cap
) {
191 ircSocket
.IRC
.CAP
.requested
.push(cap
);
193 if (opts
.length
> 0) {
194 ircSocket
.write('CAP REQ :' + opts
+ '\r\n');
196 ircSocket
.write('CAP END\r\n');
199 /*if (_.include(options, 'tls')) {
200 ircSocket.write('STARTTLS\r\n');
201 ircSocket.IRC.CAP.requested.push('tls');
205 _
.each(options
, function (cap
) {
206 ircSocket
.IRC
.CAP
.enabled
.push(cap
);
208 if (_
.last(msg
.params
.split(" ")) !== '*') {
209 ircSocket
.IRC
.CAP
.requested
= [];
210 ircSocket
.IRC
.CAP
.negotiating
= false;
211 ircSocket
.write('CAP END\r\n');
215 ircSocket
.IRC
.CAP
.requested
= [];
216 ircSocket
.IRC
.CAP
.negotiating
= false;
217 ircSocket
.write('CAP END\r\n');
221 /*case ircNumerics.RPL_STARTTLS:
224 listeners = ircSocket.listeners('data');
225 ircSocket.removeAllListeners('data');
226 ssl_socket = starttls(ircSocket, {}, function () {
227 ssl_socket.on("data", function (data) {
228 ircSocketDataHandler(data, websocket, ssl_socket);
230 ircSocket = ssl_socket;
232 _.each(listeners, function (listener) {
233 ircSocket.addListener('data', listener);
236 //console.log(ircSocket);
243 console
.log("Unknown command.\r\n");
247 var ircSocketDataHandler = function (data
, websocket
, ircSocket
) {
249 data
= data
.split("\r\n");
250 for (i
= 0; i
< data
.length
; i
++) {
252 console
.log("->" + data
[i
]);
253 parseIRCMessage(websocket
, ircSocket
, data
[i
]);
258 //setup websocket listener
259 var io
= ws
.listen(7777, {secure
: true});
260 io
.sockets
.on('connection', function (websocket
) {
261 websocket
.on('irc connect', function (nick
, host
, port
, ssl
, callback
) {
263 //setup IRC connection
265 ircSocket
= net
.createConnection(port
, host
);
267 ircSocket
= tls
.connect(port
, host
);
269 ircSocket
.setEncoding('ascii');
270 ircSocket
.IRC
= {options
: {}, CAP
: {negotiating
: true, requested
: [], enabled
: []}};
271 websocket
.ircSocket
= ircSocket
;
273 ircSocket
.on('data', function (data
) {
274 ircSocketDataHandler(data
, websocket
, ircSocket
);
277 ircSocket
.IRC
.nick
= nick
;
278 // Send the login data
279 ircSocket
.write('CAP LS\r\n');
280 ircSocket
.write('NICK ' + nick
+ '\r\n');
281 ircSocket
.write('USER ' + nick
+ '_kiwi 0 0 :' + nick
+ '\r\n');
283 if ((callback
) && (typeof (callback
) === 'function')) {
287 websocket
.on('message', function (msg
, callback
) {
290 msg
.data
= JSON
.parse(msg
.data
);
291 args
= msg
.data
.args
;
292 switch (msg
.data
.method
) {
294 if ((args
.target
) && (args
.msg
)) {
295 websocket
.ircSocket
.write('PRIVMSG ' + args
.target
+ ' :' + args
.msg
+ '\r\n');
299 if ((args
.target
) && (args
.msg
)) {
300 websocket
.ircSocket
.write('PRIVMSG ' + args
.target
+ ' :\ 1ACTION ' + args
.msg
+ '\ 1\r\n');
304 websocket
.ircSocket
.write(args
.data
+ '\r\n');
308 _
.each(args
.channel
.split(","), function (chan
) {
309 websocket
.ircSocket
.write('JOIN ' + chan
+ '\r\n');
314 websocket
.ircSocket
.end('QUIT :' + args
.message
+ '\r\n');
315 websocket
.sentQUIT
= true;
316 websocket
.ircSocket
.destroySoon();
317 websocket
.disconnect();
321 if ((callback
) && (typeof (callback
) === 'function')) {
325 console
.log("Caught error: " + e
);
328 websocket
.on('disconnect', function () {
329 if ((!websocket
.sentQUIT
) && (websocket
.ircSocket
)) {
330 websocket
.ircSocket
.end('QUIT :KiwiIRC\r\n');
331 websocket
.sentQUIT
= true;
332 websocket
.ircSocket
.destroySoon();