# HG changeset patch # User Waqas Hussain # Date 1294659343 -18000 # Node ID df212e1fa576e855c13016220445df52758ff368 # Parent 524f22ef2c2be30ecc08a018b805bd830a80082c mod_remote_roster: Initial commit. Implements 2.2, 2.3 of . diff -r 524f22ef2c2b -r df212e1fa576 mod_remote_roster/mod_remote_roster.lua --- /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);