Software /
code /
prosody
Comparison
core/stanza_router.lua @ 178:8315cf03f304
Merge presence/subscription support from waqas
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Sun, 26 Oct 2008 13:19:09 +0000 |
parent | 168:744fafa8b700 |
parent | 177:606c433955e7 |
child | 186:bfa8a30ea488 |
comparison
equal
deleted
inserted
replaced
169:92768120b717 | 178:8315cf03f304 |
---|---|
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 |
75 if res ~= origin and res.full_jid then -- to resource. FIXME is res.full_jid the correct check? Maybe it should be res.presence | 96 if res ~= origin and res.full_jid then -- to resource. FIXME is res.full_jid the correct check? Maybe it should be res.presence |
76 stanza.attr.to = res.full_jid; | 97 stanza.attr.to = res.full_jid; |
77 core_route_stanza(origin, stanza); | 98 core_route_stanza(origin, stanza); |
78 end | 99 end |
79 end | 100 end |
80 if not origin.presence then -- presence probes on initial presence | 101 if not origin.presence then -- presence probes on initial presence -- FIXME does unavailable qualify as initial presence? |
81 local probe = st.presence({from = origin.full_jid, type = "probe"}); | 102 local probe = st.presence({from = origin.full_jid, type = "probe"}); |
82 for jid in pairs(origin.roster) do -- probe all contacts we are subscribed to | 103 for jid in pairs(origin.roster) do -- probe all contacts we are subscribed to |
83 local subscription = origin.roster[jid].subscription; | 104 local subscription = origin.roster[jid].subscription; |
84 if subscription == "both" or subscription == "to" then | 105 if subscription == "both" or subscription == "to" then |
85 probe.attr.to = jid; | 106 probe.attr.to = jid; |
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 |