var util = require('util'),
events = require('events'),
_ = require('underscore'),
- IRCConnection = require('./irc-connection.js').IRCConnection,
- IRCCommands = require('./irc-commands.js'),
+ IrcConnection = require('./ircconnection.js').IrcConnection,
+ IrcCommands = require('./irc-commands.js'),
ClientCommandset = require('./client-commands.js').ClientCommandset;
+
var Client = function (websocket) {
- var c = this;
+ var that = this;
events.EventEmitter.call(this);
this.websocket = websocket;
- this.IRC_connections = [];
+ this.irc_connections = [];
this.next_connection = 0;
this.buffer = {
this.client_commands = new ClientCommandset(this);
websocket.on('irc', function () {
- handleClientMessage.apply(c, arguments);
+ handleClientMessage.apply(that, arguments);
});
websocket.on('kiwi', function () {
- kiwi_command.apply(c, arguments);
+ kiwiCommand.apply(that, arguments);
});
websocket.on('disconnect', function () {
- disconnect.apply(c, arguments);
+ websocketDisconnect.apply(that, arguments);
});
websocket.on('error', function () {
- error.apply(c, arguments);
+ websocketError.apply(that, arguments);
});
};
util.inherits(Client, events.EventEmitter);
this.websocket.emit('kiwi', command, callback);
};
-var handleClientMessage = function (msg, callback) {
+function handleClientMessage(msg, callback) {
var server, args, obj, channels, keys;
// Make sure we have a server number specified
if ((msg.server === null) || (typeof msg.server !== 'number')) {
return (typeof callback === 'function') ? callback('server not specified') : undefined;
- } else if (!this.IRC_connections[msg.server]) {
+ } else if (!this.irc_connections[msg.server]) {
return (typeof callback === 'function') ? callback('not connected to server') : undefined;
}
// The server this command is directed to
- server = this.IRC_connections[msg.server];
+ server = this.irc_connections[msg.server];
if (typeof callback !== 'function') {
callback = null;
-var kiwi_command = function (command, callback) {
+function kiwiCommand(command, callback) {
var that = this;
- console.log(typeof callback);
+
if (typeof callback !== 'function') {
callback = function () {};
}
switch (command.command) {
case 'connect':
if ((command.hostname) && (command.port) && (command.nick)) {
- var con = new IRCConnection(command.hostname, command.port, command.ssl,
+ var con = new IrcConnection(command.hostname, command.port, command.ssl,
command.nick, {hostname: this.websocket.handshake.revdns, address: this.websocket.handshake.address.address},
command.password, null);
var con_num = this.next_connection++;
- this.IRC_connections[con_num] = con;
+ this.irc_connections[con_num] = con;
- var binder = new IRCCommands.Binder(con, con_num, this);
+ var binder = new IrcCommands.Binder(con, con_num, this);
binder.bind_irc_commands();
con.on('connected', function () {
});
con.on('close', function () {
- that.IRC_connections[con_num] = null;
+ that.irc_connections[con_num] = null;
});
} else {
return callback('Hostname, port and nickname must be specified');
}
};
-var extension_command = function (command, callback) {
- if (typeof callback === 'function') {
- callback('not implemented');
- }
-};
-var disconnect = function () {
- _.each(this.IRC_connections, function (irc_connection, i, cons) {
+// Websocket has disconnected, so quit all the IRC connections
+function websocketDisconnect() {
+ _.each(this.irc_connections, function (irc_connection, i, cons) {
if (irc_connection) {
irc_connection.end('QUIT :Kiwi IRC');
cons[i] = null;
this.emit('destroy');
};
-var error = function () {
+
+// TODO: Should this close all the websocket connections too?
+function websocketError() {
this.emit('destroy');
};
--- /dev/null
+var net = require('net'),
+ tls = require('tls'),
+ events = require('events'),
+ util = require('util');
+
+var IrcConnection = function (hostname, port, ssl, nick, user, pass, webirc) {
+ var that = this;
+ events.EventEmitter.call(this);
+
+ if (ssl) {
+ this.socket = tls.connect(port, hostname, {}, connect_handler);
+ } else {
+ this.socket = net.createConnection(port, hostname);
+ this.socket.on('connect', function () {
+ connect_handler.apply(that, arguments);
+ });
+ }
+
+ this.socket.on('error', function () {
+ var a = Array.prototype.slice.call(arguments);
+ a.unshift('error');
+ that.emit.apply(this, a);
+ });
+
+ this.socket.setEncoding('utf-8');
+
+ this.socket.on('data', function () {
+ parse.apply(that, arguments);
+ });
+
+ this.socket.on('close', function () {
+ that.emit('close');
+ });
+
+ this.connected = false;
+ this.registered = false;
+ this.nick = nick;
+ this.user = user;
+ this.ssl = !(!ssl);
+ this.options = Object.create(null);
+
+ this.webirc = webirc;
+ this.password = pass;
+ this.hold_last = false;
+ this.held_data = '';
+};
+util.inherits(IrcConnection, events.EventEmitter);
+
+IrcConnection.prototype.write = function (data, callback) {
+ console.log('S<--', data);
+ write.call(this, data + '\r\n', 'utf-8', callback);
+};
+
+IrcConnection.prototype.end = function (data, callback) {
+ console.log('S<--', data);
+ console.log('Closing docket');
+ end.call(this, data + '\r\n', 'utf-8', callback);
+};
+
+var write = function (data, encoding, callback) {
+ this.socket.write(data, encoding, callback);
+};
+
+var end = function (data, encoding, callback) {
+ this.socket.end(data, encoding, callback);
+};
+
+module.exports.IrcConnection = IrcConnection;
+
+var connect_handler = function () {
+ if (this.webirc) {
+ this.write('WEBIRC ' + this.webirc.pass + ' KiwiIRC ' + this.user.hostname + ' ' + this.user.address);
+ }
+ if (this.password) {
+ this.write('PASS ' + this.password);
+ }
+ //this.write('CAP LS');
+ this.write('NICK ' + this.nick);
+ this.write('USER kiwi_' + this.nick.replace(/[^0-9a-zA-Z\-_.]/, '') + ' 0 0 :' + this.nick);
+
+ this.connected = true;
+ console.log("IrcConnection.emit('connected')");
+ this.emit('connected');
+};
+
+parse_regex = /^(?::(?:([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)|([a-z0-9\x5B-\x60\x7B-\x7D\.\-]+)!([a-z0-9~\.\-_|]+)@?([a-z0-9\.\-:\/]+)?) )?(\S+)(?: (?!:)(.+?))?(?: :(.+))?$/i;
+//alt_regex = /(?::(([0-9a-z][\x2d0-9a-z]*[0-9a-z]*(?:\x2e[0-9a-z][\x2d0-9a-z]*[0-9a-z]*)*|[\x5b-\x7d][\x2d0-9\x5b-\x7d]{0,8})(?:(?:!([\x01-\t\v\f\x0e-\x1f!-\x3f\x5b-\xff]+))?@([0-9a-z][\x2d0-9a-z]*[0-9a-z]*(?:\x2e[0-9a-z][\x2d0-9a-z]*[0-9a-z]*)*|\d{1,3}\x2e\d{1,3}\x2e\d{1,3}\x2e\d{1,3}|[0-9a-f]+(?::[0-9a-f]+){7}|0:0:0:0:0:(?:0|ffff):\d{1,3}\x2e\d{1,3}\x2e\d{1,3}\x2e\d{1,3}))?)\x20)?([a-z]+|\d{3})((?:\x20[\x01-\t\v\f\x0e-\x1f!-9;-@\x5b-\xff][\x01-\t\v\f\x0e-\x1f!-@\x5b-\xff]*){0,14}(?:\x20:[\x01-\t\v\f\x0e-@\x5b-\xff]*)?|(?:\x20[\x01-\t\v\f\x0e-\x1f!-9;-@\x5b-\xff][\x01-\t\v\f\x0e-\x1f!-@\x5b-\xff]*){14}(?:\x20:?[\x01-\t\v\f\x0e-@\x5b-\xff]*)?)?/i;
+
+var parse = function (data) {
+ var i,
+ msg,
+ msg2,
+ trm;
+
+ if ((this.hold_last) && (this.held_data !== '')) {
+ data = this.held_data + data;
+ this.hold_last = false;
+ this.held_data = '';
+ }
+ if (data.substr(-1) !== '\n') {
+ this.hold_last = true;
+ }
+ data = data.split("\n");
+ for (i = 0; i < data.length; i++) {
+ if (data[i]) {
+ if ((this.hold_last) && (i === data.length - 1)) {
+ this.held_data = data[i];
+ break;
+ }
+
+ // We have a complete line of data, parse it!
+ msg = parse_regex.exec(data[i].replace(/^\r+|\r+$/, ''));
+ //msg2 = alt_regex.exec(data[i].replace(/^\r+|\r+$/, ''));
+ console.log('S-->', data[i]);
+ console.log('Matches', msg);
+ if (msg) {
+ msg = {
+ prefix: msg[1],
+ nick: msg[2],
+ ident: msg[3],
+ hostname: msg[4] || '',
+ command: msg[5],
+ params: msg[6] || '',
+ trailing: (msg[7]) ? msg[7].trim() : ''
+ };
+ msg.params = msg.params.split(' ');
+
+ console.log('Parsed', msg);
+
+ this.emit('irc_' + msg.command.toUpperCase(), msg);
+ } else {
+ console.log("Malformed IRC line: " + data[i].replace(/^\r+|\r+$/, ''));
+ }
+ }
+ }
+};
this.ws.enable('browser client etag');
this.ws.set('transports', transports);
- this.ws.of('/kiwi').authorization(authorisation).on('connection', function () {
- connection.apply(that, arguments);
+ this.ws.of('/kiwi').authorization(authoriseConnection).on('connection', function () {
+ newConnection.apply(that, arguments);
});
this.ws.of('/kiwi').on('error', console.log);
};
}
-var authorisation = function (handshakeData, callback) {
+function authoriseConnection(handshakeData, callback) {
dns.reverse(handshakeData.address.address, function (err, domains) {
handshakeData.revdns = (err) ? handshakeData.address.address : _.first(domains);
callback(null, true);
});
};
-var connection = function (websocket) {
+function newConnection(websocket) {
//console.log(websocket);
this.emit('connection', new Client(websocket));
};