changeset 20:e4916f1763e5

Deactivable debug; fix a stupid bug in gravatar; add a better default image and make it configurable.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sat, 16 Jul 2011 13:54:47 +0200
parents 9ff8f951da99
children 46c42ec52680
files avatar.js configuration.js.example
diffstat 2 files changed, 56 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/avatar.js
+++ b/avatar.js
@@ -41,20 +41,23 @@ process.addListener('uncaughtException',
 	console.log('Uncaught exception (' + err + '), this should never happen:\n' + err.stack);
 });
 
-(function() {
-	var send = conn.send;
-	conn.send = function(s) {
-		util.log('Sent: ' + s + '');
-		send.call(conn, s);
-	};
-})();
+if (config.debug)
+	(function() {
+		var send = conn.send;
+		conn.send = function(s) {
+			console.log('Sent: ' + s + '');
+			send.call(conn, s);
+		};
+	})();
 
 conn.on('online', function () {
-	console.log('Connected');
+	util.log('Connected');
 });
 
 conn.on('stanza', function (stanza) {
-	util.log('Recv: ' + stanza + '');
+	if (config.debug)
+		console.log('Recv: ' + stanza + '');
+
 	if (stanza.is('iq'))
 		onIq(stanza);
 	else
@@ -73,12 +76,26 @@ var jids = {};
 var sent = {};
 
 var svgError = function(res, message) {
+	util.log('No avatar at all, display a default image with the error in the title.');
+
 	res.writeHead(200, {'Content-Type': 'image/svg+xml'});
 	res.write('<?xml version="1.0" encoding="UTF-8"?>\n');
-	res.write('<svg xmlns="http://www.w3.org/2000/svg" viewBox="-32 -36 64 64">\n');
-	res.write('\t<title>'+message+'</title>\n');
-	res.write('\t<rect x="-32" y="-36" width="64" height="64" fill="white"/>\n');
-	res.write('\t<text font-family="sans-serif" font-weight="bold" text-anchor="middle">Error</text>\n');
+	res.write('<svg xmlns="http://www.w3.org/2000/svg"');
+
+	if (config.defaultImage)
+		res.write(' xmlns:xlink="http://www.w3.org/1999/xlink"');
+
+	res.write(' viewBox="0 0 64 64">\n');
+	res.write('\t<title>' + message + '</title>\n');
+
+	if (config.defaultImage)
+		res.write('\t<image width="64" height="64" xlink:href="' + config.defaultImage + '"/>\n');
+	else {
+		res.write('\t<rect width="64" height="64" fill="silver"/>\n');
+		res.write('\t<circle cx="32" cy="26" r="14" fill="white"/>\n');
+		res.write('\t<ellipse cx="32" cy="64" rx="24" ry="26" fill="white"/>\n');
+	}
+
 	res.end('</svg>\n');
 }
 
@@ -92,14 +109,16 @@ var makeError = function(response) {
 }
 
 if (config.useGravatar)
-	var noAvatar = function(res, from, message) {
+	var noAvatar = function(res, to, message) {
 		var options = {
 			host: 'gravatar.com',
 			port: 80,
-			path: '/avatar/' + hash('md5').update(from).digest('hex') + '?d=404',
+			path: '/avatar/' + hash('md5').update(to).digest('hex') + '?d=404',
 			method: 'GET'
 		};
 
+		util.log('No XMPP avatar, falling back to Gravatar for ' + to + '.');
+
 		var r = http.request(options, function(r) {
 			if (r.statusCode != 200)
 				return svgError(res, message + ' Additionaly, no gravatar available.');
@@ -121,7 +140,8 @@ if (config.useGravatar)
 		return r.end();
 	};
 else
-	var noAvatar = function(res, _, message) {
+	var noAvatar = function(res, to, message) {
+		util.log('No XMPP avatar for ' + to + '.');
 		return svgError(res, message);
 	};
 
@@ -148,12 +168,12 @@ function onIq(stanza) {
 			var err = 'none';
 		}
 
-		return noAvatar(res, from, 'Error during query of this user’s vCard: “'+err+'”.');
+		return noAvatar(res, to, 'Error during query of this user’s vCard: “'+err+'”.');
 	}
 
 	var vCard = stanza.getChild('vCard', 'vcard-temp');
 	if (!vCard)
-		return noAvatar(res, from, 'Error: this user doesn’t have a vCard.');
+		return noAvatar(res, to, 'Error: this user doesn’t have a vCard.');
 
 	try {
 		var photo = vCard.getChild('PHOTO', 'vcard-temp');
@@ -165,7 +185,7 @@ function onIq(stanza) {
 			if (config.guessType)
 				type = 'image/png'; // FIXME: use magic.
 			else
-				return noAvatar(res, from, 'Error: this user’s vCard doesn’t specify the MIME type of its avatar.');
+				return noAvatar(res, to, 'Error: this user’s vCard doesn’t specify the MIME type of its avatar.');
 		}
 
 		var ext;
@@ -176,7 +196,7 @@ function onIq(stanza) {
 		// Here we don’t try to guess the extension even if the option is set.
 		if (ext === undefined) {
 			console.log('Unknown MIME type: '+type);
-			return noAvatar(res, from, 'Error: this user’s avatar is in an unknown format.');
+			return noAvatar(res, to, 'Error: this user’s avatar is in an unknown format.');
 		}
 
 		var binval = new Buffer(base64.replace(/\n/g, ''), 'base64');
@@ -186,7 +206,7 @@ function onIq(stanza) {
 			showImage(to, res);
 		});
 	} catch (e) {
-		return noAvatar(res, from, 'Error: this user doesn’t have an avatar in his/her vCard.');
+		return noAvatar(res, to, 'Error: this user doesn’t have an avatar in his/her vCard.');
 	}
 }
 
@@ -250,11 +270,11 @@ fs.readdir(config.directory, function(er
 });
 
 http.createServer(function (req, res) {
-	console.log('Connection from ' + (req.headers['x-forwarded-for'] || req.client.remoteAddress) + ' (' + req.headers['user-agent'] + ') to ' + req.method.toLocaleLowerCase() + ' “' + req.url + '”.');
+	util.log('Connection from ' + (req.headers['x-forwarded-for'] || req.client.remoteAddress) + ' (' + req.headers['user-agent'] + ') to ' + req.method.toLocaleLowerCase() + ' “' + req.url + '”.');
 
 	var easterEggs = {
 		source: {
-			re: new RegExp(config.webRoot + 'source/code$'),
+			re: new RegExp('^' + config.webRoot + 'source/code$'),
 			file: process.argv[1],
 			mime: 'application/ecmascript',
 			error: 'source code unavailable! oO'
@@ -273,11 +293,11 @@ http.createServer(function (req, res) {
 	for (var i in easterEggs) {
 		var ee = easterEggs[i];
 		var file = ee.file || i;
-		var re = ee.re || new RegExp(config.webRoot + file + '$');
+		var re = ee.re || new RegExp('^' + config.webRoot + file + '$');
 		if (re.test(req.url)) {
 			fs.readFile(file, function(err, content) {
 				if (err)
-					return noAvatar(res, from, 'Error: ' + (ee.error || file + ' unavailable.'));
+					return noAvatar(res, to, 'Error: ' + (ee.error || file + ' unavailable.'));
 
 				res.writeHead(200, {'Content-Type': ee.mime || 'text/plain'});
 				res.end(content);
@@ -286,7 +306,7 @@ http.createServer(function (req, res) {
 		}
 	}
 
-	var jid = unescape(req.url.replace(new RegExp(config.webRoot), ''));
+	var jid = unescape(req.url.replace(new RegExp('^' + config.webRoot), ''));
 
 	if (jid === 'redirect') {
 		if (req.method !== 'POST') {
--- a/configuration.js.example
+++ b/configuration.js.example
@@ -21,11 +21,11 @@
 var config = exports;
 
 // The JID and password of the account used.
-config.jid = 'avatar.example.org';
+config.jid = 'avatar@example.org';
 config.password = 'hellohello';
 
 // Root of the webservice, useful if you want to proxy it.
-config.webRoot = '^/avatar/';
+config.webRoot = '/avatar/';
 
 // These are the host and the port on which the web service will
 // listen.  If you want IPv4 connection only, instead of both IPv4 and
@@ -54,5 +54,12 @@ config.extensions = {
 	gif: 'image/gif'
 };
 
+// Image to use as fallback when there is no usable avatar. If not set,
+// a SVG image will be displayed.
+//config.defaultImage = 'http://example.org/default.svg';
+
 // Try Gravatar if there is a problem during XMPP avatar retrieving.
-config.useGravatar = true;
+//config.useGravatar = false;
+
+// Prints XMPP stanzas
+config.debug = false;