Skip to content

Commit

Permalink
Expand mod_pubsub_serverinfo
Browse files Browse the repository at this point in the history
  • Loading branch information
prefiks committed Jan 19, 2024
1 parent 70c68f9 commit d32347d
Show file tree
Hide file tree
Showing 5 changed files with 939 additions and 17 deletions.
17 changes: 17 additions & 0 deletions mod_pubsub_serverinfo/include/pubsub_serverinfo_codec.hrl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
%% Created automatically by XML generator (fxml_gen.erl)
%% Source: pubsub_serverinfo_codec.spec

-record(pubsub_serverinfo_remote_domain, {name = <<>> :: binary(),
type = [] :: ['bidi' | 'incoming' | 'outgoing']}).
-type pubsub_serverinfo_remote_domain() :: #pubsub_serverinfo_remote_domain{}.

-record(pubsub_serverinfo_domain, {name = <<>> :: binary(),
remote_domain :: 'undefined' | [#pubsub_serverinfo_remote_domain{}]}).
-type pubsub_serverinfo_domain() :: #pubsub_serverinfo_domain{}.

-record(pubsub_serverinfo, {domain = [] :: [#pubsub_serverinfo_domain{}]}).
-type pubsub_serverinfo() :: #pubsub_serverinfo{}.

-type pubsub_serverinfo_codec() :: pubsub_serverinfo() |
pubsub_serverinfo_domain() |
pubsub_serverinfo_remote_domain().
52 changes: 52 additions & 0 deletions mod_pubsub_serverinfo/spec/pubsub_serverinfo_codec.spec
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-xml(pubsub_serverinfo,
#elem{name = <<"serverinfo">>,
xmlns = <<"urn:xmpp:serverinfo:0">>,
module = pubsub_serverinfo_codec,
result = {pubsub_serverinfo, '$domain'},
refs = [#ref{name = pubsub_serverinfo_domain,
label = '$domain',
min = 0}]}).

-xml(pubsub_serverinfo_domain,
#elem{name = <<"domain">>,
xmlns = <<"urn:xmpp:serverinfo:0">>,
module = pubsub_serverinfo_codec,
result = {pubsub_serverinfo_domain, '$name', '$remote_domain'},
attrs = [#attr{name = <<"name">>,
label = '$name',
required = true}],
refs = [#ref{name = pubsub_serverinfo_federation,
label = '$remote_domain',
min = 0, max = 1}]}).

-xml(pubsub_serverinfo_federation,
#elem{name = <<"federation">>,
xmlns = <<"urn:xmpp:serverinfo:0">>,
module = pubsub_serverinfo_codec,
result = '$remote_domain',
refs = [#ref{name = pubsub_serverinfo_remote_domain,
label = '$remote_domain',
min = 0}]}).

-xml(pubsub_serverinfo_remote_domain,
#elem{name = <<"remote-domain">>,
xmlns = <<"urn:xmpp:serverinfo:0">>,
module = pubsub_serverinfo_codec,
result = {pubsub_serverinfo_remote_domain, '$name', '$type'},
attrs = [#attr{name = <<"name">>,
label = '$name',
required = true}],
refs = [#ref{name = pubsub_serverinfo_connection,
label = '$type',
min = 0}]}).

-xml(pubsub_serverinfo_connection,
#elem{name = <<"connection">>,
xmlns = <<"urn:xmpp:serverinfo:0">>,
module = pubsub_serverinfo_codec,
result = '$type',
attrs = [#attr{name = <<"type">>,
label = '$type',
required = true,
enc = {enc_enum, []},
dec = {dec_enum, [[incoming, outgoing, bidi]]}}]}).
141 changes: 124 additions & 17 deletions mod_pubsub_serverinfo/src/mod_pubsub_serverinfo.erl
Original file line number Diff line number Diff line change
Expand Up @@ -26,39 +26,146 @@
-module(mod_pubsub_serverinfo).
-author('guus.der.kinderen@gmail.com').
-behaviour(gen_mod).
-behaviour(gen_server).

-include("pubsub_serverinfo_codec.hrl").
-include("logger.hrl").
-include_lib("xmpp/include/xmpp.hrl").

%% gen_mod callbacks.
-export([start/2, stop/1, depends/2, mod_options/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(NS_SERVERINFO, <<"urn:xmpp:serverinfo:0">>).
-record(state, {host, pubsub_host, node, monitors = #{}, timer = undefined}).

start(Host, _Opts) ->
start(Host, Opts) ->
xmpp:register_codec(pubsub_serverinfo_codec),
ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 50),
ok.
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, Opts).

stop(Host) ->
ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 50),
ok.
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, _Opts]) ->
TRef = timer:send_interval(timer:minutes(5), self(), update_pubsub),
State = #state{host = Host, pubsub_host = <<"pubsub.", Host/binary>>, node = <<"serverinfo">>, timer = TRef},
self() ! {timeout, ok, update_pubsub},
{ok, State}.

handle_cast({register_in, MyDomain, Target, Pid}, #state{monitors = Mon} = State) ->
Ref = monitor(process, Pid),
Mon2 = maps:put(Ref, {incoming, {MyDomain, Target}}, Mon),
{noreply, State#state{monitors = Mon2}};
handle_cast({register_out, MyDomain, Target, Pid}, #state{monitors = Mon} = State) ->
Ref = monitor(process, Pid),
Mon2 = maps:put(Ref, {outgoing, {MyDomain, Target}}, Mon),
{noreply, State#state{monitors = Mon2}};
handle_cast(_, State) ->
{noreply, State}.

handle_call(_Request, _From, State) ->
{noreply, State}.

handle_info({timeout, _, 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) ->
[].

mod_doc() -> #{}.

get_local_features({error, _} = Acc, _From, _To, _Node,
_Lang) ->
in_auth_result(#{server_host := Host, server := LServer, remote_server := RServer} = State, true, _Server) ->
gen_server:cast(gen_mod:get_module_proc(Host, ?MODULE), {register_in, LServer, RServer, self()}),
State;
in_auth_result(State, _, _) ->
State.

out_auth_result(#{server_host := Host, server := LServer, remote_server := RServer} = State, true) ->
gen_server:cast(gen_mod:get_module_proc(Host, ?MODULE), {register_out, LServer, RServer, self()}),
State;
out_auth_result(State, _) ->
State.

update_pubsub(#state{host = Host, pubsub_host = PubsubHost, node = Node, monitors = Mons}) ->
Map = maps:fold(
fun(_, {Dir, {MyDomain, Target}}, Acc) ->
maps:update_with(MyDomain,
fun(Acc2) ->
maps:update_with(Target,
fun(Types) -> Types#{Dir => true} end,
#{Dir => true}, Acc2)
end, #{Target => #{Dir => true}}, Acc)
end, #{}, Mons),
Domains = maps:fold(
fun(MyDomain, Targets, Acc) ->
Remote = maps:fold(
fun(Remote, Types, Acc2) ->
[#pubsub_serverinfo_remote_domain{name = Remote, 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}],
mod_pubsub:publish_item(
PubsubHost, Host, Node, jid:make(Host),
<<>>, [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_local_features(Acc, _From, _To, Node, _Lang) ->
case Node of
[] ->
case Acc of
{result, Features} ->
{result, [?NS_SERVERINFO | Features]};
empty -> {result, [?NS_SERVERINFO]}
end;
_ -> Acc
end.
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] ->
NodeField = #xdata_field{var = <<"serverinfo-pubsub-node">>,
values = [<<"xmpp:pubsub.", Host/binary, "?;node=serverinfo">>]},
{stop, [XD#xdata{fields = Fields ++ [NodeField]} | Rest]};
_ ->
Acc
end;
get_info(Acc, _Host, _Mod, _Node, _Lang) ->
Acc.
Loading

0 comments on commit d32347d

Please sign in to comment.