1
0
Fork 0
mirror of https://github.com/processone/ejabberd synced 2025-10-03 17:59:31 +02:00

Merge pull request #4408 from sstrigler/mod_pubsub_serverinfo

mod_pubsub_serverinfo
This commit is contained in:
badlop 2025-07-08 10:55:14 +02:00 committed by GitHub
commit 1d79edbae0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 391 additions and 18 deletions

View file

@ -130,7 +130,7 @@ defmodule Ejabberd.MixProject do
{:p1_utils, "~> 1.0"}, {:p1_utils, "~> 1.0"},
{:pkix, "~> 1.0"}, {:pkix, "~> 1.0"},
{:stringprep, ">= 1.0.26"}, {: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"}] {:yconf, ">= 1.0.18"}]
++ cond_deps() ++ cond_deps()
end end

View file

@ -77,7 +77,7 @@
{stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}},
{if_var_true, stun, {if_var_true, stun,
{stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, {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"}}} {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}}
]}. ]}.

View file

@ -16,23 +16,23 @@
{<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1},
{<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.10">>},0},
{<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0},
{<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.17">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.18">>},0},
{<<"p1_acme">>, {<<"p1_acme">>,
{git,"https://github.com/processone/p1_acme", {git,"https://github.com/processone/p1_acme",
{ref,"27a590789add30ff507a49ffd440eeeb28c96ce5"}}, {ref,"27a590789add30ff507a49ffd440eeeb28c96ce5"}},
0}, 0},
{<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0}, {<<"p1_mysql">>,{pkg,<<"p1_mysql">>,<<"1.0.26">>},0},
{<<"p1_oauth2">>,{pkg,<<"p1_oauth2">>,<<"0.6.14">>},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}, {<<"p1_utils">>,{pkg,<<"p1_utils">>,<<"1.0.27">>},0},
{<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0}, {<<"pkix">>,{pkg,<<"pkix">>,<<"1.0.10">>},0},
{<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0}, {<<"sqlite3">>,{pkg,<<"sqlite3">>,<<"1.1.15">>},0},
{<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.31">>},0}, {<<"stringprep">>,{pkg,<<"stringprep">>,<<"1.0.32">>},0},
{<<"stun">>,{pkg,<<"stun">>,<<"1.2.17">>},0}, {<<"stun">>,{pkg,<<"stun">>,<<"1.2.19">>},0},
{<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.0">>},1}, {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1},
{<<"xmpp">>, {<<"xmpp">>,
{git,"https://github.com/processone/xmpp", {git,"https://github.com/processone/xmpp",
{ref,"74ed2d87222d3bd5e86f7c41daaa28fae59d4995"}}, {ref,"9b028c110083e4d979d88c286873d0abf08fa532"}},
0}, 0},
{<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}.
[ [
@ -50,16 +50,16 @@
{<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>},
{<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>}, {<<"jose">>, <<"A903F5227417BD2A08C8A00A0CBCC458118BE84480955E8D251297A425723F83">>},
{<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>}, {<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>},
{<<"mqtree">>, <<"82F54B8F2D22B4445DB1D6CCCB7FE9EAD049D61410C29E32475F3CEB3EE62A89">>}, {<<"mqtree">>, <<"B004E80BBEE5BC49E774B839F88162BDFF5CB654ABBDB79C8381AE4B13510A1B">>},
{<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>}, {<<"p1_mysql">>, <<"574D07C9936C53B1EC3556DB3CF064CC14A6C39039835B3D940471BFA5AC8E2B">>},
{<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>}, {<<"p1_oauth2">>, <<"1C5F82535574DE87E2059695AC4B91F8F9AEBACBC1C80287DAE6F02552D47AEA">>},
{<<"p1_pgsql">>, <<"3F95D7E3413FC8F0BE80ABB4BE1A0D7F67066A36905085CD5A423145598B0CB0">>}, {<<"p1_pgsql">>, <<"585F720C76B9BD27C5313DBB2C3CAD0CA19AB4493DE0B34A7496B51B65F5613A">>},
{<<"p1_utils">>, <<"F468D84C6FFA6E4B12A6160826DCF2D015527189D57865568A78B49C5ED972A1">>}, {<<"p1_utils">>, <<"F468D84C6FFA6E4B12A6160826DCF2D015527189D57865568A78B49C5ED972A1">>},
{<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>}, {<<"pkix">>, <<"D3BFADF7B7CFE2A3636F1B256C9CCE5F646A07CE31E57EE527668502850765A0">>},
{<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>}, {<<"sqlite3">>, <<"E819DEFD280145C328457D7AF897D2E45E8E5270E18812EE30B607C99CDD21AF">>},
{<<"stringprep">>, <<"FA1688C156DD271722AA18C423A4163E710D2F4F475AD0BC220910DF669B53AF">>}, {<<"stringprep">>, <<"63FD7FF5417A4A48DB6BB529C83D678361D34188367C1B13B3EAF39ACDEDB8E5">>},
{<<"stun">>, <<"C54614A592812EA125A2E6827AAC5A438571B591616426EC1419BA9B48252F54">>}, {<<"stun">>, <<"FF5BD2D2E3A0C2ADE41FC71A7A069EEBAA492ECDB35ECA35350FFF3C194B381A">>},
{<<"unicode_util_compat">>, <<"BC84380C9AB48177092F43AC89E4DFA2C6D62B40B8BD132B1059ECC7232F9A78">>}, {<<"unicode_util_compat">>, <<"A48703A25C170EEDADCA83B11E88985AF08D35F37C6F664D6DCFB106A97782FC">>},
{<<"yconf">>, <<"E565EDC8AABB8164C3BEBC86969095D296AD315DCBB46AF65DCCBC6C71EAE0F6">>}]}, {<<"yconf">>, <<"E565EDC8AABB8164C3BEBC86969095D296AD315DCBB46AF65DCCBC6C71EAE0F6">>}]},
{pkg_hash_ext,[ {pkg_hash_ext,[
{<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>}, {<<"base64url">>, <<"F9B3ADD4731A02A9B0410398B475B33E7566A695365237A6BDEE1BB447719F5C">>},
@ -75,15 +75,15 @@
{<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>},
{<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>}, {<<"jose">>, <<"0D6CD36FF8BA174DB29148FC112B5842186B68A90CE9FC2B3EC3AFE76593E614">>},
{<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>}, {<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>},
{<<"mqtree">>, <<"5FE8B7CF8FBC4783D0FCEB94654AC2BBF3242A58CD0397D249DED8AE021BE2A3">>}, {<<"mqtree">>, <<"F73827CECF9A310670F7D7909FC88EAB40B45290FE48E5C7E45AB7235B29B919">>},
{<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>}, {<<"p1_mysql">>, <<"EA138083F2C54719B9CF549DBF5802A288B0019EA3E5449B354C74CC03FAFDEC">>},
{<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>}, {<<"p1_oauth2">>, <<"1FD3AC474E43722D9D5A87C6DF8D36F698ED87AF7BB81CBBB66361451D99AE8F">>},
{<<"p1_pgsql">>, <<"268B01E8F4EB75C211A31495A25C2815C549AECCE2F0DF1A161C6E0A2CDE061E">>}, {<<"p1_pgsql">>, <<"3FB6A9617DB146419D420FFE7E94B9179A7CB5063D9C9450EF8B13FDAD2A709F">>},
{<<"p1_utils">>, <<"F1AF942B0A62BCFA0D59FBE30679BE4FFEB5E241A0C49ED5F094DB2F5B80F5E0">>}, {<<"p1_utils">>, <<"F1AF942B0A62BCFA0D59FBE30679BE4FFEB5E241A0C49ED5F094DB2F5B80F5E0">>},
{<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>}, {<<"pkix">>, <<"E02164F83094CB124C41B1AB28988A615D54B9ADC38575F00F19A597A3AC5D0E">>},
{<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>}, {<<"sqlite3">>, <<"3C0BA4E13322C2AD49DE4E2DDD28311366ADDE54BEAE8DBA9D9E3888F69D2857">>},
{<<"stringprep">>, <<"E9699C88E8DB16B3A41F0E45AC6874A4DA81A6E4854A77D76EDE6D09B08E3530">>}, {<<"stringprep">>, <<"6069CB059F5D18A312C42E5F374E9DE415DF68F7E3090C9C1A5505E2A8532710">>},
{<<"stun">>, <<"6B318244C21E8524A9AAE3AC9A05CD8234EE994C1C2C815DE68D306086AD768D">>}, {<<"stun">>, <<"66DC035EBF21DE8ABE51ECCC2C3D4BBF63C78650F74C3AFCAF2E4BB15C555927">>},
{<<"unicode_util_compat">>, <<"25EEE6D67DF61960CF6A794239566599B09E17E668D3700247BC498638152521">>}, {<<"unicode_util_compat">>, <<"B3A917854CE3AE233619744AD1E0102E05673136776FB2FA76234F3E03B23642">>},
{<<"yconf">>, <<"FA950EC6503F92D6417FB8CC1D982403F041697E8E1BBF4D4588FB919B9562EA">>}]} {<<"yconf">>, <<"FA950EC6503F92D6417FB8CC1D982403F041697E8E1BBF4D4588FB919B9562EA">>}]}
]. ].

View 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.