Mercurial > prosody-modules
annotate mod_sift/mod_sift.lua @ 5173:460f78654864
mod_muc_rtbl: also filter messages
This was a bit tricky because we don't want to run the JIDs
through SHA256 on each message. Took a while to come up with this
simple plan of just caching the SHA256 of the JIDs on the
occupants.
This will leave some dirt in the occupants after unloading the
module, but that should be ok; once they cycle the room, the
hashes will be gone.
This is direly needed, otherwise, there is a tight race between
the moderation activities and the actors joining the room.
author | Jonas Schäfer <jonas@wielicki.name> |
---|---|
date | Tue, 21 Feb 2023 21:37:27 +0100 |
parents | 7dbde05b48a9 |
children |
rev | line source |
---|---|
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/> | |
1343
7dbde05b48a9
all the things: Remove trailing whitespace
Florian Zeitz <florob@babelmonkeys.de>
parents:
142
diff
changeset
|
59 |
137 | 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"); | |
1343
7dbde05b48a9
all the things: Remove trailing whitespace
Florian Zeitz <florob@babelmonkeys.de>
parents:
142
diff
changeset
|
64 |
137 | 65 -- for quick lookup, convert the elements into hashtables |
66 message = to_hashtable(message); | |
67 presence = to_hashtable(presence); | |
68 iq = to_hashtable(iq); | |
1343
7dbde05b48a9
all the things: Remove trailing whitespace
Florian Zeitz <florob@babelmonkeys.de>
parents:
142
diff
changeset
|
69 |
137 | 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 | |
1343
7dbde05b48a9
all the things: Remove trailing whitespace
Florian Zeitz <florob@babelmonkeys.de>
parents:
142
diff
changeset
|
75 |
137 | 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 | |
1343
7dbde05b48a9
all the things: Remove trailing whitespace
Florian Zeitz <florob@babelmonkeys.de>
parents:
142
diff
changeset
|
78 |
137 | 79 origin.send(st.reply(stanza)); -- send back IQ result |
1343
7dbde05b48a9
all the things: Remove trailing whitespace
Florian Zeitz <florob@babelmonkeys.de>
parents:
142
diff
changeset
|
80 |
137 | 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) | |
138
61e1203e9e66
mod_sift: Use event.session, and not event.origin in the resource-unbind handler.
Waqas Hussain <waqas20@gmail.com>
parents:
137
diff
changeset
|
90 data[event.session.full_jid] = nil; -- discard data |
137 | 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]; | |
140
9a632cf13f51
mod_sift: Don't sift IQ errors and results.
Waqas Hussain <waqas20@gmail.com>
parents:
139
diff
changeset
|
97 if stanza.attr.type == "get" or stanza.attr.type == "set" then |
9a632cf13f51
mod_sift: Don't sift IQ errors and results.
Waqas Hussain <waqas20@gmail.com>
parents:
139
diff
changeset
|
98 if siftdata and siftdata.iq then -- we seem to have an IQ filter |
9a632cf13f51
mod_sift: Don't sift IQ errors and results.
Waqas Hussain <waqas20@gmail.com>
parents:
139
diff
changeset
|
99 local tag = stanza.tags[1]; -- the IQ child |
9a632cf13f51
mod_sift: Don't sift IQ errors and results.
Waqas Hussain <waqas20@gmail.com>
parents:
139
diff
changeset
|
100 if not siftdata.iq[(tag.attr.xmlns or "jabber:client").."|"..tag.name] then |
9a632cf13f51
mod_sift: Don't sift IQ errors and results.
Waqas Hussain <waqas20@gmail.com>
parents:
139
diff
changeset
|
101 -- element not allowed; sending back generic error |
9a632cf13f51
mod_sift: Don't sift IQ errors and results.
Waqas Hussain <waqas20@gmail.com>
parents:
139
diff
changeset
|
102 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); |
9a632cf13f51
mod_sift: Don't sift IQ errors and results.
Waqas Hussain <waqas20@gmail.com>
parents:
139
diff
changeset
|
103 return true; |
9a632cf13f51
mod_sift: Don't sift IQ errors and results.
Waqas Hussain <waqas20@gmail.com>
parents:
139
diff
changeset
|
104 end |
137 | 105 end |
106 end | |
107 end, 50); | |
108 | |
109 -- Message to full JID handler | |
110 module:hook("message/full", function(event) | |
111 local origin, stanza = event.origin, event.stanza; | |
112 local siftdata = data[stanza.attr.to]; | |
113 if siftdata and siftdata.message then -- we seem to have an message filter | |
114 local allowed = false; | |
115 for _, childtag in ipairs(stanza.tags) do | |
116 if siftdata.message[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then | |
117 allowed = true; | |
118 end | |
119 end | |
120 if not allowed then | |
121 -- element not allowed; sending back generic error | |
122 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); | |
123 -- FIXME maybe send to offline storage | |
124 return true; | |
125 end | |
126 end | |
127 end, 50); | |
128 | |
129 -- Message to bare JID handler | |
130 module:hook("message/bare", function(event) | |
131 local origin, stanza = event.origin, event.stanza; | |
132 local user = bare_sessions[jid_bare(stanza.attr.to)]; | |
133 local allowed = false; | |
142
f37de7e2ad14
mod_sift: Iterate on user sessions, not on the user itself, when sifting stanzas to bare JIDs.
Waqas Hussain <waqas20@gmail.com>
parents:
141
diff
changeset
|
134 for _, session in pairs(user and user.sessions or {}) do |
137 | 135 local siftdata = data[session.full_jid]; |
136 if siftdata and siftdata.message then -- we seem to have an message filter | |
137 for _, childtag in ipairs(stanza.tags) do | |
138 if siftdata.message[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then | |
139 allowed = true; | |
140 end | |
141 end | |
141
b42a88eba9ba
mod_sift: Don't disallow stanzas to bare JIDs when sifting is not in force for any resources.
Waqas Hussain <waqas20@gmail.com>
parents:
140
diff
changeset
|
142 else |
b42a88eba9ba
mod_sift: Don't disallow stanzas to bare JIDs when sifting is not in force for any resources.
Waqas Hussain <waqas20@gmail.com>
parents:
140
diff
changeset
|
143 allowed = true; |
137 | 144 end |
145 end | |
141
b42a88eba9ba
mod_sift: Don't disallow stanzas to bare JIDs when sifting is not in force for any resources.
Waqas Hussain <waqas20@gmail.com>
parents:
140
diff
changeset
|
146 if user and not allowed then |
137 | 147 -- element not allowed; sending back generic error |
148 origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); | |
149 -- FIXME maybe send to offline storage | |
150 return true; | |
151 end | |
152 end, 50); | |
153 | |
154 -- Presence to full JID handler | |
155 module:hook("presence/full", function(event) | |
156 local origin, stanza = event.origin, event.stanza; | |
157 local siftdata = data[stanza.attr.to]; | |
158 if siftdata and siftdata.presence then -- we seem to have an presence filter | |
159 local allowed = false; | |
160 for _, childtag in ipairs(stanza.tags) do | |
161 if siftdata.presence[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then | |
162 allowed = true; | |
163 end | |
164 end | |
165 if not allowed then | |
166 -- element not allowed; sending back generic error | |
167 --origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); | |
168 return true; | |
169 end | |
170 end | |
171 end, 50); | |
172 | |
173 -- Presence to bare JID handler | |
174 module:hook("presence/bare", function(event) | |
175 local origin, stanza = event.origin, event.stanza; | |
176 local user = bare_sessions[jid_bare(stanza.attr.to)]; | |
177 local allowed = false; | |
142
f37de7e2ad14
mod_sift: Iterate on user sessions, not on the user itself, when sifting stanzas to bare JIDs.
Waqas Hussain <waqas20@gmail.com>
parents:
141
diff
changeset
|
178 for _, session in pairs(user and user.sessions or {}) do |
137 | 179 local siftdata = data[session.full_jid]; |
180 if siftdata and siftdata.presence then -- we seem to have an presence filter | |
181 for _, childtag in ipairs(stanza.tags) do | |
182 if siftdata.presence[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then | |
183 allowed = true; | |
184 end | |
185 end | |
141
b42a88eba9ba
mod_sift: Don't disallow stanzas to bare JIDs when sifting is not in force for any resources.
Waqas Hussain <waqas20@gmail.com>
parents:
140
diff
changeset
|
186 else |
b42a88eba9ba
mod_sift: Don't disallow stanzas to bare JIDs when sifting is not in force for any resources.
Waqas Hussain <waqas20@gmail.com>
parents:
140
diff
changeset
|
187 allowed = true; |
137 | 188 end |
189 end | |
141
b42a88eba9ba
mod_sift: Don't disallow stanzas to bare JIDs when sifting is not in force for any resources.
Waqas Hussain <waqas20@gmail.com>
parents:
140
diff
changeset
|
190 if user and not allowed then |
137 | 191 -- element not allowed; sending back generic error |
192 --origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); | |
193 return true; | |
194 end | |
195 end, 50); |