stanza_router: Use stanza.attr.from's host instead of origin.host when routing stanza...
[prosody.git] / core / stanza_router.lua
index 1a9fb26ce6d85283898ec9cd72c7be8608786aa1..22064f3f2a0304e2f829a933d6e8f2f08f01abb3 100644 (file)
@@ -20,9 +20,6 @@ local rostermanager = require "core.rostermanager";
 local sessionmanager = require "core.sessionmanager";
 local offlinemanager = require "core.offlinemanager";
 
-local s2s_verify_dialback = require "core.s2smanager".verify_dialback;
-local s2s_make_authenticated = require "core.s2smanager".make_authenticated;
-
 local modules_handle_stanza = require "core.modulemanager".handle_stanza;
 local component_handle_stanza = require "core.componentmanager".handle_stanza;
 
@@ -42,7 +39,9 @@ local ipairs = ipairs;
 local jid_split = require "util.jid".split;
 local jid_prepped_split = require "util.jid".prepped_split;
 local print = print;
-local fire_event = require "core.eventmanager2".fire_event;
+local fire_event = prosody.events.fire_event;
+
+local select_best_resources;
 
 function core_process_stanza(origin, stanza)
        (origin.log or log)("debug", "Received[%s]: %s", origin.type, stanza:top_tag())
@@ -60,9 +59,10 @@ function core_process_stanza(origin, stanza)
        end
 
        if origin.type == "c2s" and not origin.full_jid
-               and not(stanza.name == "iq" and stanza.tags[1].name == "bind"
+               and not(stanza.name == "iq" and stanza.attr.type == "set" and stanza.tags[1] and stanza.tags[1].name == "bind"
                                and stanza.tags[1].attr.xmlns == "urn:ietf:params:xml:ns:xmpp-bind") then
-               error("Client MUST bind resource after auth");
+               -- authenticated client isn't bound and current stanza is not a bind request
+               origin.send(st.error_reply(stanza, "auth", "not-authorized")); -- FIXME maybe allow stanzas to account or server
        end
 
        -- TODO also, stanzas should be returned to their original state before the function ends
@@ -78,7 +78,9 @@ function core_process_stanza(origin, stanza)
                node, host, resource = jid_prepped_split(to);
                if not host then
                        log("warn", "Received stanza with invalid destination JID: %s", to);
-                       origin.send(st.error_reply(stanza, "modify", "jid-malformed", "The destination address is invalid: "..to));
+                       if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
+                               origin.send(st.error_reply(stanza, "modify", "jid-malformed", "The destination address is invalid: "..to));
+                       end
                        return;
                end
                to_bare = node and (node.."@"..host) or host; -- bare JID
@@ -90,7 +92,9 @@ function core_process_stanza(origin, stanza)
                from_node, from_host, from_resource = jid_prepped_split(from);
                if not from_host then
                        log("warn", "Received stanza with invalid source JID: %s", from);
-                       origin.send(st.error_reply(stanza, "modify", "jid-malformed", "The source address is invalid: "..from));
+                       if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
+                               origin.send(st.error_reply(stanza, "modify", "jid-malformed", "The source address is invalid: "..from));
+                       end
                        return;
                end
                from_bare = from_node and (from_node.."@"..from_host) or from_host; -- bare JID
@@ -113,6 +117,21 @@ function core_process_stanza(origin, stanza)
                end
                core_post_stanza(origin, stanza);
        else
+               local h = hosts[stanza.attr.to or origin.host or origin.to_host];
+               if h then
+                       local event;
+                       if stanza.attr.xmlns == "jabber:client" then
+                               if stanza.name == "iq" and (stanza.attr.type == "set" or stanza.attr.type == "get") then
+                                       event = "stanza/iq/"..stanza.tags[1].attr.xmlns..":"..stanza.tags[1].name;
+                               else
+                                       event = "stanza/"..stanza.name;
+                               end
+                       else
+                               event = "stanza/"..stanza.attr.xmlns..":"..stanza.name;
+                       end
+                       if h.events.fire_event(event, {origin = origin, stanza = stanza}) then return; end
+               end
+               if host and not hosts[host] then host = nil; end -- workaround for a Pidgin bug which sets 'to' to the SRV result
                modules_handle_stanza(host or origin.host or origin.to_host, origin, stanza);
        end
 end
@@ -122,8 +141,41 @@ function core_post_stanza(origin, stanza)
        local node, host, resource = jid_split(to);
        local to_bare = node and (node.."@"..host) or host; -- bare JID
 
+       local to_type;
+       if node then
+               if resource then
+                       to_type = '/full';
+               else
+                       to_type = '/bare';
+                       if node == origin.username and host == origin.host then
+                               stanza.attr.to = nil;
+                       end
+               end
+       else
+               if host then
+                       to_type = '/host';
+               else
+                       to_type = '/bare';
+               end
+       end
+
        local event_data = {origin=origin, stanza=stanza};
-       if fire_event(tostring(host or origin.host).."/"..stanza.name, event_data) then
+       if origin.full_jid then -- c2s connection
+               if hosts[origin.host].events.fire_event('pre-'..stanza.name..to_type, event_data) then return; end -- do preprocessing
+       end
+       local h = hosts[to_bare] or hosts[host or origin.host];
+       if h then
+               if h.type == "component" then
+                       component_handle_stanza(origin, stanza);
+                       return;
+               else
+                       if h.events.fire_event(stanza.name..to_type, event_data) then return; end -- do processing
+               end
+       end
+
+       if host and fire_event(host.."/"..stanza.name, event_data) then
+               -- event handled
+       elseif stanza.name == "presence" and origin.host and fire_event(origin.host.."/"..stanza.name, event_data) then
                -- event handled
        elseif not to then
                modules_handle_stanza(host or origin.host or origin.to_host, origin, stanza);
@@ -193,25 +245,12 @@ function core_route_stanza(origin, stanza)
                                                                session.send(stanza);
                                                        end
                                                end
-                                       elseif resource and stanza.attr.type == 'groupchat' then
+                                       elseif stanza.attr.type == 'groupchat' then
                                                -- Groupchat message sent to offline resource
                                                origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
                                        else
-                                               local priority = 0;
-                                               local recipients = {};
-                                               for _, session in pairs(user.sessions) do -- find resource with greatest priority
-                                                       if session.presence then
-                                                               local p = session.priority;
-                                                               if p > priority then
-                                                                       priority = p;
-                                                                       recipients = {session};
-                                                               elseif p == priority then
-                                                                       t_insert(recipients, session);
-                                                               end
-                                                       end
-                                               end
                                                local count = 0;
-                                               for _, session in ipairs(recipients) do
+                                               for _, session in ipairs(select_best_resources(user)) do
                                                        session.send(stanza);
                                                        count = count + 1;
                                                end
@@ -271,10 +310,27 @@ function core_route_stanza(origin, stanza)
                stanza.attr.xmlns = xmlns; -- reset
        elseif origin.type == "component" or origin.type == "local" then
                -- Route via s2s for components and modules
-               log("debug", "Routing outgoing stanza for %s to %s", origin.host, host);
-               send_s2s(origin.host, host, stanza);
+               log("debug", "Routing outgoing stanza for %s to %s", from_host, host);
+               send_s2s(from_host, host, stanza);
        else
                log("warn", "received stanza from unhandled connection type: %s", origin.type);
        end
        stanza.attr.to = to; -- reset
 end
+
+function select_best_resources(user)
+       local priority = 0;
+       local recipients = {};
+       for _, session in pairs(user.sessions) do -- find resource with greatest priority
+               if session.presence then
+                       local p = session.priority;
+                       if p > priority then
+                               priority = p;
+                               recipients = {session};
+                       elseif p == priority then
+                               t_insert(recipients, session);
+                       end
+               end
+       end
+       return recipients;
+end