view mod_service_directories/mod_service_directories.lua @ 5627:3a5cf8d80089

mod_http_oauth2: Tweak method of centering the UI The percentage here was relative to the viewport width, which on some very wide screens may put the UI slightly outside of the view, requiring scrolling to see. By using a unit relative to the height of the viewport, this is avoided and should work better. But no guarantees, it's still possible to resize the browser or adjust font sizes so that the UI goes out of view.
author Kim Alvefur <zash@zash.se>
date Mon, 31 Jul 2023 07:28:09 +0200
parents 7dbde05b48a9
children
line wrap: on
line source

-- Prosody IM
-- Copyright (C) 2011 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--

-- An implementation of [XEP-0309: Service Directories]

-- Imports and defines

local st = require "util.stanza";
local jid_split = require "util.jid".split;
local adhoc_new = module:require "adhoc".new;
local to_ascii = require "util.encodings".idna.to_ascii;
local nameprep = require "util.encodings".stringprep.nameprep;
local dataforms_new = require "util.dataforms".new;
local pairs, ipairs = pairs, ipairs;
local module = module;
local hosts = hosts;

local subscription_from = {};
local subscription_to = {};
local contact_features = {};
local contact_vcards = {};

-- Advertise in disco

module:add_identity("server", "directory", module:get_option_string("name", "Prosody"));
module:add_feature("urn:xmpp:server-presence");

-- Handle subscriptions

module:hook("presence/host", function(event) -- inbound presence to the host
	local origin, stanza = event.origin, event.stanza;

	local node, host, resource = jid_split(stanza.attr.from);
	if stanza.attr.from ~= host then return; end -- not from a host

	local t = stanza.attr.type;
	if t == "probe" then
		module:send(st.presence({ from = module.host, to = host, id = stanza.attr.id }));
	elseif t == "subscribe" then
		subscription_from[host] = true;
		module:send(st.presence({ from = module.host, to = host, id = stanza.attr.id, type = "subscribed" }));
		module:send(st.presence({ from = module.host, to = host, id = stanza.attr.id }));
		add_contact(host);
	elseif t == "subscribed" then
		subscription_to[host] = true;
		query_host(host);
	elseif t == "unsubscribe" then
		subscription_from[host] = nil;
		module:send(st.presence({ from = module.host, to = host, id = stanza.attr.id, type = "unsubscribed" }));
		remove_contact(host);
	elseif t == "unsubscribed" then
		subscription_to[host] = nil;
		remove_contact(host);
	end
	return true;
end, 10); -- priority over mod_presence

function remove_contact(host, id)
	contact_features[host] = nil;
	contact_vcards[host] = nil;
	if subscription_to[host] then
		subscription_to[host] = nil;
		module:send(st.presence({ from = module.host, to = host, id = id, type = "unsubscribe" }));
	end
	if subscription_from[host] then
		subscription_from[host] = nil;
		module:send(st.presence({ from = module.host, to = host, id = id, type = "unsubscribed" }));
	end
end
function add_contact(host, id)
	if not subscription_to[host] then
		module:send(st.presence({ from = module.host, to = host, id = id, type = "subscribe" }));
	end
end

-- Admin ad-hoc command to subscribe

local function add_contact_handler(self, data, state)
	local layout = dataforms_new{
		title = "Adding a Server Buddy";
		instructions = "Fill out this form to add a \"server buddy\".";

		{ name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/admin" };
		{ name = "peerjid", type = "jid-single", required = true, label = "The server to add" };
	};

	if not state then
		return { status = "executing", form = layout }, "executing";
	elseif data.action == "canceled" then
		return { status = "canceled" };
	else
		local fields = layout:data(data.form);
		local peerjid = nameprep(fields.peerjid);
		if not peerjid or peerjid == "" or #peerjid > 1023 or not to_ascii(peerjid) then
			return { status = "completed", error = { message = "Invalid JID" } };
		end
		add_contact(peerjid);
		return { status = "completed" };
	end
end

local add_contact_command = adhoc_new("Adding a Server Buddy", "http://jabber.org/protocol/admin#server-buddy", add_contact_handler, "admin");
module:add_item("adhoc", add_contact_command);

-- Disco query remote host
function query_host(host)
	local stanza = st.iq({ from = module.host, to = host, type = "get", id = "mod_service_directories:disco" })
		:query("http://jabber.org/protocol/disco#info");
	module:send(stanza);
end

-- Handle disco query result
module:hook("iq-result/bare/mod_service_directories:disco", function(event)
	local origin, stanza = event.origin, event.stanza;

	if not subscription_to[stanza.attr.from] then return; end -- not from a contact
	local host = stanza.attr.from;

	local query = stanza:get_child("query", "http://jabber.org/protocol/disco#info")
	if not query then return; end

	-- extract disco features
	local features = {};
	for _,tag in ipairs(query.tags) do
		if tag.name == "feature" and tag.attr.var then
			features[tag.attr.var] = true;
		end
	end
	contact_features[host] = features;

	if features["urn:ietf:params:xml:ns:vcard-4.0"] then
		local stanza = st.iq({ from = module.host, to = host, type = "get", id = "mod_service_directories:vcard" })
			:tag("vcard", { xmlns = "urn:ietf:params:xml:ns:vcard-4.0" });
		module:send(stanza);
	end
	return true;
end);

-- Handle vcard result
module:hook("iq-result/bare/mod_service_directories:vcard", function(event)
	local origin, stanza = event.origin, event.stanza;

	if not subscription_to[stanza.attr.from] then return; end -- not from a contact
	local host = stanza.attr.from;

	local vcard = stanza:get_child("vcard", "urn:ietf:params:xml:ns:vcard-4.0");
	if not vcard then return; end

	contact_vcards[host] = st.clone(vcard);
	return true;
end);

-- PubSub

-- TODO the following should be replaced by mod_pubsub

module:hook("iq-get/host/http://jabber.org/protocol/pubsub:pubsub", function(event)
	local origin, stanza = event.origin, event.stanza;
	local payload = stanza.tags[1];

	local items = payload:get_child("items", "http://jabber.org/protocol/pubsub");
	if items and items.attr.node == "urn:xmpp:contacts" then
		local reply = st.reply(stanza)
			:tag("pubsub", { xmlns = "http://jabber.org/protocol/pubsub" })
				:tag("items", { node = "urn:xmpp:contacts" });
		for host, vcard in pairs(contact_vcards) do
			reply:tag("item", { id = host })
				:add_child(vcard)
			:up();
		end
		origin.send(reply);
		return true;
	end
end);