view psgxs.js @ 33:62cbb1c49bc5

Fix publish; add owner metadata field; add HTTP module and Atom HTTP module.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Fri, 05 Nov 2010 14:01:45 +0100
parents 13011b26181c
children dcf1f09f8cee
line wrap: on
line source

#!/usr/bin/env node

/*
 *  Copyright (C) 2010  Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
 *
 *  This file is part of PSĜS, a PubSub server written in JavaScript.
 *
 *  PSĜS is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License.
 *
 *  PSĜS is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with PSĜS.  If not, see <http://www.gnu.org/licenses/>.
 */

var xmpp = require('xmpp');
var sha1 = require('sha1');
require('./iso8601');
var storage = require('./storage');
var errors = require('./errors');
var makeError = errors.makeError;
var utils = require('./util');
var toBareJID = utils.toBareJID;
var config = require('./configuration');
var forms = require('./forms');
var conn = new xmpp.Connection();

var notifs = require('./notifs');
notifs.setConnection(conn);

var modules = require('./modules');

var service_configuration = config.service_configuration;
var componentJID = config.jid;
var componentPassword = config.password;

conn.log = function (_, m) { console.log(m); };

function _(obj, color) {
	var str = require('sys').inspect(obj, false, null);
	if (color)
		console.log('\033['+c+';1m' + str + '\033[0m');
	else
		console.log(str);
};

process.addListener('uncaughtException', function (err) {
	console.log('\033[41;1mUncaught exception (' + err + '), this should never happen:\033[0m\n' + err.stack);
});

if (typeof xmpp.StanzaBuilder.cnode != 'function' || typeof xmpp.StanzaBuilder.prototype.cnode != 'function') {
	xmpp.StanzaBuilder.prototype.cnode = function (stanza)
	{
		var parent = this.last_node[this.last_node.length-1];
		parent.tags.push(stanza);
		parent.children.push(stanza);
		this.last_node.push(stanza);
		return this;
	};
}

function onIq(stanza) {
	var type = stanza.getAttribute('type');
	var from = stanza.getAttribute('to');
	var to = stanza.getAttribute('from');
	var id = stanza.getAttribute('id');

	var response;
	if (id)
		response = xmpp.iq({to: to, from: from, type: 'result', id: id});
	else
		response = xmpp.iq({to: to, from: from, type: 'result'});

	var send = false;

	for (var i in stanza.tags) {
		var child1 = stanza.tags[i];
		if (child1.name == 'pubsub') {
			for (var j in child1.tags) {
				var child2 = child1.tags[j];

				for (var k in modules) {
					var module = modules[k];

					if (module.type && (module.type != type))
						continue;

					if (module.child2 && (module.child2 != child2.name))
						continue;

					if (module.ns && (module.ns != child2.attr.xmlns))
						continue;

					if (module.number && (module.number != j))
						continue;

					var toSend = module.func(response, stanza, child2, to);
					if (toSend) {
						response = toSend;
						send = true;
						delete toSend;
					}
				}
			}
		} else {
			for (var k in modules) {
				var module = modules[k];

				if (module.type && (module.type != type))
					continue;

				if (module.child && (module.child != child1.name))
					continue;

				if (module.ns && (module.ns != child1.attr.xmlns))
					continue;

				if (module.number && (module.number != k))
					continue;

				var toSend = module.func(response, stanza, child1, to);
				if (toSend) {
					response = toSend;
					send = true;
					delete toSend;
				}
			}
		}
	}

	conn.send(send? response: makeError(response, errors.feature_not_implemented.n));
}

function onMessage(stanza) {
	var from = stanza.getAttribute('to');
	var to = stanza.getAttribute('from');
	var id = stanza.getAttribute('id');

	var response;
	if (id)
		response = xmpp.message({to: to, from: from, id: id});
	else
		response = xmpp.message({to: to, from: from});

	var x = stanza.getChild('x', 'jabber:x:data');
	if (x) {
		var form = forms.parse(x);
		if (form.type == 'submit' && form.fields.FORM_TYPE.value == 'subscribe_authorization') {
			if (form.fields.subid && form.fields.subid.value)
				var subID = form.fields.subid.value;
			if (form.fields.node && form.fields.node.value)
				var nodeID = form.fields.node.value;
			if (form.fields.subscriber_jid && form.fields.subscriber_jid.value)
				var jid = form.fields.subscriber_jid.value;
			if (form.fields.allow && form.fields.allow.value)
				var allow = form.fields.allow.value;

			var type = allow? 'subscribed': 'none';
			var set = storage.subscribe(nodeID, jid, type)
			//if (set.subid != subID) //TODO: support the multi-subscribe feature
			notifs.send(jid, 'subscription', nodeID, {jid: jid, subscription: type});
		} else
			return makeError(response, errors.feature_not_implemented.n);
	} else
		return makeError(response, errors.feature_not_implemented.n);
	conn.send(response)
}

function onPresence(stanza) {
	var from = stanza.getAttribute('to');
	var to = stanza.getAttribute('from');
	var id = stanza.getAttribute('id');

	var response;
	if (id)
		response = xmpp.presence({to: to, from: from, id: id});
	else
		response = xmpp.presence({to: to, from: from});

	makeError(response, errors.feature_not_implemented.n);
}

conn.connect(componentJID, componentPassword, function (status, condition) {
	if (status == xmpp.Status.CONNECTED) {
		conn.addHandler(onMessage, null, 'message', null, null,  null);
		conn.addHandler(onIq, null, 'iq', null, null,  null);
		conn.addHandler(onPresence, null, 'presence', null, null,  null);

		if (process.argv.length >= 3)
			storage.load(process.argv[2]);
		else
			storage.load();

		var stdin = process.openStdin();
		stdin.setEncoding('utf8');
		stdin.addListener('data', storage.debug);
	} else
		conn.log(xmpp.LogLevel.DEBUG, 'New connection status: ' + status + (condition? (' ('+condition+')'): ''));
});