6 require "core.servermanager" |
6 require "core.servermanager" |
7 |
7 |
8 local log = require "util.logger".init("stanzarouter") |
8 local log = require "util.logger".init("stanzarouter") |
9 |
9 |
10 local st = require "util.stanza"; |
10 local st = require "util.stanza"; |
11 local send = require "core.sessionmanager".send_to_session; |
11 local _send = require "core.sessionmanager".send_to_session; |
12 local send_s2s = require "core.s2smanager".send_to_host; |
12 local send_s2s = require "core.s2smanager".send_to_host; |
|
13 function send(session, stanza) |
|
14 if session.type == "c2s" then |
|
15 _send(session, stanza); |
|
16 else |
|
17 local xmlns = stanza.attr.xmlns; |
|
18 --stanza.attr.xmlns = "jabber:server"; |
|
19 stanza.attr.xmlns = nil; |
|
20 log("debug", "sending s2s stanza: %s", tostring(stanza)); |
|
21 send_s2s(session.host, host, stanza); -- TODO handle remote routing errors |
|
22 stanza.attr.xmlns = xmlns; -- reset |
|
23 end |
|
24 end |
13 local user_exists = require "core.usermanager".user_exists; |
25 local user_exists = require "core.usermanager".user_exists; |
|
26 |
|
27 local rostermanager = require "core.rostermanager"; |
|
28 local sessionmanager = require "core.sessionmanager"; |
14 |
29 |
15 local s2s_verify_dialback = require "core.s2smanager".verify_dialback; |
30 local s2s_verify_dialback = require "core.s2smanager".verify_dialback; |
16 local s2s_make_authenticated = require "core.s2smanager".make_authenticated; |
31 local s2s_make_authenticated = require "core.s2smanager".make_authenticated; |
17 local format = string.format; |
32 local format = string.format; |
18 local tostring = tostring; |
33 local tostring = tostring; |
42 if origin.type == "c2s" then |
57 if origin.type == "c2s" then |
43 stanza.attr.from = origin.full_jid; -- quick fix to prevent impersonation (FIXME this would be incorrect when the origin is not c2s) |
58 stanza.attr.from = origin.full_jid; -- quick fix to prevent impersonation (FIXME this would be incorrect when the origin is not c2s) |
44 end |
59 end |
45 |
60 |
46 if not to then |
61 if not to then |
47 core_handle_stanza(origin, stanza); |
62 core_handle_stanza(origin, stanza); |
|
63 elseif origin.type == "c2s" and stanza.name == "presence" and stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable" then |
|
64 local node, host = jid_split(stanza.attr.to); |
|
65 local to_bare = node and (node.."@"..host) or host; -- bare JID |
|
66 local from_node, from_host = jid_split(stanza.attr.from); |
|
67 local from_bare = from_node and (from_node.."@"..from_host) or from_host; -- bare JID |
|
68 handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare); |
48 elseif hosts[to] and hosts[to].type == "local" then |
69 elseif hosts[to] and hosts[to].type == "local" then |
49 core_handle_stanza(origin, stanza); |
70 core_handle_stanza(origin, stanza); |
50 elseif stanza.name == "iq" and not select(3, jid_split(to)) then |
71 elseif stanza.name == "iq" and not select(3, jid_split(to)) then |
51 core_handle_stanza(origin, stanza); |
72 core_handle_stanza(origin, stanza); |
52 elseif origin.type == "c2s" or origin.type == "s2sin" then |
73 elseif origin.type == "c2s" or origin.type == "s2sin" then |
160 else |
181 else |
161 log("warn", "Unhandled origin: %s", origin.type); |
182 log("warn", "Unhandled origin: %s", origin.type); |
162 end |
183 end |
163 end |
184 end |
164 |
185 |
165 function is_authorized_to_see_presence(origin, username, host) |
186 function send_presence_of_available_resources(user, host, jid, recipient_session) |
166 local roster = datamanager.load(username, host, "roster") or {}; |
187 local h = hosts[host]; |
167 local item = roster[origin.username.."@"..origin.host]; |
188 local count = 0; |
168 return item and (item.subscription == "both" or item.subscription == "from"); |
189 if h and h.type == "local" then |
|
190 local u = h.sessions[user]; |
|
191 if u then |
|
192 for k, session in pairs(u.sessions) do |
|
193 local pres = session.presence; |
|
194 if pres then |
|
195 pres.attr.to = jid; |
|
196 pres.attr.from = session.full_jid; |
|
197 send(recipient_session, pres); |
|
198 pres.attr.to = nil; |
|
199 pres.attr.from = nil; |
|
200 count = count + 1; |
|
201 end |
|
202 end |
|
203 end |
|
204 end |
|
205 return count; |
|
206 end |
|
207 |
|
208 function handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare) |
|
209 local node, host = jid_split(from_bare); |
|
210 local st_from, st_to = stanza.attr.from, stanza.attr.to; |
|
211 stanza.attr.from, stanza.attr.to = from_bare, to_bare; |
|
212 if stanza.attr.type == "subscribe" then |
|
213 log("debug", "outbound subscribe from "..from_bare.." for "..to_bare); |
|
214 -- 1. route stanza |
|
215 -- 2. roster push (subscription = none, ask = subscribe) |
|
216 if rostermanager.set_contact_pending_out(node, host, to_bare) then |
|
217 rostermanager.roster_push(node, host, to_bare); |
|
218 end -- else file error |
|
219 core_route_stanza(origin, stanza); |
|
220 elseif stanza.attr.type == "unsubscribe" then |
|
221 log("debug", "outbound unsubscribe from "..from_bare.." for "..to_bare); |
|
222 -- 1. route stanza |
|
223 -- 2. roster push (subscription = none or from) |
|
224 if rostermanager.unsubscribe(node, host, to_bare) then |
|
225 rostermanager.roster_push(node, host, to_bare); -- FIXME do roster push when roster has in fact not changed? |
|
226 end -- else file error |
|
227 core_route_stanza(origin, stanza); |
|
228 elseif stanza.attr.type == "subscribed" then |
|
229 log("debug", "outbound subscribed from "..from_bare.." for "..to_bare); |
|
230 -- 1. route stanza |
|
231 -- 2. roster_push () |
|
232 -- 3. send_presence_of_available_resources |
|
233 if rostermanager.subscribed(node, host, to_bare) then |
|
234 rostermanager.roster_push(node, host, to_bare); |
|
235 core_route_stanza(origin, stanza); |
|
236 send_presence_of_available_resources(node, host, to_bare, origin); |
|
237 end |
|
238 elseif stanza.attr.type == "unsubscribed" then |
|
239 log("debug", "outbound unsubscribed from "..from_bare.." for "..to_bare); |
|
240 -- 1. route stanza |
|
241 -- 2. roster push (subscription = none or to) |
|
242 if rostermanager.unsubscribed(node, host, to_bare) then |
|
243 rostermanager.roster_push(node, host, to_bare); |
|
244 core_route_stanza(origin, stanza); |
|
245 end |
|
246 end |
|
247 stanza.attr.from, stanza.attr.to = st_from, st_to; |
|
248 end |
|
249 |
|
250 function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare) |
|
251 local node, host = jid_split(to_bare); |
|
252 local st_from, st_to = stanza.attr.from, stanza.attr.to; |
|
253 stanza.attr.from, stanza.attr.to = from_bare, to_bare; |
|
254 if stanza.attr.type == "probe" then |
|
255 if rostermanager.is_contact_subscribed(node, host, from_bare) then |
|
256 if 0 == send_presence_of_available_resources(node, host, from_bare, origin) then |
|
257 -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too) |
|
258 end |
|
259 else |
|
260 core_route_stanza(origin, st.presence({from=to_bare, to=from_bare, type="unsubscribed"})); |
|
261 end |
|
262 elseif stanza.attr.type == "subscribe" then |
|
263 log("debug", "inbound subscribe from "..from_bare.." for "..to_bare); |
|
264 if rostermanager.is_contact_subscribed(node, host, from_bare) then |
|
265 core_route_stanza(origin, st.presence({from=to_bare, to=from_bare, type="subscribed"})); -- already subscribed |
|
266 else |
|
267 if not rostermanager.is_contact_pending_in(node, host, from_bare) then |
|
268 if rostermanager.set_contact_pending_in(node, host, from_bare) then |
|
269 sessionmanager.send_to_available_resources(node, host, stanza); |
|
270 end -- TODO else return error, unable to save |
|
271 end |
|
272 end |
|
273 elseif stanza.attr.type == "unsubscribe" then |
|
274 log("debug", "inbound unsubscribe from "..from_bare.." for "..to_bare); |
|
275 if rostermanager.process_inbound_unsubscribe(node, host, from_bare) then |
|
276 rostermanager.roster_push(node, host, from_bare); |
|
277 end |
|
278 elseif stanza.attr.type == "subscribed" then |
|
279 log("debug", "inbound subscribed from "..from_bare.." for "..to_bare); |
|
280 if rostermanager.process_inbound_subscription_approval(node, host, from_bare) then |
|
281 rostermanager.roster_push(node, host, from_bare); |
|
282 end |
|
283 elseif stanza.attr.type == "unsubscribed" then |
|
284 log("debug", "inbound unsubscribed from "..from_bare.." for "..to_bare); |
|
285 if rostermanager.process_inbound_subscription_approval(node, host, from_bare) then |
|
286 rostermanager.roster_push(node, host, from_bare); |
|
287 end |
|
288 end -- discard any other type |
|
289 stanza.attr.from, stanza.attr.to = st_from, st_to; |
169 end |
290 end |
170 |
291 |
171 function core_route_stanza(origin, stanza) |
292 function core_route_stanza(origin, stanza) |
172 -- Hooks |
293 -- Hooks |
173 --- ...later |
294 --- ...later |
174 |
295 |
175 -- Deliver |
296 -- Deliver |
176 local to = stanza.attr.to; |
297 local to = stanza.attr.to; |
177 local node, host, resource = jid_split(to); |
298 local node, host, resource = jid_split(to); |
|
299 local to_bare = node and (node.."@"..host) or host; -- bare JID |
|
300 local from = stanza.attr.from; |
|
301 local from_node, from_host, from_resource = jid_split(from); |
|
302 local from_bare = from_node and (from_node.."@"..from_host) or from_host; -- bare JID |
178 |
303 |
179 if stanza.name == "presence" and (stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable") then resource = nil; end |
304 if stanza.name == "presence" and (stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable") then resource = nil; end |
180 |
305 |
181 local host_session = hosts[host] |
306 local host_session = hosts[host] |
182 if host_session and host_session.type == "local" then |
307 if host_session and host_session.type == "local" then |
186 local res = user.sessions[resource]; |
311 local res = user.sessions[resource]; |
187 if not res then |
312 if not res then |
188 -- if we get here, resource was not specified or was unavailable |
313 -- if we get here, resource was not specified or was unavailable |
189 if stanza.name == "presence" then |
314 if stanza.name == "presence" then |
190 if stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable" then |
315 if stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable" then |
191 if stanza.attr.type == "probe" then |
316 handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare); |
192 if is_authorized_to_see_presence(origin, node, host) then |
|
193 for k in pairs(user.sessions) do -- return presence for all resources |
|
194 if user.sessions[k].presence then |
|
195 local pres = user.sessions[k].presence; |
|
196 pres.attr.to = origin.full_jid; |
|
197 pres.attr.from = user.sessions[k].full_jid; |
|
198 send(origin, pres); |
|
199 pres.attr.to = nil; |
|
200 pres.attr.from = nil; |
|
201 end |
|
202 end |
|
203 else |
|
204 send(origin, st.presence({from=user.."@"..host, to=origin.username.."@"..origin.host, type="unsubscribed"})); |
|
205 end |
|
206 elseif stanza.attr.type == "subscribe" then |
|
207 -- TODO |
|
208 elseif stanza.attr.type == "unsubscribe" then |
|
209 -- TODO |
|
210 elseif stanza.attr.type == "subscribed" then |
|
211 -- TODO |
|
212 elseif stanza.attr.type == "unsubscribed" then |
|
213 -- TODO |
|
214 end -- discard any other type |
|
215 else -- sender is available or unavailable |
317 else -- sender is available or unavailable |
216 for k in pairs(user.sessions) do -- presence broadcast to all user resources |
318 for k in pairs(user.sessions) do -- presence broadcast to all user resources. FIXME should this be just for available resources? Do we need to check subscription? |
217 if user.sessions[k].full_jid then |
319 if user.sessions[k].full_jid then |
218 stanza.attr.to = user.sessions[k].full_jid; |
320 stanza.attr.to = user.sessions[k].full_jid; -- reset at the end of function |
219 send(user.sessions[k], stanza); |
321 send(user.sessions[k], stanza); |
220 end |
322 end |
221 end |
323 end |
222 end |
324 end |
223 elseif stanza.name == "message" then -- select a resource to recieve message |
325 elseif stanza.name == "message" then -- select a resource to recieve message |
232 else |
334 else |
233 -- TODO send IQ error |
335 -- TODO send IQ error |
234 end |
336 end |
235 else |
337 else |
236 -- User + resource is online... |
338 -- User + resource is online... |
237 stanza.attr.to = res.full_jid; |
339 stanza.attr.to = res.full_jid; -- reset at the end of function |
238 send(res, stanza); -- Yay \o/ |
340 send(res, stanza); -- Yay \o/ |
239 end |
341 end |
240 else |
342 else |
241 -- user not online |
343 -- user not online |
242 if user_exists(node, host) then |
344 if user_exists(node, host) then |
243 if stanza.name == "presence" then |
345 if stanza.name == "presence" then |
244 if stanza.attr.type == "probe" and is_authorized_to_see_presence(origin, node, host) then -- FIXME what to do for not c2s? |
346 if stanza.attr.type ~= nil and stanza.attr.type ~= "unavailable" then |
245 -- TODO send last recieved unavailable presence |
347 handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_bare, to_bare); |
246 else |
348 else |
247 -- TODO send unavailable presence |
349 -- TODO send unavailable presence or unsubscribed |
248 end |
350 end |
249 elseif stanza.name == "message" then |
351 elseif stanza.name == "message" then |
250 -- TODO send message error, or store offline messages |
352 -- TODO send message error, or store offline messages |
251 elseif stanza.name == "iq" then |
353 elseif stanza.name == "iq" then |
252 -- TODO send IQ error |
354 -- TODO send IQ error |
253 end |
355 end |
254 else -- user does not exist |
356 else -- user does not exist |
255 -- TODO we would get here for nodeless JIDs too. Do something fun maybe? Echo service? Let plugins use xmpp:server/resource addresses? |
357 -- TODO we would get here for nodeless JIDs too. Do something fun maybe? Echo service? Let plugins use xmpp:server/resource addresses? |
256 if stanza.name == "presence" then |
358 if stanza.name == "presence" then |
257 if stanza.attr.type == "probe" then |
359 if stanza.attr.type == "probe" then |
258 send(origin, st.presence({from = stanza.attr.to, to = stanza.attr.from, type = "unsubscribed"})); |
360 send(origin, st.presence({from = to_bare, to = from_bare, type = "unsubscribed"})); |
259 end |
361 end |
260 -- else ignore |
362 -- else ignore |
261 else |
363 else |
262 send(origin, st.error_reply(stanza, "cancel", "service-unavailable")); |
364 send(origin, st.error_reply(stanza, "cancel", "service-unavailable")); |
263 end |
365 end |
264 end |
366 end |
265 end |
367 end |
266 elseif origin.type == "c2s" then |
368 elseif origin.type == "c2s" then |
267 -- Remote host |
369 -- Remote host |
|
370 local xmlns = stanza.attr.xmlns; |
268 --stanza.attr.xmlns = "jabber:server"; |
371 --stanza.attr.xmlns = "jabber:server"; |
269 stanza.attr.xmlns = nil; |
372 stanza.attr.xmlns = nil; |
270 log("debug", "sending s2s stanza: %s", tostring(stanza)); |
373 log("debug", "sending s2s stanza: %s", tostring(stanza)); |
271 send_s2s(origin.host, host, stanza); |
374 send_s2s(origin.host, host, stanza); -- TODO handle remote routing errors |
|
375 stanza.attr.xmlns = xmlns; -- reset |
272 else |
376 else |
273 log("warn", "received stanza from unhandled connection type: %s", origin.type); |
377 log("warn", "received stanza from unhandled connection type: %s", origin.type); |
274 end |
378 end |
275 stanza.attr.to = to; -- reset |
379 stanza.attr.to = to; -- reset |
276 end |
380 end |