Software /
code /
prosody
Comparison
core/stanza_router.lua @ 113:9026fdad1531
Working presence, presence probes and other fixes
author | Waqas Hussain <waqas20@gmail.com> |
---|---|
date | Thu, 23 Oct 2008 00:46:38 +0500 |
parent | 106:f2a3d204a76a |
child | 119:b48a573608e8 |
comparison
equal
deleted
inserted
replaced
112:df54cab4ff9c | 113:9026fdad1531 |
---|---|
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 user_exists = require "core.usermanager".user_exists; | |
12 | 13 |
13 local jid_split = require "util.jid".split; | 14 local jid_split = require "util.jid".split; |
15 local print = print; | |
14 | 16 |
15 function core_process_stanza(origin, stanza) | 17 function core_process_stanza(origin, stanza) |
16 log("debug", "Received: "..tostring(stanza)) | 18 log("debug", "Received: "..tostring(stanza)) |
17 -- TODO verify validity of stanza (as well as JID validity) | 19 -- TODO verify validity of stanza (as well as JID validity) |
18 if stanza.name == "iq" and not(#stanza.tags == 1 and stanza.tags[1].attr.xmlns) then | 20 if stanza.name == "iq" and not(#stanza.tags == 1 and stanza.tags[1].attr.xmlns) then |
19 error("Invalid IQ"); | 21 if stanza.attr.type == "set" or stanza.attr.type == "get" then |
22 error("Invalid IQ"); | |
23 elseif #stanza.tags > 1 or not(stanza.attr.type == "error" or stanza.attr.type == "result") then | |
24 error("Invalid IQ"); | |
25 end | |
20 end | 26 end |
21 | 27 |
22 if origin.type == "c2s" and not origin.full_jid | 28 if origin.type == "c2s" and not origin.full_jid |
23 and not(stanza.name == "iq" and stanza.tags[1].name == "bind" | 29 and not(stanza.name == "iq" and stanza.tags[1].name == "bind" |
24 and stanza.tags[1].attr.xmlns == "urn:ietf:params:xml:ns:xmpp-bind") then | 30 and stanza.tags[1].attr.xmlns == "urn:ietf:params:xml:ns:xmpp-bind") then |
25 error("Client MUST bind resource after auth"); | 31 error("Client MUST bind resource after auth"); |
26 end | 32 end |
27 | 33 |
34 local to = stanza.attr.to; | |
35 stanza.attr.from = origin.full_jid; -- quick fix to prevent impersonation (FIXME this would be incorrect when the origin is not c2s) | |
36 -- TODO also, stazas should be returned to their original state before the function ends | |
28 | 37 |
29 local to = stanza.attr.to; | 38 -- TODO presence subscriptions |
30 stanza.attr.from = origin.full_jid -- quick fix to prevent impersonation | 39 if not to then |
31 | 40 if stanza.name == "presence" and origin.roster then |
32 if not to or (hosts[to] and hosts[to].type == "local") then | 41 if stanza.attr.type == nil or stanza.attr.type == "available" or stanza.attr.type == "unavailable" then |
42 --stanza.attr.from = origin.full_jid; | |
43 for jid in pairs(origin.roster) do -- broadcast to all interested contacts | |
44 local subscription = origin.roster[jid].subscription; | |
45 if subscription == "both" or subscription == "from" then | |
46 stanza.attr.to = jid; | |
47 core_route_stanza(origin, stanza); | |
48 end | |
49 end | |
50 --[[local node, host = jid_split(stanza.attr.from); | |
51 for _, res in pairs(hosts[host].sessions[node].sessions) do -- broadcast to all resources | |
52 if res.full_jid then | |
53 res = user.sessions[k]; | |
54 break; | |
55 end | |
56 end]] | |
57 if not origin.presence then -- presence probes on initial presence | |
58 local probe = st.presence({from = origin.full_jid, type = "probe"}); | |
59 for jid in pairs(origin.roster) do | |
60 local subscription = origin.roster[jid].subscription; | |
61 if subscription == "both" or subscription == "to" then | |
62 probe.attr.to = jid; | |
63 core_route_stanza(origin, probe); | |
64 end | |
65 end | |
66 end | |
67 origin.presence = stanza; | |
68 stanza.attr.to = nil; -- reset it | |
69 else | |
70 -- TODO error, bad type | |
71 end | |
72 else | |
73 core_handle_stanza(origin, stanza); | |
74 end | |
75 elseif hosts[to] and hosts[to].type == "local" then | |
33 core_handle_stanza(origin, stanza); | 76 core_handle_stanza(origin, stanza); |
34 elseif to and stanza.name == "iq" and not select(3, jid_split(to)) then | 77 elseif stanza.name == "iq" and not select(3, jid_split(to)) then |
35 core_handle_stanza(origin, stanza); | 78 core_handle_stanza(origin, stanza); |
36 elseif origin.type == "c2s" then | 79 elseif origin.type == "c2s" then |
37 core_route_stanza(origin, stanza); | 80 core_route_stanza(origin, stanza); |
38 end | 81 end |
39 end | 82 end |
40 | 83 |
41 function core_handle_stanza(origin, stanza) | 84 function core_handle_stanza(origin, stanza) |
42 -- Handlers | 85 -- Handlers |
43 if origin.type == "c2s" or origin.type == "c2s_unauthed" then | 86 if origin.type == "c2s" or origin.type == "c2s_unauthed" then |
44 local session = origin; | 87 local session = origin; |
45 stanza.attr.from = session.full_jid; | |
46 | 88 |
47 log("debug", "Routing stanza"); | 89 log("debug", "Routing stanza"); |
48 -- Stanza has no to attribute | 90 -- Stanza has no to attribute |
49 --local to_node, to_host, to_resource = jid_split(stanza.attr.to); | 91 --local to_node, to_host, to_resource = jid_split(stanza.attr.to); |
50 --if not to_host then error("Invalid destination JID: "..string.format("{ %q, %q, %q } == %q", to_node or "", to_host or "", to_resource or "", stanza.attr.to or "nil")); end | 92 --if not to_host then error("Invalid destination JID: "..string.format("{ %q, %q, %q } == %q", to_node or "", to_host or "", to_resource or "", stanza.attr.to or "nil")); end |
53 log("debug", "Routing stanza to local"); | 95 log("debug", "Routing stanza to local"); |
54 handle_stanza(session, stanza); | 96 handle_stanza(session, stanza); |
55 end | 97 end |
56 end | 98 end |
57 | 99 |
100 function is_authorized_to_see_presence(origin, username, host) | |
101 local roster = datamanager.load(username, host, "roster") or {}; | |
102 local item = roster[origin.username.."@"..origin.host]; | |
103 return item and (item.subscription == "both" or item.subscription == "from"); | |
104 end | |
105 | |
58 function core_route_stanza(origin, stanza) | 106 function core_route_stanza(origin, stanza) |
59 -- Hooks | 107 -- Hooks |
60 --- ...later | 108 --- ...later |
61 | 109 |
62 -- Deliver | 110 -- Deliver |
63 local node, host, resource = jid_split(stanza.attr.to); | 111 local to = stanza.attr.to; |
112 local node, host, resource = jid_split(to); | |
113 | |
114 if stanza.name == "presence" and stanza.attr.type == "probe" then resource = nil; end | |
115 | |
64 local host_session = hosts[host] | 116 local host_session = hosts[host] |
65 if host_session and host_session.type == "local" then | 117 if host_session and host_session.type == "local" then |
66 -- Local host | 118 -- Local host |
67 local user = host_session.sessions[node]; | 119 local user = host_session.sessions[node]; |
68 if user then | 120 if user then |
69 local res = user.sessions[resource]; | 121 local res = user.sessions[resource]; |
70 if not res then | 122 if not res then |
71 -- if we get here, resource was not specified or was unavailable | 123 -- if we get here, resource was not specified or was unavailable |
72 if stanza.name == "presence" then | 124 if stanza.name == "presence" then |
73 for k in pairs(user.sessions) do -- presence broadcast to all user resources | 125 if stanza.attr.type == "probe" then |
74 if user.sessions[k].full_jid then | 126 if is_authorized_to_see_presence(origin, node, host) then |
75 stanza.attr.to = user.sessions[k].full_jid; | 127 for k in pairs(user.sessions) do -- return presence for all resources |
76 send(user.sessions[k], stanza); | 128 if user.sessions[k].presence then |
129 local pres = user.sessions[k].presence; | |
130 pres.attr.to = origin.full_jid; | |
131 pres.attr.from = user.sessions[k].full_jid; | |
132 send(origin, pres); | |
133 pres.attr.to = nil; | |
134 pres.attr.from = nil; | |
135 end | |
136 end | |
137 else | |
138 send(origin, st.presence({from = user.."@"..host, to = origin.username.."@"..origin.host, type = "unsubscribed"})); | |
77 end | 139 end |
78 end | 140 else |
79 else if stanza.name == "message" then -- select a resource to recieve message | 141 for k in pairs(user.sessions) do -- presence broadcast to all user resources |
142 if user.sessions[k].full_jid then | |
143 stanza.attr.to = user.sessions[k].full_jid; | |
144 send(user.sessions[k], stanza); | |
145 end | |
146 end | |
147 end | |
148 elseif stanza.name == "message" then -- select a resource to recieve message | |
80 for k in pairs(user.sessions) do | 149 for k in pairs(user.sessions) do |
81 if user.sessions[k].full_jid then | 150 if user.sessions[k].full_jid then |
82 res = user.sessions[k]; | 151 res = user.sessions[k]; |
83 break; | 152 break; |
84 end | 153 end |
85 end | 154 end |
86 -- TODO find resource with greatest priority | 155 -- TODO find resource with greatest priority |
156 send(res, stanza); | |
87 else | 157 else |
88 error("IQs should't get here"); | 158 -- TODO send IQ error |
89 end | 159 end |
90 end | 160 else |
91 if res then | |
92 stanza.attr.to = res.full_jid; | 161 stanza.attr.to = res.full_jid; |
93 send(res, stanza); -- Yay \o/ | 162 send(res, stanza); -- Yay \o/ |
94 elseif stanza.name == "message" then | |
95 -- TODO return message error | |
96 end | 163 end |
97 else | 164 else |
98 -- user not found | 165 -- user not online |
99 send(origin, st.error_reply(stanza, "cancel", "service-unavailable")); | 166 if user_exists(node, host) then |
167 if stanza.name == "presence" then | |
168 if stanza.attr.type == "probe" and is_authorized_to_see_presence(origin, node, host) then -- FIXME what to do for not c2s? | |
169 -- TODO send last recieved unavailable presence | |
170 else | |
171 -- TODO send unavailable presence | |
172 end | |
173 elseif stanza.name == "message" then | |
174 -- TODO send message error, or store offline messages | |
175 elseif stanza.name == "iq" then | |
176 -- TODO send IQ error | |
177 end | |
178 else -- user does not exist | |
179 -- TODO we would get here for nodeless JIDs too. Do something fun maybe? Echo service? Let plugins use xmpp:server/resource addresses? | |
180 if stanza.name == "presence" then | |
181 if stanza.attr.type == "probe" then | |
182 send(origin, st.presence({from = user.."@"..host, to = origin.username.."@"..origin.host, type = "unsubscribed"})); | |
183 end | |
184 -- else ignore | |
185 else | |
186 send(origin, st.error_reply(stanza, "cancel", "service-unavailable")); | |
187 end | |
188 end | |
100 end | 189 end |
101 else | 190 else |
102 -- Remote host | 191 -- Remote host |
103 if host_session then | 192 if host_session then |
104 -- Send to session | 193 -- Send to session |
105 else | 194 else |
106 -- Need to establish the connection | 195 -- Need to establish the connection |
107 end | 196 end |
108 end | 197 end |
198 stanza.attr.to = to; -- reset | |
109 end | 199 end |
110 | 200 |
111 function handle_stanza_nodest(stanza) | 201 function handle_stanza_nodest(stanza) |
112 if stanza.name == "iq" then | 202 if stanza.name == "iq" then |
113 handle_stanza_iq_no_to(session, stanza); | 203 handle_stanza_iq_no_to(session, stanza); |