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