Changeset

314:df212e1fa576

mod_remote_roster: Initial commit. Implements 2.2, 2.3 of <http://jkaluza.fedorapeople.org/remote-roster.html>.
author Waqas Hussain <waqas20@gmail.com>
date Mon, 10 Jan 2011 16:35:43 +0500
parents 313:524f22ef2c2b
children 315:dead242beee3
files mod_remote_roster/mod_remote_roster.lua
diffstat 1 files changed, 165 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_remote_roster/mod_remote_roster.lua	Mon Jan 10 16:35:43 2011 +0500
@@ -0,0 +1,165 @@
+--
+-- mod_remote_roster
+--
+-- This is an experimental implementation of http://jkaluza.fedorapeople.org/remote-roster.html
+--
+
+local st = require "util.stanza";
+local jid_split = require "util.jid".split;
+local jid_prep = require "util.jid".prep;
+local t_concat = table.concat;
+local tonumber = tonumber;
+local pairs, ipairs = pairs, ipairs;
+local hosts = hosts;
+
+local load_roster = require "core.rostermanager".load_roster;
+local rm_remove_from_roster = require "core.rostermanager".remove_from_roster;
+local rm_add_to_roster = require "core.rostermanager".add_to_roster;
+local rm_roster_push = require "core.rostermanager".roster_push;
+local core_post_stanza = core_post_stanza;
+local user_exists = require "core.usermanager".user_exists;
+local add_task = require "util.timer".add_task;
+
+module:hook("iq-get/bare/jabber:iq:roster:query", function(event)
+	local origin, stanza = event.origin, event.stanza;
+	
+	if origin.type == "component" and stanza.attr.from == origin.host then
+		local node, host = jid_split(stanza.attr.to);
+		local roster = load_roster(node, host);
+		
+		local reply = st.reply(stanza):query("jabber:iq:roster");
+		for jid, item in pairs(roster) do
+			if jid ~= "pending" and jid then
+				local node, host = jid_split(jid);
+				if host == origin.host then -- only include contacts which are on this component
+					reply:tag("item", {
+						jid = jid,
+						subscription = item.subscription,
+						ask = item.ask,
+						name = item.name,
+					});
+					for group in pairs(item.groups) do
+						reply:tag("group"):text(group):up();
+					end
+					reply:up(); -- move out from item
+				end
+			end
+		end
+		origin.send(reply);
+		--origin.interested = true; -- resource is interested in roster updates
+		return true;
+	end
+end);
+
+module:hook("iq-set/bare/jabber:iq:roster:query", function(event)
+	local session, stanza = event.origin, event.stanza;
+	
+	if not(session.type == "component" and stanza.attr.from == session.host) then return; end
+	local from_node, from_host = jid_split(stanza.attr.to);
+	if not(user_exists(from_node, from_host)) then return; end
+	local roster = load_roster(from_node, from_host);
+	if not(roster) then return; end
+
+	local query = stanza.tags[1];
+	if #query.tags == 1 and query.tags[1].name == "item"
+			and query.tags[1].attr.xmlns == "jabber:iq:roster" and query.tags[1].attr.jid
+			-- Protection against overwriting roster.pending, until we move it
+			and query.tags[1].attr.jid ~= "pending" then
+		local item = query.tags[1];
+		local jid = jid_prep(item.attr.jid);
+		local node, host, resource = jid_split(jid);
+		if not resource and host then
+			if jid ~= stanza.attr.to then
+				if item.attr.subscription == "remove" then
+					local r_item = roster[jid];
+					if r_item then
+						local to_bare = node and (node.."@"..host) or host; -- bare JID
+						if r_item.subscription == "both" or r_item.subscription == "from" or (roster.pending and roster.pending[jid]) then
+							core_post_stanza(session, st.presence({type="unsubscribed", from=session.full_jid, to=to_bare}));
+						end
+						if r_item.subscription == "both" or r_item.subscription == "to" or r_item.ask then
+							core_post_stanza(session, st.presence({type="unsubscribe", from=session.full_jid, to=to_bare}));
+						end
+						local success, err_type, err_cond, err_msg = rm_remove_from_roster(session, jid);
+						if success then
+							session.send(st.reply(stanza));
+							rm_roster_push(from_node, from_host, jid);
+						else
+							session.send(st.error_reply(stanza, err_type, err_cond, err_msg));
+						end
+					else
+						session.send(st.error_reply(stanza, "modify", "item-not-found"));
+					end
+				else
+					local r_item = {name = item.attr.name, groups = {}};
+					if r_item.name == "" then r_item.name = nil; end
+					if session.roster[jid] then
+						r_item.subscription = session.roster[jid].subscription;
+						r_item.ask = session.roster[jid].ask;
+					else
+						r_item.subscription = "none";
+					end
+					for _, child in ipairs(item) do
+						if child.name == "group" then
+							local text = t_concat(child);
+							if text and text ~= "" then
+								r_item.groups[text] = true;
+							end
+						end
+					end
+					local success, err_type, err_cond, err_msg = rm_add_to_roster(session, jid, r_item);
+					if success then -- Ok, send success
+						session.send(st.reply(stanza));
+						-- and push change to all resources
+						rm_roster_push(from_node, from_host, jid);
+					else -- Adding to roster failed
+						session.send(st.error_reply(stanza, err_type, err_cond, err_msg));
+					end
+				end
+			else -- Trying to add self to roster
+				session.send(st.error_reply(stanza, "cancel", "not-allowed"));
+			end
+		else -- Invalid JID added to roster
+			session.send(st.error_reply(stanza, "modify", "bad-request")); -- FIXME what's the correct error?
+		end
+	else -- Roster set didn't include a single item, or its name wasn't  'item'
+		session.send(st.error_reply(stanza, "modify", "bad-request"));
+	end
+	return true;
+end);
+
+function component_roster_push(node, host, jid)
+	local roster = load_roster(node, host);
+	if roster then
+		local item = roster[jid];
+		local contact_node, contact_host = jid_split(jid);
+		local stanza = st.iq({ type="set", from=node.."@"..host, to=contact_host }):query("jabber:iq:roster");
+		if item then
+			stanza:tag("item", { jid = jid, subscription = item.subscription, name = item.name, ask = item.ask });
+			for group in pairs(item.groups) do
+				stanza:tag("group"):text(group):up();
+			end
+		else
+			stanza:tag("item", {jid = jid, subscription = "remove"});
+		end
+		stanza:up(); -- move out from item
+		stanza:up(); -- move out from stanza
+		core_post_stanza(hosts[module.host], stanza);
+	end
+end
+
+module:hook("iq-set/bare/jabber:iq:roster:query", function(event)
+	local origin, stanza = event.origin, event.stanza;
+	local query = stanza.tags[1];
+	local item = query.tags[1];
+	local contact_jid = item and item.name == "item" and item.attr.jid ~= "pending" and item.attr.jid;
+	if contact_jid then
+		local contact_node, contact_host = jid_split(contact_jid);
+		if hosts[contact_host] and hosts[contact_host].type == "component" then
+			local node, host = jid_split(stanza.attr.to or origin.full_jid);
+			add_task(0, function()
+				component_roster_push(node, host, contact_jid);
+			end);
+		end
+	end
+end, 100);