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