Skip to content

Commit

Permalink
make server more stable
Browse files Browse the repository at this point in the history
  • Loading branch information
kkaefer committed Jan 30, 2011
1 parent 9a8c7f3 commit aa93b4d
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 46 deletions.
2 changes: 1 addition & 1 deletion .ndistro
@@ -1,2 +1,2 @@
node 0.2.5
node 0.2.3
module robrighter node-xml master
12 changes: 11 additions & 1 deletion README.md
Expand Up @@ -6,4 +6,14 @@ This code is still in alpha; there is barely any error handling and sometimes th

## Installation

To install, you need [ndistro](https://github.com/visionmedia/ndistro). Then type `ndistro` in the root directory and run the gateway with `bin/node medium.js`.
To install, you need [ndistro](https://github.com/visionmedia/ndistro). Then type `ndistro` in the root directory and run the gateway with `bin/node medium.js`.

## Configuration

Medium can only act as a gateway to one Jabber server at a time. The configuration is in `config.js`:

* `debug`: When set, the gateway outputs incoming and outcoming traffic to the command line.
* `xmpp.host`: The domain of the Jabber server
* `xmpp.jid`: The host part of the Jabber ID for connecting users. This is required for some servers where the `host` does not match the `jid`, e.g. Google Talk.
* `xmpp.muc`: The conference server.
* `xmpp.port`: Port for XMPP connection. Defaults to 5222.
3 changes: 2 additions & 1 deletion config.js
Expand Up @@ -7,7 +7,8 @@ exports.xmpp = {

host: 'localhost',
jid: 'localhost',
muc: 'conference.localhost'
muc: 'conference.localhost',
noSSL: true
};

exports.irc = {
Expand Down
14 changes: 8 additions & 6 deletions lib/ircd.js
@@ -1,5 +1,5 @@
var net = require('net');
var util = require('util');
var sys = require('sys');
var crypto = require('crypto');
var EventEmitter = require('events').EventEmitter;
var dns = require('dns');
Expand Down Expand Up @@ -35,7 +35,7 @@ function Connection(stream, config) {

this.setup();
}
util.inherits(Connection, EventEmitter);
sys.inherits(Connection, EventEmitter);
exports.Connection = Connection;

Connection.prototype.setup = function() {
Expand Down Expand Up @@ -149,19 +149,21 @@ Connection.prototype.QUIT = function(line) {

Connection.prototype.reply = function(code, msg) {
var str = ':' + this.config.name + ' ' + code + ' ' + this.user.nick + ' ' + msg + '\r\n';
if (config.debug) console.log(GREEN('IRC out>> ' + util.inspect(str)));
if (config.debug) console.log(GREEN('IRC out>> ' + sys.inspect(str)));
this.stream.write(str);
};

Connection.prototype.error = function(msg) {
var str = 'ERROR :' + msg + '\r\n';
if (config.debug) console.log(GREEN('IRC out>> ' + util.inspect(str)));
this.stream.write(str);
if (config.debug) console.log(GREEN('IRC out>> ' + sys.inspect(str)));
if (this.stream.readyState !== 'readOnly' && this.stream.readyState !== 'closed') {
this.stream.write(str);
}
};

Connection.prototype.command = function(cmd, msg, user) {
var str = ':' + (user || (this.user.nick + '!' + this.user.user)) + ' ' + cmd + ' ' + msg + '\r\n';
if (config.debug) console.log(GREEN('IRC out>> ' + util.inspect(str)));
if (config.debug) console.log(GREEN('IRC out>> ' + sys.inspect(str)));
this.stream.write(str);
};

Expand Down
53 changes: 32 additions & 21 deletions lib/xmpp.js
@@ -1,5 +1,5 @@
var net = require('net');
var util = require('util');
var sys = require('sys');
var crypto = require('crypto');
var EventEmitter = require('events').EventEmitter;
var Buffer = require('buffer').Buffer;
Expand Down Expand Up @@ -43,6 +43,7 @@ function Connection(config) {
this.config = config;

this.config.port = config.port || 5222;
this.config.noSSL = config.noSSL || false;

this.setupStream();
this.setupHandlers();
Expand All @@ -51,7 +52,7 @@ function Connection(config) {
this.uniqueID = 1;
}
exports.Connection = Connection;
util.inherits(Connection, EventEmitter);
sys.inherits(Connection, EventEmitter);

Connection.prototype.once = function(type, listener) {
var self = this;
Expand Down Expand Up @@ -126,16 +127,18 @@ Connection.prototype.setupStream = function() {
};

Connection.prototype.setupHandlers = function() {
this.once('features', function(features) {
if (find(features, 'starttls').length)
this.startTLS();
else {
this.emit('error', 'Server does not support STARTTLS.');

// DEBUG: Proceed anyway without SSL!
this.detectFeatures(features);
}
});
if (this.config.noSSL) {
this.once('features', this.detectFeatures.bind(this));
}
else {
this.once('features', function(features) {
if (find(features, 'starttls').length)
this.startTLS();
else {
this.emit('error', 'Server does not support STARTTLS.');
}
});
}
};

Connection.prototype.initStream = function() {
Expand All @@ -145,11 +148,13 @@ Connection.prototype.initStream = function() {
this.once('stream', function(stream) {
self.id = stream.id;
});
this.stream.write('<stream:stream ' +
var stanza = '<stream:stream ' +
'xmlns="jabber:client" ' +
'xmlns:stream="http://etherx.jabber.org/streams" ' +
'to="' + this.config.jid + '" ' +
'version="1.0">');
'version="1.0">';
if (config.debug) console.log(PURPLE('out -> ' + stanza));
this.stream.write(stanza);
};

Connection.prototype.detectFeatures = function(features) {
Expand Down Expand Up @@ -208,7 +213,7 @@ Connection.prototype.end = function() {
case 'closed': // The stream is already closed.
// Nothing to do.
break;

}
};

Expand All @@ -218,17 +223,23 @@ Connection.prototype.request = function(stanza, callback) {
if (callback) this.requests[id] = callback;
stanza.$attrs.id = id;
stanza = xml.$render(stanza);
if (config.debug) console.log(PURPLE('out -> ' + stanza));
this.stream.write(stanza);
if (this.stream.readyState !== 'readOnly' && this.stream.readyState !== 'closed') {
if (config.debug) console.log(PURPLE('out -> ' + stanza));
this.stream.write(stanza);
}
else if (config.debug) console.log(PURPLE('out -> (NOT SENT)' + stanza));
};

Connection.prototype.send = function(stanza) {
if (!stanza.$attrs.id) {
stanza.$attrs.id = 'medium' + (this.uniqueID++);
}
stanza = xml.$render(stanza);
if (config.debug) console.log(PURPLE('out -> ' + stanza));
this.stream.write(stanza);
if (this.stream.readyState !== 'readOnly' && this.stream.readyState !== 'closed') {
if (config.debug) console.log(PURPLE('out -> ' + stanza));
this.stream.write(stanza);
}
else if (config.debug) console.log(PURPLE('out -> (NOT SENT)' + stanza));
};

Connection.prototype.bind = function() {
Expand Down Expand Up @@ -283,8 +294,8 @@ Connection.prototype.roomNick = function(nickJID, callback) {
this.request(stanza, callback);
};

Connection.prototype.leaveRoom = function(roomJID) {
var stanza = xml.presence({ to: roomJID, type: 'unavailable' });
Connection.prototype.leaveRoom = function(nickJID) {
var stanza = xml.presence({ to: nickJID, type: 'unavailable' });
this.request(stanza);
};

Expand Down
64 changes: 48 additions & 16 deletions medium.js
Expand Up @@ -24,7 +24,8 @@ process.on('SIGINT', function() {
ircd.createServer(config.irc, function (irc) {
var jabber = new xmpp.Connection(config.xmpp);

jabber.on('error', function fn() {
jabber.on('error', function fn(err) {
console.warn('Jabber error: ' + err);
if (!irc.loggedIn) return irc.on('login', fn.bind(this));

irc.error('Could not connect to Jabber server.');
Expand All @@ -39,14 +40,21 @@ ircd.createServer(config.irc, function (irc) {
return rooms[name];
}

connections.push(function() {
for (var name in rooms) rooms[name].destroy();
jabber.end();
irc.end();
console.log(RED('Killed all clients.'));
delete jabber;
delete irc;
});
var disconnectFn = function() {
for (var name in rooms) {
rooms[name].destroy();
delete rooms[name];
}
// Wait a bit until all messages have been sent.
setTimeout(function() {
jabber.end();
irc.end();
console.log(RED('Killed all clients.'));
delete jabber;
delete irc;
}, 500);
};
connections.push(disconnectFn);


// ===========================================================================
Expand Down Expand Up @@ -130,13 +138,24 @@ ircd.createServer(config.irc, function (irc) {
irc.on('quit', function fn(line) {
if (!jabber.session) return jabber.on('session', fn.bind(this, line));

if (!irc.quit && !jabber.quit) {
if (!irc.quit) {
irc.quit = true;
// Leave all rooms.
for (var name in rooms) {
rooms[name].jabberLeave();
irc.end();

if (!jabber.quit) {
// Leave all rooms.
for (var name in rooms) {
rooms[name].destroy();
delete rooms[name];
}
jabber.end();
}
else {
delete irc;
delete jabber;
var index = connections.indexOf(disconnectFn);
if (index >= 0) connections.splice(index, 1);
}
jabber.end();
}
});

Expand Down Expand Up @@ -174,6 +193,12 @@ ircd.createServer(config.irc, function (irc) {
irc.error('The Jabber server disconnected.');
irc.end();
}
else {
delete irc;
delete jabber;
var index = connections.indexOf(disconnectFn);
if (index >= 0) connections.splice(index, 1);
}
});

jabber.on('close', function() {
Expand Down Expand Up @@ -209,8 +234,14 @@ ircd.createServer(config.irc, function (irc) {
if (x && x.stamp) delay = x.stamp.replace(/^(\d\d\d\d)(\d\d)(\d\d)T(\d\d:\d\d:\d\d)$/, '$1-$2-$3T$4Z');
else if (delay && delay.stamp) delay = delay.stamp;


if (message.type === 'groupchat' && jid.host === jabber.config.muc) {
if (message.from === jid.user + '@' + config.xmpp.muc + '/' + irc.user.nick) {
// This is the notification of our own message.
// Some servers send this without an ID so it ends up hear instead
// of being intercepted by the noop function.
return;
}

var obj;
if (obj = path(message, 'subject')) {
// This is a topic change.
Expand Down Expand Up @@ -348,6 +379,7 @@ Room.prototype = {
var info = this.members[id];

var item = path(presence, 'x.item');
if (!item) return;
info.role = item.role || '';
info.nick = jid.resource;
info.realJID = item.jid;
Expand Down Expand Up @@ -401,7 +433,7 @@ Room.prototype = {
},

jabberLeave: function() {
if (this.joined) this.jabber.leaveRoom(this.JID);
if (this.joined) this.jabber.leaveRoom(this.nickJID);
},

destroy: function() {
Expand Down

0 comments on commit aa93b4d

Please sign in to comment.