Mercurial > prosody-modules
view mod_muc_mav/mod_muc_mav.lua @ 6556:7477e97a9045
mod_firewall: Apply pre-reload state before re-reading config
This change makes load/reload a bit more robust. module.load() runs before
module.restore() and it reads from the config and updates the state (if
needed).
However, after this, module.restore() could run and apply the old state again.
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Sun, 24 May 2026 20:03:20 +0100 |
| parents | 19bee2a6436a |
| children |
line wrap: on
line source
local st = require "util.stanza"; local hashes = require "util.hashes"; local base64 = require "util.encodings".base64; local util_jid = require "util.jid"; local mod_muc = module:depends"muc"; local xmlns_mav = "urn:xmpp:muc:affiliations:1"; local xmlns_muc = "http://jabber.org/protocol/muc"; local xmlns_muc_user = "http://jabber.org/protocol/muc#user"; local xmlns_occupant_id = "urn:xmpp:occupant-id:0"; local mav_versions = {} local function get_affiliations(from, room, hash_only) local mav_since = nil local jid_affiliations = {} local xs = {} local x = st.stanza("x", {xmlns = xmlns_muc_user;}); local count = 0 for jid, affiliation, affiliation_data in room:each_affiliation() do table.insert(jid_affiliations, jid.."\0"..affiliation); if not hash_only then local occupant_id = room:get_occupant_id_from_jid(jid); local nick = affiliation_data and affiliation_data.reserved_nickname or nil; local show_jid = (not room:is_anonymous_for(from) or util_jid.bare(from) == jid) and jid or nil x:tag("item", {affiliation = affiliation or "none"; jid = show_jid; nick = nick;}) :tag("occupant-id", { xmlns = xmlns_occupant_id, id = occupant_id }):up():up(); count = count + 1 if count > 400 then table.sort(jid_affiliations) local hash_input = table.concat(jid_affiliations, "\0") local hash = base64.encode(hashes.sha256(hash_input)) x:tag("mav", {xmlns = xmlns_mav; since = mav_since; ["until"] = hash;}):up() table.insert(xs, x) x = st.stanza("x", {xmlns = xmlns_muc_user;}) count = 0 mav_since = hash end end end table.sort(jid_affiliations); local hash_input = table.concat(jid_affiliations, "\0"); local hash = base64.encode(hashes.sha256(hash_input)) if not hash_only and (count > 0 or #xs < 1) then x:tag("mav", {xmlns = xmlns_mav; since = mav_since; ["until"] = hash;}):up(); table.insert(xs, x); end return hash, xs; end local function handle_stanza_with_x(room, stanza, mav_current) local muc_x = stanza:get_child("x", xmlns_muc_user); local is_initial = false; for child in muc_x:childtags("status") do if child.attr.code == "110" then is_initial = true; end; end if is_initial then muc_x:tag("mav", {xmlns = xmlns_mav; ["until"] = mav_current;}):up(); else -- TODO: stop recomputing the hash on every stanza in the presence flood local mav_new = get_affiliations(stanza.attr.from, room, true); if mav_current ~= mav_new then module:log("info", "Update version of %s from %s to %s in affiliation update", stanza.attr.to, mav_current, mav_new); muc_x:tag("mav", {xmlns = xmlns_mav; ["since"] = mav_current; ["until"] = mav_new; }):up(); mav_versions[room.jid.."\0"..stanza.attr.to] = mav_new; end end end local function room_route_stanza(room, stanza) local muc_x = stanza:get_child("x", xmlns_muc_user); if muc_x then local mav_current = mav_versions[room.jid.."\0"..stanza.attr.to]; if mav_current then if stanza.name == "presence" or stanza.name == "message" then handle_stanza_with_x(room, stanza, mav_current); end end end module:send(stanza); end -- TODO: Stop this hack local original_room_route_stanza = mod_muc.room_mt.route_stanza; mod_muc.room_mt.route_stanza = room_route_stanza; function module.unload() mod_muc.room_mt.route_stanza = original_room_route_stanza; end module:hook("muc-disco#info", function(event) event.reply:tag("feature", {var = xmlns_mav}):up(); end); module:hook("muc-occupant-pre-join", function(event) local room, stanza = event.room, event.stanza; local muc_x = stanza:get_child("x", xmlns_muc); if not muc_x then return; end local mav_child = muc_x:get_child("mav", xmlns_mav); if not mav_child then return; end local mav_since = mav_child.attr["since"]; mav_versions[room.jid.."\0"..stanza.attr.from] = mav_since; local mav_current, xs = get_affiliations(stanza.attr.from, room); if mav_current ~= mav_since then -- TODO: Support for diffs -- Send full response module:log("info", "Old version of %s is %s, sending full response for %s", stanza.attr.from, mav_since, mav_current); for _, x in ipairs(xs) do local announce_msg = st.message({ from = room.jid, to = stanza.attr.from }):add_child(x); room:route_stanza(announce_msg); end mav_versions[room.jid.."\0"..stanza.attr.from] = mav_current; else module:log("info", "Old version of %s is %s, still current", stanza.attr.from, mav_since); end end, -100); module:hook("muc-occupant-left", function(event) local room, occupant = event.room, event.occupant; for jid in occupant:each_session() do mav_versions[room.jid.."\0"..jid] = nil; end end); module:hook("muc-occupant-pre-leave", function(event) local room, stanza = event.room, event.stanza; mav_versions[room.jid.."\0"..stanza.attr.from] = nil; end);
