Remove now useless debug output
[prosody.git] / core / stanza_router.lua
1
2 -- The code in this file should be self-explanatory, though the logic is horrible
3 -- for more info on that, see doc/stanza_routing.txt, which attempts to condense
4 -- the rules from the RFCs (mainly 3921)
5
6 require "core.servermanager"
7
8 local log = require "util.logger".init("stanzarouter")
9
10 require "util.jid"
11 local jid_split = jid.split;
12
13 function core_process_stanza(origin, stanza)
14         local to = stanza.attr.to;
15         
16         if not to or (hosts[to] and hosts[to].type == "local") then
17                 core_handle_stanza(origin, stanza);
18         elseif origin.type == "c2s" then
19                 core_route_stanza(origin, stanza);
20         end
21                 
22 end
23
24 function core_handle_stanza(origin, stanza)
25         -- Handlers
26         if origin.type == "c2s" or origin.type == "c2s_unauthed" then
27                 local session = origin;
28                 stanza.attr.from = session.full_jid;
29                 
30                 log("debug", "Routing stanza");
31                 -- Stanza has no to attribute
32                 --local to_node, to_host, to_resource = jid_split(stanza.attr.to);
33                 --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
34                 
35                 -- Stanza is to this server, or a user on this server
36                 log("debug", "Routing stanza to local");
37                 handle_stanza(session, stanza);
38         end     
39 end
40
41 function core_route_stanza(origin, stanza)
42         -- Hooks
43         -- Deliver
44 end
45
46 function handle_stanza_nodest(stanza)
47         if stanza.name == "iq" then
48                 handle_stanza_iq_no_to(session, stanza);
49         elseif stanza.name == "presence" then
50                 -- Broadcast to this user's contacts
51                 handle_stanza_presence_broadcast(session, stanza);
52                 -- also, if it is initial presence, send out presence probes
53                 if not session.last_presence then
54                         handle_stanza_presence_probe_broadcast(session, stanza);
55                 end
56                 session.last_presence = stanza;
57         elseif stanza.name == "message" then
58                 -- Treat as if message was sent to bare JID of the sender
59                 handle_stanza_to_local_user(stanza);
60         end
61 end
62
63 function handle_stanza_tolocal(stanza)
64         local node, host, resource = jid.split(stanza.attr.to);
65         if host and hosts[host] and hosts[host].type == "local" then
66                         -- Is a local host, handle internally
67                         if node then
68                                 -- Is a local user, send to their session
69                                 log("debug", "Routing stanza to %s@%s", node, host);
70                                 if not session.username then return; end --FIXME: Correct response when trying to use unauthed stream is what?
71                                 handle_stanza_to_local_user(stanza);
72                         else
73                                 -- Is sent to this server, let's handle it...
74                                 log("debug", "Routing stanza to %s", host);
75                                 handle_stanza_to_server(stanza, session);
76                         end
77         end
78 end
79
80 function handle_stanza_toremote(stanza)
81         log("error", "Stanza bound for remote host, but s2s is not implemented");
82 end
83
84
85 --[[
86 local function route_c2s_stanza(session, stanza)
87         stanza.attr.from = session.full_jid;
88         if not stanza.attr.to and session.username then
89                 -- Has no 'to' attribute, handle internally
90                 if stanza.name == "iq" then
91                         handle_stanza_iq_no_to(session, stanza);
92                 elseif stanza.name == "presence" then
93                         -- Broadcast to this user's contacts
94                         handle_stanza_presence_broadcast(session, stanza);
95                         -- also, if it is initial presence, send out presence probes
96                         if not session.last_presence then
97                                 handle_stanza_presence_probe_broadcast(session, stanza);
98                         end
99                         session.last_presence = stanza;
100                 elseif stanza.name == "message" then
101                         -- Treat as if message was sent to bare JID of the sender
102                         handle_stanza_to_local_user(stanza);
103                 end
104         end
105         local node, host, resource = jid.split(stanza.attr.to);
106         if host and hosts[host] and hosts[host].type == "local" then
107                         -- Is a local host, handle internally
108                         if node then
109                                 -- Is a local user, send to their session
110                                 if not session.username then return; end --FIXME: Correct response when trying to use unauthed stream is what?
111                                 handle_stanza_to_local_user(stanza);
112                         else
113                                 -- Is sent to this server, let's handle it...
114                                 handle_stanza_to_server(stanza, session);
115                         end
116         else
117                 -- Is not for us or a local user, route accordingly
118                 route_s2s_stanza(stanza);
119         end
120 end
121
122 function handle_stanza_no_to(session, stanza)
123         if not stanza.attr.id then log("warn", "<iq> without id attribute is invalid"); end
124         local xmlns = (stanza.tags[1].attr and stanza.tags[1].attr.xmlns);
125         if stanza.attr.type == "get" or stanza.attr.type == "set" then
126                 if iq_handlers[xmlns] then
127                         if iq_handlers[xmlns](stanza) then return; end; -- If handler returns true, it handled it
128                 end
129                 -- Oh, handler didn't handle it. Need to send service-unavailable now.
130                 log("warn", "Unhandled namespace: "..xmlns);
131                 session:send(format("<iq type='error' id='%s'><error type='cancel'><service-unavailable/></error></iq>", stanza.attr.id));
132                 return; -- All done!
133         end
134 end
135
136 function handle_stanza_to_local_user(stanza)
137         if stanza.name == "message" then
138                 handle_stanza_message_to_local_user(stanza);
139         elseif stanza.name == "presence" then
140                 handle_stanza_presence_to_local_user(stanza);
141         elseif stanza.name == "iq" then
142                 handle_stanza_iq_to_local_user(stanza);
143         end
144 end
145
146 function handle_stanza_message_to_local_user(stanza)
147         local node, host, resource = stanza.to.node, stanza.to.host, stanza.to.resource;
148         local destuser = hosts[host].sessions[node];
149         if destuser then
150                 if resource and destuser[resource] then
151                         destuser[resource]:send(stanza);
152                 else
153                         -- Bare JID, or resource offline
154                         local best_session;
155                         for resource, session in pairs(destuser.sessions) do
156                                 if not best_session then best_session = session;
157                                 elseif session.priority >= best_session.priority and session.priority >= 0 then
158                                         best_session = session;
159                                 end
160                         end
161                         if not best_session then
162                                 offlinemessage.new(node, host, stanza);
163                         else
164                                 print("resource '"..resource.."' was not online, have chosen to send to '"..best_session.username.."@"..best_session.host.."/"..best_session.resource.."'");
165                                 destuser[best_session]:send(stanza);
166                         end
167                 end
168         else
169                 -- User is offline
170                 offlinemessage.new(node, host, stanza);
171         end
172 end
173
174 function handle_stanza_presence_to_local_user(stanza)
175         local node, host, resource = stanza.to.node, stanza.to.host, stanza.to.resource;
176         local destuser = hosts[host].sessions[node];
177         if destuser then
178                 if resource then
179                         if destuser[resource] then
180                                 destuser[resource]:send(stanza);
181                         else
182                                 return;
183                         end
184                 else
185                         -- Broadcast to all user's resources
186                         for resource, session in pairs(destuser.sessions) do
187                                 session:send(stanza);
188                         end
189                 end
190         end
191 end
192
193 function handle_stanza_iq_to_local_user(stanza)
194
195 end
196
197 function foo()
198                 local node, host, resource = stanza.to.node, stanza.to.host, stanza.to.resource;
199                 local destuser = hosts[host].sessions[node];
200                 if destuser and destuser.sessions then
201                         -- User online
202                         if resource and destuser.sessions[resource] then
203                                 stanza.to:send(stanza);
204                         else
205                                 --User is online, but specified resource isn't (or no resource specified)
206                                 local best_session;
207                                 for resource, session in pairs(destuser.sessions) do
208                                         if not best_session then best_session = session;
209                                         elseif session.priority >= best_session.priority and session.priority >= 0 then
210                                                 best_session = session;
211                                         end
212                                 end
213                                 if not best_session then
214                                         offlinemessage.new(node, host, stanza);
215                                 else
216                                         print("resource '"..resource.."' was not online, have chosen to send to '"..best_session.username.."@"..best_session.host.."/"..best_session.resource.."'");
217                                         resource = best_session.resource;
218                                 end
219                         end
220                         if destuser.sessions[resource] == session then
221                                 log("warn", "core", "Attempt to send stanza to self, dropping...");
222                         else
223                                 print("...sending...", tostring(stanza));
224                                 --destuser.sessions[resource].conn.write(tostring(data));
225                                 print("   to conn ", destuser.sessions[resource].conn);
226                                 destuser.sessions[resource].conn.write(tostring(stanza));
227                                 print("...sent")
228                         end
229                 elseif stanza.name == "message" then
230                         print("   ...will be stored offline");
231                         offlinemessage.new(node, host, stanza);
232                 elseif stanza.name == "iq" then
233                         print("   ...is an iq");
234                         stanza.from:send(st.reply(stanza)
235                                 :tag("error", { type = "cancel" })
236                                         :tag("service-unavailable", { xmlns = "urn:ietf:params:xml:ns:xmpp-stanzas" }));
237                 end
238 end
239
240 -- Broadcast a presence stanza to all of a user's contacts
241 function handle_stanza_presence_broadcast(session, stanza)
242         if session.roster then
243                 local initial_presence = not session.last_presence;
244                 session.last_presence = stanza;
245                 
246                 -- Broadcast presence and probes
247                 local broadcast = st.presence({ from = session.full_jid, type = stanza.attr.type });
248
249                 for child in stanza:childtags() do
250                         broadcast:add_child(child);
251                 end
252                 for contact_jid in pairs(session.roster) do
253                         broadcast.attr.to = contact_jid;
254                         send_to(contact_jid, broadcast);
255                         if initial_presence then
256                                 local node, host = jid.split(contact_jid);
257                                 if hosts[host] and hosts[host].type == "local" then
258                                         local contact = hosts[host].sessions[node]
259                                         if contact then
260                                                 local pres = st.presence { to = session.full_jid };
261                                                 for resource, contact_session in pairs(contact.sessions) do
262                                                         if contact_session.last_presence then
263                                                                 pres.tags = contact_session.last_presence.tags;
264                                                                 pres.attr.from = contact_session.full_jid;
265                                                                 send(pres);
266                                                         end
267                                                 end
268                                         end
269                                         --FIXME: Do we send unavailable if they are offline?
270                                 else
271                                         probe.attr.to = contact;
272                                         send_to(contact, probe);
273                                 end
274                         end
275                 end
276                 
277                 -- Probe for our contacts' presence
278         end
279 end
280
281 -- Broadcast presence probes to all of a user's contacts
282 function handle_stanza_presence_probe_broadcast(session, stanza)
283 end
284
285 -- 
286 function handle_stanza_to_server(stanza)
287 end
288
289 function handle_stanza_iq_no_to(session, stanza)
290 end
291 ]]