core/stanza_router.lua

changeset 113
9026fdad1531
parent 106
f2a3d204a76a
child 119
b48a573608e8
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);

mercurial