Permalink
Browse files

make server more stable

  • Loading branch information...
1 parent 9a8c7f3 commit aa93b4d10a2ff4ea84f6f1213857b1ee79615130 @kkaefer committed Jan 30, 2011
Showing with 102 additions and 46 deletions.
  1. +1 −1 .ndistro
  2. +11 −1 README.md
  3. +2 −1 config.js
  4. +8 −6 lib/ircd.js
  5. +32 −21 lib/xmpp.js
  6. +48 −16 medium.js
View
@@ -1,2 +1,2 @@
-node 0.2.5
+node 0.2.3
module robrighter node-xml master
View
@@ -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.
View
@@ -7,7 +7,8 @@ exports.xmpp = {
host: 'localhost',
jid: 'localhost',
- muc: 'conference.localhost'
+ muc: 'conference.localhost',
+ noSSL: true
};
exports.irc = {
View
@@ -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');
@@ -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() {
@@ -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);
};
View
@@ -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;
@@ -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();
@@ -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;
@@ -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() {
@@ -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) {
@@ -208,7 +213,7 @@ Connection.prototype.end = function() {
case 'closed': // The stream is already closed.
// Nothing to do.
break;
-
+
}
};
@@ -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() {
@@ -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);
};
View
@@ -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.');
@@ -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);
// ===========================================================================
@@ -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();
}
});
@@ -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() {
@@ -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.
@@ -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;
@@ -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() {

0 comments on commit aa93b4d

Please sign in to comment.