137
|
1 |
|
2 local st = require "util.stanza"; |
|
3 local jid_bare = require "util.jid".bare; |
|
4 |
|
5 -- advertise disco features |
|
6 module:add_feature("urn:xmpp:sift:1"); |
|
7 |
|
8 -- supported features |
|
9 module:add_feature("urn:xmpp:sift:stanzas:iq"); |
|
10 module:add_feature("urn:xmpp:sift:stanzas:message"); |
|
11 module:add_feature("urn:xmpp:sift:stanzas:presence"); |
|
12 module:add_feature("urn:xmpp:sift:recipients:all"); |
|
13 module:add_feature("urn:xmpp:sift:senders:all"); |
|
14 |
|
15 -- allowed values of 'sender' and 'recipient' attributes |
|
16 local senders = { |
|
17 ["all"] = true; |
|
18 ["local"] = true; |
|
19 ["others"] = true; |
|
20 ["remote"] = true; |
|
21 ["self"] = true; |
|
22 }; |
|
23 local recipients = { |
|
24 ["all"] = true; |
|
25 ["bare"] = true; |
|
26 ["full"] = true; |
|
27 }; |
|
28 |
|
29 -- this function converts a <message/>, <presence/> or <iq/> element in |
|
30 -- the SIFT namespace into a hashtable, for easy lookup |
|
31 local function to_hashtable(element) |
|
32 if element ~= nil then |
|
33 local hash = {}; |
|
34 -- make sure the sender and recipient attributes has a valid value |
|
35 hash.sender = element.attr.sender or "all"; |
|
36 if not senders[hash.sender] then return false; end -- bad value, returning false |
|
37 hash.recipient = element.attr.recipient or "all"; |
|
38 if not recipients[hash.recipient] then return false; end -- bad value, returning false |
|
39 -- next we loop over all <allow/> elements |
|
40 for _, tag in ipairs(element) do |
|
41 if tag.name == "allow" and tag.attr.xmlns == "urn:xmpp:sift:1" then |
|
42 -- make sure the element is valid |
|
43 if not tag.attr.name or not tag.attr.ns then return false; end -- missing required attributes, returning false |
|
44 hash[tag.attr.ns.."|"..tag.attr.name] = true; |
|
45 hash.allowed = true; -- just a flag indicating we have some elements allowed |
|
46 end |
|
47 end |
|
48 return hash; |
|
49 end |
|
50 end |
|
51 |
|
52 local data = {}; -- table with all our data |
|
53 |
|
54 -- handle SIFT set |
|
55 module:hook("iq/self/urn:xmpp:sift:1:sift", function(event) |
|
56 local origin, stanza = event.origin, event.stanza; |
|
57 if stanza.attr.type == "set" then |
|
58 local sifttag = stanza.tags[1]; -- <sift/> |
|
59 |
|
60 -- first, get the elements we are interested in |
|
61 local message = sifttag:get_child("message"); |
|
62 local presence = sifttag:get_child("presence"); |
|
63 local iq = sifttag:get_child("iq"); |
|
64 |
|
65 -- for quick lookup, convert the elements into hashtables |
|
66 message = to_hashtable(message); |
|
67 presence = to_hashtable(presence); |
|
68 iq = to_hashtable(iq); |
|
69 |
|
70 -- make sure elements were valid |
|
71 if message == false or presence == false or iq == false then |
|
72 origin.send(st.error_reply(stanza, "modify", "bad-request")); |
|
73 return true; |
|
74 end |
|
75 |
|
76 local existing = data[origin.full_jid] or {}; -- get existing data, if any |
|
77 data[origin.full_jid] = { presence = presence, message = message, iq = iq }; -- store new data |
|
78 |
|
79 origin.send(st.reply(stanza)); -- send back IQ result |
|
80 |
|
81 if not existing.presence and not origin.presence and presence then |
|
82 -- TODO send probes |
|
83 end |
|
84 return true; |
|
85 end |
|
86 end); |
|
87 |
|
88 -- handle user disconnect |
|
89 module:hook("resource-unbind", function(event) |
|
90 data[event.origin.full_jid] = nil; -- discard data |
|
91 end); |
|
92 |
|
93 -- IQ handler |
|
94 module:hook("iq/full", function(event) |
|
95 local origin, stanza = event.origin, event.stanza; |
|
96 local siftdata = data[stanza.attr.to]; |
|
97 if siftdata and siftdata.iq then -- we seem to have an IQ filter |
|
98 local tag = stanza.tags[1]; -- the IQ child |
|
99 if not siftdata.iq[tag.attr.xmlns.."|"..tag.name] then |
|
100 -- element not allowed; sending back generic error |
|
101 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); |
|
102 return true; |
|
103 end |
|
104 end |
|
105 end, 50); |
|
106 |
|
107 -- Message to full JID handler |
|
108 module:hook("message/full", function(event) |
|
109 local origin, stanza = event.origin, event.stanza; |
|
110 local siftdata = data[stanza.attr.to]; |
|
111 if siftdata and siftdata.message then -- we seem to have an message filter |
|
112 local allowed = false; |
|
113 for _, childtag in ipairs(stanza.tags) do |
|
114 if siftdata.message[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then |
|
115 allowed = true; |
|
116 end |
|
117 end |
|
118 if not allowed then |
|
119 -- element not allowed; sending back generic error |
|
120 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); |
|
121 -- FIXME maybe send to offline storage |
|
122 return true; |
|
123 end |
|
124 end |
|
125 end, 50); |
|
126 |
|
127 -- Message to bare JID handler |
|
128 module:hook("message/bare", function(event) |
|
129 local origin, stanza = event.origin, event.stanza; |
|
130 local user = bare_sessions[jid_bare(stanza.attr.to)]; |
|
131 local allowed = false; |
|
132 for _, session in pairs(user or {}) do |
|
133 local siftdata = data[session.full_jid]; |
|
134 if siftdata and siftdata.message then -- we seem to have an message filter |
|
135 for _, childtag in ipairs(stanza.tags) do |
|
136 if siftdata.message[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then |
|
137 allowed = true; |
|
138 end |
|
139 end |
|
140 end |
|
141 end |
|
142 if not allowed then |
|
143 -- element not allowed; sending back generic error |
|
144 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); |
|
145 -- FIXME maybe send to offline storage |
|
146 return true; |
|
147 end |
|
148 end, 50); |
|
149 |
|
150 -- Presence to full JID handler |
|
151 module:hook("presence/full", function(event) |
|
152 local origin, stanza = event.origin, event.stanza; |
|
153 local siftdata = data[stanza.attr.to]; |
|
154 if siftdata and siftdata.presence then -- we seem to have an presence filter |
|
155 local allowed = false; |
|
156 for _, childtag in ipairs(stanza.tags) do |
|
157 if siftdata.presence[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then |
|
158 allowed = true; |
|
159 end |
|
160 end |
|
161 if not allowed then |
|
162 -- element not allowed; sending back generic error |
|
163 --origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); |
|
164 return true; |
|
165 end |
|
166 end |
|
167 end, 50); |
|
168 |
|
169 -- Presence to bare JID handler |
|
170 module:hook("presence/bare", function(event) |
|
171 local origin, stanza = event.origin, event.stanza; |
|
172 local user = bare_sessions[jid_bare(stanza.attr.to)]; |
|
173 local allowed = false; |
|
174 for _, session in pairs(user or {}) do |
|
175 local siftdata = data[session.full_jid]; |
|
176 if siftdata and siftdata.presence then -- we seem to have an presence filter |
|
177 for _, childtag in ipairs(stanza.tags) do |
|
178 if siftdata.presence[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then |
|
179 allowed = true; |
|
180 end |
|
181 end |
|
182 end |
|
183 end |
|
184 if not allowed then |
|
185 -- element not allowed; sending back generic error |
|
186 --origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); |
|
187 return true; |
|
188 end |
|
189 end, 50); |