diff options
Diffstat (limited to 'plugins/mod_roster.lua')
-rw-r--r-- | plugins/mod_roster.lua | 170 |
1 files changed, 151 insertions, 19 deletions
diff --git a/plugins/mod_roster.lua b/plugins/mod_roster.lua index 8b4a3a0a..d530bb45 100644 --- a/plugins/mod_roster.lua +++ b/plugins/mod_roster.lua @@ -1,24 +1,156 @@ +-- Prosody IM +-- Copyright (C) 2008-2010 Matthew Wild +-- Copyright (C) 2008-2010 Waqas Hussain +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- + local st = require "util.stanza" -local send = require "core.sessionmanager".send_to_session - -add_iq_handler("c2s", "jabber:iq:roster", - function (session, stanza) - if stanza.attr.type == "get" then - session.roster = session.roster or rostermanager.getroster(session.username, session.host); - if session.roster == false then - send(session, st.reply(stanza) - :tag("error", { type = "wait" }) - :tag("internal-server-error", { xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas"})); - return true; - else session.roster = session.roster or {}; + +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 rm_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 = prosody.core_post_stanza; + +module:add_feature("jabber:iq:roster"); + +local rosterver_stream_feature = st.stanza("ver", {xmlns="urn:xmpp:features:rosterver"}); +module:hook("stream-features", function(event) + local origin, features = event.origin, event.features; + if origin.username then + features:add_child(rosterver_stream_feature); + end +end); + +module:hook("iq/self/jabber:iq:roster:query", function(event) + local session, stanza = event.origin, event.stanza; + + if stanza.attr.type == "get" then + local roster = st.reply(stanza); + + local client_ver = tonumber(stanza.tags[1].attr.ver); + local server_ver = tonumber(session.roster[false].version or 1); + + if not (client_ver and server_ver) or client_ver ~= server_ver then + roster:query("jabber:iq:roster"); + -- Client does not support versioning, or has stale roster + for jid, item in pairs(session.roster) do + if jid ~= "pending" and jid then + roster:tag("item", { + jid = jid, + subscription = item.subscription, + ask = item.ask, + name = item.name, + }); + for group in pairs(item.groups) do + roster:tag("group"):text(group):up(); + end + roster:up(); -- move out from item end - local roster = st.reply(stanza) - :query("jabber:iq:roster"); - for jid in pairs(session.roster) do - roster:tag("item", { jid = jid, subscription = "none" }):up(); + end + roster.tags[1].attr.ver = server_ver; + end + session.send(roster); + session.interested = true; -- resource is interested in roster updates + else -- stanza.attr.type == "set" + 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 from_node, from_host = jid_split(stanza.attr.from); + local jid = jid_prep(item.attr.jid); + local node, host, resource = jid_split(jid); + if not resource and host then + if jid ~= from_node.."@"..from_host then + if item.attr.subscription == "remove" then + local roster = session.roster; + 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 - send(session, roster); - return true; + 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 + end + return true; +end); + +module:hook_global("user-deleted", function(event) + local username, host = event.username, event.host; + if host ~= module.host then return end + local bare = username .. "@" .. host; + local roster = rm_load_roster(username, host); + for jid, item in pairs(roster) do + if jid and jid ~= "pending" then + if item.subscription == "both" or item.subscription == "from" or (roster.pending and roster.pending[jid]) then + module:send(st.presence({type="unsubscribed", from=bare, to=jid})); + end + if item.subscription == "both" or item.subscription == "to" or item.ask then + module:send(st.presence({type="unsubscribe", from=bare, to=jid})); end - end);
\ No newline at end of file + end + end +end, 300); |