aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/muc/occupant_id.lib.lua
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/muc/occupant_id.lib.lua')
-rw-r--r--plugins/muc/occupant_id.lib.lua70
1 files changed, 70 insertions, 0 deletions
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;
+};