mirror of
https://github.com/processone/ejabberd
synced 2025-10-03 01:39:35 +02:00
Merge pull request #4408 from sstrigler/mod_pubsub_serverinfo
mod_pubsub_serverinfo
This commit is contained in:
commit
1d79edbae0
4 changed files with 391 additions and 18 deletions
2
mix.exs
2
mix.exs
|
@ -130,7 +130,7 @@ defmodule Ejabberd.MixProject do
|
|||
{:p1_utils, "~> 1.0"},
|
||||
{:pkix, "~> 1.0"},
|
||||
{:stringprep, ">= 1.0.26"},
|
||||
{:xmpp, git: "https://github.com/processone/xmpp", ref: "74ed2d87222d3bd5e86f7c41daaa28fae59d4995", override: true},
|
||||
{:xmpp, git: "https://github.com/processone/xmpp", ref: "9b028c110083e4d979d88c286873d0abf08fa532", override: true},
|
||||
{:yconf, ">= 1.0.18"}]
|
||||
++ cond_deps()
|
||||
end
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
{stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}},
|
||||
{if_var_true, stun,
|
||||
{stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}},
|
||||
{xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "74ed2d87222d3bd5e86f7c41daaa28fae59d4995"}},
|
||||
{xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "9b028c110083e4d979d88c286873d0abf08fa532"}},
|
||||
{yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}}
|
||||
]}.
|
||||
|
||||
|
|
32
rebar.lock
32
rebar.lock
|
@ -16,23 +16,23 @@
|
|||
{<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1},
|
||||
{<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0},
|
||||
{<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0},
|
||||
{<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0},
|
||||
{<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.18">>},0},
|
||||
{<<"p1_acme">>,
|
||||
{git,"https://github.com/processone/p1_acme",
|
||||
{ref,"27a590789add30ff507a49ffd440eeeb28c96ce5"}},
|
||||
0},
|
||||
{<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0},
|
||||
{<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},0},
|
||||
{<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.32">>},0},
|
||||
{<<"p1_pgsql">>,{pkg,<<"p1_pgsql">>,<<"1.1.33">>},0},
|
||||
{<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.27">>},0},
|
||||
{<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0},
|
||||
{<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0},
|
||||
{<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.31">>},0},
|
||||
{<<"stun">>,{pkg,<<"stun">>,<<"1.2.17">>},0},
|
||||
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1},
|
||||
{<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.32">>},0},
|
||||
{<<"stun">>,{pkg,<<"stun">>,<<"1.2.19">>},0},
|
||||
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1},
|
||||
{<<"xmpp">>,
|
||||
{git,"https://github.com/processone/xmpp",
|
||||
{ref,"74ed2d87222d3bd5e86f7c41daaa28fae59d4995"}},
|
||||
{ref,"9b028c110083e4d979d88c286873d0abf08fa532"}},
|
||||
0},
|
||||
{<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}.
|
||||
[
|
||||
|
@ -50,16 +50,16 @@
|
|||
{<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>},
|
||||
{<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>},
|
||||
{<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>},
|
||||
{<<"mqtree">>, <<"82F54B8F2D22B4445DB1D6CCCB7FE9EAD049D61410C29E32475F3CEB3EE62A89">>},
|
||||
{<<"mqtree">>, <<"B004E80BBEE5BC49E774B839F88162BDFF5CB654ABBDB79C8381AE4B13510A1B">>},
|
||||
{<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>},
|
||||
{<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>},
|
||||
{<<"p1_pgsql">>, <<"3F95D7E3413FC8F0BE80ABB4BE1A0D7F67066A36905085CD5A423145598B0CB0">>},
|
||||
{<<"p1_pgsql">>, <<"585F720C76B9BD27C5313DBB2C3CAD0CA19AB4493DE0B34A7496B51B65F5613A">>},
|
||||
{<<"p1_utils">>, <<"F468D84C6FFA6E4B12A6160826DCF2D015527189D57865568A78B49C5ED972A1">>},
|
||||
{<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>},
|
||||
{<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>},
|
||||
{<<"stringprep">>, <<"FA1688C156DD271722AA18C423A4163E710D2F4F475AD0BC220910DF669B53AF">>},
|
||||
{<<"stun">>, <<"C54614A592812EA125A2E6827AAC5A438571B591616426EC1419BA9B48252F54">>},
|
||||
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>},
|
||||
{<<"stringprep">>, <<"63FD7FF5417A4A48DB6BB529C83D678361D34188367C1B13B3EAF39ACDEDB8E5">>},
|
||||
{<<"stun">>, <<"FF5BD2D2E3A0C2ADE41FC71A7A069EEBAA492ECDB35ECA35350FFF3C194B381A">>},
|
||||
{<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>},
|
||||
{<<"yconf">>, <<"E565EDC8AABB8164C3BEBC86969095D296AD315DCBB46AF65DCCBC6C71EAE0F6">>}]},
|
||||
{pkg_hash_ext,[
|
||||
{<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>},
|
||||
|
@ -75,15 +75,15 @@
|
|||
{<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>},
|
||||
{<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>},
|
||||
{<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>},
|
||||
{<<"mqtree">>, <<"5FE8B7CF8FBC4783D0FCEB94654AC2BBF3242A58CD0397D249DED8AE021BE2A3">>},
|
||||
{<<"mqtree">>, <<"F73827CECF9A310670F7D7909FC88EAB40B45290FE48E5C7E45AB7235B29B919">>},
|
||||
{<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>},
|
||||
{<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>},
|
||||
{<<"p1_pgsql">>, <<"268B01E8F4EB75C211A31495A25C2815C549AECCE2F0DF1A161C6E0A2CDE061E">>},
|
||||
{<<"p1_pgsql">>, <<"3FB6A9617DB146419D420FFE7E94B9179A7CB5063D9C9450EF8B13FDAD2A709F">>},
|
||||
{<<"p1_utils">>, <<"F1AF942B0A62BCFA0D59FBE30679BE4FFEB5E241A0C49ED5F094DB2F5B80F5E0">>},
|
||||
{<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>},
|
||||
{<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>},
|
||||
{<<"stringprep">>, <<"E9699C88E8DB16B3A41F0E45AC6874A4DA81A6E4854A77D76EDE6D09B08E3530">>},
|
||||
{<<"stun">>, <<"6B318244C21E8524A9AAE3AC9A05CD8234EE994C1C2C815DE68D306086AD768D">>},
|
||||
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>},
|
||||
{<<"stringprep">>, <<"6069CB059F5D18A312C42E5F374E9DE415DF68F7E3090C9C1A5505E2A8532710">>},
|
||||
{<<"stun">>, <<"66DC035EBF21DE8ABE51ECCC2C3D4BBF63C78650F74C3AFCAF2E4BB15C555927">>},
|
||||
{<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>},
|
||||
{<<"yconf">>, <<"FA950EC6503F92D6417FB8CC1D982403F041697E8E1BBF4D4588FB919B9562EA">>}]}
|
||||
].
|
||||
|
|
373
src/mod_pubsub_serverinfo.erl
Normal file
373
src/mod_pubsub_serverinfo.erl
Normal file
|
@ -0,0 +1,373 @@
|
|||
%%%----------------------------------------------------------------------
|
||||
%%% File : mod_pubsub_serverinfo.erl
|
||||
%%% Author : Stefan Strigler <stefan@strigler.de>
|
||||
%%% Purpose : Exposes server information over Pub/Sub
|
||||
%%% Created : 26 Dec 2023 by Guus der Kinderen <guus.der.kinderen@gmail.com>
|
||||
%%%
|
||||
%%%
|
||||
%%% ejabberd, Copyright (C) 2023 - 2025 ProcessOne
|
||||
%%%
|
||||
%%% This program is free software; you can redistribute it and/or
|
||||
%%% modify it under the terms of the GNU General Public License as
|
||||
%%% published by the Free Software Foundation; either version 2 of the
|
||||
%%% License, or (at your option) any later version.
|
||||
%%%
|
||||
%%% This program is distributed in the hope that it will be useful,
|
||||
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
%%% General Public License for more details.
|
||||
%%%
|
||||
%%% You should have received a copy of the GNU General Public License along
|
||||
%%% with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
%%%
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(mod_pubsub_serverinfo).
|
||||
-author('stefan@strigler.de').
|
||||
-behaviour(gen_mod).
|
||||
-behaviour(gen_server).
|
||||
|
||||
-include("logger.hrl").
|
||||
-include("translate.hrl").
|
||||
|
||||
-include_lib("xmpp/include/xmpp.hrl").
|
||||
|
||||
%% gen_mod callbacks.
|
||||
-export([start/2, stop/1, depends/2, mod_options/1, mod_opt_type/1, get_local_features/5, mod_doc/0]).
|
||||
-export([init/1, handle_cast/2, handle_call/3, handle_info/2, terminate/2]).
|
||||
-export([in_auth_result/3, out_auth_result/2, get_info/5]).
|
||||
|
||||
-define(NS_URN_SERVERINFO, <<"urn:xmpp:serverinfo:0">>).
|
||||
-define(PUBLIC_HOSTS_URL, <<"https://data.xmpp.net/providers/v2/providers-Ds.json">>).
|
||||
|
||||
-record(state, {host, pubsub_host, node, monitors = #{}, timer = undefined, public_hosts = []}).
|
||||
|
||||
%% @format-begin
|
||||
|
||||
start(Host, Opts) ->
|
||||
case pubsub_host(Host, Opts) of
|
||||
{error, _Reason} = Error ->
|
||||
Error;
|
||||
PubsubHost ->
|
||||
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 50),
|
||||
ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 50),
|
||||
ejabberd_hooks:add(s2s_out_auth_result, Host, ?MODULE, out_auth_result, 50),
|
||||
ejabberd_hooks:add(s2s_in_auth_result, Host, ?MODULE, in_auth_result, 50),
|
||||
gen_mod:start_child(?MODULE, Host, PubsubHost)
|
||||
end.
|
||||
|
||||
stop(Host) ->
|
||||
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 50),
|
||||
ejabberd_hooks:delete(disco_info, Host, ?MODULE, get_info, 50),
|
||||
ejabberd_hooks:delete(s2s_out_auth_result, Host, ?MODULE, out_auth_result, 50),
|
||||
ejabberd_hooks:delete(s2s_in_auth_result, Host, ?MODULE, in_auth_result, 50),
|
||||
gen_mod:stop_child(?MODULE, Host).
|
||||
|
||||
init([Host, PubsubHost]) ->
|
||||
TRef =
|
||||
timer:send_interval(
|
||||
timer:minutes(5), self(), update_pubsub),
|
||||
Monitors = init_monitors(Host),
|
||||
PublicHosts = fetch_public_hosts(),
|
||||
State =
|
||||
#state{host = Host,
|
||||
pubsub_host = PubsubHost,
|
||||
node = <<"serverinfo">>,
|
||||
timer = TRef,
|
||||
monitors = Monitors,
|
||||
public_hosts = PublicHosts},
|
||||
self() ! update_pubsub,
|
||||
{ok, State}.
|
||||
|
||||
-spec init_monitors(binary()) -> map().
|
||||
init_monitors(Host) ->
|
||||
lists:foldl(fun(Domain, Monitors) ->
|
||||
RefIn = make_ref(), % just dummies
|
||||
RefOut = make_ref(),
|
||||
maps:merge(#{RefIn => {incoming, {Host, Domain, true}},
|
||||
RefOut => {outgoing, {Host, Domain, true}}},
|
||||
Monitors)
|
||||
end,
|
||||
#{},
|
||||
ejabberd_option:hosts() -- [Host]).
|
||||
|
||||
-spec fetch_public_hosts() -> list().
|
||||
fetch_public_hosts() ->
|
||||
try
|
||||
{ok, {{_, 200, _}, _Headers, Body}} =
|
||||
httpc:request(get, {?PUBLIC_HOSTS_URL, []}, [{timeout, 1000}], [{body_format, binary}]),
|
||||
case misc:json_decode(Body) of
|
||||
PublicHosts when is_list(PublicHosts) ->
|
||||
PublicHosts;
|
||||
Other ->
|
||||
?WARNING_MSG("Parsed JSON for public hosts was not a list: ~p", [Other]),
|
||||
[]
|
||||
end
|
||||
catch
|
||||
E:R ->
|
||||
?WARNING_MSG("Failed fetching public hosts (~p): ~p", [E, R]),
|
||||
[]
|
||||
end.
|
||||
|
||||
handle_cast({Event, Domain, Pid}, #state{host = Host, monitors = Mons} = State)
|
||||
when Event == register_in; Event == register_out ->
|
||||
Ref = monitor(process, Pid),
|
||||
IsPublic = check_if_public(Domain, State),
|
||||
NewMons = maps:put(Ref, {event_to_dir(Event), {Host, Domain, IsPublic}}, Mons),
|
||||
{noreply, State#state{monitors = NewMons}};
|
||||
handle_cast(_, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
event_to_dir(register_in) ->
|
||||
incoming;
|
||||
event_to_dir(register_out) ->
|
||||
outgoing.
|
||||
|
||||
handle_call(pubsub_host, _From, #state{pubsub_host = PubsubHost} = State) ->
|
||||
{reply, {ok, PubsubHost}, State};
|
||||
handle_call(_Request, _From, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({iq_reply, IQReply, {LServer, RServer}}, #state{monitors = Mons} = State) ->
|
||||
case IQReply of
|
||||
#iq{type = result, sub_els = [El]} ->
|
||||
case xmpp:decode(El) of
|
||||
#disco_info{features = Features} ->
|
||||
case lists:member(?NS_URN_SERVERINFO, Features) of
|
||||
true ->
|
||||
NewMons =
|
||||
maps:fold(fun (Ref, {Dir, {LServer0, RServer0, _}}, Acc)
|
||||
when LServer == LServer0, RServer == RServer0 ->
|
||||
maps:put(Ref,
|
||||
{Dir, {LServer, RServer, true}},
|
||||
Acc);
|
||||
(Ref, Other, Acc) ->
|
||||
maps:put(Ref, Other, Acc)
|
||||
end,
|
||||
#{},
|
||||
Mons),
|
||||
{noreply, State#state{monitors = NewMons}};
|
||||
_ ->
|
||||
{noreply, State}
|
||||
end;
|
||||
_ ->
|
||||
{noreply, State}
|
||||
end;
|
||||
_ ->
|
||||
{noreply, State}
|
||||
end;
|
||||
handle_info(update_pubsub, State) ->
|
||||
update_pubsub(State),
|
||||
{noreply, State};
|
||||
handle_info({'DOWN', Mon, process, _Pid, _Info}, #state{monitors = Mons} = State) ->
|
||||
{noreply, State#state{monitors = maps:remove(Mon, Mons)}};
|
||||
handle_info(_Request, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
terminate(_Reason, #state{monitors = Mons, timer = Timer}) ->
|
||||
case is_reference(Timer) of
|
||||
true ->
|
||||
case erlang:cancel_timer(Timer) of
|
||||
false ->
|
||||
receive
|
||||
{timeout, Timer, _} ->
|
||||
ok
|
||||
after 0 ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
maps:fold(fun(Mon, _, _) -> demonitor(Mon) end, ok, Mons).
|
||||
|
||||
depends(_Host, _Opts) ->
|
||||
[{mod_pubsub, hard}].
|
||||
|
||||
mod_options(_Host) ->
|
||||
[{pubsub_host, undefined}].
|
||||
|
||||
mod_opt_type(pubsub_host) ->
|
||||
econf:either(undefined, econf:host()).
|
||||
|
||||
mod_doc() ->
|
||||
#{desc => [?T("Exposes s2s information over Pub/Sub"), "",
|
||||
?T("Announces support for the ProtoXEP PubSub Server Information, by adding its Service Discovery feature."
|
||||
"Active S2S connections are published to a local pubsub node as advertised by Service Discovery. Only those connections that support this feature as well are exposed with their domain names, otherwise they are shown as anonymous nodes. At startup a list of well known public servers is being fetched. Those are not shown as anonymous even if they don't support this feature."
|
||||
"Currently the name of the node is hardcoded as \"serverinfo\". The local service to be used can be configured as `pubsub_host`. Otherwise a good guess is taken."
|
||||
"This module has a hard dependency on `mod_pubsub` for this reason. Also `mod_disco` must be configured for this feature to work."), "",
|
||||
?T("NOTE: The module only shows S2S connections established while the module is running: after installing the module, please run `ejabberdctl stop_s2s_connections`, or restart ejabberd.")],
|
||||
note => "added in 25.xx",
|
||||
opts => [{pubsub_host,
|
||||
#{value => "undefined | string()",
|
||||
desc => ?T("This option specifies which pubsub host to use to advertise S2S connections. This must be a vhost local to this service and handled by `mod_pubsub`. This is only needed if your configuration has more than one vhost in mod_pubsub's `hosts` option. If there's more than one and this option is not given, we just pick the first one.")}
|
||||
}],
|
||||
example =>
|
||||
["modules:",
|
||||
" mod_pubsub_serverinfo:",
|
||||
" pubsub_host: custom.pubsub.domain.local"]
|
||||
}.
|
||||
|
||||
in_auth_result(#{server_host := Host, remote_server := RServer} = State, true, _Server) ->
|
||||
gen_server:cast(
|
||||
gen_mod:get_module_proc(Host, ?MODULE), {register_in, RServer, self()}),
|
||||
State;
|
||||
in_auth_result(State, _, _) ->
|
||||
State.
|
||||
|
||||
out_auth_result(#{server_host := Host, remote_server := RServer} = State, true) ->
|
||||
gen_server:cast(
|
||||
gen_mod:get_module_proc(Host, ?MODULE), {register_out, RServer, self()}),
|
||||
State;
|
||||
out_auth_result(State, _) ->
|
||||
State.
|
||||
|
||||
check_if_public(Domain, State) ->
|
||||
maybe_send_disco_info(is_public(Domain, State) orelse is_monitored(Domain, State),
|
||||
Domain,
|
||||
State).
|
||||
|
||||
is_public(Domain, #state{public_hosts = PublicHosts}) ->
|
||||
lists:member(Domain, PublicHosts).
|
||||
|
||||
is_monitored(Domain, #state{host = Host, monitors = Mons}) ->
|
||||
maps:size(
|
||||
maps:filter(fun (_Ref, {_Dir, {LServer, RServer, IsPublic}})
|
||||
when LServer == Host, RServer == Domain ->
|
||||
IsPublic;
|
||||
(_Ref, _Other) ->
|
||||
false
|
||||
end,
|
||||
Mons))
|
||||
=/= 0.
|
||||
|
||||
maybe_send_disco_info(true, _Domain, _State) ->
|
||||
true;
|
||||
maybe_send_disco_info(false, Domain, #state{host = Host}) ->
|
||||
Proc = gen_mod:get_module_proc(Host, ?MODULE),
|
||||
IQ = #iq{type = get,
|
||||
from = jid:make(Host),
|
||||
to = jid:make(Domain),
|
||||
sub_els = [#disco_info{}]},
|
||||
ejabberd_router:route_iq(IQ, {Host, Domain}, Proc),
|
||||
false.
|
||||
|
||||
update_pubsub(#state{host = Host,
|
||||
pubsub_host = PubsubHost,
|
||||
node = Node,
|
||||
monitors = Mons}) ->
|
||||
Map = maps:fold(fun(_, {Dir, {MyDomain, Target, IsPublic}}, Acc) ->
|
||||
maps:update_with(MyDomain,
|
||||
fun(Acc2) ->
|
||||
maps:update_with(Target,
|
||||
fun({Types, _}) ->
|
||||
{Types#{Dir => true}, IsPublic}
|
||||
end,
|
||||
{#{Dir => true}, IsPublic},
|
||||
Acc2)
|
||||
end,
|
||||
#{Target => {#{Dir => true}, IsPublic}},
|
||||
Acc)
|
||||
end,
|
||||
#{},
|
||||
Mons),
|
||||
Domains =
|
||||
maps:fold(fun(MyDomain, Targets, Acc) ->
|
||||
Remote =
|
||||
maps:fold(fun (Remote, {Types, true}, Acc2) ->
|
||||
[#pubsub_serverinfo_remote_domain{name = Remote,
|
||||
type =
|
||||
maps:keys(Types)}
|
||||
| Acc2];
|
||||
(_HiddenRemote, {Types, false}, Acc2) ->
|
||||
[#pubsub_serverinfo_remote_domain{type =
|
||||
maps:keys(Types)}
|
||||
| Acc2]
|
||||
end,
|
||||
[],
|
||||
Targets),
|
||||
[#pubsub_serverinfo_domain{name = MyDomain, remote_domain = Remote} | Acc]
|
||||
end,
|
||||
[],
|
||||
Map),
|
||||
|
||||
PubOpts = [{persist_items, true}, {max_items, 1}, {access_model, open}],
|
||||
?DEBUG("Publishing serverinfo pubsub item on ~s: ~p", [PubsubHost, Domains]),
|
||||
mod_pubsub:publish_item(PubsubHost,
|
||||
Host,
|
||||
Node,
|
||||
jid:make(Host),
|
||||
<<"current">>,
|
||||
[xmpp:encode(#pubsub_serverinfo{domain = Domains})],
|
||||
PubOpts,
|
||||
all).
|
||||
|
||||
get_local_features({error, _} = Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc;
|
||||
get_local_features(Acc, _From, _To, Node, _Lang) when Node == <<>> ->
|
||||
case Acc of
|
||||
{result, Features} ->
|
||||
{result, [?NS_URN_SERVERINFO | Features]};
|
||||
empty ->
|
||||
{result, [?NS_URN_SERVERINFO]}
|
||||
end;
|
||||
get_local_features(Acc, _From, _To, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
get_info(Acc, Host, Mod, Node, Lang)
|
||||
when Mod == undefined orelse Mod == mod_disco, Node == <<"">> ->
|
||||
case mod_disco:get_info(Acc, Host, Mod, Node, Lang) of
|
||||
[#xdata{fields = Fields} = XD | Rest] ->
|
||||
PubsubHost = pubsub_host(Host),
|
||||
NodeField =
|
||||
#xdata_field{var = <<"serverinfo-pubsub-node">>,
|
||||
values = [<<"xmpp:", PubsubHost/binary, "?;node=serverinfo">>]},
|
||||
{stop, [XD#xdata{fields = Fields ++ [NodeField]} | Rest]};
|
||||
_ ->
|
||||
Acc
|
||||
end;
|
||||
get_info(Acc, Host, Mod, Node, _Lang) when Node == <<"">>, is_atom(Mod) ->
|
||||
PubsubHost = pubsub_host(Host),
|
||||
[#xdata{type = result,
|
||||
fields =
|
||||
[#xdata_field{type = hidden,
|
||||
var = <<"FORM_TYPE">>,
|
||||
values = [?NS_SERVERINFO]},
|
||||
#xdata_field{var = <<"serverinfo-pubsub-node">>,
|
||||
values = [<<"xmpp:", PubsubHost/binary, "?;node=serverinfo">>]}]}
|
||||
| Acc];
|
||||
get_info(Acc, _Host, _Mod, _Node, _Lang) ->
|
||||
Acc.
|
||||
|
||||
pubsub_host(Host) ->
|
||||
{ok, PubsubHost} = gen_server:call(gen_mod:get_module_proc(Host, ?MODULE), pubsub_host),
|
||||
PubsubHost.
|
||||
|
||||
pubsub_host(Host, Opts) ->
|
||||
case gen_mod:get_opt(pubsub_host, Opts) of
|
||||
undefined ->
|
||||
PubsubHost = hd(get_mod_pubsub_hosts(Host)),
|
||||
?INFO_MSG("No pubsub_host in configuration for ~p, choosing ~s", [?MODULE, PubsubHost]),
|
||||
PubsubHost;
|
||||
PubsubHost ->
|
||||
case check_pubsub_host_exists(Host, PubsubHost) of
|
||||
true ->
|
||||
PubsubHost;
|
||||
false ->
|
||||
{error, {pubsub_host_does_not_exist, PubsubHost}}
|
||||
end
|
||||
end.
|
||||
|
||||
check_pubsub_host_exists(Host, PubsubHost) ->
|
||||
lists:member(PubsubHost, get_mod_pubsub_hosts(Host)).
|
||||
|
||||
get_mod_pubsub_hosts(Host) ->
|
||||
case gen_mod:get_module_opt(Host, mod_pubsub, hosts) of
|
||||
[] ->
|
||||
[gen_mod:get_module_opt(Host, mod_pubsub, host)];
|
||||
PubsubHosts ->
|
||||
PubsubHosts
|
||||
end.
|
Loading…
Add table
Add a link
Reference in a new issue