changeset 5651:b40750891bee

mod_pubsub_subscription: support subscribing from a bare JID Allow subscribing from a bare JID on the component instead of only the component host, useful for subscribing to whitelist access model nodes that want to see a particular JID in the from.
author Stephen Paul Weber <singpolyma@singpolyma.net>
date Sat, 06 May 2023 19:42:08 -0500
parents 0eb2d5ea2428
children eade7ff9f52c
files mod_pubsub_subscription/mod_pubsub_subscription.lua
diffstat 1 files changed, 50 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/mod_pubsub_subscription/mod_pubsub_subscription.lua	Sat May 06 19:40:23 2023 -0500
+++ b/mod_pubsub_subscription/mod_pubsub_subscription.lua	Sat May 06 19:42:08 2023 -0500
@@ -12,7 +12,7 @@
 
 local pending_subscription = cache.new(256); -- uuid → node
 local pending_unsubscription = cache.new(256); -- uuid → node
-local active_subscriptions = mt.new() -- service | node | uuid | { item }
+local active_subscriptions = mt.new() -- service | node | subscriber | uuid | { item }
 function module.save()
 	return { active_subscriptions = active_subscriptions.data }
 end
@@ -28,9 +28,10 @@
 	local item = item_event.item;
 	assert(item.service, "pubsub subscription item MUST have a 'service' field.");
 	assert(item.node, "pubsub subscription item MUST have a 'node' field.");
+	item.from = item.from or module.host;
 
 	local already_subscibed = false;
-	for _ in active_subscriptions:iter(item.service, item.node, nil) do -- luacheck: ignore 512
+	for _ in active_subscriptions:iter(item.service, item.node, item.from, nil) do -- luacheck: ignore 512
 		already_subscibed = true;
 		break
 	end
@@ -38,24 +39,30 @@
 	item._id = uuid.generate();
 	local iq_id = uuid.generate();
 	pending_subscription:set(iq_id, item._id);
-	active_subscriptions:set(item.service, item.node, item._id, item);
+	active_subscriptions:set(item.service, item.node, item.from, item._id, item);
 
 	if not already_subscibed then
-		module:send(st.iq({ type = "set", id = iq_id, from = module.host, to = item.service })
+		module:send(st.iq({ type = "set", id = iq_id, from = item.from, to = item.service })
 			:tag("pubsub", { xmlns = xmlns_pubsub })
-				:tag("subscribe", { jid = module.host, node = item.node }));
+				:tag("subscribe", { jid = item.from, node = item.node }));
 	end
 end
 
 for _, event_name in ipairs(valid_events) do
 	module:hook("pubsub-event/host/"..event_name, function (event)
-		for _, _, _, _, cb in active_subscriptions:iter(event.service, event.node, nil, "on_"..event_name) do
+		for _, _, _, _, _, cb in active_subscriptions:iter(event.service, event.node, event.stanza.attr.to, nil, "on_"..event_name) do
+			pcall(cb, event);
+		end
+	end);
+
+	module:hook("pubsub-event/bare/"..event_name, function (event)
+		for _, _, _, _, _, cb in active_subscriptions:iter(event.service, event.node, event.stanza.attr.to, nil, "on_"..event_name) do
 			pcall(cb, event);
 		end
 	end);
 end
 
-module:hook("iq/host", function (event)
+function handle_iq(context, event)
 	local stanza = event.stanza;
 	local service = stanza.attr.from;
 
@@ -79,7 +86,7 @@
 			what = "on_unsubscribed";
 		end
 		if not what then return end -- there are other states but we don't handle them
-		for _, _, _, _, cb in active_subscriptions:iter(service, node, nil, what) do
+		for _, _, _, _, _, cb in active_subscriptions:iter(service, node, stanza.attr.to, nil, what) do
 			cb(event);
 		end
 		return true;
@@ -89,40 +96,48 @@
 		local error_type, error_condition, reason, pubsub_error = stanza:get_error();
 		local err = { type = error_type, condition = error_condition, text = reason, extra = pubsub_error };
 		if active_subscriptions:get(service) then
-			for _, _, _, _, cb in active_subscriptions:iter(service, node, nil, "on_error") do
+			for _, _, _, _, _, cb in active_subscriptions:iter(service, node, stanza.attr.to, nil, "on_error") do
 				cb(err);
 			end
 			return true;
 		end
 	end
+end
+
+module:hook("iq/host", function (event)
+	handle_iq("host", event);
+end, 1);
+
+module:hook("iq/bare", function (event)
+	handle_iq("bare", event);
 end, 1);
 
 local function subscription_removed(item_event)
 	local item = item_event.item;
-	active_subscriptions:set(item.service, item.node, item._id, nil);
-	local node_subs = active_subscriptions:get(item.service, item.node);
+	active_subscriptions:set(item.service, item.node, item.from, item._id, nil);
+	local node_subs = active_subscriptions:get(item.service, item.node, item.from);
 	if node_subs and next(node_subs) then return end
 
 	local iq_id = uuid.generate();
 	pending_unsubscription:set(iq_id, item._id);
 
-	module:send(st.iq({ type = "set", id = iq_id, from = module.host, to = item.service })
+	module:send(st.iq({ type = "set", id = iq_id, from = item.from, to = item.service })
 		:tag("pubsub", { xmlns = xmlns_pubsub })
-			:tag("unsubscribe", { jid = module.host, node = item.node }))
+			:tag("unsubscribe", { jid = item.from, node = item.node }))
 end
 
 module:handle_items("pubsub-subscription", subscription_added, subscription_removed, true);
 
-module:hook("message/host", function(event)
+function handle_message(context, event)
 	local origin, stanza = event.origin, event.stanza;
 	local ret = nil;
 	local service = stanza.attr.from;
-	module:log("debug", "Got message/host: %s", stanza:top_tag());
+	module:log("debug", "Got message/%s: %s", context, stanza:top_tag());
 	for event_container in stanza:childtags("event", xmlns_pubsub_event) do
 		for pubsub_event in event_container:childtags() do
 			module:log("debug", "Got pubsub event %s", pubsub_event:top_tag());
 			local node = pubsub_event.attr.node;
-			module:fire_event("pubsub-event/host/"..pubsub_event.name, {
+			module:fire_event("pubsub-event/" .. context .. "/"..pubsub_event.name, {
 					stanza = stanza;
 					origin = origin;
 					event = pubsub_event;
@@ -133,13 +148,30 @@
 		end
 	end
 	return ret;
+end
+
+module:hook("message/host", function(event)
+	return handle_message("host", event);
 end);
 
-module:hook("pubsub-event/host/items", function (event)
+module:hook("message/bare", function(event)
+	return handle_message("bare", event);
+end);
+
+
+function handle_items(context, event)
 	for item in event.event:childtags() do
 		module:log("debug", "Got pubsub item event %s", item:top_tag());
 		event.item = item;
 		event.payload = item.tags[1];
-		module:fire_event("pubsub-event/host/"..item.name, event);
+		module:fire_event("pubsub-event/" .. context .. "/"..item.name, event);
 	end
+end
+
+module:hook("pubsub-event/host/items", function (event)
+	handle_items("host", event);
 end);
+
+module:hook("pubsub-event/bare/items", function (event)
+	handle_items("bare", event);
+end);