changeset 6399:00735045109d

mod_invites: Remove, these are shipped with the oldest supported Prosody
author Kim Alvefur <zash@zash.se>
date Sat, 07 Feb 2026 19:48:30 +0100
parents 9c9889ad4302
children f4efb8606b92
files mod_invites/README.md mod_invites/mod_invites.lua mod_invites_adhoc/README.md mod_invites_adhoc/mod_invites_adhoc.lua mod_invites_register/README.md mod_invites_register/mod_invites_register.lua
diffstat 6 files changed, 9 insertions(+), 838 deletions(-) [+]
line wrap: on
line diff
--- a/mod_invites/README.md	Sat Feb 07 12:25:52 2026 +0100
+++ b/mod_invites/README.md	Sat Feb 07 19:48:30 2026 +0100
@@ -2,94 +2,12 @@
 labels:
 - 'Stage-Merged'
 summary: 'Invite management module for Prosody'
+superseded_by: mod_invites
 ...
 
 Introduction
 ============
 
-::: {.alert .alert-info}
-This module has been merged into Prosody as
-[mod_invites][doc:modules:mod_invites]. Users of Prosody **0.12**
-and later should not install this version.
+::: {.alert .alert-error}
+This module has been merged into Prosody as [mod_invites][doc:modules:mod_invites].
 :::
-
-This module is part of the suite of modules that implement invite-based
-account registration for Prosody. The other modules are:
-
-- [mod_invites_adhoc][doc:modules:mod_invites_adhoc]
-- [mod_invites_register][doc:modules:mod_invites_register]
-- [mod_invites_page]
-- [mod_invites_register_web]
-- [mod_invites_api]
-- [mod_register_apps]
-
-This module manages the creation and consumption of invite codes for the
-host(s) it is loaded onto. It currently does not expose any admin/user-facing
-functionality (though in the future it will probably gain a way to view/manage
-pending invites).
-
-Instead, other modules can use the API from this module to create invite tokens
-which can be used to e.g. register accounts or create automatic subscription
-approvals.
-
-This module should not be confused with the similarly named mod_invite (note the
-missing 's'!). That module was a precursor to this one that helped test and prove
-the concept of invite-based registration, and is now deprecated.
-
-# Configuration
-
-This module exposes just one option - the length of time that a generated invite
-should be valid for by default.
-
-``` {.lua}
--- Configure the number of seconds a token is valid for (default 7 days)
-invite_expiry = 86400 * 7
-```
-
-# Invites setup
-
-For a fully-featured invite-based setup, the following provides an example
-configuration:
-
-``` {.lua}
--- Specify the external URL format of the invite links
-
-VirtualHost "example.com"
-    invites_page = "https://example.com/invite?{invite.token}"
-    http_external_url = "https://example.com/"
-    http_paths = {
-        invites_page = "/invite";
-        invites_register_web = "/register";
-    }
-    modules_enabled = {
-        "invites";
-        "invites_adhoc";
-        "invites_page";
-        "invites_register";
-        "invites_register_web";
-
-        "http_libjs"; -- See 'external dependencies' below
-    }
-```
-
-Restart Prosody and create a new invite using an ad-hoc command in an XMPP client connected
-to your admin account, or use the command line:
-
-    prosodyctl mod_invites generate example.com
-
-## External dependencies
-
-The default HTML templates for the web-based modules depend on some CSS and Javascript
-libraries. They expect these to be available at `https://example.com/share`. An easy
-way of doing this if you are on Debian 10 (buster) is to enable mod_http_libjs and install
-the following packages:
-
-    apt install libjs-bootstrap4 libjs-jquery
-
-On other systems you will need to manually put these libraries somewhere on the filesystem
-that Prosody can read, and serve them using mod_http_libjs with a custom `libjs_path`
-setting.
-
-# Compatibility
-
-0.11 and later.
--- a/mod_invites/mod_invites.lua	Sat Feb 07 12:25:52 2026 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,339 +0,0 @@
-local id = require "util.id";
-local it = require "util.iterators";
-local url = require "socket.url";
-local jid_node = require "util.jid".node;
-local jid_split = require "util.jid".split;
-
-local default_ttl = module:get_option_number("invite_expiry", 86400 * 7);
-
-local token_storage;
-if prosody.process_type == "prosody" or prosody.shutdown then
-	token_storage = module:open_store("invite_token", "map");
-end
-
-local function get_uri(action, jid, token, params) --> string
-	return url.build({
-			scheme = "xmpp",
-			path = jid,
-			query = action..";preauth="..token..(params and (";"..params) or ""),
-		});
-end
-
-local function create_invite(invite_action, invite_jid, allow_registration, additional_data, ttl, reusable)
-	local token = id.medium();
-
-	local created_at = os.time();
-	local expires = created_at + (ttl or default_ttl);
-
-	local invite_params = (invite_action == "roster" and allow_registration) and "ibr=y" or nil;
-
-	local invite = {
-		type = invite_action;
-		jid = invite_jid;
-
-		token = token;
-		allow_registration = allow_registration;
-		additional_data = additional_data;
-
-		uri = get_uri(invite_action, invite_jid, token, invite_params);
-
-		created_at = created_at;
-		expires = expires;
-
-		reusable = reusable;
-	};
-
-	module:fire_event("invite-created", invite);
-
-	if allow_registration then
-		local ok, err = token_storage:set(nil, token, invite);
-		if not ok then
-			module:log("warn", "Failed to store account invite: %s", err);
-			return nil, "internal-server-error";
-		end
-	end
-
-	if invite_action == "roster" then
-		local username = jid_node(invite_jid);
-		local ok, err = token_storage:set(username, token, expires);
-		if not ok then
-			module:log("warn", "Failed to store subscription invite: %s", err);
-			return nil, "internal-server-error";
-		end
-	end
-
-	return invite;
-end
-
--- Create invitation to register an account (optionally restricted to the specified username)
-function create_account(account_username, additional_data, ttl) --luacheck: ignore 131/create_account
-	local jid = account_username and (account_username.."@"..module.host) or module.host;
-	return create_invite("register", jid, true, additional_data, ttl);
-end
-
--- Create invitation to reset the password for an account
-function create_account_reset(account_username, ttl) --luacheck: ignore 131/create_account_reset
-	return create_account(account_username, { allow_reset = account_username }, ttl or 86400);
-end
-
--- Create invitation to become a contact of a local user
-function create_contact(username, allow_registration, additional_data, ttl) --luacheck: ignore 131/create_contact
-	return create_invite("roster", username.."@"..module.host, allow_registration, additional_data, ttl);
-end
-
--- Create invitation to register an account and join a user group
--- If explicit ttl is passed, invite is valid for multiple signups
--- during that time period
-function create_group(group_ids, additional_data, ttl) --luacheck: ignore 131/create_group
-	local merged_additional_data = {
-		groups = group_ids;
-	};
-	if additional_data then
-		for k, v in pairs(additional_data) do
-			merged_additional_data[k] = v;
-		end
-	end
-	return create_invite("register", module.host, true, merged_additional_data, ttl, not not ttl);
-end
-
--- Iterates pending (non-expired, unused) invites that allow registration
-function pending_account_invites() --luacheck: ignore 131/pending_account_invites
-	local store = module:open_store("invite_token");
-	local now = os.time();
-	local function is_valid_invite(_, invite)
-		return invite.expires > now;
-	end
-	return it.filter(is_valid_invite, pairs(store:get(nil) or {}));
-end
-
-function get_account_invite_info(token) --luacheck: ignore 131/get_account_invite_info
-	if not token then
-		return nil, "no-token";
-	end
-
-	-- Fetch from host store (account invite)
-	local token_info = token_storage:get(nil, token);
-	if not token_info then
-		return nil, "token-invalid";
-	elseif os.time() > token_info.expires then
-		return nil, "token-expired";
-	end
-
-	return token_info;
-end
-
-function delete_account_invite(token) --luacheck: ignore 131/delete_account_invite
-	if not token then
-		return nil, "no-token";
-	end
-
-	return token_storage:set(nil, token, nil);
-end
-
-local valid_invite_methods = {};
-local valid_invite_mt = { __index = valid_invite_methods };
-
-function valid_invite_methods:use()
-	if self.reusable then
-		return true;
-	end
-
-	if self.username then
-		-- Also remove the contact invite if present, on the
-		-- assumption that they now have a mutual subscription
-		token_storage:set(self.username, self.token, nil);
-	end
-	token_storage:set(nil, self.token, nil);
-
-	return true;
-end
-
--- Get a validated invite (or nil, err). Must call :use() on the
--- returned invite after it is actually successfully used
--- For "roster" invites, the username of the local user (who issued
--- the invite) must be passed.
--- If no username is passed, but the registration is a roster invite
--- from a local user, the "inviter" field of the returned invite will
--- be set to their username.
-function get(token, username)
-	if not token then
-		return nil, "no-token";
-	end
-
-	local valid_until, inviter;
-
-	-- Fetch from host store (account invite)
-	local token_info = token_storage:get(nil, token);
-
-	if username then -- token being used for subscription
-		-- Fetch from user store (subscription invite)
-		valid_until = token_storage:get(username, token);
-	else -- token being used for account creation
-		valid_until = token_info and token_info.expires;
-		if token_info and token_info.type == "roster" then
-			username = jid_node(token_info.jid);
-			inviter = username;
-		end
-	end
-
-	if not valid_until then
-		module:log("debug", "Got unknown token: %s", token);
-		return nil, "token-invalid";
-	elseif os.time() > valid_until then
-		module:log("debug", "Got expired token: %s", token);
-		return nil, "token-expired";
-	end
-
-	return setmetatable({
-		token = token;
-		username = username;
-		inviter = inviter;
-		type = token_info and token_info.type or "roster";
-		uri = token_info and token_info.uri or get_uri("roster", username.."@"..module.host, token);
-		additional_data = token_info and token_info.additional_data or nil;
-		reusable = token_info and token_info.reusable or false;
-	}, valid_invite_mt);
-end
-
-function use(token) --luacheck: ignore 131/use
-	local invite = get(token);
-	return invite and invite:use();
-end
-
---- shell command
-do
-	-- Since the console is global this overwrites the command for
-	-- each host it's loaded on, but this should be fine.
-
-	local get_module = require "core.modulemanager".get_module;
-
-	local console_env = module:shared("/*/admin_shell/env");
-
-	-- luacheck: ignore 212/self
-	console_env.invite = {};
-	function console_env.invite:create_account(user_jid)
-		local username, host = jid_split(user_jid);
-		local mod_invites, err = get_module(host, "invites");
-		if not mod_invites then return nil, err or "mod_invites not loaded on this host"; end
-		local invite, err = mod_invites.create_account(username);
-		if not invite then return nil, err; end
-		return true, invite.uri;
-	end
-
-	function console_env.invite:create_contact(user_jid, allow_registration)
-		local username, host = jid_split(user_jid);
-		local mod_invites, err = get_module(host, "invites");
-		if not mod_invites then return nil, err or "mod_invites not loaded on this host"; end
-		local invite, err = mod_invites.create_contact(username, allow_registration);
-		if not invite then return nil, err; end
-		return true, invite.uri;
-	end
-end
-
---- prosodyctl command
-function module.command(arg)
-	if #arg < 2 or arg[1] ~= "generate" then
-		print("usage: prosodyctl mod_"..module.name.." generate example.com");
-		return 2;
-	end
-	table.remove(arg, 1); -- pop command
-
-	local sm = require "core.storagemanager";
-	local mm = require "core.modulemanager";
-
-	local host = arg[1];
-	assert(hosts[host], "Host "..tostring(host).." does not exist");
-	sm.initialize_host(host);
-	table.remove(arg, 1); -- pop host
-	module.host = host; --luacheck: ignore 122/module
-	token_storage = module:open_store("invite_token", "map");
-
-	-- Load mod_invites
-	local invites = module:depends("invites");
-	local invites_page_module = module:get_option_string("invites_page_module", "invites_page");
-	if mm.get_modules_for_host(host):contains(invites_page_module) then
-		module:depends(invites_page_module);
-	end
-
-	local allow_reset;
-	local roles;
-	local groups = {};
-
-	while #arg > 0 do
-		local value = arg[1];
-		table.remove(arg, 1);
-		if value == "--help" then
-			print("usage: prosodyctl mod_"..module.name.." generate DOMAIN --reset USERNAME")
-			print("usage: prosodyctl mod_"..module.name.." generate DOMAIN [--admin] [--role ROLE] [--group GROUPID]...")
-			print()
-			print("This command has two modes: password reset and new account.")
-			print("If --reset is given, the command operates in password reset mode and in new account mode otherwise.")
-			print()
-			print("required arguments in password reset mode:")
-			print()
-			print("    --reset USERNAME  Generate a password reset link for the given USERNAME.")
-			print()
-			print("optional arguments in new account mode:")
-			print()
-			print("    --admin           Make the new user privileged")
-			print("                      Equivalent to --role prosody:admin")
-			print("    --role ROLE       Grant the given ROLE to the new user")
-			print("    --group GROUPID   Add the user to the group with the given ID")
-			print("                      Can be specified multiple times")
-			print()
-			print("--role and --admin override each other; the last one wins")
-			print("--group can be specified multiple times; the user will be added to all groups.")
-			print()
-			print("--reset and the other options cannot be mixed.")
-			return 2
-		elseif value == "--reset" then
-			local nodeprep = require "util.encodings".stringprep.nodeprep;
-			local username = nodeprep(arg[1])
-			table.remove(arg, 1);
-			if not username then
-				print("Please supply a valid username to generate a reset link for");
-				return 2;
-			end
-			allow_reset = username;
-		elseif value == "--admin" then
-			roles = { ["prosody:admin"] = true };
-		elseif value == "--role" then
-			local rolename = arg[1];
-			if not rolename then
-				print("Please supply a role name");
-				return 2;
-			end
-			roles = { [rolename] = true };
-			table.remove(arg, 1);
-		elseif value == "--group" or value == "-g" then
-			local groupid = arg[1];
-			if not groupid then
-				print("Please supply a group ID")
-				return 2;
-			end
-			table.insert(groups, groupid);
-			table.remove(arg, 1);
-		else
-			print("unexpected argument: "..value)
-		end
-	end
-
-	local invite;
-	if allow_reset then
-		if roles then
-			print("--role/--admin and --reset are mutually exclusive")
-			return 2;
-		end
-		if #groups > 0 then
-			print("--group and --reset are mutually exclusive")
-		end
-		invite = assert(invites.create_account_reset(allow_reset));
-	else
-		invite = assert(invites.create_account(nil, {
-			roles = roles,
-			groups = groups
-		}));
-	end
-
-	print(invite.landing_page or invite.uri);
-end
--- a/mod_invites_adhoc/README.md	Sat Feb 07 12:25:52 2026 +0100
+++ b/mod_invites_adhoc/README.md	Sat Feb 07 19:48:30 2026 +0100
@@ -2,56 +2,12 @@
 labels:
 - 'Stage-Merged'
 summary: 'Enable ad-hoc command for XMPP clients to create invitations'
+superseded_by: mod_invites_adhoc
 ...
 
 Introduction
 ============
 
-::: {.alert .alert-info}
-This module has been merged into Prosody as
-[mod_invites_adhoc][doc:modules:mod_invites_adhoc]. Users of Prosody **0.12**
-and later should not install this version.
+::: {.alert .alert-error}
+This module has been merged into Prosody as [mod_invites_adhoc][doc:modules:mod_invites_adhoc].
 :::
-
-This module is part of the suite of modules that implement invite-based
-account registration for Prosody. The other modules are:
-
-- [mod_invites][doc:modules:mod_invites]
-- [mod_invites_register][doc:modules:mod_invites_register]
-- [mod_invites_page]
-- [mod_invites_register_web]
-- [mod_invites_api]
-- [mod_register_apps]
-
-For details and a full overview, start with the [mod_invites] documentation.
-
-Details
-=======
-
-mod_invites_adhoc allows XMPP clients to create new invites on the server.
-Clients must support either XEP-0401 (Easy Onboarding) or XEP-0050 (Ad-hoc
-commands).
-
-There are three types of invitation that can be created:
-
-| Invite type | Description |
-|--|--|
-| Account-only invites | These can be used to register a new account |
-| Contact-only invites | These can be shared with a contact so they can easily add you to their contact list |
-| Account-and-contact invites | Like a contact-only invite, but also allows the contact to register on the current server if they don't already have an XMPP account |
-
-Only configured admins of the server are able to create account-only invites. By default
-normal users may only create contact-only invites, but account-and-contact invites can
-be enabled with the `allow_user_invites` option.
-
-Configuration
-=============
-
-| Name                  | Description                                                           | Default                                   |
-|-----------------------|-----------------------------------------------------------------------|-------------------------------------------|
-| allow_user_invites    | Whether non-admin users can invite contacts to register on this server| `false`                                   |
-| allow_contact_invites | Whether non-admin users can invite contacts to their roster           | `true`                                    |
-
-The `allow_user_invites` option should be set as desired. However it is
-strongly recommended to leave the other option (`allow_contact_invites`)
-at its default to provide the best user experience.
--- a/mod_invites_adhoc/mod_invites_adhoc.lua	Sat Feb 07 12:25:52 2026 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
--- XEP-0401: Easy User Onboarding
-local dataforms = require "util.dataforms";
-local datetime = require "util.datetime";
-local split_jid = require "util.jid".split;
-local usermanager = require "core.usermanager";
-
-local new_adhoc = module:require("adhoc").new;
-
--- Whether local users can invite other users to create an account on this server
-local allow_user_invites = module:get_option_boolean("allow_user_invites", false);
--- Who can see and use the contact invite command. It is strongly recommended to
--- keep this available to all local users. To allow/disallow invite-registration
--- on the server, use the option above instead.
-local allow_contact_invites = module:get_option_boolean("allow_contact_invites", true);
-
--- These options are deprecated since module:may()
-local allow_user_invite_roles = module:get_option_set("allow_user_invites_by_roles", {});
-local deny_user_invite_roles = module:get_option_set("deny_user_invites_by_roles", {});
-
-if module.may then
-	if allow_user_invites then
-		if require "core.features".available:contains("split-user-roles") then
-			module:default_permission("prosody:registered", ":invite-new-users");
-		else -- COMPAT
-			module:default_permission("prosody:user", ":invite-new-users");
-		end
-	end
-	if not allow_user_invite_roles:empty() or not deny_user_invite_roles:empty() then
-		return error("allow_user_invites_by_roles and deny_user_invites_by_roles are deprecated options");
-	end
-end
-
-local invites;
-if prosody.shutdown then -- COMPAT hack to detect prosodyctl
-	invites = module:depends("invites");
-end
-
-local invite_result_form = dataforms.new({
-		title = "Your invite has been created",
-		{
-			name = "url" ;
-			var = "landing-url";
-			label = "Invite web page";
-			desc = "Share this link";
-		},
-		{
-			name = "uri";
-			label = "Invite URI";
-			desc = "This alternative link can be opened with some XMPP clients";
-		},
-		{
-			name = "expire";
-			label = "Invite valid until";
-		},
-	});
-
--- This is for checking if the specified JID may create invites
--- that allow people to register accounts on this host.
-local function may_invite_new_users(jid, context)
-	if module.may then
-		return module:may(":invite-new-users", context);
-	elseif usermanager.get_roles then -- COMPAT w/0.12
-		local user_roles = usermanager.get_roles(jid, module.host);
-		if not user_roles then
-			-- User has no roles we can check, just return default
-			return allow_user_invites;
-		end
-
-		if user_roles["prosody:admin"] then
-			return true;
-		end
-		if allow_user_invite_roles then
-			for allowed_role in allow_user_invite_roles do
-				if user_roles[allowed_role] then
-					return true;
-				end
-			end
-		end
-		if deny_user_invite_roles then
-			for denied_role in deny_user_invite_roles do
-				if user_roles[denied_role] then
-					return false;
-				end
-			end
-		end
-	elseif usermanager.is_admin(jid, module.host) then -- COMPAT w/0.11
-		return true; -- Admins may always create invitations
-	end
-	-- No role matches, so whatever the default is
-	return allow_user_invites;
-end
-
-module:depends("adhoc");
-
--- This command is available to all local users, even if allow_user_invites = false
--- If allow_user_invites is false, creating an invite still works, but the invite will
--- not be valid for registration on the current server, only for establishing a roster
--- subscription.
-module:provides("adhoc", new_adhoc("Create new contact invite", "urn:xmpp:invite#invite",
-		function (_, data)
-			local username, host = split_jid(data.from);
-			if host ~= module.host then
-				return {
-					status = "completed";
-					error = {
-						message = "This command is only available to users of "..module.host;
-					};
-				};
-			end
-			local invite = invites.create_contact(username, may_invite_new_users(data.from, data), {
-				source = data.from
-			});
-			--TODO: check errors
-			return {
-				status = "completed";
-				form = {
-					layout = invite_result_form;
-					values = {
-						uri = invite.uri;
-						url = invite.landing_page;
-						expire = datetime.datetime(invite.expires);
-					};
-				};
-			};
-		end, allow_contact_invites and "local_user" or "admin"));
-
--- This is an admin-only command that creates a new invitation suitable for registering
--- a new account. It does not add the new user to the admin's roster.
-module:provides("adhoc", new_adhoc("Create new account invite", "urn:xmpp:invite#create-account",
-		function (_, data)
-			local invite = invites.create_account(nil, {
-				source = data.from
-			});
-			--TODO: check errors
-			return {
-				status = "completed";
-				form = {
-					layout = invite_result_form;
-					values = {
-						uri = invite.uri;
-						url = invite.landing_page;
-						expire = datetime.datetime(invite.expires);
-					};
-				};
-			};
-		end, "admin"));
--- a/mod_invites_register/README.md	Sat Feb 07 12:25:52 2026 +0100
+++ b/mod_invites_register/README.md	Sat Feb 07 19:48:30 2026 +0100
@@ -2,69 +2,12 @@
 labels:
 - 'Stage-Merged'
 summary: 'Allow account registration using invite tokens'
+superseded_by: mod_invites_register
 ...
 
 Introduction
 ============
 
-::: {.alert .alert-info}
-This module has been merged into Prosody as
-[mod_invites_register][doc:modules:mod_invites_register]. Users of
-Prosody **0.12** and later should not install this version.
+::: {.alert .alert-error}
+This module has been merged into Prosody as [mod_invites_register][doc:modules:mod_invites_register].
 :::
-
-This module is part of the suite of modules that implement invite-based
-account registration for Prosody. The other modules are:
-
-- [mod_invites][doc:modules:mod_invites]
-- [mod_invites_adhoc][doc:modules:mod_invites_adhoc]
-- [mod_invites_page]
-- [mod_invites_register_web]
-- [mod_invites_api]
-- [mod_register_apps]
-
-For details and a full overview, start with the [mod_invites] documentation.
-
-Details
-=======
-
-This module allows clients to register an account using an invite ('preauth')
-token generated by mod_invites. It implements the protocol described at
-[docs.modernxmpp.org/client/invites](https://docs.modernxmpp.org/client/invites)
-and [XEP-0401 version 0.3.0](https://xmpp.org/extensions/attic/xep-0401-0.3.0.html).
-
-**Note to developers:** the XEP-0401 protocol is expected to change in the future,
-though Prosody will attempt to maintain backwards compatibility with the 0.3.0 protocol
-for as long as necessary.
-
-This module is also responsible for implementing the optional server-side part
-of [XEP-0379: Pre-Authenticated Roster Subscriptions](https://xmpp.org/extensions/xep-0379.html).
-
-**Note to admins:** Loading this module will disable registration for users
-without an invite token by default. Control this behaviour 
-
-# Configuration
-
-| Name                     | Description                                              | Default |
-|--------------------------|----------------------------------------------------------|---------|
-| registration_invite_only | Require an invitation token for all account registration | `true`  |
-
-## Example: Invite-only registration
-
-This setup enables registration **only** for users that have a valid
-invite token.
-
-``` {.lua}
-allow_registration = true
-registration_invite_only = true
-```
-
-## Example: Open registration
-
-This setup allows completely **open registration**, even without
-an invite token.
-
-``` {.lua}
-allow_registration = true
-registration_invite_only = false
-```
--- a/mod_invites_register/mod_invites_register.lua	Sat Feb 07 12:25:52 2026 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,161 +0,0 @@
-local st = require "util.stanza";
-local jid_split = require "util.jid".split;
-local jid_bare = require "util.jid".bare;
-local rostermanager = require "core.rostermanager";
-
-local require_encryption = module:get_option_boolean("c2s_require_encryption",
-	module:get_option_boolean("require_encryption", false));
-local invite_only = module:get_option_boolean("registration_invite_only", true);
-
-local invites;
-if prosody.shutdown then -- COMPAT hack to detect prosodyctl
-	invites = module:depends("invites");
-end
-
-local legacy_invite_stream_feature = st.stanza("register", { xmlns = "urn:xmpp:invite" }):up();
-local invite_stream_feature = st.stanza("register", { xmlns = "urn:xmpp:ibr-token:0" }):up();
-module:hook("stream-features", function(event)
-	local session, features = event.origin, event.features;
-
-	-- Advertise to unauthorized clients only.
-	if session.type ~= "c2s_unauthed" or (require_encryption and not session.secure) then
-		return
-	end
-
-	features:add_child(legacy_invite_stream_feature);
-	features:add_child(invite_stream_feature);
-end);
-
--- XEP-0379: Pre-Authenticated Roster Subscription
-module:hook("presence/bare", function (event)
-	local stanza = event.stanza;
-	if stanza.attr.type ~= "subscribe" then return end
-
-	local preauth = stanza:get_child("preauth", "urn:xmpp:pars:0");
-	if not preauth then return end
-	local token = preauth.attr.token;
-	if not token then return end
-
-	local username, host = jid_split(stanza.attr.to);
-
-	local invite, err = invites.get(token, username);
-
-	if not invite then
-		module:log("debug", "Got invalid token, error: %s", err);
-		return;
-	end
-
-	local contact = jid_bare(stanza.attr.from);
-
-	module:log("debug", "Approving inbound subscription to %s from %s", username, contact);
-	if rostermanager.set_contact_pending_in(username, host, contact, stanza) then
-		if rostermanager.subscribed(username, host, contact) then
-			invite:use();
-			rostermanager.roster_push(username, host, contact);
-
-			-- Send back a subscription request (goal is mutual subscription)
-			if not rostermanager.is_user_subscribed(username, host, contact)
-			and not rostermanager.is_contact_pending_out(username, host, contact) then
-				module:log("debug", "Sending automatic subscription request to %s from %s", contact, username);
-				if rostermanager.set_contact_pending_out(username, host, contact) then
-					rostermanager.roster_push(username, host, contact);
-					module:send(st.presence({type = "subscribe", from = username.."@"..host, to = contact }));
-				else
-					module:log("warn", "Failed to set contact pending out for %s", username);
-				end
-			end
-		end
-	end
-end, 1);
-
--- Client is submitting a preauth token to allow registration
-module:hook("stanza/iq/urn:xmpp:pars:0:preauth", function(event)
-	local preauth = event.stanza.tags[1];
-	local token = preauth.attr.token;
-	local validated_invite = invites.get(token);
-	if not validated_invite then
-		local reply = st.error_reply(event.stanza, "cancel", "forbidden", "The invite token is invalid or expired");
-		event.origin.send(reply);
-		return true;
-	end
-	event.origin.validated_invite = validated_invite;
-	local reply = st.reply(event.stanza);
-	event.origin.send(reply);
-	return true;
-end);
-
--- Registration attempt - ensure a valid preauth token has been supplied
-module:hook("user-registering", function (event)
-	local validated_invite = event.validated_invite or (event.session and event.session.validated_invite);
-	if invite_only and not validated_invite then
-		event.allowed = false;
-		event.reason = "Registration on this server is through invitation only";
-		return;
-	elseif not validated_invite then
-		-- This registration is not using an invite, but
-		-- the server is not in invite-only mode, so nothing
-		-- for this module to do...
-		return;
-	end
-	if validated_invite and validated_invite.additional_data and validated_invite.additional_data.allow_reset then
-		event.allow_reset = validated_invite.additional_data.allow_reset;
-	end
-end);
-
--- Make a *one-way* subscription. User will see when contact is online,
--- contact will not see when user is online.
-function subscribe(host, user_username, contact_username)
-	local user_jid = user_username.."@"..host;
-	local contact_jid = contact_username.."@"..host;
-	-- Update user's roster to say subscription request is pending...
-	rostermanager.set_contact_pending_out(user_username, host, contact_jid);
-	-- Update contact's roster to say subscription request is pending...
-	rostermanager.set_contact_pending_in(contact_username, host, user_jid);
-	-- Update contact's roster to say subscription request approved...
-	rostermanager.subscribed(contact_username, host, user_jid);
-	-- Update user's roster to say subscription request approved...
-	rostermanager.process_inbound_subscription_approval(user_username, host, contact_jid);
-end
-
--- Make a mutual subscription between jid1 and jid2. Each JID will see
--- when the other one is online.
-function subscribe_both(host, user1, user2)
-	subscribe(host, user1, user2);
-	subscribe(host, user2, user1);
-end
-
--- Registration successful, if there was a preauth token, mark it as used
-module:hook("user-registered", function (event)
-	local validated_invite = event.validated_invite or (event.session and event.session.validated_invite);
-	if not validated_invite then
-		return;
-	end
-	local inviter_username = validated_invite.inviter;
-	local contact_username = event.username;
-	validated_invite:use();
-
-	if inviter_username then
-		module:log("debug", "Creating mutual subscription between %s and %s", inviter_username, contact_username);
-		subscribe_both(module.host, inviter_username, contact_username);
-		rostermanager.roster_push(inviter_username, module.host, contact_username.."@"..module.host);
-	end
-
-	if validated_invite.additional_data then
-		module:log("debug", "Importing roles from invite");
-		local roles = validated_invite.additional_data.roles;
-		if roles then
-			module:open_store("roles"):set(contact_username, roles);
-		end
-	end
-end);
-
--- Equivalent of user-registered but for when the account already existed
--- (i.e. password reset)
-module:hook("user-password-reset", function (event)
-	local validated_invite = event.validated_invite or (event.session and event.session.validated_invite);
-	if not validated_invite then
-		return;
-	end
-	validated_invite:use();
-end);
-