Software /
code /
prosody-modules
Diff
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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_sift/mod_sift.lua Sat Mar 06 21:47:20 2010 +0500 @@ -0,0 +1,189 @@ + +local st = require "util.stanza"; +local jid_bare = require "util.jid".bare; + +-- advertise disco features +module:add_feature("urn:xmpp:sift:1"); + +-- supported features +module:add_feature("urn:xmpp:sift:stanzas:iq"); +module:add_feature("urn:xmpp:sift:stanzas:message"); +module:add_feature("urn:xmpp:sift:stanzas:presence"); +module:add_feature("urn:xmpp:sift:recipients:all"); +module:add_feature("urn:xmpp:sift:senders:all"); + +-- allowed values of 'sender' and 'recipient' attributes +local senders = { + ["all"] = true; + ["local"] = true; + ["others"] = true; + ["remote"] = true; + ["self"] = true; +}; +local recipients = { + ["all"] = true; + ["bare"] = true; + ["full"] = true; +}; + +-- this function converts a <message/>, <presence/> or <iq/> element in +-- the SIFT namespace into a hashtable, for easy lookup +local function to_hashtable(element) + if element ~= nil then + local hash = {}; + -- make sure the sender and recipient attributes has a valid value + hash.sender = element.attr.sender or "all"; + if not senders[hash.sender] then return false; end -- bad value, returning false + hash.recipient = element.attr.recipient or "all"; + if not recipients[hash.recipient] then return false; end -- bad value, returning false + -- next we loop over all <allow/> elements + for _, tag in ipairs(element) do + if tag.name == "allow" and tag.attr.xmlns == "urn:xmpp:sift:1" then + -- make sure the element is valid + if not tag.attr.name or not tag.attr.ns then return false; end -- missing required attributes, returning false + hash[tag.attr.ns.."|"..tag.attr.name] = true; + hash.allowed = true; -- just a flag indicating we have some elements allowed + end + end + return hash; + end +end + +local data = {}; -- table with all our data + +-- handle SIFT set +module:hook("iq/self/urn:xmpp:sift:1:sift", function(event) + local origin, stanza = event.origin, event.stanza; + if stanza.attr.type == "set" then + local sifttag = stanza.tags[1]; -- <sift/> + + -- first, get the elements we are interested in + local message = sifttag:get_child("message"); + local presence = sifttag:get_child("presence"); + local iq = sifttag:get_child("iq"); + + -- for quick lookup, convert the elements into hashtables + message = to_hashtable(message); + presence = to_hashtable(presence); + iq = to_hashtable(iq); + + -- make sure elements were valid + if message == false or presence == false or iq == false then + origin.send(st.error_reply(stanza, "modify", "bad-request")); + return true; + end + + local existing = data[origin.full_jid] or {}; -- get existing data, if any + data[origin.full_jid] = { presence = presence, message = message, iq = iq }; -- store new data + + origin.send(st.reply(stanza)); -- send back IQ result + + if not existing.presence and not origin.presence and presence then + -- TODO send probes + end + return true; + end +end); + +-- handle user disconnect +module:hook("resource-unbind", function(event) + data[event.origin.full_jid] = nil; -- discard data +end); + +-- IQ handler +module:hook("iq/full", function(event) + local origin, stanza = event.origin, event.stanza; + local siftdata = data[stanza.attr.to]; + if siftdata and siftdata.iq then -- we seem to have an IQ filter + local tag = stanza.tags[1]; -- the IQ child + if not siftdata.iq[tag.attr.xmlns.."|"..tag.name] then + -- element not allowed; sending back generic error + origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); + return true; + end + end +end, 50); + +-- Message to full JID handler +module:hook("message/full", function(event) + local origin, stanza = event.origin, event.stanza; + local siftdata = data[stanza.attr.to]; + if siftdata and siftdata.message then -- we seem to have an message filter + local allowed = false; + for _, childtag in ipairs(stanza.tags) do + if siftdata.message[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then + allowed = true; + end + end + if not allowed then + -- element not allowed; sending back generic error + origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); + -- FIXME maybe send to offline storage + return true; + end + end +end, 50); + +-- Message to bare JID handler +module:hook("message/bare", function(event) + local origin, stanza = event.origin, event.stanza; + local user = bare_sessions[jid_bare(stanza.attr.to)]; + local allowed = false; + for _, session in pairs(user or {}) do + local siftdata = data[session.full_jid]; + if siftdata and siftdata.message then -- we seem to have an message filter + for _, childtag in ipairs(stanza.tags) do + if siftdata.message[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then + allowed = true; + end + end + end + end + if not allowed then + -- element not allowed; sending back generic error + origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); + -- FIXME maybe send to offline storage + return true; + end +end, 50); + +-- Presence to full JID handler +module:hook("presence/full", function(event) + local origin, stanza = event.origin, event.stanza; + local siftdata = data[stanza.attr.to]; + if siftdata and siftdata.presence then -- we seem to have an presence filter + local allowed = false; + for _, childtag in ipairs(stanza.tags) do + if siftdata.presence[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then + allowed = true; + end + end + if not allowed then + -- element not allowed; sending back generic error + --origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); + return true; + end + end +end, 50); + +-- Presence to bare JID handler +module:hook("presence/bare", function(event) + local origin, stanza = event.origin, event.stanza; + local user = bare_sessions[jid_bare(stanza.attr.to)]; + local allowed = false; + for _, session in pairs(user or {}) do + local siftdata = data[session.full_jid]; + if siftdata and siftdata.presence then -- we seem to have an presence filter + for _, childtag in ipairs(stanza.tags) do + if siftdata.presence[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then + allowed = true; + end + end + end + end + if not allowed then + -- element not allowed; sending back generic error + --origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); + return true; + end +end, 50);