diff options
Diffstat (limited to 'plugins/mod_roster.lua')
-rw-r--r-- | plugins/mod_roster.lua | 206 |
1 files changed, 137 insertions, 69 deletions
diff --git a/plugins/mod_roster.lua b/plugins/mod_roster.lua index 8a20f76d..d530bb45 100644 --- a/plugins/mod_roster.lua +++ b/plugins/mod_roster.lua @@ -1,88 +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 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; -add_iq_handler("c2s", "jabber:iq:roster", - function (session, stanza) - if stanza.tags[1].name == "query" then - if stanza.attr.type == "get" then - local roster = st.reply(stanza) - :query("jabber:iq:roster"); - for jid in pairs(session.roster) do - roster:tag("item", { - jid = jid, - subscription = session.roster[jid].subscription, - name = session.roster[jid].name, - }); - for group in pairs(session.roster[jid].groups) do - roster:tag("group"):text(group):up(); - end +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 - send(session, roster); - session.interested = true; -- resource is interested in roster updates - return true; - elseif stanza.attr.type == "set" then - 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 then - local item = query.tags[1]; - local from_node, from_host = jid_split(stanza.attr.from); - local node, host, resource = jid_split(item.attr.jid); - if not resource then - if item.attr.jid ~= from_node.."@"..from_host then - if item.attr.subscription == "remove" then - if session.roster[item.attr.jid] then - local success, err_type, err_cond, err_msg = rm_remove_from_roster(session, item.attr.jid); - if success then - send(session, st.reply(stanza)); - rm_roster_push(from_node, from_host, item.attr.jid); - else - send(session, st.error_reply(stanza, err_type, err_cond, err_msg)); - end - else - send(session, 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[item.attr.jid] then - r_item.subscription = session.roster[item.attr.jid].subscription; - 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, item.attr.jid, r_item); - if success then - send(session, st.reply(stanza)); - rm_roster_push(from_node, from_host, item.attr.jid); - else - send(session, st.error_reply(stanza, err_type, err_cond, err_msg)); - end - end + roster:up(); -- move out from item + end + 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 - send(session, st.error_reply(stanza, "cancel", "not-allowed")); + session.send(st.error_reply(stanza, err_type, err_cond, err_msg)); end else - send(session, st.error_reply(stanza, "modify", "bad-request")); -- FIXME what's the correct error? + session.send(st.error_reply(stanza, "modify", "item-not-found")); end else - send(session, st.error_reply(stanza, "modify", "bad-request")); + 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 - return true; + 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 + 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); |