core/stanza_router.lua

changeset 118
76ac96c53ee5
parent 113
9026fdad1531
child 119
b48a573608e8
equal deleted inserted replaced
99:ba08b8a4eeef 118:76ac96c53ee5
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 12 local user_exists = require "core.usermanager".user_exists;
13 require "util.jid" 13
14 local jid_split = jid.split; 14 local jid_split = require "util.jid".split;
15 local print = print;
15 16
16 function core_process_stanza(origin, stanza) 17 function core_process_stanza(origin, stanza)
17 log("debug", "Received: "..tostring(stanza)) 18 log("debug", "Received: "..tostring(stanza))
18 -- TODO verify validity of stanza (as well as JID validity) 19 -- TODO verify validity of stanza (as well as JID validity)
19 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
20 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
21 end 26 end
22 27
23 if origin.type == "c2s" and not origin.full_jid 28 if origin.type == "c2s" and not origin.full_jid
24 and not(stanza.name == "iq" and stanza.tags[1].name == "bind" 29 and not(stanza.name == "iq" and stanza.tags[1].name == "bind"
25 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
26 error("Client MUST bind resource after auth"); 31 error("Client MUST bind resource after auth");
27 end 32 end
28 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
29 37
30 local to = stanza.attr.to; 38 -- TODO presence subscriptions
31 stanza.attr.from = origin.full_jid -- quick fix to prevent impersonation 39 if not to then
32 40 if stanza.name == "presence" and origin.roster then
33 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
34 core_handle_stanza(origin, stanza); 76 core_handle_stanza(origin, stanza);
35 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
36 core_handle_stanza(origin, stanza); 78 core_handle_stanza(origin, stanza);
37 elseif origin.type == "c2s" then 79 elseif origin.type == "c2s" then
38 core_route_stanza(origin, stanza); 80 core_route_stanza(origin, stanza);
39 end 81 end
40 end 82 end
41 83
42 function core_handle_stanza(origin, stanza) 84 function core_handle_stanza(origin, stanza)
43 -- Handlers 85 -- Handlers
44 if origin.type == "c2s" or origin.type == "c2s_unauthed" then 86 if origin.type == "c2s" or origin.type == "c2s_unauthed" then
45 local session = origin; 87 local session = origin;
46 stanza.attr.from = session.full_jid;
47 88
48 log("debug", "Routing stanza"); 89 log("debug", "Routing stanza");
49 -- Stanza has no to attribute 90 -- Stanza has no to attribute
50 --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);
51 --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
54 log("debug", "Routing stanza to local"); 95 log("debug", "Routing stanza to local");
55 handle_stanza(session, stanza); 96 handle_stanza(session, stanza);
56 end 97 end
57 end 98 end
58 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
59 function core_route_stanza(origin, stanza) 106 function core_route_stanza(origin, stanza)
60 -- Hooks 107 -- Hooks
61 --- ...later 108 --- ...later
62 109
63 -- Deliver 110 -- Deliver
64 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
65 local host_session = hosts[host] 116 local host_session = hosts[host]
66 if host_session and host_session.type == "local" then 117 if host_session and host_session.type == "local" then
67 -- Local host 118 -- Local host
68 local user = host_session.sessions[node]; 119 local user = host_session.sessions[node];
69 if user then 120 if user then
70 local res = user.sessions[resource]; 121 local res = user.sessions[resource];
71 -- TODO do something about presence broadcast
72 if not res then 122 if not res then
73 -- if we get here, resource was not specified or was unavailable 123 -- if we get here, resource was not specified or was unavailable
74 for k in pairs(user.sessions) do 124 if stanza.name == "presence" then
75 res = user.sessions[k]; 125 if stanza.attr.type == "probe" then
76 break; 126 if is_authorized_to_see_presence(origin, node, host) then
77 end 127 for k in pairs(user.sessions) do -- return presence for all resources
78 -- TODO find resource with greatest priority 128 if user.sessions[k].presence then
79 end 129 local pres = user.sessions[k].presence;
80 stanza.attr.to = res.full_jid; 130 pres.attr.to = origin.full_jid;
81 send(res, stanza); -- Yay \o/ 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"}));
139 end
140 else
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
149 for k in pairs(user.sessions) do
150 if user.sessions[k].full_jid then
151 res = user.sessions[k];
152 break;
153 end
154 end
155 -- TODO find resource with greatest priority
156 send(res, stanza);
157 else
158 -- TODO send IQ error
159 end
160 else
161 stanza.attr.to = res.full_jid;
162 send(res, stanza); -- Yay \o/
163 end
82 else 164 else
83 -- user not found 165 -- user not online
84 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
85 end 189 end
86 else 190 else
87 -- Remote host 191 -- Remote host
88 if host_session then 192 if host_session then
89 -- Send to session 193 -- Send to session
90 else 194 else
91 -- Need to establish the connection 195 -- Need to establish the connection
92 end 196 end
93 end 197 end
198 stanza.attr.to = to; -- reset
94 end 199 end
95 200
96 function handle_stanza_nodest(stanza) 201 function handle_stanza_nodest(stanza)
97 if stanza.name == "iq" then 202 if stanza.name == "iq" then
98 handle_stanza_iq_no_to(session, stanza); 203 handle_stanza_iq_no_to(session, stanza);

mercurial