diff options
-rw-r--r-- | plugins/muc/mod_muc.lua | 2 | ||||
-rw-r--r-- | plugins/muc/occupant_id.lib.lua | 70 |
2 files changed, 72 insertions, 0 deletions
diff --git a/plugins/muc/mod_muc.lua b/plugins/muc/mod_muc.lua index e9fd1521..bc44815e 100644 --- a/plugins/muc/mod_muc.lua +++ b/plugins/muc/mod_muc.lua @@ -91,6 +91,8 @@ room_mt.get_presence_broadcast = presence_broadcast.get; room_mt.set_presence_broadcast = presence_broadcast.set; room_mt.get_valid_broadcast_roles = presence_broadcast.get_valid_broadcast_roles; +local occupant_id = module:require "muc/occupant_id"; +room_mt.get_occupant_id = occupant_id.get_occupant_id; local jid_split = require "util.jid".split; local jid_prep = require "util.jid".prep; diff --git a/plugins/muc/occupant_id.lib.lua b/plugins/muc/occupant_id.lib.lua new file mode 100644 index 00000000..1a44462c --- /dev/null +++ b/plugins/muc/occupant_id.lib.lua @@ -0,0 +1,70 @@ +-- Implementation of https://xmpp.org/extensions/inbox/occupant-id.html +-- XEP-0421: Anonymous unique occupant identifiers for MUCs + +-- (C) 2020 Maxime “pep” Buquet <pep@bouah.net> +-- (C) 2020 Matthew Wild <mwild1@gmail.com> + +local uuid = require "util.uuid"; +local hmac_sha256 = require "util.hashes".hmac_sha256; +local b64encode = require "util.encodings".base64.encode; + +local xmlns_occupant_id = "urn:xmpp:occupant-id:0"; + +local function get_occupant_id(room, occupant) + if occupant.stable_id then + return occupant.stable_id; + end + + local salt = room._data.occupant_id_salt; + if not salt then + salt = uuid.generate(); + room._data.occupant_id_salt = salt; + end + + occupant.stable_id = b64encode(hmac_sha256(occupant.bare_jid, salt)); + + return occupant.stable_id; +end + +local function update_occupant(event) + local stanza, room, occupant, dest_occupant = event.stanza, event.room, event.occupant, event.dest_occupant; + + -- "muc-occupant-pre-change" provides "dest_occupant" but not "occupant". + if dest_occupant ~= nil then + occupant = dest_occupant; + end + + -- strip any existing <occupant-id/> tags to avoid forgery + stanza:remove_children("occupant-id", xmlns_occupant_id); + + local unique_id = get_occupant_id(room, occupant); + stanza:tag("occupant-id", { xmlns = xmlns_occupant_id, id = unique_id }):up(); +end + +local function muc_private(event) + local stanza, room = event.stanza, event.room; + local occupant = room._occupants[stanza.attr.from]; + + update_occupant({ + stanza = stanza, + room = room, + occupant = occupant, + }); +end + +if module:get_option_boolean("muc_occupant_id", true) then + module:add_feature(xmlns_occupant_id); + module:hook("muc-disco#info", function (event) + event.reply:tag("feature", { var = xmlns_occupant_id }):up(); + end); + + module:hook("muc-broadcast-presence", update_occupant); + module:hook("muc-occupant-pre-join", update_occupant); + module:hook("muc-occupant-pre-change", update_occupant); + module:hook("muc-occupant-groupchat", update_occupant); + module:hook("muc-private-message", muc_private); +end + +return { + get_occupant_id = get_occupant_id; +}; |