Comparison

mod_sift/mod_sift.lua @ 137:34a3ec3e7dc3

mod_sift: Initial commit.
author Waqas Hussain <waqas20@gmail.com>
date Sat, 06 Mar 2010 21:47:20 +0500
child 138:61e1203e9e66
comparison
equal deleted inserted replaced
136:0525c66e7d13 137:34a3ec3e7dc3
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);