-- Prosody IM
-- Copyright (C) 2008-2009 Matthew Wild
-- Copyright (C) 2008-2009 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
local log = require " util.logger " . init ( " stanzarouter " )
local hosts = _G.prosody . hosts ;
local tostring = tostring ;
local st = require " util.stanza " ;
local send_s2s = require " core.s2smanager " . send_to_host ;
local modules_handle_stanza = require " core.modulemanager " . handle_stanza ;
local component_handle_stanza = require " core.componentmanager " . handle_stanza ;
local jid_split = require " util.jid " . split ;
local jid_prepped_split = require " util.jid " . prepped_split ;
local full_sessions = _G.prosody . full_sessions ;
local bare_sessions = _G.prosody . bare_sessions ;
function core_process_stanza ( origin , stanza )
( origin.log or log ) ( " debug " , " Received[%s]: %s " , origin.type , stanza : top_tag ( ) )
-- Currently we guarantee every stanza to have an xmlns, should we keep this rule?
if not stanza.attr . xmlns then stanza.attr . xmlns = " jabber:client " ; end
-- TODO verify validity of stanza (as well as JID validity)
if stanza.attr . type == " error " and # stanza.tags == 0 then return ; end -- TODO invalid stanza, log
if stanza.name == " iq " then
if not stanza.attr . id then stanza.attr . id = " " ; end -- COMPAT Jabiru doesn't send the id attribute on roster requests
if ( stanza.attr . type == " set " or stanza.attr . type == " get " ) and ( # stanza.tags ~= 1 ) then
origin.send ( st.error_reply ( stanza , " modify " , " bad-request " ) ) ;
return ;
end
end
if origin.type == " c2s " then
if not origin.full_jid
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
-- 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
return ;
end
-- TODO also, stanzas should be returned to their original state before the function ends
stanza.attr . from = origin.full_jid ;
end
local to , xmlns = stanza.attr . to , stanza.attr . xmlns ;
local from = stanza.attr . from ;
local node , host , resource ;
local from_node , from_host , from_resource ;
local to_bare , from_bare ;
if to then
if full_sessions [ to ] or bare_sessions [ to ] or hosts [ to ] then
node , host = jid_split ( to ) ; -- TODO only the host is needed, optimize
else
node , host , resource = jid_prepped_split ( to ) ;
if not host then
log ( " warn " , " Received stanza with invalid destination JID: %s " , 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
if resource then to = to_bare .. " / " .. resource ; else to = to_bare ; end
stanza.attr . to = to ;
end
end
if from and not origin.full_jid then
-- We only stamp the 'from' on c2s stanzas, so we still need to check validity
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 ) ;
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
if from_resource then from = from_bare .. " / " .. from_resource ; else from = from_bare ; end
stanza.attr . from = from ;
end
--[[if to and not(hosts[to]) and not(hosts[to_bare]) and (hosts[host] and hosts[host].type ~= "local") then -- not for us?
log ( " warn " , " stanza recieved for a non-local server " ) ;
return ; -- FIXME what should we do here?
end ] ] -- FIXME
if ( origin.type == " s2sin " or origin.type == " c2s " or origin.type == " component " ) and xmlns == " jabber:client " then
if origin.type == " s2sin " and not origin.dummy then
local host_status = origin.hosts [ from_host ] ;
if not host_status or not host_status.authed then -- remote server trying to impersonate some other server?
log ( " warn " , " Received a stanza claiming to be from %s, over a stream authed for %s! " , from_host , origin.from_host ) ;
return ; -- FIXME what should we do here? does this work with subdomains?
end
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 -- COMPAT: 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
function core_post_stanza ( origin , stanza )
local to = stanza.attr . to ;
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 origin.full_jid == stanza.attr . from 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.events . fire_event ( stanza.name .. to_type , event_data ) then return ; end -- do processing
if h.type == " component " then
component_handle_stanza ( origin , stanza ) ;
return ;
end
modules_handle_stanza ( h.host , origin , stanza ) ;
else
core_route_stanza ( origin , stanza ) ;
end
end
function core_route_stanza ( origin , stanza )
local node , host , resource = jid_split ( stanza.attr . to ) ;
local from_node , from_host , from_resource = jid_split ( stanza.attr . from ) ;
-- Auto-detect origin if not specified
origin = origin or hosts [ from_host ] ;
if not origin then return false ; end
if hosts [ host ] then
-- old stanza routing code removed
core_post_stanza ( origin , stanza ) ;
elseif origin.type == " c2s " then
-- Remote host
if not hosts [ from_host ] then
log ( " error " , " No hosts[from_host] (please report): %s " , tostring ( stanza ) ) ;
end
if ( not hosts [ from_host ] ) or ( not hosts [ from_host ] . disallow_s2s ) then
local xmlns = stanza.attr . xmlns ;
--stanza.attr.xmlns = "jabber:server";
stanza.attr . xmlns = nil ;
log ( " debug " , " sending s2s stanza: %s " , tostring ( stanza ) ) ;
send_s2s ( origin.host , host , stanza ) ; -- TODO handle remote routing errors
stanza.attr . xmlns = xmlns ; -- reset
else
core_route_stanza ( hosts [ from_host ] , st.error_reply ( stanza , " cancel " , " not-allowed " , " Communication with remote servers is not allowed " ) ) ;
end
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 " , from_host , host ) ;
send_s2s ( from_host , host , stanza ) ;
else
log ( " warn " , " received stanza from unhandled connection type: %s " , origin.type ) ;
end
end