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