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); |