mod_presence: Re-probe for contacts presence after outgoing 'subscribed' (fixes ...
[prosody.git] / plugins / mod_presence.lua
index 8e6ef85a35ab97ab928f6a7e7a4dcbd6d9c5c492..a5b4f282a4da5635cedf7b81f7e86ac5c9bc17de 100644 (file)
@@ -9,20 +9,23 @@
 local log = module._log;
 
 local require = require;
-local pairs, ipairs = pairs, ipairs;
+local pairs = pairs;
 local t_concat, t_insert = table.concat, table.insert;
 local s_find = string.find;
 local tonumber = tonumber;
 
+local core_post_stanza = prosody.core_post_stanza;
 local st = require "util.stanza";
 local jid_split = require "util.jid".split;
 local jid_bare = require "util.jid".bare;
-local hosts = hosts;
+local datetime = require "util.datetime";
+local hosts = prosody.hosts;
+local bare_sessions = prosody.bare_sessions;
+local full_sessions = prosody.full_sessions;
 local NULL = {};
 
 local rostermanager = require "core.rostermanager";
 local sessionmanager = require "core.sessionmanager";
-local offlinemanager = require "core.offlinemanager";
 
 local function select_top_resources(user)
        local priority = 0;
@@ -59,6 +62,15 @@ function handle_normal_presence(origin, stanza)
                        priority[1] = "0";
                end
        end
+       local priority = stanza:child_with_name("priority");
+       if priority and #priority > 0 then
+               priority = t_concat(priority);
+               if s_find(priority, "^[+-]?[0-9]+$") then
+                       priority = tonumber(priority);
+                       if priority < -128 then priority = -128 end
+                       if priority > 127 then priority = 127 end
+               else priority = 0; end
+       else priority = 0; end
        if full_sessions[origin.full_jid] then -- if user is still connected
                origin.send(stanza); -- reflect their presence back to them
        end
@@ -105,12 +117,10 @@ function handle_normal_presence(origin, stanza)
                                core_post_stanza(origin, request, true);
                        end
                end
-               local offline = offlinemanager.load(node, host);
-               if offline then
-                       for _, msg in ipairs(offline) do
-                               origin.send(msg); -- FIXME do we need to modify to/from in any way?
-                       end
-                       offlinemanager.deleteAll(node, host);
+
+               if priority >= 0 then
+                       local event = { origin = origin }
+                       module:fire_event('message/offline/broadcast', event);
                end
        end
        if stanza.attr.type == "unavailable" then
@@ -128,15 +138,7 @@ function handle_normal_presence(origin, stanza)
                end
        else
                origin.presence = stanza;
-               local priority = stanza:child_with_name("priority");
-               if priority and #priority > 0 then
-                       priority = t_concat(priority);
-                       if s_find(priority, "^[+-]?[0-9]+$") then
-                               priority = tonumber(priority);
-                               if priority < -128 then priority = -128 end
-                               if priority > 127 then priority = 127 end
-                       else priority = 0; end
-               else priority = 0; end
+               stanza:tag("delay", { xmlns = "urn:xmpp:delay", from = host, stamp = datetime.datetime() }):up();
                if origin.priority ~= priority then
                        origin.priority = priority;
                        recalc_resource_map(user);
@@ -163,7 +165,7 @@ function send_presence_of_available_resources(user, host, jid, recipient_session
                        end
                end
        end
-       log("debug", "broadcasted presence of "..count.." resources from "..user.."@"..host.." to "..jid);
+       log("debug", "broadcasted presence of %d resources from %s@%s to %s", count, user, host, jid);
        return count;
 end
 
@@ -172,7 +174,7 @@ function handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_
        if to_bare == from_bare then return; end -- No self contacts
        local st_from, st_to = stanza.attr.from, stanza.attr.to;
        stanza.attr.from, stanza.attr.to = from_bare, to_bare;
-       log("debug", "outbound presence "..stanza.attr.type.." from "..from_bare.." for "..to_bare);
+       log("debug", "outbound presence %s from %s for %s", stanza.attr.type, from_bare, to_bare);
        if stanza.attr.type == "probe" then
                stanza.attr.from, stanza.attr.to = st_from, st_to;
                return;
@@ -199,13 +201,23 @@ function handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_
                end
                core_post_stanza(origin, stanza);
                send_presence_of_available_resources(node, host, to_bare, origin);
+               core_post_stanza(origin, st.presence({ type = "probe", from = from_bare, to = to_bare }));
        elseif stanza.attr.type == "unsubscribed" then
-               -- 1. route stanza
-               -- 2. roster push (subscription = none or to)
-               if rostermanager.unsubscribed(node, host, to_bare) then
-                       rostermanager.roster_push(node, host, to_bare);
+               -- 1. send unavailable
+               -- 2. route stanza
+               -- 3. roster push (subscription = from or both)
+               local success, pending_in, subscribed = rostermanager.unsubscribed(node, host, to_bare);
+               if success then
+                       if subscribed then
+                               rostermanager.roster_push(node, host, to_bare);
+                       end
+                       core_post_stanza(origin, stanza);
+                       if subscribed then
+                               send_presence_of_available_resources(node, host, to_bare, origin, st.presence({ type = "unavailable" }));
+                       end
                end
-               core_post_stanza(origin, stanza);
+       else
+               origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid presence type"));
        end
        stanza.attr.from, stanza.attr.to = st_from, st_to;
        return true;
@@ -215,7 +227,7 @@ function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_b
        local node, host = jid_split(to_bare);
        local st_from, st_to = stanza.attr.from, stanza.attr.to;
        stanza.attr.from, stanza.attr.to = from_bare, to_bare;
-       log("debug", "inbound presence "..stanza.attr.type.." from "..from_bare.." for "..to_bare);
+       log("debug", "inbound presence %s from %s for %s", stanza.attr.type, from_bare, to_bare);
        
        if stanza.attr.type == "probe" then
                local result, err = rostermanager.is_contact_subscribed(node, host, from_bare);
@@ -256,7 +268,9 @@ function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_b
                        sessionmanager.send_to_interested_resources(node, host, stanza);
                        rostermanager.roster_push(node, host, from_bare);
                end
-       end -- discard any other type
+       else
+               origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid presence type"));
+       end
        stanza.attr.from, stanza.attr.to = st_from, st_to;
        return true;
 end
@@ -310,6 +324,8 @@ module:hook("presence/bare", function(data)
                end -- no resources not online, discard
        elseif not t or t == "unavailable" then
                handle_normal_presence(origin, stanza);
+       else
+               origin.send(st.error_reply(stanza, "modify", "bad-request", "Invalid presence type"));
        end
        return true;
 end);
@@ -331,7 +347,7 @@ module:hook("presence/full", function(data)
 end);
 module:hook("presence/host", function(data)
        -- inbound presence to the host
-       local origin, stanza = data.origin, data.stanza;
+       local stanza = data.stanza;
        
        local from_bare = jid_bare(stanza.attr.from);
        local t = stanza.attr.type;
@@ -349,13 +365,15 @@ module:hook("resource-unbind", function(event)
        -- Send unavailable presence
        if session.presence then
                local pres = st.presence{ type = "unavailable" };
-               if not(err) or err == "closed" then err = "connection closed"; end
-               pres:tag("status"):text("Disconnected: "..err):up();
+               if err then
+                       pres:tag("status"):text("Disconnected: "..err):up();
+               end
                session:dispatch_stanza(pres);
        elseif session.directed then
                local pres = st.presence{ type = "unavailable", from = session.full_jid };
-               if not(err) or err == "closed" then err = "connection closed"; end
-               pres:tag("status"):text("Disconnected: "..err):up();
+               if err then
+                       pres:tag("status"):text("Disconnected: "..err):up();
+               end
                for jid in pairs(session.directed) do
                        pres.attr.to = jid;
                        core_post_stanza(session, pres, true);