2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
9 local log = require
"util.logger".init("stanzarouter")
11 local hosts
= _G
.prosody
.hosts
;
12 local tostring = tostring;
13 local st
= require
"util.stanza";
14 local jid_split
= require
"util.jid".split
;
15 local jid_prepped_split
= require
"util.jid".prepped_split
;
17 local full_sessions
= _G
.prosody
.full_sessions
;
18 local bare_sessions
= _G
.prosody
.bare_sessions
;
20 local core_post_stanza
, core_process_stanza
, core_route_stanza
;
22 local valid_stanzas
= { message
= true, presence
= true, iq
= true };
23 local function handle_unhandled_stanza(host
, origin
, stanza
) --luacheck: ignore 212/host
24 local name
, xmlns
, origin_type
= stanza
.name
, stanza
.attr
.xmlns
or "jabber:client", origin
.type;
25 if xmlns
== "jabber:client" and valid_stanzas
[name
] then
27 local st_type
= stanza
.attr
.type;
28 if st_type
== "error" or (name
== "iq" and st_type
== "result") then
29 if st_type
== "error" then
30 local err_type
, err_condition
, err_message
= stanza
:get_error();
31 log("debug", "Discarding unhandled error %s (%s, %s) from %s: %s",
32 name
, err_type
, err_condition
or "unknown condition", origin_type
, stanza
:top_tag());
34 log("debug", "Discarding %s from %s of type: %s", name
, origin_type
, st_type
or '<nil>');
38 if name
== "iq" and (st_type
== "get" or st_type
== "set") and stanza
.tags
[1] then
39 xmlns
= stanza
.tags
[1].attr
.xmlns
or "jabber:client";
41 log("debug", "Unhandled %s stanza: %s; xmlns=%s", origin_type
, name
, xmlns
);
43 origin
.send(st
.error_reply(stanza
, "cancel", "service-unavailable"));
46 log("warn", "Unhandled %s stream element or stanza: %s; xmlns=%s: %s",
47 origin_type
, name
, xmlns
, tostring(stanza
)); -- we didn't handle it
48 origin
:close("unsupported-stanza-type");
52 local iq_types
= { set
=true, get
=true, result
=true, error=true };
53 function core_process_stanza(origin
, stanza
)
54 (origin
.log or log)("debug", "Received[%s]: %s", origin
.type, stanza
:top_tag())
56 if origin
.type == "c2s" and not stanza
.attr
.xmlns
then
57 local name
, st_type
= stanza
.name
, stanza
.attr
.type;
58 if st_type
== "error" and #stanza
.tags
== 0 then
59 return handle_unhandled_stanza(origin
.host
, origin
, stanza
);
62 if not iq_types
[st_type
] then
63 origin
.send(st
.error_reply(stanza
, "modify", "bad-request", "Invalid IQ type"));
65 elseif not stanza
.attr
.id
then
66 origin
.send(st
.error_reply(stanza
, "modify", "bad-request", "Missing required 'id' attribute"));
68 elseif (st_type
== "set" or st_type
== "get") and (#stanza
.tags
~= 1) then
69 origin
.send(st
.error_reply(stanza
, "modify", "bad-request", "Incorrect number of children for IQ stanza"));
74 -- TODO also, stanzas should be returned to their original state before the function ends
75 stanza
.attr
.from
= origin
.full_jid
;
77 local to
, xmlns
= stanza
.attr
.to
, stanza
.attr
.xmlns
;
78 local from
= stanza
.attr
.from
;
79 local node
, host
, resource
;
80 local from_node
, from_host
, from_resource
;
81 local to_bare
, from_bare
;
83 if full_sessions
[to
] or bare_sessions
[to
] or hosts
[to
] then
84 node
, host
= jid_split(to
); -- TODO only the host is needed, optimize
86 node
, host
, resource
= jid_prepped_split(to
);
88 log("warn", "Received stanza with invalid destination JID: %s", to
);
89 if stanza
.attr
.type ~= "error" and stanza
.attr
.type ~= "result" then
90 origin
.send(st
.error_reply(stanza
, "modify", "jid-malformed", "The destination address is invalid: "..to
));
94 to_bare
= node
and (node
.."@"..host
) or host
; -- bare JID
95 if resource
then to
= to_bare
.."/"..resource
; else to
= to_bare
; end
99 if from
and not origin
.full_jid
then
100 -- We only stamp the 'from' on c2s stanzas, so we still need to check validity
101 from_node
, from_host
, from_resource
= jid_prepped_split(from
);
102 if not from_host
then
103 log("warn", "Received stanza with invalid source JID: %s", from
);
104 if stanza
.attr
.type ~= "error" and stanza
.attr
.type ~= "result" then
105 origin
.send(st
.error_reply(stanza
, "modify", "jid-malformed", "The source address is invalid: "..from
));
109 from_bare
= from_node
and (from_node
.."@"..from_host
) or from_host
; -- bare JID
110 if from_resource
then from
= from_bare
.."/"..from_resource
; else from
= from_bare
; end
111 stanza
.attr
.from
= from
;
114 if (origin
.type == "s2sin" or origin
.type == "s2sout" or origin
.type == "c2s" or origin
.type == "component") and xmlns
== nil then
115 if (origin
.type == "s2sin" or origin
.type == "s2sout") and not origin
.dummy
then
116 local host_status
= origin
.hosts
[from_host
];
117 if not host_status
or not host_status
.authed
then -- remote server trying to impersonate some other server?
118 log("warn", "Received a stanza claiming to be from %s, over a stream authed for %s!", from_host
, origin
.from_host
);
119 origin
:close("not-authorized");
121 elseif not hosts
[host
] then
122 log("warn", "Remote server %s sent us a stanza for %s, closing stream", origin
.from_host
, host
);
123 origin
:close("host-unknown");
127 core_post_stanza(origin
, stanza
, origin
.full_jid
);
129 local h
= hosts
[stanza
.attr
.to
or origin
.host
or origin
.to_host
];
133 if stanza
.name
== "iq" and (stanza
.attr
.type == "set" or stanza
.attr
.type == "get")
134 and stanza
.tags
[1] and stanza
.tags
[1].attr
.xmlns
then
135 event
= "stanza/iq/"..stanza
.tags
[1].attr
.xmlns
..":"..stanza
.tags
[1].name
;
137 event
= "stanza/"..stanza
.name
;
140 event
= "stanza/"..xmlns
..":"..stanza
.name
;
142 if h
.events
.fire_event(event
, {origin
= origin
, stanza
= stanza
}) then return; end
144 if host
and not hosts
[host
] then host
= nil; end -- COMPAT: workaround for a Pidgin bug which sets 'to' to the SRV result
145 handle_unhandled_stanza(host
or origin
.host
or origin
.to_host
, origin
, stanza
);
149 function core_post_stanza(origin
, stanza
, preevents
)
150 local to
= stanza
.attr
.to
;
151 local node
, host
, resource
= jid_split(to
);
152 local to_bare
= node
and (node
.."@"..host
) or host
; -- bare JID
154 local to_type
, to_self
;
160 if node
== origin
.username
and host
== origin
.host
then
161 stanza
.attr
.to
= nil;
174 local event_data
= {origin
=origin
, stanza
=stanza
};
175 if preevents
then -- c2s connection
176 if hosts
[origin
.host
].events
.fire_event('pre-'..stanza
.name
..to_type
, event_data
) then return; end -- do preprocessing
178 local h
= hosts
[to_bare
] or hosts
[host
or origin
.host
];
180 if h
.events
.fire_event(stanza
.name
..to_type
, event_data
) then return; end -- do processing
181 if to_self
and h
.events
.fire_event(stanza
.name
..'/self', event_data
) then return; end -- do processing
182 handle_unhandled_stanza(h
.host
, origin
, stanza
);
184 core_route_stanza(origin
, stanza
);
188 function core_route_stanza(origin
, stanza
)
189 local node
, host
, resource
= jid_split(stanza
.attr
.to
);
190 local from_node
, from_host
, from_resource
= jid_split(stanza
.attr
.from
);
192 -- Auto-detect origin if not specified
193 origin
= origin
or hosts
[from_host
];
194 if not origin
then return false; end
197 -- old stanza routing code removed
198 core_post_stanza(origin
, stanza
);
200 local host_session
= hosts
[from_host
];
201 if not host_session
then
202 log("error", "No hosts[from_host] (please report): %s", stanza
);
204 local xmlns
= stanza
.attr
.xmlns
;
205 stanza
.attr
.xmlns
= nil;
206 local routed
= host_session
.events
.fire_event("route/remote", {
207 origin
= origin
, stanza
= stanza
, from_host
= from_host
, to_host
= host
});
208 stanza
.attr
.xmlns
= xmlns
; -- reset
210 log("debug", "Could not route stanza to remote");
211 if stanza
.attr
.type == "error" or (stanza
.name
== "iq" and stanza
.attr
.type == "result") then return; end
212 core_route_stanza(host_session
, st
.error_reply(stanza
, "cancel", "not-allowed",
213 "Communication with remote domains is not enabled"));
219 --luacheck: ignore 122/prosody
220 prosody
.core_process_stanza
= core_process_stanza
;
221 prosody
.core_post_stanza
= core_post_stanza
;
222 prosody
.core_route_stanza
= core_route_stanza
;