diff psgxs.js @ 1:c2954a9e5665

Add a super-owner that has power over all nodes; add support for affiliation changes; support pubsub#creator metadata; change pubsub#digest_frequency default; and use better default config values.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Mon, 28 Jun 2010 21:55:30 +0200
parents 9ee956af41e3
children d3ae2f8b685d
line wrap: on
line diff
--- a/psgxs.js
+++ b/psgxs.js
@@ -59,12 +59,13 @@ function onIq(stanza) {
 		response = xmpp.iq({to: to, from: from, type: 'result'});
 
 	if (type == 'get') {
+
+		// XEP-0092
 		if (stanza.getChild('query', 'jabber:iq:version')) {
-			var os = 'GNU/Linux';
 			var query = xmpp.stanza('query', {xmlns: 'jabber:iq:version'})
-				.s('name').t('PubSubJS')
-				.s('version').t('Pas releasé')
-				.s('os').t(os);
+				.s('name').t('PSĜS')
+				.s('version').t(config.version)
+				.s('os').t(config.os);
 			response.cx(query);
 
 		// SECTION 5.1
@@ -104,6 +105,7 @@ function onIq(stanza) {
 					.s('feature', {'var': 'http://jabber.org/protocol/disco#info'})
 					.s('feature', {'var': 'http://jabber.org/protocol/disco#items'})
 					.s('feature', {'var': 'http://jabber.org/protocol/pubsub'})
+					.s('feature', {'var': 'jabber:iq:version'})
 //					.s('feature', {'var': 'http://jabber.org/protocol/commands'})
 					for (var i in config.activated)
 						if (typeof i == 'string')
@@ -266,7 +268,7 @@ function onIq(stanza) {
 					return makeError(response, errors.node_does_not_exist.n);
 
 				var affil = storage.getAffiliation(toBareJID(to), nodeID);
-				if (affil != 'owner' && affil != 'publisher' && affil != 'member')
+				if (affil != 'super-owner' && affil != 'owner' && affil != 'publisher' && affil != 'member')
 					return makeError(response, errors.pub.publish.insufficient_privileges.n);
 
 				var item = [];
@@ -317,7 +319,7 @@ function onIq(stanza) {
 		} else if (stanza.getChild('pubsub', 'http://jabber.org/protocol/pubsub#owner')) {
 			var pubsub = stanza.getChild('pubsub', 'http://jabber.org/protocol/pubsub#owner');
 
-			// SECTION
+			// SECTION 8.2
 			if (pubsub.getChild('configure')) {
 				if (!config.enabled('config-node'))
 					return makeError(response, errors.owner.configure.node_configuration_not_supported.n);
@@ -329,7 +331,7 @@ function onIq(stanza) {
 					return makeError(response, errors.node_does_not_exist.n);
 
 				var affil = storage.getAffiliation(toBareJID(to), nodeID);
-				if (affil != 'owner' && affil != 'publish-only')
+				if (affil != 'super-owner' && affil != 'owner' && affil != 'publish-only')
 					return makeError(response, errors.pub.publish.insufficient_privileges.n);
 
 				var p = xmpp.stanza('pubsub', {xmlns: 'http://jabber.org/protocol/pubsub#owner'});
@@ -339,7 +341,7 @@ function onIq(stanza) {
 				p.cx(s);
 				response.cx(p);
 
-			// SECTION
+			// SECTION 8.3
 			} else if (pubsub.getChild('default')) {
 				if (!config.enabled('config-node'))
 					return makeError(response, errors.owner.default_options.node_configuration_not_supported.n);
@@ -351,7 +353,7 @@ function onIq(stanza) {
 				p.cx(s);
 				response.cx(p);
 
-			// SECTION
+			// SECTION 8.8
 			} else if (pubsub.getChild('subscriptions')) {
 				if (!config.enabled('manage-subscriptions'))
 					return makeError(response, errors.owner.manage_subscriptions.not_supported.n);
@@ -364,7 +366,8 @@ function onIq(stanza) {
 				if (!storage.existsNode(nodeID))
 					return makeError(response, errors.node_does_not_exist.n);
 
-				if (storage.getAffiliation(toBareJID(to), nodeID) != 'owner')
+				var affil = storage.getAffiliation(toBareJID(to), nodeID);
+				if (affil != 'super-owner' && affil != 'owner')
 					return makeError(response, errors.forbidden.n);
 
 				var p = xmpp.stanza('pubsub', {xmlns: 'http://jabber.org/protocol/pubsub#owner'});
@@ -377,7 +380,7 @@ function onIq(stanza) {
 				p.cx(s);
 				response.cx(p);
 
-			// SECTION
+			// SECTION 8.9
 			} else if (pubsub.getChild('affiliations')) {
 				if (!config.enabled('modify-affiliations'))
 					return makeError(response, errors.owner.manage_affiliations.not_supported.n);
@@ -390,15 +393,16 @@ function onIq(stanza) {
 				if (!storage.existsNode(nodeID))
 					return makeError(response, errors.node_does_not_exist.n);
 
-				var affil = storage.getAffiliationsFromNodeID(nodeID);
-				if (affil[toBareJID(to)] != 'owner')
+				var affils = storage.getAffiliationsFromNodeID(nodeID);
+				var affil = affils[toBareJID(to)];
+				if (affil != 'super-owner' && affil != 'owner')
 					return makeError(response, errors.owner.manage_affiliations.retrieve_list.entity_is_not_an_owner.n);
 
 				var p = xmpp.stanza('pubsub', {xmlns: 'http://jabber.org/protocol/pubsub#owner'});
 				var s = xmpp.stanza('affiliations', {node: nodeID});
 
-				for (var jid in affil)
-					s.s('affiliation', {jid: jid, affiliation: affil[jid]})
+				for (var jid in affils)
+					s.s('affiliation', {jid: jid, affiliation: affils[jid]})
 
 				p.cx(s);
 				response.cx(p);
@@ -463,7 +467,7 @@ function onIq(stanza) {
 						return makeError(response, subID);
 				} else if (configuration['pubsub#access_model'] == 'whitelist') {
 					var affil = storage.getAffiliation(jid, nodeID);
-					if (affil != 'owner' && affil != 'publisher' && affil != 'member')
+					if (affil != 'super-owner' && affil != 'owner' && affil != 'publisher' && affil != 'member')
 						return makeError(response, errors.sub.subscribe.not_on_whitelist.n);
 
 					subID = storage.subscribe(nodeID, jid, conf);
@@ -481,7 +485,7 @@ function onIq(stanza) {
 					var affiliates = storage.getAffiliationsFromNodeID(nodeID);
 					var form = forms.build('form', 'subscribe_authorization', {allow: false, node: nodeID, subscriber_jid: jid}, true); //168
 					for (var i in affiliates) {
-						if (affiliates[i] == 'owner') {
+						if (affiliates[i] == 'super-owner' || affiliates[i] == 'owner') {
 							var message = xmpp.message({to: i}).cx(form);
 							conn.send(message);
 						}
@@ -549,7 +553,7 @@ function onIq(stanza) {
 				if (typeof form == 'number')
 					return makeError(response, form);
 
-			// SECTION
+			// SECTION 7.1
 			} else if (pubsub.getChild('publish')) {
 				if (!config.enabled('publish'))
 					return makeError(response, errors.pub.publish.item_publication_not_supported.n);
@@ -562,7 +566,7 @@ function onIq(stanza) {
 				var affil = storage.getAffiliation(toBareJID(to), nodeID);
 				if (typeof affil == 'number')
 					return makeError(response, affil);
-				if (affil != 'owner' && affil != 'publisher' && affil != 'publish-only')
+				if (affil != 'super-owner' && affil != 'owner' && affil != 'publisher' && affil != 'publish-only')
 					return makeError(response, errors.forbidden.n);
 
 				var item = publish.getChild('item');
@@ -600,12 +604,19 @@ function onIq(stanza) {
 						return makeError(response, r);
 				}
 
-				if (autocreate)
-					storage.createNode(nodeID, form);
+				if (autocreate) {
+					if (!form)
+						form = {};
+					form['pubsub#creator'] = toBareJID(to);
+
+					var r = storage.createNode(nodeID, form);
+					if (typeof r == 'number')
+						return makeError(response, r);
+				}
 
 				var content = item.getChild();
+
 				subscribers = storage.setItem(nodeID, itemID, content);
-
 				if (typeof subscribers == 'number')
 					return makeError(response, subscribers);
 
@@ -620,7 +631,7 @@ function onIq(stanza) {
 					.c('publish', {node: nodeID})
 						.c('item', {id: itemID});
 
-			// SECTION
+			// SECTION 7.2
 			} else if (pubsub.getChild('retract')) {
 				if (!config.enabled('retract-items'))
 					return makeError(response, errors.pub.retract.item_deletion_not_supported.n);
@@ -649,7 +660,7 @@ function onIq(stanza) {
 				attrs[itemID] = {};
 				sendNotifs(subscribers, 'items', nodeID, attrs, 'retract')
 
-			// SECTION
+			// SECTION 8.1
 			} else if (pubsub.getChild('create')) {
 				if (!config.enabled('create-nodes'))
 					return makeError(response, errors.owner.create.node_creation_not_supported.n);
@@ -667,7 +678,7 @@ function onIq(stanza) {
 					return makeError(response, errors.nodeid_already_exists.n);
 
 				var affil = storage.getAffiliation(toBareJID(to), nodeID);
-				if (affil != 'owner' && affil != 'publish-only')
+				if (affil != 'super-owner' && affil != 'owner' && affil != 'publish-only')
 					return makeError(response, errors.forbidden.n);
 
 				var configure = pubsub.getChild('configure');
@@ -689,6 +700,9 @@ function onIq(stanza) {
 					var conf = form;
 				}
 
+				if (!conf)
+					conf = {};
+				conf['pubsub#creator'] = toBareJID(to);
 				var r = storage.createNode(nodeID, conf);
 				if (typeof r == 'number')
 					return makeError(response, r);
@@ -701,7 +715,7 @@ function onIq(stanza) {
 		} else if (stanza.getChild('pubsub', 'http://jabber.org/protocol/pubsub#owner')) {
 			var pubsub = stanza.getChild('pubsub', 'http://jabber.org/protocol/pubsub#owner');
 
-			// SECTION
+			// SECTION 8.2.4
 			if (pubsub.getChild('configure')) {
 				if (!config.enabled('config-node'))
 					return makeError(response, errors.owner.configure.node_configuration_not_supported.n);
@@ -713,7 +727,7 @@ function onIq(stanza) {
 					return makeError(response, errors.node_does_not_exist.n);
 
 				var affil = storage.getAffiliation(toBareJID(to), nodeID);
-				if (affil != 'owner' && affil != 'publish-only')
+				if (affil != 'super-owner' && affil != 'owner' && affil != 'publish-only')
 					return makeError(response, errors.forbidden.n);
 
 				var x = configure.getChild('x', 'jabber:x:data');
@@ -730,7 +744,7 @@ function onIq(stanza) {
 				if (typeof set == 'number')
 					return makeError(response, set);
 
-			// SECTION
+			// SECTION 8.4
 			} else if (pubsub.getChild('delete')) {
 				if (!config.enabled('delete-nodes'))
 					return makeError(response, errors.feature_not_implemented.n); //XXX
@@ -743,7 +757,8 @@ function onIq(stanza) {
 				if (!storage.existsNode(nodeID))
 					return makeError(response, errors.node_does_not_exist.n);
 
-				if (storage.getAffiliation(toBareJID(to), nodeID) != 'owner')
+				var affil = storage.getAffiliation(toBareJID(to), nodeID);
+				if (affil != 'super-owner' && affil != 'owner')
 					return makeError(response, errors.forbidden.n);
 
 				var notifs = storage.deleteNode(nodeID);
@@ -752,7 +767,7 @@ function onIq(stanza) {
 
 				sendNotifs(notifs, 'delete', nodeID);
 
-			// SECTION
+			// SECTION 8.5
 			} else if (pubsub.getChild('purge')) {
 				if (!config.enabled('purge-nodes'))
 					return makeError(response, errors.owner.purge.node_purging_not_supported.n); //XXX
@@ -765,7 +780,8 @@ function onIq(stanza) {
 				if (!storage.existsNode(nodeID))
 					return makeError(response, errors.node_does_not_exist.n);
 
-				if (storage.getAffiliation(toBareJID(to), nodeID) != 'owner')
+				var affil = storage.getAffiliation(toBareJID(to), nodeID);
+				if (affil != 'super-owner' && affil != 'owner')
 					return makeError(response, errors.forbidden.n);
 
 				if (!config.enabled('persistent-items')) //FIXME: autre condition, supporté par le node
@@ -777,7 +793,7 @@ function onIq(stanza) {
 
 				sendNotifs(notifs, 'purge', nodeID);
 
-			// SECTION
+			// SECTION 8.8.2
 			} else if (pubsub.getChild('subscriptions')) {
 				if (!config.enabled('manage-subscriptions'))
 					return makeError(response, errors.owner.manage_subscriptions.not_supported.n); //XXX
@@ -790,7 +806,8 @@ function onIq(stanza) {
 				if (!storage.existsNode(nodeID))
 					return makeError(response, errors.node_does_not_exist.n);
 
-				if (storage.getAffiliation(toBareJID(to), nodeID) != 'owner')
+				var affil = storage.getAffiliation(toBareJID(to), nodeID);
+				if (affil != 'super-owner' && affil != 'owner')
 					return makeError(response, errors.forbidden.n);
 
 				var e = false;
@@ -802,6 +819,7 @@ function onIq(stanza) {
 					if (typeof set == 'number')
 						e = true;
 					else {
+						// SECTION 8.8.4
 						sendNotifs(jid, 'subscription', nodeID, {jid: jid, subscription: subscription});
 						subscriptions.tags.splice(i, 1);
 					}
@@ -810,7 +828,7 @@ function onIq(stanza) {
 				if (e)
 					return makeError(response, errors.owner.manage_subscriptions.modify.multiple_simultaneous_modifications.n, pubsub);
 
-			// SECTION
+			// SECTION 8.9.2
 			} else if (pubsub.getChild('affiliations')) {
 				if (!config.enabled('modify-affiliations'))
 					return makeError(response, errors.owner.manage_affiliations.not_supported.n); //XXX
@@ -823,7 +841,8 @@ function onIq(stanza) {
 				if (!storage.existsNode(nodeID))
 					return makeError(response, errors.node_does_not_exist.n);
 
-				if (storage.getAffiliation(toBareJID(to), nodeID) != 'owner')
+				var affil = storage.getAffiliation(toBareJID(to), nodeID);
+				if (affil != 'super-owner' && affil != 'owner')
 					return makeError(response, errors.forbidden.n);
 
 				var e = false;
@@ -834,8 +853,11 @@ function onIq(stanza) {
 					var set = storage.setAffiliation(nodeID, jid, affiliation);
 					if (typeof set == 'number')
 						e = true;
-					else
+					else {
+						// SECTION 8.9.4
+						sendNotifs(jid, 'affiliations', nodeID, {jid: jid, affiliation: affiliation});
 						affiliations.children.splice(i, 1);
+					}
 				}
 
 				if (e)
@@ -922,7 +944,25 @@ function makeError(response, errorNumber
 function sendNotifs(notifs, type, nodeID, a1, a2) {
 	var ev = xmpp.stanza('event', {xmlns: 'http://jabber.org/protocol/pubsub#event'});
 
-	if (type == 'collection') {
+	if (type == 'affiliations') {
+		ev.attr.xmlns = 'http://jabber.org/protocol/pubsub';
+
+		var args = {};
+		for (i in a1) {
+			var attr = a1[i];
+			if (i == 'affiliation')
+				args.affiliation = attr;
+			else if (i == 'jid')
+				args.jid = attr;
+		}
+		if (!args.jid || args.jid == '') {
+			_('Error #2', 41)
+			return;
+		}
+		var affiliation = xmpp.stanza('affiliation', );
+		var affiliations = xmpp.stanza('affiliations', {node: nodeID});
+		ev.cx(affiliations);
+	} else if (type == 'collection') {
 		var collection = xmpp.stanza('collection', {node: nodeID});
 		if (a1 == 'associate')
 			collection.cx('associate', {node: nodeID});