mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2025-12-30 11:22:31 +00:00
Remove the "focus" external component, use client_proxy instead. (#8381)
* feat: Add mod_client_proxy and mod_roster_command. Taken from prosody-modules 4317:456b9f608fcf with the mod_roster_command patch applied. * feat: Use mod_client_proxy to proxy to jicofo.
This commit is contained in:
15
debian/jitsi-meet-prosody.postinst
vendored
15
debian/jitsi-meet-prosody.postinst
vendored
@@ -142,6 +142,21 @@ case "$1" in
|
|||||||
echo -e " admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\", \"jvb@auth.$JVB_HOSTNAME\" }" >> $PROSODY_HOST_CONFIG
|
echo -e " admins = { \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\", \"jvb@auth.$JVB_HOSTNAME\" }" >> $PROSODY_HOST_CONFIG
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Convert the old focus component config to the new one.
|
||||||
|
# Old:
|
||||||
|
# Component "focus.jitmeet.example.com"
|
||||||
|
# component_secret = "focusSecret"
|
||||||
|
# New:
|
||||||
|
# Component "focus.jitmeet.example.com" "client_proxy"
|
||||||
|
# target_address = "focus@auth.jitmeet.example.com"
|
||||||
|
if grep -q "Component \"focus.$JVB_HOSTNAME\"" $PROSODY_HOST_CONFIG && ! grep "Component \"focus.$JVB_HOSTNAME\" \"client_proxy\"" $PROSODY_HOST_CONFIG ;then
|
||||||
|
sed -i -e "s/Component \"focus.$JVB_HOSTNAME\"/Component \"focus.$JVB_HOSTNAME\" \"client_proxy\"\n target_address = \"$JICOFO_AUTH_USER@auth.$JVB_HOSTNAME\"/" $PROSODY_HOST_CONFIG
|
||||||
|
PROSODY_CONFIG_PRESENT="false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Make sure the focus@auth user's roster includes the proxy component (this is idempotent)
|
||||||
|
prosodyctl mod_roster_command subscribe focus.$JVB_HOSTNAME $JICOFO_AUTH_USER@auth.$JVB_HOSTNAME
|
||||||
|
|
||||||
if [ ! -f /var/lib/prosody/$JVB_HOSTNAME.crt ]; then
|
if [ ! -f /var/lib/prosody/$JVB_HOSTNAME.crt ]; then
|
||||||
# prosodyctl takes care for the permissions
|
# prosodyctl takes care for the permissions
|
||||||
# echo for using all default values
|
# echo for using all default values
|
||||||
|
|||||||
@@ -77,8 +77,9 @@ Component "internal.auth.jitmeet.example.com" "muc"
|
|||||||
VirtualHost "auth.jitmeet.example.com"
|
VirtualHost "auth.jitmeet.example.com"
|
||||||
authentication = "internal_plain"
|
authentication = "internal_plain"
|
||||||
|
|
||||||
Component "focus.jitmeet.example.com"
|
-- Proxy to jicofo's user JID, so that it doesn't have to register as a component.
|
||||||
component_secret = "focusSecret"
|
Component "focus.jitmeet.example.com" "client_proxy"
|
||||||
|
target_address = "focusUser@auth.jitmeet.example.com"
|
||||||
|
|
||||||
Component "speakerstats.jitmeet.example.com" "speakerstats_component"
|
Component "speakerstats.jitmeet.example.com" "speakerstats_component"
|
||||||
muc_component = "conference.jitmeet.example.com"
|
muc_component = "conference.jitmeet.example.com"
|
||||||
|
|||||||
202
resources/prosody-plugins/mod_client_proxy.lua
Normal file
202
resources/prosody-plugins/mod_client_proxy.lua
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
if module:get_host_type() ~= "component" then
|
||||||
|
error("proxy_component should be loaded as component", 0);
|
||||||
|
end
|
||||||
|
|
||||||
|
local jid_split = require "util.jid".split;
|
||||||
|
local jid_bare = require "util.jid".bare;
|
||||||
|
local jid_prep = require "util.jid".prep;
|
||||||
|
local st = require "util.stanza";
|
||||||
|
local array = require "util.array";
|
||||||
|
|
||||||
|
local target_address = module:get_option_string("target_address");
|
||||||
|
|
||||||
|
sessions = array{};
|
||||||
|
local sessions = sessions;
|
||||||
|
|
||||||
|
local function handle_target_presence(stanza)
|
||||||
|
local type = stanza.attr.type;
|
||||||
|
module:log("debug", "received presence from destination: %s", type)
|
||||||
|
local _, _, resource = jid_split(stanza.attr.from);
|
||||||
|
if type == "error" then
|
||||||
|
-- drop all known sessions
|
||||||
|
for k in pairs(sessions) do
|
||||||
|
sessions[k] = nil
|
||||||
|
end
|
||||||
|
module:log(
|
||||||
|
"debug",
|
||||||
|
"received error presence, dropping all target sessions",
|
||||||
|
resource
|
||||||
|
)
|
||||||
|
elseif type == "unavailable" then
|
||||||
|
for k in pairs(sessions) do
|
||||||
|
if sessions[k] == resource then
|
||||||
|
sessions[k] = nil
|
||||||
|
module:log(
|
||||||
|
"debug",
|
||||||
|
"dropped target session: %s",
|
||||||
|
resource
|
||||||
|
)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif not type then
|
||||||
|
-- available
|
||||||
|
local found = false;
|
||||||
|
for k in pairs(sessions) do
|
||||||
|
if sessions[k] == resource then
|
||||||
|
found = true;
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not found then
|
||||||
|
module:log(
|
||||||
|
"debug",
|
||||||
|
"registered new target session: %s",
|
||||||
|
resource
|
||||||
|
)
|
||||||
|
sessions:push(resource)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_from_target(stanza)
|
||||||
|
local type = stanza.attr.type
|
||||||
|
module:log(
|
||||||
|
"debug",
|
||||||
|
"non-presence stanza from target: name = %s, type = %s",
|
||||||
|
stanza.name,
|
||||||
|
type
|
||||||
|
)
|
||||||
|
if stanza.name == "iq" then
|
||||||
|
if type == "error" or type == "result" then
|
||||||
|
-- de-NAT message
|
||||||
|
local _, _, denatted_to_unprepped = jid_split(stanza.attr.to);
|
||||||
|
local denatted_to = jid_prep(denatted_to_unprepped);
|
||||||
|
if not denatted_to then
|
||||||
|
module:log(
|
||||||
|
"debug",
|
||||||
|
"cannot de-NAT stanza, invalid to: %s",
|
||||||
|
denatted_to_unprepped
|
||||||
|
)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local denatted_from = module:get_host();
|
||||||
|
|
||||||
|
module:log(
|
||||||
|
"debug",
|
||||||
|
"de-NAT-ed stanza: from: %s -> %s, to: %s -> %s",
|
||||||
|
stanza.attr.from,
|
||||||
|
denatted_from,
|
||||||
|
stanza.attr.to,
|
||||||
|
denatted_to
|
||||||
|
)
|
||||||
|
|
||||||
|
stanza.attr.from = denatted_from
|
||||||
|
stanza.attr.to = denatted_to
|
||||||
|
|
||||||
|
module:send(stanza)
|
||||||
|
else
|
||||||
|
-- FIXME: we don’t support NATing outbund requests atm.
|
||||||
|
module:send(st.error_reply(stanza, "cancel", "feature-not-implemented"))
|
||||||
|
end
|
||||||
|
elseif stanza.name == "message" then
|
||||||
|
-- not implemented yet, we need a way to ensure that routing doesn’t
|
||||||
|
-- break
|
||||||
|
module:send(st.error_reply(stanza, "cancel", "feature-not-implemented"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function handle_to_target(stanza)
|
||||||
|
local type = stanza.attr.type;
|
||||||
|
module:log(
|
||||||
|
"debug",
|
||||||
|
"stanza to target: name = %s, type = %s",
|
||||||
|
stanza.name, type
|
||||||
|
)
|
||||||
|
if stanza.name == "presence" then
|
||||||
|
if type ~= "error" then
|
||||||
|
module:send(st.error_reply(stanza, "cancel", "bad-request"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
elseif stanza.name == "iq" then
|
||||||
|
if type == "get" or type == "set" then
|
||||||
|
if #sessions == 0 then
|
||||||
|
-- no sessions available to send to
|
||||||
|
module:log("debug", "no sessions to send to!")
|
||||||
|
module:send(st.error_reply(stanza, "cancel", "service-unavailable"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- find a target session
|
||||||
|
local target_session = sessions:random()
|
||||||
|
local target = target_address .. "/" .. target_session
|
||||||
|
|
||||||
|
-- encode sender JID in resource
|
||||||
|
local natted_from = module:get_host() .. "/" .. stanza.attr.from;
|
||||||
|
|
||||||
|
module:log(
|
||||||
|
"debug",
|
||||||
|
"NAT-ed stanza: from: %s -> %s, to: %s -> %s",
|
||||||
|
stanza.attr.from,
|
||||||
|
natted_from,
|
||||||
|
stanza.attr.to,
|
||||||
|
target
|
||||||
|
)
|
||||||
|
|
||||||
|
stanza.attr.from = natted_from
|
||||||
|
stanza.attr.to = target
|
||||||
|
|
||||||
|
module:send(stanza)
|
||||||
|
end
|
||||||
|
-- FIXME: handle and forward result/error correctly
|
||||||
|
elseif stanza.name == "message" then
|
||||||
|
-- not implemented yet, we need a way to ensure that routing doesn’t
|
||||||
|
-- break
|
||||||
|
module:send(st.error_reply(stanza, "cancel", "feature-not-implemented"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function stanza_handler(event)
|
||||||
|
local origin, stanza = event.origin, event.stanza
|
||||||
|
module:log("debug", "received stanza from %s session", origin.type)
|
||||||
|
|
||||||
|
local bare_from = jid_bare(stanza.attr.from);
|
||||||
|
local _, _, to = jid_split(stanza.attr.to);
|
||||||
|
if bare_from == target_address then
|
||||||
|
-- from our target, to whom?
|
||||||
|
if not to then
|
||||||
|
-- directly to component
|
||||||
|
if stanza.name == "presence" then
|
||||||
|
handle_target_presence(stanza)
|
||||||
|
else
|
||||||
|
module:send(st.error_reply(stanza, "cancel", "bad-request"))
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- to someone else
|
||||||
|
handle_from_target(stanza)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
handle_to_target(stanza)
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
module:hook("iq/bare", stanza_handler, -1);
|
||||||
|
module:hook("message/bare", stanza_handler, -1);
|
||||||
|
module:hook("presence/bare", stanza_handler, -1);
|
||||||
|
module:hook("iq/full", stanza_handler, -1);
|
||||||
|
module:hook("message/full", stanza_handler, -1);
|
||||||
|
module:hook("presence/full", stanza_handler, -1);
|
||||||
|
module:hook("iq/host", stanza_handler, -1);
|
||||||
|
module:hook("message/host", stanza_handler, -1);
|
||||||
|
module:hook("presence/host", stanza_handler, -1);
|
||||||
|
|
||||||
|
module:log("debug", "loaded proxy on %s", module:get_host())
|
||||||
|
|
||||||
|
subscription_request = st.presence({
|
||||||
|
type = "subscribe",
|
||||||
|
to = target_address,
|
||||||
|
from = module:get_host()}
|
||||||
|
)
|
||||||
|
module:send(subscription_request)
|
||||||
165
resources/prosody-plugins/mod_roster_command.lua
Normal file
165
resources/prosody-plugins/mod_roster_command.lua
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
-----------------------------------------------------------
|
||||||
|
-- mod_roster_command: Manage rosters through prosodyctl
|
||||||
|
-- version 0.02
|
||||||
|
-----------------------------------------------------------
|
||||||
|
-- Copyright (C) 2011 Matthew Wild
|
||||||
|
-- Copyright (C) 2011 Adam Nielsen
|
||||||
|
--
|
||||||
|
-- This project is MIT/X11 licensed. Please see the
|
||||||
|
-- COPYING file in the source package for more information.
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
if module.host ~= "*" then
|
||||||
|
module:log("error", "Do not load this module in Prosody, for correct usage see: https://modules.prosody.im/mod_roster_command.html");
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Workaround for lack of util.startup...
|
||||||
|
local prosody = _G.prosody;
|
||||||
|
local hosts = prosody.hosts;
|
||||||
|
prosody.bare_sessions = prosody.bare_sessions or {};
|
||||||
|
_G.bare_sessions = _G.bare_sessions or prosody.bare_sessions;
|
||||||
|
|
||||||
|
local usermanager = require "core.usermanager";
|
||||||
|
local rostermanager = require "core.rostermanager";
|
||||||
|
local storagemanager = require "core.storagemanager";
|
||||||
|
local jid = require "util.jid";
|
||||||
|
local warn = require"util.prosodyctl".show_warning;
|
||||||
|
|
||||||
|
-- Make a *one-way* subscription. User will see when contact is online,
|
||||||
|
-- contact will not see when user is online.
|
||||||
|
function subscribe(user_jid, contact_jid)
|
||||||
|
local user_username, user_host = jid.split(user_jid);
|
||||||
|
local contact_username, contact_host = jid.split(contact_jid);
|
||||||
|
if not hosts[user_host] then
|
||||||
|
warn("The host '%s' is not configured for this server.", user_host);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
if hosts[user_host].users.name == "null" then
|
||||||
|
storagemanager.initialize_host(user_host);
|
||||||
|
usermanager.initialize_host(user_host);
|
||||||
|
end
|
||||||
|
-- Update user's roster to say subscription request is pending. Bare hosts (e.g. components) don't have rosters.
|
||||||
|
if user_username ~= nil then
|
||||||
|
rostermanager.set_contact_pending_out(user_username, user_host, contact_jid);
|
||||||
|
end
|
||||||
|
if hosts[contact_host] then
|
||||||
|
if contact_host ~= user_host and hosts[contact_host].users.name == "null" then
|
||||||
|
storagemanager.initialize_host(contact_host);
|
||||||
|
usermanager.initialize_host(contact_host);
|
||||||
|
end
|
||||||
|
-- Update contact's roster to say subscription request is pending...
|
||||||
|
rostermanager.set_contact_pending_in(contact_username, contact_host, user_jid);
|
||||||
|
-- Update contact's roster to say subscription request approved...
|
||||||
|
rostermanager.subscribed(contact_username, contact_host, user_jid);
|
||||||
|
-- Update user's roster to say subscription request approved. Bare hosts (e.g. components) don't have rosters.
|
||||||
|
if user_username ~= nil then
|
||||||
|
rostermanager.process_inbound_subscription_approval(user_username, user_host, contact_jid);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Make a mutual subscription between jid1 and jid2. Each JID will see
|
||||||
|
-- when the other one is online.
|
||||||
|
function subscribe_both(jid1, jid2)
|
||||||
|
subscribe(jid1, jid2);
|
||||||
|
subscribe(jid2, jid1);
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Unsubscribes user from contact (not contact from user, if subscribed).
|
||||||
|
function unsubscribe(user_jid, contact_jid)
|
||||||
|
local user_username, user_host = jid.split(user_jid);
|
||||||
|
local contact_username, contact_host = jid.split(contact_jid);
|
||||||
|
if not hosts[user_host] then
|
||||||
|
warn("The host '%s' is not configured for this server.", user_host);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
if hosts[user_host].users.name == "null" then
|
||||||
|
storagemanager.initialize_host(user_host);
|
||||||
|
usermanager.initialize_host(user_host);
|
||||||
|
end
|
||||||
|
-- Update user's roster to say subscription is cancelled...
|
||||||
|
rostermanager.unsubscribe(user_username, user_host, contact_jid);
|
||||||
|
if hosts[contact_host] then
|
||||||
|
if contact_host ~= user_host and hosts[contact_host].users.name == "null" then
|
||||||
|
storagemanager.initialize_host(contact_host);
|
||||||
|
usermanager.initialize_host(contact_host);
|
||||||
|
end
|
||||||
|
-- Update contact's roster to say subscription is cancelled...
|
||||||
|
rostermanager.unsubscribed(contact_username, contact_host, user_jid);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Cancel any subscription in either direction.
|
||||||
|
function unsubscribe_both(jid1, jid2)
|
||||||
|
unsubscribe(jid1, jid2);
|
||||||
|
unsubscribe(jid2, jid1);
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set the name shown and group used in the contact list
|
||||||
|
function rename(user_jid, contact_jid, contact_nick, contact_group)
|
||||||
|
local user_username, user_host = jid.split(user_jid);
|
||||||
|
if not hosts[user_host] then
|
||||||
|
warn("The host '%s' is not configured for this server.", user_host);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
if hosts[user_host].users.name == "null" then
|
||||||
|
storagemanager.initialize_host(user_host);
|
||||||
|
usermanager.initialize_host(user_host);
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Load user's roster and find the contact
|
||||||
|
local roster = rostermanager.load_roster(user_username, user_host);
|
||||||
|
local item = roster[contact_jid];
|
||||||
|
if item then
|
||||||
|
if contact_nick then
|
||||||
|
item.name = contact_nick;
|
||||||
|
end
|
||||||
|
if contact_group then
|
||||||
|
item.groups = {}; -- Remove from all current groups
|
||||||
|
item.groups[contact_group] = true;
|
||||||
|
end
|
||||||
|
rostermanager.save_roster(user_username, user_host, roster);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function remove(user_jid, contact_jid)
|
||||||
|
unsubscribe_both(user_jid, contact_jid);
|
||||||
|
local user_username, user_host = jid.split(user_jid);
|
||||||
|
local roster = rostermanager.load_roster(user_username, user_host);
|
||||||
|
roster[contact_jid] = nil;
|
||||||
|
rostermanager.save_roster(user_username, user_host, roster);
|
||||||
|
end
|
||||||
|
|
||||||
|
function module.command(arg)
|
||||||
|
local command = arg[1];
|
||||||
|
if not command then
|
||||||
|
warn("Valid subcommands: (un)subscribe(_both) | rename");
|
||||||
|
return 0;
|
||||||
|
end
|
||||||
|
table.remove(arg, 1);
|
||||||
|
if command == "subscribe" then
|
||||||
|
subscribe(arg[1], arg[2]);
|
||||||
|
return 0;
|
||||||
|
elseif command == "subscribe_both" then
|
||||||
|
subscribe_both(arg[1], arg[2]);
|
||||||
|
return 0;
|
||||||
|
elseif command == "unsubscribe" then
|
||||||
|
unsubscribe(arg[1], arg[2]);
|
||||||
|
return 0;
|
||||||
|
elseif command == "unsubscribe_both" then
|
||||||
|
unsubscribe_both(arg[1], arg[2]);
|
||||||
|
return 0;
|
||||||
|
elseif command == "remove" then
|
||||||
|
remove(arg[1], arg[2]);
|
||||||
|
return 0;
|
||||||
|
elseif command == "rename" then
|
||||||
|
rename(arg[1], arg[2], arg[3], arg[4]);
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
warn("Unknown command: %s", command);
|
||||||
|
return 1;
|
||||||
|
end
|
||||||
|
return 0;
|
||||||
|
end
|
||||||
47
resources/prosody-plugins/mod_roster_command.patch
Normal file
47
resources/prosody-plugins/mod_roster_command.patch
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# HG changeset patch
|
||||||
|
# User Boris Grozev <boris@jitsi.org>
|
||||||
|
# Date 1609874100 21600
|
||||||
|
# Tue Jan 05 13:15:00 2021 -0600
|
||||||
|
# Node ID f646babfc401494ff33f2126ef6c4df541ebf846
|
||||||
|
# Parent 456b9f608fcf9667cfba1bd7bf9eba2151af50d0
|
||||||
|
mod_roster_command: Fix subscription when the "user JID" is a bare domain.
|
||||||
|
|
||||||
|
Do not attempt to update the roster when the user is bare domain (e.g. a
|
||||||
|
component), since they don't have rosters and the attempt results in an error:
|
||||||
|
|
||||||
|
$ prosodyctl mod_roster_command subscribe proxy.example.com contact@example.com
|
||||||
|
xxxxxxxxxxFailed to execute command: Error: /usr/lib/prosody/core/rostermanager.lua:104: attempt to concatenate local 'username' (a nil value)
|
||||||
|
stack traceback:
|
||||||
|
/usr/lib/prosody/core/rostermanager.lua:104: in function 'load_roster'
|
||||||
|
/usr/lib/prosody/core/rostermanager.lua:305: in function 'set_contact_pending_out'
|
||||||
|
mod_roster_command.lua:44: in function 'subscribe'
|
||||||
|
|
||||||
|
diff -r 456b9f608fcf -r f646babfc401 mod_roster_command/mod_roster_command.lua
|
||||||
|
--- a/mod_roster_command/mod_roster_command.lua Tue Jan 05 13:49:50 2021 +0000
|
||||||
|
+++ b/mod_roster_command/mod_roster_command.lua Tue Jan 05 13:15:00 2021 -0600
|
||||||
|
@@ -40,8 +40,10 @@
|
||||||
|
storagemanager.initialize_host(user_host);
|
||||||
|
usermanager.initialize_host(user_host);
|
||||||
|
end
|
||||||
|
- -- Update user's roster to say subscription request is pending...
|
||||||
|
- rostermanager.set_contact_pending_out(user_username, user_host, contact_jid);
|
||||||
|
+ -- Update user's roster to say subscription request is pending. Bare hosts (e.g. components) don't have rosters.
|
||||||
|
+ if user_username ~= nil then
|
||||||
|
+ rostermanager.set_contact_pending_out(user_username, user_host, contact_jid);
|
||||||
|
+ end
|
||||||
|
if hosts[contact_host] then
|
||||||
|
if contact_host ~= user_host and hosts[contact_host].users.name == "null" then
|
||||||
|
storagemanager.initialize_host(contact_host);
|
||||||
|
@@ -51,8 +53,10 @@
|
||||||
|
rostermanager.set_contact_pending_in(contact_username, contact_host, user_jid);
|
||||||
|
-- Update contact's roster to say subscription request approved...
|
||||||
|
rostermanager.subscribed(contact_username, contact_host, user_jid);
|
||||||
|
- -- Update user's roster to say subscription request approved...
|
||||||
|
- rostermanager.process_inbound_subscription_approval(user_username, user_host, contact_jid);
|
||||||
|
+ -- Update user's roster to say subscription request approved. Bare hosts (e.g. components) don't have rosters.
|
||||||
|
+ if user_username ~= nil then
|
||||||
|
+ rostermanager.process_inbound_subscription_approval(user_username, user_host, contact_jid);
|
||||||
|
+ end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
Reference in New Issue
Block a user