client + ircconnection cleanup
authorDarren <darren@darrenwhitlen.com>
Sun, 21 Oct 2012 14:02:21 +0000 (15:02 +0100)
committerDarren <darren@darrenwhitlen.com>
Sun, 21 Oct 2012 14:02:21 +0000 (15:02 +0100)
server/client.js
server/ircconnection.js [new file with mode: 0644]
server/weblistener.js

index 4b31da74a589f2d1fcff56822fcc2237873d67d3..46515e999ebd62b51f77b979cc09ad24b3b29f37 100755 (executable)
@@ -1,17 +1,18 @@
 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 = {
@@ -23,16 +24,16 @@ var Client = function (websocket) {
     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);
@@ -55,18 +56,18 @@ Client.prototype.sendKiwiCommand = function (command, callback) {
     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;
@@ -86,23 +87,23 @@ var handleClientMessage = function (msg, callback) {
 
 
 
-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 () {
@@ -115,7 +116,7 @@ var kiwi_command = function (command, callback) {
                                });
                 
                 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');
@@ -126,14 +127,10 @@ var kiwi_command = function (command, callback) {
     }
 };
 
-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;
@@ -142,6 +139,8 @@ var disconnect = function () {
     this.emit('destroy');
 };
 
-var error = function () {
+
+// TODO: Should this close all the websocket connections too?
+function websocketError() {
     this.emit('destroy');
 };
diff --git a/server/ircconnection.js b/server/ircconnection.js
new file mode 100644 (file)
index 0000000..68abf1d
--- /dev/null
@@ -0,0 +1,136 @@
+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+$/, ''));
+            }
+        }
+    }
+};
index e35a112f84434c55c2334f4fa2b645b3300a0730..60e58ff8eda09a71dd8be03fe522982a826c56cc 100644 (file)
@@ -59,8 +59,8 @@ var WebListener = function (config, transports) {
     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);
 };
@@ -78,14 +78,14 @@ function handleHttpRequest(request, response) {
 }
 
 
-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));
 };