diff --git a/big_tests/tests/mod_ping_SUITE.erl b/big_tests/tests/mod_ping_SUITE.erl index 029ae72950..ed793f775d 100644 --- a/big_tests/tests/mod_ping_SUITE.erl +++ b/big_tests/tests/mod_ping_SUITE.erl @@ -107,24 +107,30 @@ start_mod_ping(Opts) -> setup_pong_hook(Config) -> Pid = self(), HostType = domain_helper:host_type(mim), - Handler = mongoose_helper:successful_rpc(?MODULE, setup_pong_hook, [HostType, Pid]), - [{pong_handler, Handler} | Config]. + mongoose_helper:successful_rpc(?MODULE, setup_pong_hook, [HostType, Pid]), + [{pid, Pid} | Config]. setup_pong_hook(HostType, Pid) -> - Handler = fun(_Acc, _HostType, JID, _Response, _TDelta) -> - Pid ! {pong, jid:to_binary(jid:to_lower(JID))} - end, - ejabberd_hooks:add(user_ping_response, HostType, Handler, 50), - Handler. + gen_hook:add_handler(user_ping_response, HostType, + fun ?MODULE:pong_hook_handler/3, + #{pid => Pid}, 50). + +pong_hook_handler(Acc, + #{jid := JID} = _Params, + #{pid := Pid} = _Extra) -> + Pid ! {pong, jid:to_binary(jid:to_lower(JID))}, + {ok, Acc}. clear_pong_hook(Config) -> - {value, {_, Handler}, NConfig} = lists:keytake(pong_handler, 1, Config), + {value, {_, Pid}, NConfig} = lists:keytake(pid, 1, Config), HostType = domain_helper:host_type(mim), - mongoose_helper:successful_rpc(?MODULE, clear_pong_hook, [HostType, Handler]), + mongoose_helper:successful_rpc(?MODULE, clear_pong_hook, [HostType, Pid]), NConfig. -clear_pong_hook(HostType, Handler) -> - ejabberd_hooks:delete(user_ping_response, HostType, Handler, 50). +clear_pong_hook(HostType, Pid) -> + gen_hook:delete_handler(user_ping_response, HostType, + fun ?MODULE:pong_hook_handler/3, + #{pid => Pid}, 50). %%-------------------------------------------------------------------- %% Ping tests diff --git a/big_tests/tests/push_SUITE.erl b/big_tests/tests/push_SUITE.erl index 5187fc02eb..0b872b8bc0 100644 --- a/big_tests/tests/push_SUITE.erl +++ b/big_tests/tests/push_SUITE.erl @@ -134,14 +134,14 @@ init_per_testcase(CaseName = push_notifications_not_listed_disco_when_not_availa escalus:init_per_testcase(CaseName, Config); init_per_testcase(CaseName, Config0) -> Config1 = escalus_fresh:create_users(Config0, [{bob, 1}, {alice, 1}, {kate, 1}]), - Config = add_pubsub_jid([{case_name, CaseName} | Config1]), + Config2 = add_pubsub_jid([{case_name, CaseName} | Config1]), - case ?config(pubsub_host, Config0) of - virtual -> - start_hook_listener(Config); - _ -> - start_route_listener(Config) - end, + Config = case ?config(pubsub_host, Config0) of + virtual -> + start_hook_listener(Config2); + _ -> + start_route_listener(Config2) + end, escalus:init_per_testcase(CaseName, Config). @@ -151,7 +151,12 @@ end_per_testcase(CaseName = push_notifications_listed_disco_when_available, Conf end_per_testcase(CaseName = push_notifications_not_listed_disco_when_not_available, Config) -> escalus:end_per_testcase(CaseName, Config); end_per_testcase(CaseName, Config) -> - rpc(ejabberd_router, unregister_route, [atom_to_binary(CaseName, utf8)]), + case ?config(pubsub_host, Config) of + virtual -> + stop_hook_listener(Config); + _ -> + stop_route_listener(Config) + end, escalus:end_per_testcase(CaseName, Config). %% --------------------- Helpers ------------------------ @@ -692,7 +697,12 @@ start_route_listener(Config) -> push_form_ns => push_helper:push_form_type() }, Handler = rpc(mongoose_packet_handler, new, [?MODULE, #{state => State}]), Domain = pubsub_domain(Config), - rpc(ejabberd_router, register_route, [Domain, Handler]). + rpc(ejabberd_router, register_route, [Domain, Handler]), + Config. + +stop_route_listener(Config) -> + Domain = pubsub_domain(Config), + rpc(ejabberd_router, unregister_route, [Domain]). process_packet(_Acc, _From, To, El, #{state := State}) -> #{ pid := TestCasePid, pub_options_ns := PubOptionsNS, push_form_ns := PushFormNS } = State, @@ -739,26 +749,41 @@ valid_ns_if_defined(NS, FormProplist) -> start_hook_listener(Config) -> TestCasePid = self(), PubSubJID = pubsub_jid(Config), - rpc(?MODULE, rpc_start_hook_handler, [TestCasePid, PubSubJID]). + rpc(?MODULE, rpc_start_hook_handler, [TestCasePid, PubSubJID]), + [{pid, TestCasePid}, {jid, PubSubJID} | Config]. + +stop_hook_listener(Config) -> + TestCasePid = proplists:get_value(pid, Config), + PubSubJID = proplists:get_value(jid, Config), + rpc(?MODULE, rpc_stop_hook_handler, [TestCasePid, PubSubJID]). rpc_start_hook_handler(TestCasePid, PubSubJID) -> - Handler = fun(Acc, _Host, [PayloadMap], OptionMap) -> - try jid:to_binary(mongoose_acc:get(push_notifications, pubsub_jid, Acc)) of - PubSubJIDBin when PubSubJIDBin =:= PubSubJID-> - TestCasePid ! push_notification(PubSubJIDBin, - maps:to_list(PayloadMap), - maps:to_list(OptionMap)); - _ -> ok - catch - C:R:S -> - TestCasePid ! #{ event => handler_error, - class => C, - reason => R, - stacktrace => S } - end, - Acc - end, - ejabberd_hooks:add(push_notifications, <<"localhost">>, Handler, 50). + gen_hook:add_handler(push_notifications, <<"localhost">>, + fun ?MODULE:hook_handler_fn/3, + #{pid => TestCasePid, jid => PubSubJID}, 50). + +hook_handler_fn(Acc, + #{notification_forms := [PayloadMap], options := OptionMap} = _Params, + #{pid := TestCasePid, jid := PubSubJID} = _Extra) -> + try jid:to_binary(mongoose_acc:get(push_notifications, pubsub_jid, Acc)) of + PubSubJIDBin when PubSubJIDBin =:= PubSubJID -> + TestCasePid ! push_notification(PubSubJIDBin, + maps:to_list(PayloadMap), + maps:to_list(OptionMap)); + _ -> ok + catch + C:R:S -> + TestCasePid ! #{event => handler_error, + class => C, + reason => R, + stacktrace => S} + end, + {ok, Acc}. + +rpc_stop_hook_handler(TestCasePid, PubSubJID) -> + gen_hook:delete_handler(push_notifications, <<"localhost">>, + fun ?MODULE:hook_handler_fn/3, + #{pid => TestCasePid, jid => PubSubJID}, 50). %%-------------------------------------------------------------------- %% Test helpers diff --git a/big_tests/tests/sm_SUITE.erl b/big_tests/tests/sm_SUITE.erl index bfd41a7f21..bfb24245bb 100644 --- a/big_tests/tests/sm_SUITE.erl +++ b/big_tests/tests/sm_SUITE.erl @@ -865,7 +865,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> AliceSpec0 = escalus_fresh:create_fresh_user(Config, alice), Resource = proplists:get_value(username, AliceSpec0), AliceSpec = [{resource, Resource} | AliceSpec0], - start_hook_listener(Resource), + HookHandlerExtra = start_hook_listener(Resource), {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps ++ [stream_resumption]), SMID = proplists:get_value(smid, Alice#client.props), escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), @@ -912,7 +912,7 @@ unacknowledged_message_hook_common(RestartConnectionFN, Config) -> escalus:assert(is_chat_message, [<<"msg-3">>], wait_for_unacked_msg_hook(1, NewResource, 100)), escalus:assert(is_chat_message, [<<"msg-4">>], wait_for_unacked_msg_hook(1, NewResource, 100)), ?assertEqual(timeout, wait_for_unacked_msg_hook(0, Resource, 100)), - + stop_hook_listener(HookHandlerExtra), escalus_connection:stop(Bob). resume_session(Config) -> @@ -1323,20 +1323,34 @@ start_hook_listener(Resource) -> TestCasePid = self(), rpc(mim(), ?MODULE, rpc_start_hook_handler, [TestCasePid, Resource, host_type()]). +stop_hook_listener(HookExtra) -> + rpc(mim(), ?MODULE, rpc_stop_hook_handler, [HookExtra, host_type()]). + rpc_start_hook_handler(TestCasePid, User, HostType) -> - LUser=jid:nodeprep(User), - Handler = fun(Acc, Jid) -> - {U, _S, R} = jid:to_lower(Jid), - case U of - LUser -> - Counter = mongoose_acc:get(sm_test, counter, 0, Acc), - El = mongoose_acc:element(Acc), - TestCasePid ! {sm_test, Counter, R, El}, - mongoose_acc:set_permanent(sm_test, counter, Counter + 1, Acc); - _ -> Acc - end - end, - ejabberd_hooks:add(unacknowledged_message, HostType, Handler, 50). + LUser = jid:nodeprep(User), + Extra = #{luser => LUser, pid => TestCasePid}, + gen_hook:add_handler(unacknowledged_message, HostType, + fun ?MODULE:hook_handler_fn/3, + Extra, 50), + Extra. + +rpc_stop_hook_handler(HookExtra, HostType) -> + gen_hook:delete_handler(unacknowledged_message, HostType, + fun ?MODULE:hook_handler_fn/3, + HookExtra, 50). + +hook_handler_fn(Acc, + #{args := [Jid]} = _Params, + #{luser := LUser, pid := TestCasePid} = _Extra) -> + {U, _S, R} = jid:to_lower(Jid), + case U of + LUser -> + Counter = mongoose_acc:get(sm_test, counter, 0, Acc), + El = mongoose_acc:element(Acc), + TestCasePid ! {sm_test, Counter, R, El}, + {ok, mongoose_acc:set_permanent(sm_test, counter, Counter + 1, Acc)}; + _ -> {ok, Acc} + end. wait_for_unacked_msg_hook(Counter, Res, Timeout) -> receive diff --git a/src/ejabberd_hooks.erl b/src/ejabberd_hooks.erl index 9b5cc5eafc..58d53353cc 100644 --- a/src/ejabberd_hooks.erl +++ b/src/ejabberd_hooks.erl @@ -26,74 +26,38 @@ -module(ejabberd_hooks). -author('alexey@process-one.net'). --behaviour(gen_server). - %% External exports --export([start_link/0, - add/4, - add/5, - delete/4, +-export([add/5, delete/5, - run_global/3, - run_for_host_type/4]). - -%% gen_server callbacks --export([init/1, - handle_call/3, - handle_cast/2, - code_change/3, - handle_info/2, - terminate/2]). + add_args/2]). -export([add/1, delete/1]). --export([error_running_hook/3]). +-export([gen_hook_fn_wrapper/3]). -ignore_xref([add/4, delete/4, error_running_hook/3, start_link/0]). -include("mongoose.hrl"). -type hook() :: {HookName :: atom(), - HostType :: binary() | global, - Module :: module() | undefined, - Fn :: fun() | atom(), + HostType :: mongooseim:host_type() | global, + Module :: module(), + Fn :: atom(), Priority:: integer()}. --type key() :: {HookName :: atom(), - HostType :: binary() | global}. - --type element() :: {Priority:: integer(), %% must be first to ensure proper sorting - Module :: module() | undefined, - Fn :: fun() | atom()}. - --record(state, {}). - -export_type([hook/0]). - %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ejabberd_hooks}, ejabberd_hooks, [], []). - -%% @doc Add a fun to the given hook. -%% The integer Priority is used to sort the calls: -%% low numbers are executed before high numbers. --spec add(HookName :: atom(), - HostType :: binary() | global, - Function :: fun() | atom(), - Priority :: integer()) -> ok. -add(HookName, HostType, Function, Priority) when is_function(Function) -> - add_hook({HookName, HostType, undefined, Function, Priority}). %% @doc Add a module and function to the given hook. %% The integer Priority is used to sort the calls: %% low numbers are executed before high numbers. -spec add(HookName :: atom(), - HostType :: binary() | global, - Module :: atom(), - Function :: fun() | atom(), + HostType :: mongooseim:host_type() | global, + Module :: module(), + Function :: atom(), Priority :: integer()) -> ok. add(HookName, HostType, Module, Function, Priority) -> add_hook({HookName, HostType, Module, Function, Priority}). @@ -103,23 +67,12 @@ add(Hooks) when is_list(Hooks) -> [add_hook(Hook) || Hook <- Hooks], ok. --spec add_hook(hook()) -> ok. -add_hook(Hook) -> - gen_server:call(ejabberd_hooks, {add, Hook}). - %% @doc Delete a module and function from this hook. -%% It is important to indicate exactly the same information than when the call was added. --spec delete(HookName :: atom(), - HostType :: binary() | global, - Function :: fun() | atom(), - Priority :: integer()) -> ok. -delete(HookName, HostType, Function, Priority) when is_function(Function) -> - delete_hook({HookName, HostType, undefined, Function, Priority}). - +%% It is important to indicate exactly the same information as when the call was added. -spec delete(HookName :: atom(), - HostType :: binary() | global, - Module :: atom(), - Function :: fun() | atom(), + HostType :: mongooseim:host_type() | global, + Module :: module(), + Function :: atom(), Priority :: integer()) -> ok. delete(HookName, HostType, Module, Function, Priority) -> delete_hook({HookName, HostType, Module, Function, Priority}). @@ -129,184 +82,32 @@ delete(Hooks) when is_list(Hooks) -> [delete_hook(Hook) || Hook <- Hooks], ok. --spec delete_hook(hook()) -> ok. -delete_hook(Hook) -> - gen_server:call(ejabberd_hooks, {delete, Hook}). - -%% @doc run global hook, for more details see run_fold/4 documentation. --spec run_global(HookName :: atom(), Acc :: term(), Args :: [term()]) -> - NewAcc :: term() | stopped. -run_global(HookName, Acc, Args) -> - run_fold(HookName, global, Acc, Args). - -%% @doc run hook for the host type, for more details see run_fold/4 documentation. --spec run_for_host_type(HookName :: atom(), - HostType :: binary() | undefined, - Acc :: term(), - Args :: [term()]) -> - NewAcc :: term() | stopped. -run_for_host_type(HookName, undefined, Acc, Args) -> - ?LOG_ERROR(#{what => undefined_host_type, - text => <<"Running hook for an undefined host type">>, - hook_name => HookName, hook_acc => Acc, hook_args => Args}), - Acc; -run_for_host_type(HookName, HostType, Acc, Args) when is_binary(HostType) -> - run_fold(HookName, HostType, Acc, Args). - -%%%---------------------------------------------------------------------- -%%% Callback functions from gen_server -%%%---------------------------------------------------------------------- - -%%---------------------------------------------------------------------- -%% Func: init/1 -%% Returns: {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%%---------------------------------------------------------------------- -init([]) -> - ets:new(hooks, [named_table, {read_concurrency, true}]), - {ok, #state{}}. - -%%---------------------------------------------------------------------- -%% Func: handle_call/3 -%% Returns: {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | (terminate/2 is called) -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- -handle_call({add, Hook}, _From, State) -> - {Key, El} = get_key_and_el(Hook), - Reply = case ets:lookup(hooks, Key) of - [{_, Ls}] -> - case lists:member(El, Ls) of - true -> - ok; - false -> - %% NB: lists:merge/2 returns sorted list! - NewLs = lists:merge(Ls, [El]), - ets:insert(hooks, {Key, NewLs}), - ok - end; - [] -> - NewLs = [El], - ets:insert(hooks, {Key, NewLs}), - create_hook_metric(Key), - ok - end, - {reply, Reply, State}; - -handle_call({delete, Hook}, _From, State) -> - {Key, El} = get_key_and_el(Hook), - Reply = case ets:lookup(hooks, Key) of - [{_, Ls}] -> - NewLs = lists:delete(El, Ls), - ets:insert(hooks, {Key, NewLs}), - ok; - [] -> - ok - end, - {reply, Reply, State}; - -handle_call(_Request, _From, State) -> - Reply = ok, - {reply, Reply, State}. - -%%---------------------------------------------------------------------- -%% Func: handle_cast/2 -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- -handle_cast(_Msg, State) -> - {noreply, State}. - -%%---------------------------------------------------------------------- -%% Func: handle_info/2 -%% Returns: {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} (terminate/2 is called) -%%---------------------------------------------------------------------- -handle_info(_Info, State) -> - {noreply, State}. - -%%---------------------------------------------------------------------- -%% Func: terminate/2 -%% Purpose: Shutdown the server -%% Returns: any (ignored by gen_server) -%%---------------------------------------------------------------------- -terminate(_Reason, _State) -> - ets:delete(hooks), - ok. - - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. +-spec add_args(HookParams :: map(), LegacyArgsList :: [term()]) -> + HookParamsWithArgs :: map(). +add_args(HookParams, LegacyArgsList) -> + HookParams#{args => LegacyArgsList}. %%%---------------------------------------------------------------------- %%% Internal functions %%%---------------------------------------------------------------------- -%% @doc Run the handlers of the hook in order. -%% The arguments passed to the hook handler function are: [Acc | Args]. -%% The result of a call is used as Acc for the next call. -%% If a call returns 'stop', no more calls are performed and 'stopped' is returned. -%% If a call returns {stop, NewAcc}, no more calls are performed and NewAcc is returned. -%% If hook doesn't need accumulator and doesn't care about the return value. The 'ok' -%% atom can be used as initial Acc value. -%% -%% Note that every hook handler MUST return a valid Acc. if any hook handler is not -%% interested in Acc parameter (or even if Acc is not used for a hook at all), it must -%% return (pass through) an unchanged input accumulator value. --spec run_fold(HookName :: atom(), - HostType :: global | binary(), - Acc :: term(), - Args :: [term()]) -> - term() | stopped. -run_fold(HookName, HostType, Acc, Args) -> - Key = {HookName, HostType}, - case ets:lookup(hooks, Key) of - [{_, Ls}] -> - mongoose_metrics:increment_generic_hook_metric(HostType, HookName), - run_hook(Ls, Key, Acc, Args); - [] -> - Acc - end. +-spec add_hook(hook()) -> ok. +add_hook({HookName, HostType, Module, Function, Priority}) when is_atom(Function) -> + gen_hook:add_handler(HookName, HostType, + fun ?MODULE:gen_hook_fn_wrapper/3, + #{module => Module, function => Function}, + Priority). -run_hook([], _Key, Val, _Args) -> - Val; -run_hook([{_Priority, Module, Function} | Ls], Key, Acc, Args) -> - Res = hook_apply_function(Module, Function, Acc, Args), - case Res of - {'EXIT', Reason} -> - error_running_hook(Reason, Key, Args), - run_hook(Ls, Key, Acc, Args); - stop -> - stopped; - {stop, NewAcc} -> - NewAcc; - NewAcc -> - run_hook(Ls, Key, NewAcc, Args) +-spec delete_hook(hook()) -> ok. +delete_hook({HookName, HostType, Module, Function, Priority}) when is_atom(Function) -> + gen_hook:delete_handler(HookName, HostType, + fun ?MODULE:gen_hook_fn_wrapper/3, + #{module => Module, function => Function}, + Priority). + +gen_hook_fn_wrapper(Acc, #{args := Args}, #{module := Module, function := Function}) -> + case apply(Module, Function, [Acc | Args]) of + stop -> {stop, stopped}; + {stop, NewAcc} -> {stop, NewAcc}; + NewAcc -> {ok, NewAcc} end. - -hook_apply_function(_Module, Function, Acc, Args) when is_function(Function) -> - safely:apply(Function, [Acc | Args]); -hook_apply_function(Module, Function, Acc, Args) -> - safely:apply(Module, Function, [Acc | Args]). - -error_running_hook(Reason, Key, Args) -> - ?LOG_ERROR(#{what => hook_failed, - text => <<"Error running hook">>, - hook_key => Key, args => Args, reason => Reason}). - --spec get_key_and_el(hook()) -> {key(), element()}. -get_key_and_el({HookName, HostType, Module, Function, Priority}) -> - Key = {HookName, HostType}, - El = {Priority, Module, Function}, - {Key, El}. - --spec create_hook_metric(Key :: key()) -> any(). -create_hook_metric({HookName, HostType}) -> - mongoose_metrics:create_generic_hook_metric(HostType, HookName). diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl index 4a43012b4b..97670ad0e3 100644 --- a/src/ejabberd_sup.erl +++ b/src/ejabberd_sup.erl @@ -38,12 +38,12 @@ start_link() -> init([]) -> Hooks = - {ejabberd_hooks, - {ejabberd_hooks, start_link, []}, + {gen_hook, + {gen_hook, start_link, []}, permanent, brutal_kill, worker, - [ejabberd_hooks]}, + [gen_hook]}, Cleaner = {mongoose_cleaner, {mongoose_cleaner, start_link, []}, diff --git a/src/gen_hook.erl b/src/gen_hook.erl new file mode 100644 index 0000000000..ad896f6c98 --- /dev/null +++ b/src/gen_hook.erl @@ -0,0 +1,281 @@ +-module(gen_hook). + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, + add_handler/5, + delete_handler/5, + add_handlers/1, + delete_handlers/1, + run_fold/4]). + +%% gen_server callbacks +-export([init/1, + handle_call/3, + handle_cast/2, + code_change/3, + handle_info/2, + terminate/2]). + +%% exported for unit tests only +-export([error_running_hook/4]). + +-ignore_xref([start_link/0, add_handlers/1, delete_handlers/1]). + +-include("mongoose.hrl"). + +-type hook_name() :: atom(). +-type hook_tag() :: mongoose:host_type() | global. + +%% while Accumulator is not limited to any type, it's recommended to use maps. +-type hook_acc() :: any(). +-type hook_params() :: map(). +-type hook_extra() :: map(). + +-type hook_fn_ret_value() :: {ok | stop, NewAccumulator :: hook_acc()}. +-type hook_fn() :: %% see run_fold/4 documentation + fun((Accumulator :: hook_acc(), + ExecutionParameters :: hook_params(), + ExtraParameters :: hook_extra()) -> hook_fn_ret_value()). + +-type key() :: {HookName :: atom(), + Tag :: any()}. + +-type hook_tuple() :: {HookName :: hook_name(), + Tag :: hook_tag(), + Function :: hook_fn(), + Extra :: hook_extra(), + Priority :: pos_integer()}. + +-type hook_list() :: [hook_tuple()]. + +-export_type([hook_fn/0, hook_list/0]). + +-record(hook_handler, {key :: key(), + %% 'prio' field must go right after the 'key', + %% this is required for the proper sorting. + prio :: pos_integer(), + module :: module(), + function :: atom(), %% function name + extra :: map()}). + +-define(TABLE, ?MODULE). +%%%---------------------------------------------------------------------- +%%% API +%%%---------------------------------------------------------------------- +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +%% @doc Add a handler for a hook. +%% Priority is used to sort the calls (lower numbers are executed first). +-spec add_handler(HookName :: hook_name(), + Tag :: hook_tag(), + Function :: hook_fn(), + Extra :: hook_extra(), + Priority :: pos_integer()) -> ok. +add_handler(HookName, Tag, Function, Extra, Priority) -> + add_handler({HookName, Tag, Function, Extra, Priority}). + +-spec add_handlers(hook_list()) -> ok. +add_handlers(List) -> + [add_handler(HookTuple) || HookTuple <- List], + ok. + +-spec add_handler(hook_tuple()) -> ok. +add_handler(HookTuple) -> + Handler = make_hook_handler(HookTuple), + gen_server:call(?MODULE, {add_handler, Handler}). + +%% @doc Delete a hook handler. +%% It is important to indicate exactly the same information than when the call was added. +-spec delete_handler(HookName :: hook_name(), + Tag :: hook_tag(), + Function :: hook_fn(), + Extra :: hook_extra(), + Priority :: pos_integer()) -> ok. +delete_handler(HookName, Tag, Function, Extra, Priority) -> + delete_handler({HookName, Tag, Function, Extra, Priority}). + +-spec delete_handlers(hook_list()) -> ok. +delete_handlers(List) -> + [delete_handler(HookTuple) || HookTuple <- List], + ok. + +-spec delete_handler(hook_tuple()) -> ok. +delete_handler(HookTuple) -> + Handler = make_hook_handler(HookTuple), + gen_server:call(?MODULE, {delete_handler, Handler}). + +%% @doc Run hook handlers in order of priority (lower number means higher priority). +%% * if a hook handler returns {ok, NewAcc}, the NewAcc value is used +%% as an accumulator parameter for the following hook handler. +%% * if a hook handler returns {stop, NewAcc}, execution stops immediately +%% without invoking lower priority hook handlers. +%% * if a hook handler crashes, the error is logged and the next hook handler +%% is executed. +%% Note that every hook handler MUST return a valid Acc. If a hook handler is not +%% interested in changing Acc parameter (or even if Acc is not used for a hook +%% at all), it must return (pass through) an unchanged input accumulator value. +-spec run_fold(HookName :: hook_name(), + Tag :: hook_tag(), + Acc :: hook_acc(), + Params :: hook_params()) -> hook_fn_ret_value(). +run_fold(HookName, Tag, Acc, Params) -> + Key = hook_key(HookName, Tag), + case ets:lookup(?TABLE, Key) of + [{_, Ls}] -> + mongoose_metrics:increment_generic_hook_metric(Tag, HookName), + run_hook(Ls, Acc, Params); + [] -> + {ok, Acc} + end. + +%%%---------------------------------------------------------------------- +%%% gen_server callback functions +%%%---------------------------------------------------------------------- + +init([]) -> + ets:new(?TABLE, [named_table, {read_concurrency, true}]), + {ok, no_state}. + +handle_call({add_handler, #hook_handler{key = Key} = HookHandler}, _From, State) -> + Reply = case ets:lookup(?TABLE, Key) of + [{_, Ls}] -> + case lists:member(HookHandler, Ls) of + true -> + ok; + false -> + %% NB: lists:merge/2 returns sorted list! + NewLs = lists:merge(Ls, [HookHandler]), + ets:insert(?TABLE, {Key, NewLs}), + ok + end; + [] -> + NewLs = [HookHandler], + ets:insert(?TABLE, {Key, NewLs}), + create_hook_metric(Key), + ok + end, + {reply, Reply, State}; +handle_call({delete_handler, #hook_handler{key = Key} = HookHandler}, _From, State) -> + Reply = case ets:lookup(?TABLE, Key) of + [{_, Ls}] -> + NewLs = lists:delete(HookHandler, Ls), + ets:insert(?TABLE, {Key, NewLs}), + ok; + [] -> + ok + end, + {reply, Reply, State}; +handle_call(Request, From, State) -> + ?UNEXPECTED_CALL(Request, From), + {reply, bad_request, State}. + +handle_cast(Msg, State) -> + ?UNEXPECTED_CAST(Msg), + {noreply, State}. + +handle_info(Info, State) -> + ?UNEXPECTED_INFO(Info), + {noreply, State}. + +terminate(_Reason, _State) -> + ets:delete(?TABLE), + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%---------------------------------------------------------------------- +%%% Internal functions +%%%---------------------------------------------------------------------- +-spec run_hook([#hook_handler{}], hook_acc(), hook_params()) -> hook_fn_ret_value(). +run_hook([], Acc, _Params) -> + {ok, Acc}; +run_hook([Handler | Ls], Acc, Params) -> + case apply_hook_function(Handler, Acc, Params) of + {'EXIT', Reason} -> + ?MODULE:error_running_hook(Reason, Handler, Acc, Params), + run_hook(Ls, Acc, Params); + {stop, NewAcc} -> + {stop, NewAcc}; + {ok, NewAcc} -> + run_hook(Ls, NewAcc, Params) + end. + +-spec apply_hook_function(#hook_handler{}, hook_acc(), hook_params()) -> + hook_fn_ret_value() | {'EXIT', Reason :: any()}. +apply_hook_function(#hook_handler{module = Module, function = Function, extra = Extra}, + Acc, Params) -> + safely:apply(Module, Function, [Acc, Params, Extra]). + +error_running_hook(Reason, Handler, Acc, Params) -> + ?LOG_ERROR(#{what => hook_failed, + text => <<"Error running hook">>, + handler => Handler, + acc => Acc, + params => Params, + reason => Reason}). + +-spec make_hook_handler(hook_tuple()) -> #hook_handler{}. +make_hook_handler({HookName, Tag, Function, Extra, Priority} = HookTuple) + when is_atom(HookName), is_binary(Tag) or (Tag =:= global), + is_function(Function, 3), is_map(Extra), + is_integer(Priority), Priority > 0 -> + NewExtra = extend_extra(HookTuple), + {Module, FunctionName} = check_hook_function(Function), + #hook_handler{key = hook_key(HookName, Tag), + prio = Priority, + module = Module, + function = FunctionName, + extra = NewExtra}. + +-spec check_hook_function(hook_fn()) -> {module(), atom()}. +check_hook_function(Function) when is_function(Function, 3) -> + case erlang:fun_info(Function, type) of + {type, external} -> + {module, Module} = erlang:fun_info(Function, module), + {name, FunctionName} = erlang:fun_info(Function, name), + case code:ensure_loaded(Module) of + {module, Module} -> ok; + Error -> + throw_error(#{what => module_is_not_loaded, + module => Module, error => Error}) + end, + case erlang:function_exported(Module, FunctionName, 3) of + true -> ok; + false -> + throw_error(#{what => function_is_not_exported, + function => Function}) + end, + {Module, FunctionName}; + {type, local} -> + throw_error(#{what => only_external_function_references_allowed, + function => Function}) + end. + +-spec throw_error(map()) -> no_return(). +throw_error(ErrorMap) -> + error(ErrorMap). + +-spec hook_key(HookName :: hook_name(), Tag :: hook_tag()) -> key(). +hook_key(HookName, Tag) -> + {HookName, Tag}. + +-spec extend_extra(hook_tuple()) -> hook_extra(). +extend_extra({HookName, Tag, _Function, OriginalExtra, _Priority}) -> + ExtraExtension = case Tag of + global -> #{hook_name => HookName, hook_tag => Tag}; + HostType when is_binary(HostType) -> + #{hook_name => HookName, hook_tag => Tag, + host_type => HostType} + end, + %% KV pairs of the OriginalExtra map will remain unchanged, + %% only the new keys from the ExtraExtension map will be added + %% to the NewExtra map + maps:merge(ExtraExtension, OriginalExtra). + +-spec create_hook_metric(Key :: key()) -> any(). +create_hook_metric({HookName, Tag}) -> + mongoose_metrics:create_generic_hook_metric(Tag, HookName). diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index d6fbccda18..ea2810ac7a 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -7,6 +7,7 @@ -include("jlib.hrl"). -include("mod_privacy.hrl"). +-include("mongoose.hrl"). -export([adhoc_local_commands/4, adhoc_sm_commands/4, @@ -168,8 +169,8 @@ C2SState :: ejabberd_c2s:state(), Result :: term(). % ok | empty_state | HandlerState c2s_remote_hook(HostType, Tag, Args, HandlerState, C2SState) -> - ejabberd_hooks:run_for_host_type(c2s_remote_hook, HostType, HandlerState, - [Tag, Args, C2SState]). + run_hook_for_host_type(c2s_remote_hook, HostType, HandlerState, + [Tag, Args, C2SState]). -spec adhoc_local_commands(LServer, From, To, AdhocRequest) -> Result when LServer :: jid:lserver(), @@ -178,8 +179,8 @@ c2s_remote_hook(HostType, Tag, Args, HandlerState, C2SState) -> AdhocRequest :: adhoc:request(), Result :: mod_adhoc:command_hook_acc(). adhoc_local_commands(LServer, From, To, AdhocRequest) -> - ejabberd_hooks:run_for_host_type(adhoc_local_commands, LServer, empty, - [From, To, AdhocRequest]). + run_hook_for_host_type(adhoc_local_commands, LServer, empty, + [From, To, AdhocRequest]). -spec adhoc_sm_commands(LServer, From, To, AdhocRequest) -> Result when LServer :: jid:lserver(), @@ -188,8 +189,7 @@ adhoc_local_commands(LServer, From, To, AdhocRequest) -> AdhocRequest :: adhoc:request(), Result :: mod_adhoc:command_hook_acc(). adhoc_sm_commands(LServer, From, To, AdhocRequest) -> - ejabberd_hooks:run_for_host_type(adhoc_sm_commands, LServer, empty, - [From, To, AdhocRequest]). + run_hook_for_host_type(adhoc_sm_commands, LServer, empty, [From, To, AdhocRequest]). %%% @doc The `anonymous_purge_hook' hook is called when anonymous user's data is removed. -spec anonymous_purge_hook(LServer, Acc, LUser) -> Result when @@ -199,8 +199,7 @@ adhoc_sm_commands(LServer, From, To, AdhocRequest) -> Result :: mongose_acc:t(). anonymous_purge_hook(LServer, Acc, LUser) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(anonymous_purge_hook, HostType, Acc, - [LUser, LServer]). + run_hook_for_host_type(anonymous_purge_hook, HostType, Acc, [LUser, LServer]). -spec auth_failed(HostType, Server, Username) -> Result when HostType :: binary(), @@ -208,7 +207,7 @@ anonymous_purge_hook(LServer, Acc, LUser) -> Username :: jid:user() | unknown, Result :: ok. auth_failed(HostType, Server, Username) -> - ejabberd_hooks:run_for_host_type(auth_failed, HostType, ok, [Username, Server]). + run_hook_for_host_type(auth_failed, HostType, ok, [Username, Server]). -spec does_user_exist(HostType, Jid, RequestType) -> Result when HostType :: mongooseim:host_type(), @@ -216,33 +215,36 @@ auth_failed(HostType, Server, Username) -> RequestType :: ejabberd_auth:exist_type(), Result :: boolean(). does_user_exist(HostType, Jid, RequestType) -> - ejabberd_hooks:run_for_host_type(does_user_exist, HostType, - false, [HostType, Jid, RequestType]). + run_hook_for_host_type(does_user_exist, HostType, false, + [HostType, Jid, RequestType]). -spec remove_domain(HostType, Domain) -> Result when HostType :: binary(), Domain :: jid:lserver(), Result :: ok. remove_domain(HostType, Domain) -> - ejabberd_hooks:run_for_host_type(remove_domain, HostType, #{}, [HostType, Domain]). + run_hook_for_host_type(remove_domain, HostType, #{}, [HostType, Domain]). -spec node_cleanup(Node :: node()) -> Acc :: map(). node_cleanup(Node) -> - ejabberd_hooks:run_global(node_cleanup, #{}, [Node]). + Params = #{node => Node}, + Args = [Node], + ParamsWithLegacyArgs = ejabberd_hooks:add_args(Params, Args), + run_global_hook(node_cleanup, #{}, ParamsWithLegacyArgs). -spec ejabberd_ctl_process(Acc, Args) -> Result when Acc :: any(), Args :: [string()], Result :: any(). ejabberd_ctl_process(Acc, Args) -> - ejabberd_hooks:run_global(ejabberd_ctl_process, Acc, [Args]). + run_global_hook(ejabberd_ctl_process, Acc, [Args]). -spec failed_to_store_message(Acc) -> Result when Acc :: mongoose_acc:t(), Result :: mongoose_acc:t(). failed_to_store_message(Acc) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(failed_to_store_message, HostType, Acc, []). + run_hook_for_host_type(failed_to_store_message, HostType, Acc, []). %%% @doc The `filter_local_packet' hook is called to filter out %%% stanzas routed with `mongoose_local_delivery'. @@ -255,7 +257,7 @@ failed_to_store_message(Acc) -> Result :: drop | filter_packet_acc(). filter_local_packet(FilterAcc = {_From, _To, Acc, _Packet}) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(filter_local_packet, HostType, FilterAcc, []). + run_hook_for_host_type(filter_local_packet, HostType, FilterAcc, []). %%% @doc The `filter_packet' hook is called to filter out %%% stanzas routed with `mongoose_router_global'. @@ -263,7 +265,7 @@ filter_local_packet(FilterAcc = {_From, _To, Acc, _Packet}) -> Acc :: filter_packet_acc(), Result :: drop | filter_packet_acc(). filter_packet(Acc) -> - ejabberd_hooks:run_global(filter_packet, Acc, []). + run_global_hook(filter_packet, Acc, []). %%% @doc The `inbox_unread_count' hook is called to get the number %%% of unread messages in the inbox for a user. @@ -273,7 +275,7 @@ filter_packet(Acc) -> User :: jid:jid(), Result :: mongoose_acc:t(). inbox_unread_count(LServer, Acc, User) -> - ejabberd_hooks:run_for_host_type(inbox_unread_count, LServer, Acc, [User]). + run_hook_for_host_type(inbox_unread_count, LServer, Acc, [User]). %%% @doc The `get_key' hook is called to extract a key from `mod_keystore'. -spec get_key(LServer, KeyName) -> Result when @@ -281,8 +283,7 @@ inbox_unread_count(LServer, Acc, User) -> KeyName :: atom(), Result :: mod_keystore:key_list(). get_key(LServer, KeyName) -> - ejabberd_hooks:run_for_host_type(get_key, LServer, [], - [{KeyName, LServer}]). + run_hook_for_host_type(get_key, LServer, [], [{KeyName, LServer}]). -spec packet_to_component(Acc, From, To) -> Result when Acc :: mongoose_acc:t(), @@ -290,7 +291,7 @@ get_key(LServer, KeyName) -> To :: jid:jid(), Result :: mongoose_acc:t(). packet_to_component(Acc, From, To) -> - ejabberd_hooks:run_global(packet_to_component, Acc, [From, To]). + run_global_hook(packet_to_component, Acc, [From, To]). -spec presence_probe_hook(HostType, Acc, From, To, Pid) -> Result when HostType :: binary(), @@ -300,8 +301,7 @@ packet_to_component(Acc, From, To) -> Pid :: pid(), Result :: mongoose_acc:t(). presence_probe_hook(HostType, Acc, From, To, Pid) -> - ejabberd_hooks:run_for_host_type(presence_probe_hook, HostType, Acc, - [From, To, Pid]). + run_hook_for_host_type(presence_probe_hook, HostType, Acc, [From, To, Pid]). %%% @doc The `push_notifications' hook is called to push notifications. -spec push_notifications(Server, Acc, NotificationForms, Options) -> Result when @@ -311,8 +311,11 @@ presence_probe_hook(HostType, Acc, From, To, Pid) -> Options :: #{atom() => binary()}, Result :: ok | {error, any()}. push_notifications(Server, Acc, NotificationForms, Options) -> - ejabberd_hooks:run_for_host_type(push_notifications, Server, Acc, - [Server, NotificationForms, Options]). + Params = #{server => Server, options => Options, + notification_forms => NotificationForms}, + Args = [Server, NotificationForms, Options], + ParamsWithLegacyArgs = ejabberd_hooks:add_args(Params, Args), + run_hook_for_host_type(push_notifications, Server, Acc, ParamsWithLegacyArgs). %%% @doc The `register_command' hook is called when a command %%% is registered in `mongoose_commands'. @@ -320,7 +323,7 @@ push_notifications(Server, Acc, NotificationForms, Options) -> Command :: mongoose_commands:t(), Result :: drop. register_command(Command) -> - ejabberd_hooks:run_global(register_command, Command, []). + run_global_hook(register_command, Command, []). %%% @doc The `register_subhost' hook is called when a component %%% is registered for ejabberd_router. @@ -329,7 +332,7 @@ register_command(Command) -> IsHidden :: boolean(), Result :: any(). register_subhost(LDomain, IsHidden) -> - ejabberd_hooks:run_global(register_subhost, ok, [LDomain, IsHidden]). + run_global_hook(register_subhost, ok, [LDomain, IsHidden]). %%% @doc The `register_user' hook is called when a user is successfully %%% registered in an authentication backend. @@ -339,7 +342,7 @@ register_subhost(LDomain, IsHidden) -> LUser :: jid:luser(), Result :: any(). register_user(HostType, LServer, LUser) -> - ejabberd_hooks:run_for_host_type(register_user, HostType, ok, [LUser, LServer]). + run_hook_for_host_type(register_user, HostType, ok, [LUser, LServer]). %%% @doc The `remove_user' hook is called when a user is removed. -spec remove_user(Acc, LServer, LUser) -> Result when @@ -349,7 +352,7 @@ register_user(HostType, LServer, LUser) -> Result :: mongoose_acc:t(). remove_user(Acc, LServer, LUser) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(remove_user, HostType, Acc, [LUser, LServer]). + run_hook_for_host_type(remove_user, HostType, Acc, [LUser, LServer]). -spec resend_offline_messages_hook(Acc, JID) -> Result when Acc :: mongoose_acc:t(), @@ -357,7 +360,7 @@ remove_user(Acc, LServer, LUser) -> Result :: mongoose_acc:t(). resend_offline_messages_hook(Acc, JID) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(resend_offline_messages_hook, HostType, Acc, [JID]). + run_hook_for_host_type(resend_offline_messages_hook, HostType, Acc, [JID]). %%% @doc The `rest_user_send_packet' hook is called when a user sends %%% a message using the REST API. @@ -369,8 +372,7 @@ resend_offline_messages_hook(Acc, JID) -> Result :: mongoose_acc:t(). rest_user_send_packet(Acc, From, To, Packet) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(rest_user_send_packet, HostType, Acc, - [From, To, Packet]). + run_hook_for_host_type(rest_user_send_packet, HostType, Acc, [From, To, Packet]). %%% @doc The `session_cleanup' hook is called when sm backend cleans up a user's session. -spec session_cleanup(Server, Acc, User, Resource, SID) -> Result when @@ -382,8 +384,8 @@ rest_user_send_packet(Acc, From, To, Packet) -> Result :: mongoose_acc:t(). session_cleanup(Server, Acc, User, Resource, SID) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(session_cleanup, HostType, Acc, - [User, Server, Resource, SID]). + run_hook_for_host_type(session_cleanup, HostType, Acc, + [User, Server, Resource, SID]). %%% @doc The `set_vcard' hook is called when the caller wants to set the VCard. -spec set_vcard(LServer, User, VCard) -> Result when @@ -392,8 +394,8 @@ session_cleanup(Server, Acc, User, Resource, SID) -> VCard :: exml:element(), Result :: ok | {error, any()}. set_vcard(LServer, User, VCard) -> - ejabberd_hooks:run_for_host_type(set_vcard, LServer, {error, no_handler_defined}, - [User, VCard]). + run_hook_for_host_type(set_vcard, LServer, {error, no_handler_defined}, + [User, VCard]). -spec unacknowledged_message(HostType, Acc, JID) -> Result when HostType :: binary(), @@ -401,7 +403,10 @@ set_vcard(LServer, User, VCard) -> JID :: jid:jid(), Result :: mongoose_acc:t(). unacknowledged_message(HostType, Acc, JID) -> - ejabberd_hooks:run_for_host_type(unacknowledged_message, HostType, Acc, [JID]). + Params = #{jid => JID}, + Args = [JID], + ParamsWithLegacyArgs = ejabberd_hooks:add_args(Params, Args), + run_hook_for_host_type(unacknowledged_message, HostType, Acc, ParamsWithLegacyArgs). %%% @doc The `unregister_command' hook is called when a command %%% is unregistered from `mongoose_commands'. @@ -409,7 +414,7 @@ unacknowledged_message(HostType, Acc, JID) -> Command :: mongoose_commands:t(), Result :: drop. unregister_command(Command) -> - ejabberd_hooks:run_global(unregister_command, Command, []). + run_global_hook(unregister_command, Command, []). %%% @doc The `unregister_subhost' hook is called when a component %%% is unregistered from ejabberd_router. @@ -417,7 +422,7 @@ unregister_command(Command) -> LDomain :: binary(), Result :: any(). unregister_subhost(LDomain) -> - ejabberd_hooks:run_global(unregister_subhost, ok, [LDomain]). + run_global_hook(unregister_subhost, ok, [LDomain]). -spec user_available_hook(HostType, Acc, JID) -> Result when HostType :: binary(), @@ -425,7 +430,7 @@ unregister_subhost(LDomain) -> JID :: jid:jid(), Result :: mongoose_acc:t(). user_available_hook(HostType, Acc, JID) -> - ejabberd_hooks:run_for_host_type(user_available_hook, HostType, Acc, [JID]). + run_hook_for_host_type(user_available_hook, HostType, Acc, [JID]). %%% @doc The `user_ping_response' hook is called when a user responds to a ping. -spec user_ping_response(HostType, Acc, JID, Response, TDelta) -> Result when @@ -436,8 +441,10 @@ user_available_hook(HostType, Acc, JID) -> TDelta :: non_neg_integer(), Result :: mongoose_acc:t(). user_ping_response(HostType, Acc, JID, Response, TDelta) -> - ejabberd_hooks:run_for_host_type(user_ping_response, HostType, Acc, - [HostType, JID, Response, TDelta]). + Params = #{jid => JID, response => Response, time_delta => TDelta}, + Args = [HostType, JID, Response, TDelta], + ParamsWithLegacyArgs = ejabberd_hooks:add_args(Params, Args), + run_hook_for_host_type(user_ping_response, HostType, Acc, ParamsWithLegacyArgs). %%% @doc The `user_ping_timeout' hook is called when there is a timeout %%% when waiting for a ping response from a user. @@ -446,7 +453,7 @@ user_ping_response(HostType, Acc, JID, Response, TDelta) -> JID :: jid:jid(), Result :: any(). user_ping_timeout(HostType, JID) -> - ejabberd_hooks:run_for_host_type(user_ping_timeout, HostType, ok, [JID]). + run_hook_for_host_type(user_ping_timeout, HostType, ok, [JID]). -spec user_receive_packet(HostType, Acc, JID, From, To, El) -> Result when HostType :: binary(), @@ -457,15 +464,14 @@ user_ping_timeout(HostType, JID) -> El :: exml:element(), Result :: mongoose_acc:t(). user_receive_packet(HostType, Acc, JID, From, To, El) -> - ejabberd_hooks:run_for_host_type(user_receive_packet, HostType, Acc, - [JID, From, To, El]). + run_hook_for_host_type(user_receive_packet, HostType, Acc, [JID, From, To, El]). -spec user_sent_keep_alive(HostType, JID) -> Result when HostType :: binary(), JID :: jid:jid(), Result :: any(). user_sent_keep_alive(HostType, JID) -> - ejabberd_hooks:run_for_host_type(user_sent_keep_alive, HostType, ok, [JID]). + run_hook_for_host_type(user_sent_keep_alive, HostType, ok, [JID]). %%% @doc A hook called when a user sends an XMPP stanza. %%% The hook's handler is expected to accept four parameters: @@ -479,8 +485,7 @@ user_sent_keep_alive(HostType, JID) -> Result :: mongoose_acc:t(). user_send_packet(Acc, From, To, Packet) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(user_send_packet, HostType, Acc, - [From, To, Packet]). + run_hook_for_host_type(user_send_packet, HostType, Acc, [From, To, Packet]). %%% @doc The `vcard_set' hook is called to inform that the vcard %%% has been set in mod_vcard backend. @@ -490,7 +495,7 @@ user_send_packet(Acc, From, To, Packet) -> VCard :: exml:element(), Result :: any(). vcard_set(Server, LUser, VCard) -> - ejabberd_hooks:run_for_host_type(vcard_set, Server, ok, [LUser, Server, VCard]). + run_hook_for_host_type(vcard_set, Server, ok, [LUser, Server, VCard]). -spec xmpp_send_element(HostType, Acc, El) -> Result when HostType :: binary(), @@ -498,7 +503,7 @@ vcard_set(Server, LUser, VCard) -> El :: exml:element(), Result :: mongoose_acc:t(). xmpp_send_element(HostType, Acc, El) -> - ejabberd_hooks:run_for_host_type(xmpp_send_element, HostType, Acc, [El]). + run_hook_for_host_type(xmpp_send_element, HostType, Acc, [El]). %%% @doc The `xmpp_stanza_dropped' hook is called to inform that %%% an xmpp stanza has been dropped. @@ -510,8 +515,7 @@ xmpp_send_element(HostType, Acc, El) -> Result :: any(). xmpp_stanza_dropped(Acc, From, To, Packet) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(xmpp_stanza_dropped, HostType, Acc, - [From, To, Packet]). + run_hook_for_host_type(xmpp_stanza_dropped, HostType, Acc, [From, To, Packet]). %% C2S related hooks @@ -523,8 +527,8 @@ xmpp_stanza_dropped(Acc, From, To, Packet) -> Result :: [jid:simple_jid()]. c2s_broadcast_recipients(State, Type, From, Packet) -> HostType = ejabberd_c2s_state:host_type(State), - ejabberd_hooks:run_for_host_type(c2s_broadcast_recipients, HostType, [], - [State, Type, From, Packet]). + run_hook_for_host_type(c2s_broadcast_recipients, HostType, [], + [State, Type, From, Packet]). -spec c2s_filter_packet(State, Feature, To, Packet) -> Result when State :: ejabberd_c2s:state(), @@ -534,8 +538,8 @@ c2s_broadcast_recipients(State, Type, From, Packet) -> Result :: boolean(). c2s_filter_packet(State, Feature, To, Packet) -> HostType = ejabberd_c2s_state:host_type(State), - ejabberd_hooks:run_for_host_type(c2s_filter_packet, HostType, true, - [State, Feature, To, Packet]). + run_hook_for_host_type(c2s_filter_packet, HostType, true, + [State, Feature, To, Packet]). -spec c2s_preprocessing_hook(HostType, Acc, State) -> Result when HostType :: mongooseim:host_type(), @@ -543,7 +547,7 @@ c2s_filter_packet(State, Feature, To, Packet) -> State :: ejabberd_c2s:state(), Result :: mongoose_acc:t(). c2s_preprocessing_hook(HostType, Acc, State) -> - ejabberd_hooks:run_for_host_type(c2s_preprocessing_hook, HostType, Acc, [State]). + run_hook_for_host_type(c2s_preprocessing_hook, HostType, Acc, [State]). -spec c2s_presence_in(State, From, To, Packet) -> Result when State :: ejabberd_c2s:state(), @@ -553,14 +557,14 @@ c2s_preprocessing_hook(HostType, Acc, State) -> Result :: ejabberd_c2s:state(). c2s_presence_in(State, From, To, Packet) -> HostType = ejabberd_c2s_state:host_type(State), - ejabberd_hooks:run_for_host_type(c2s_presence_in, HostType, State, [From, To, Packet]). + run_hook_for_host_type(c2s_presence_in, HostType, State, [From, To, Packet]). -spec c2s_stream_features(HostType, LServer) -> Result when HostType :: mongooseim:host_type(), LServer :: jid:lserver(), Result :: [exml:element()]. c2s_stream_features(HostType, LServer) -> - ejabberd_hooks:run_for_host_type(c2s_stream_features, HostType, [], [HostType, LServer]). + run_hook_for_host_type(c2s_stream_features, HostType, [], [HostType, LServer]). -spec c2s_unauthenticated_iq(HostType, Server, IQ, IP) -> Result when HostType :: mongooseim:host_type(), @@ -569,21 +573,20 @@ c2s_stream_features(HostType, LServer) -> IP :: {inet:ip_address(), inet:port_number()} | undefined, Result :: exml:element() | empty. c2s_unauthenticated_iq(HostType, Server, IQ, IP) -> - ejabberd_hooks:run_for_host_type(c2s_unauthenticated_iq, HostType, empty, - [Server, IQ, IP]). + run_hook_for_host_type(c2s_unauthenticated_iq, HostType, empty, [Server, IQ, IP]). -spec c2s_update_presence(HostType, Acc) -> Result when HostType :: mongooseim:host_type(), Acc :: mongoose_acc:t(), Result :: mongoose_acc:t(). c2s_update_presence(HostType, Acc) -> - ejabberd_hooks:run_for_host_type(c2s_update_presence, HostType, Acc, []). + run_hook_for_host_type(c2s_update_presence, HostType, Acc, []). -spec check_bl_c2s(IP) -> Result when IP :: inet:ip_address(), Result :: boolean(). check_bl_c2s(IP) -> - ejabberd_hooks:run_global(check_bl_c2s, false, [IP]). + run_global_hook(check_bl_c2s, false, [IP]). -spec forbidden_session_hook(HostType, Acc, JID) -> Result when HostType :: mongooseim:host_type(), @@ -591,7 +594,7 @@ check_bl_c2s(IP) -> JID :: jid:jid(), Result :: mongoose_acc:t(). forbidden_session_hook(HostType, Acc, JID) -> - ejabberd_hooks:run_for_host_type(forbidden_session_hook, HostType, Acc, [JID]). + run_hook_for_host_type(forbidden_session_hook, HostType, Acc, [JID]). -spec session_opening_allowed_for_user(HostType, JID) -> Result when HostType :: mongooseim:host_type(), @@ -599,8 +602,7 @@ forbidden_session_hook(HostType, Acc, JID) -> Result :: allow | any(). %% anything else than 'allow' is interpreted %% as not allowed session_opening_allowed_for_user(HostType, JID) -> - ejabberd_hooks:run_for_host_type(session_opening_allowed_for_user, - HostType, allow, [JID]). + run_hook_for_host_type(session_opening_allowed_for_user, HostType, allow, [JID]). %% Privacy related hooks @@ -614,8 +616,8 @@ session_opening_allowed_for_user(HostType, JID) -> privacy_check_packet(Acc, JID, PrivacyList, FromToNameType, Dir) -> HostType = mongoose_acc:host_type(Acc), AccWithRes = mongoose_acc:set(hook, result, allow, Acc), - ejabberd_hooks:run_for_host_type(privacy_check_packet, HostType, AccWithRes, - [JID, PrivacyList, FromToNameType, Dir]). + run_hook_for_host_type(privacy_check_packet, HostType, AccWithRes, + [JID, PrivacyList, FromToNameType, Dir]). -spec privacy_get_user_list(HostType, JID) -> Result when HostType :: binary(), @@ -623,7 +625,7 @@ privacy_check_packet(Acc, JID, PrivacyList, FromToNameType, Dir) -> Result :: mongoose_privacy:userlist(). privacy_get_user_list(HostType, JID) -> %% TODO: ejabberd_c2s calls this hook with host type, fix other places. - ejabberd_hooks:run_for_host_type(privacy_get_user_list, HostType, #userlist{}, [JID]). + run_hook_for_host_type(privacy_get_user_list, HostType, #userlist{}, [JID]). -spec privacy_iq_get(HostType, Acc, From, To, IQ, PrivList) -> Result when HostType :: binary(), @@ -634,8 +636,7 @@ privacy_get_user_list(HostType, JID) -> PrivList :: mongoose_privacy:userlist(), Result :: mongoose_acc:t(). privacy_iq_get(HostType, Acc, From, To, IQ, PrivList) -> - ejabberd_hooks:run_for_host_type(privacy_iq_get, HostType, Acc, - [From, To, IQ, PrivList]). + run_hook_for_host_type(privacy_iq_get, HostType, Acc, [From, To, IQ, PrivList]). -spec privacy_iq_set(HostType, Acc, From, To, IQ) -> Result when HostType :: binary(), @@ -645,8 +646,7 @@ privacy_iq_get(HostType, Acc, From, To, IQ, PrivList) -> IQ :: jlib:iq(), Result :: mongoose_acc:t(). privacy_iq_set(HostType, Acc, From, To, IQ) -> - ejabberd_hooks:run_for_host_type(privacy_iq_set, HostType, Acc, - [From, To, IQ]). + run_hook_for_host_type(privacy_iq_set, HostType, Acc, [From, To, IQ]). -spec privacy_updated_list(HostType, OldList, NewList) -> Result when HostType :: binary(), @@ -654,8 +654,7 @@ privacy_iq_set(HostType, Acc, From, To, IQ) -> NewList :: mongoose_privacy:userlist(), Result :: false | mongoose_privacy:userlist(). privacy_updated_list(HostType, OldList, NewList) -> - ejabberd_hooks:run_for_host_type(privacy_updated_list, HostType, false, - [OldList, NewList]). + run_hook_for_host_type(privacy_updated_list, HostType, false, [OldList, NewList]). %% Session management related hooks @@ -667,8 +666,8 @@ privacy_updated_list(HostType, OldList, NewList) -> Result :: mongoose_acc:t(). offline_groupchat_message_hook(Acc, From, To, Packet) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(offline_groupchat_message_hook, HostType, Acc, - [From, To, Packet]). + run_hook_for_host_type(offline_groupchat_message_hook, HostType, Acc, + [From, To, Packet]). -spec offline_message_hook(Acc, From, To, Packet) -> Result when Acc :: mongoose_acc:t(), @@ -678,18 +677,18 @@ offline_groupchat_message_hook(Acc, From, To, Packet) -> Result :: mongoose_acc:t(). offline_message_hook(Acc, From, To, Packet) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(offline_message_hook, HostType, Acc, - [From, To, Packet]). + run_hook_for_host_type(offline_message_hook, HostType, Acc, [From, To, Packet]). -spec set_presence_hook(Acc, JID, Presence) -> Result when Acc :: mongoose_acc:t(), JID :: jid:jid(), Presence :: any(), Result :: mongoose_acc:t(). -set_presence_hook(Acc, #jid{luser = LUser, lserver = LServer, lresource = LResource}, Presence) -> +set_presence_hook(Acc, JID, Presence) -> + #jid{luser = LUser, lserver = LServer, lresource = LResource} = JID, HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(set_presence_hook, HostType, Acc, - [LUser, LServer, LResource, Presence]). + run_hook_for_host_type(set_presence_hook, HostType, Acc, + [LUser, LServer, LResource, Presence]). -spec sm_broadcast(Acc, From, To, Broadcast, SessionCount) -> Result when Acc :: mongoose_acc:t(), @@ -700,8 +699,8 @@ set_presence_hook(Acc, #jid{luser = LUser, lserver = LServer, lresource = LResou Result :: mongoose_acc:t(). sm_broadcast(Acc, From, To, Broadcast, SessionCount) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(sm_broadcast, HostType, Acc, - [From, To, Broadcast, SessionCount]). + run_hook_for_host_type(sm_broadcast, HostType, Acc, + [From, To, Broadcast, SessionCount]). -spec sm_filter_offline_message(HostType, From, To, Packet) -> Result when HostType :: binary(), @@ -710,8 +709,8 @@ sm_broadcast(Acc, From, To, Broadcast, SessionCount) -> Packet :: exml:element(), Result :: boolean(). sm_filter_offline_message(HostType, From, To, Packet) -> - ejabberd_hooks:run_for_host_type(sm_filter_offline_message, HostType, false, - [From, To, Packet]). + run_hook_for_host_type(sm_filter_offline_message, HostType, false, + [From, To, Packet]). -spec sm_register_connection_hook(HostType, SID, JID, Info) -> Result when HostType :: binary(), @@ -720,8 +719,8 @@ sm_filter_offline_message(HostType, From, To, Packet) -> Info :: ejabberd_sm:info(), Result :: ok. sm_register_connection_hook(HostType, SID, JID, Info) -> - ejabberd_hooks:run_for_host_type(sm_register_connection_hook, HostType, ok, - [HostType, SID, JID, Info]). + run_hook_for_host_type(sm_register_connection_hook, HostType, ok, + [HostType, SID, JID, Info]). -spec sm_remove_connection_hook(Acc, SID, JID, Info, Reason) -> Result when Acc :: mongoose_acc:t(), @@ -732,25 +731,26 @@ sm_register_connection_hook(HostType, SID, JID, Info) -> Result :: mongoose_acc:t(). sm_remove_connection_hook(Acc, SID, JID, Info, Reason) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(sm_remove_connection_hook, HostType, Acc, - [SID, JID, Info, Reason]). + run_hook_for_host_type(sm_remove_connection_hook, HostType, Acc, + [SID, JID, Info, Reason]). -spec unset_presence_hook(Acc, JID, Status) -> Result when Acc :: mongoose_acc:t(), JID:: jid:jid(), Status :: binary(), Result :: mongoose_acc:t(). -unset_presence_hook(Acc, #jid{luser = LUser, lserver = LServer, lresource = LResource}, Status) -> +unset_presence_hook(Acc, JID, Status) -> + #jid{luser = LUser, lserver = LServer, lresource = LResource} = JID, HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(unset_presence_hook, HostType, Acc, - [LUser, LServer, LResource, Status]). + run_hook_for_host_type(unset_presence_hook, HostType, Acc, + [LUser, LServer, LResource, Status]). -spec xmpp_bounce_message(Acc) -> Result when Acc :: mongoose_acc:t(), Result :: mongoose_acc:t(). xmpp_bounce_message(Acc) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(xmpp_bounce_message, HostType, Acc, []). + run_hook_for_host_type(xmpp_bounce_message, HostType, Acc, []). %% Roster related hooks @@ -761,14 +761,14 @@ xmpp_bounce_message(Acc) -> Result :: mongoose_acc:t(). roster_get(Acc, JID) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(roster_get, HostType, Acc, [JID]). + run_hook_for_host_type(roster_get, HostType, Acc, [JID]). %%% @doc The `roster_groups' hook is called to extract roster groups. -spec roster_groups(LServer) -> Result when LServer :: jid:lserver(), Result :: list(). roster_groups(LServer) -> - ejabberd_hooks:run_for_host_type(roster_groups, LServer, [], [LServer]). + run_hook_for_host_type(roster_groups, LServer, [], [LServer]). %%% @doc The `roster_get_jid_info' hook is called to determine the %%% subscription state between a given pair of users. @@ -784,8 +784,8 @@ roster_groups(LServer) -> RemoteJID :: jid:jid() | jid:simple_jid(), Result :: {mod_roster:subscription_state(), [binary()]}. roster_get_jid_info(HostType, ToJID, RemBareJID) -> - ejabberd_hooks:run_for_host_type(roster_get_jid_info, HostType, {none, []}, - [HostType, ToJID, RemBareJID]). + run_hook_for_host_type(roster_get_jid_info, HostType, {none, []}, + [HostType, ToJID, RemBareJID]). %%% @doc The `roster_get_subscription_lists' hook is called to extract %%% user's subscription list. @@ -795,8 +795,8 @@ roster_get_jid_info(HostType, ToJID, RemBareJID) -> JID :: jid:jid(), Result :: mongoose_acc:t(). roster_get_subscription_lists(HostType, Acc, JID) -> - ejabberd_hooks:run_for_host_type(roster_get_subscription_lists, HostType, Acc, - [jid:to_bare(JID)]). + run_hook_for_host_type(roster_get_subscription_lists, HostType, Acc, + [jid:to_bare(JID)]). %%% @doc The `roster_get_versioning_feature' hook is %%% called to determine if roster versioning is enabled. @@ -804,7 +804,7 @@ roster_get_subscription_lists(HostType, Acc, JID) -> HostType :: binary(), Result :: [exml:element()]. roster_get_versioning_feature(HostType) -> - ejabberd_hooks:run_for_host_type(roster_get_versioning_feature, HostType, [], [HostType]). + run_hook_for_host_type(roster_get_versioning_feature, HostType, [], [HostType]). %%% @doc The `roster_in_subscription' hook is called to determine %%% if a subscription presence is routed to a user. @@ -817,8 +817,8 @@ roster_get_versioning_feature(HostType) -> Result :: mongoose_acc:t(). roster_in_subscription(Acc, To, From, Type, Reason) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(roster_in_subscription, HostType, Acc, - [jid:to_bare(To), From, Type, Reason]). + run_hook_for_host_type(roster_in_subscription, HostType, Acc, + [jid:to_bare(To), From, Type, Reason]). %%% @doc The `roster_out_subscription' hook is called %%% when a user sends out subscription. @@ -830,8 +830,8 @@ roster_in_subscription(Acc, To, From, Type, Reason) -> Result :: mongoose_acc:t(). roster_out_subscription(Acc, From, To, Type) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(roster_out_subscription, HostType, Acc, - [jid:to_bare(From), To, Type]). + run_hook_for_host_type(roster_out_subscription, HostType, Acc, + [jid:to_bare(From), To, Type]). %%% @doc The `roster_process_item' hook is called when a user's roster is set. -spec roster_process_item(HostType, LServer, Item) -> Result when @@ -840,7 +840,7 @@ roster_out_subscription(Acc, From, To, Type) -> Item :: mod_roster:roster(), Result :: mod_roster:roster(). roster_process_item(HostType, LServer, Item) -> - ejabberd_hooks:run_for_host_type(roster_process_item, HostType, Item, [LServer]). + run_hook_for_host_type(roster_process_item, HostType, Item, [LServer]). %%% @doc The `roster_push' hook is called when a roster item is %%% being pushed and roster versioning is not enabled. @@ -850,7 +850,7 @@ roster_process_item(HostType, LServer, Item) -> Item :: mod_roster:roster(), Result :: any(). roster_push(LServer, From, Item) -> - ejabberd_hooks:run_for_host_type(roster_push, LServer, ok, [From, Item]). + run_hook_for_host_type(roster_push, LServer, ok, [From, Item]). %%% @doc The `roster_set' hook is called when a user's roster is set through an IQ. -spec roster_set(HostType, From, To, SubEl) -> Result when @@ -860,7 +860,7 @@ roster_push(LServer, From, Item) -> SubEl :: exml:element(), Result :: any(). roster_set(HostType, From, To, SubEl) -> - ejabberd_hooks:run_for_host_type(roster_set, HostType, ok, [From, To, SubEl]). + run_hook_for_host_type(roster_set, HostType, ok, [From, To, SubEl]). %% MUC related hooks @@ -877,8 +877,7 @@ roster_set(HostType, From, To, SubEl) -> User :: jid:jid(), Result :: boolean(). is_muc_room_owner(HostType, Room, User) -> - ejabberd_hooks:run_for_host_type(is_muc_room_owner, HostType, false, - [HostType, Room, User]). + run_hook_for_host_type(is_muc_room_owner, HostType, false, [HostType, Room, User]). %%% @doc The `can_access_identity' hook is called to determine if %%% a given user can see the real identity of the people in a room. @@ -888,8 +887,7 @@ is_muc_room_owner(HostType, Room, User) -> User :: jid:jid(), Result :: boolean(). can_access_identity(HostType, Room, User) -> - ejabberd_hooks:run_for_host_type(can_access_identity, HostType, false, - [HostType, Room, User]). + run_hook_for_host_type(can_access_identity, HostType, false, [HostType, Room, User]). %%% @doc The `can_access_room' hook is called to determine %%% if a given user can access a room. @@ -899,8 +897,7 @@ can_access_identity(HostType, Room, User) -> User :: jid:jid(), Result :: boolean(). can_access_room(HostType, Room, User) -> - ejabberd_hooks:run_for_host_type(can_access_room, HostType, false, - [HostType, Room, User]). + run_hook_for_host_type(can_access_room, HostType, false, [HostType, Room, User]). %% MAM related hooks @@ -914,8 +911,7 @@ can_access_room(HostType, Room, User) -> OwnerJID :: jid:jid(), Result :: undefined | mod_mam:archive_id(). mam_archive_id(HostType, OwnerJID) -> - ejabberd_hooks:run_for_host_type(mam_archive_id, HostType, undefined, - [HostType, OwnerJID]). + run_hook_for_host_type(mam_archive_id, HostType, undefined, [HostType, OwnerJID]). %%% @doc The `mam_archive_size' hook is called to determine the size %%% of the archive for a given JID @@ -925,8 +921,8 @@ mam_archive_id(HostType, OwnerJID) -> OwnerJID :: jid:jid(), Result :: integer(). mam_archive_size(HostType, ArchiveID, OwnerJID) -> - ejabberd_hooks:run_for_host_type(mam_archive_size, HostType, 0, - [HostType, ArchiveID, OwnerJID]). + run_hook_for_host_type(mam_archive_size, HostType, 0, + [HostType, ArchiveID, OwnerJID]). %%% @doc The `mam_get_behaviour' hooks is called to determine if a message %%% should be archived or not based on a given pair of JIDs. @@ -938,8 +934,8 @@ mam_archive_size(HostType, ArchiveID, OwnerJID) -> RemoteJID :: jid:jid(), Result :: mod_mam:archive_behaviour(). mam_get_behaviour(HookServer, ArchiveID, OwnerJID, RemoteJID) -> - ejabberd_hooks:run_for_host_type(mam_get_behaviour, HookServer, always, - [HookServer, ArchiveID, OwnerJID, RemoteJID]). + run_hook_for_host_type(mam_get_behaviour, HookServer, always, + [HookServer, ArchiveID, OwnerJID, RemoteJID]). %%% @doc The `mam_set_prefs' hook is called to set a user's archive preferences. %%% @@ -954,9 +950,9 @@ mam_get_behaviour(HookServer, ArchiveID, OwnerJID, RemoteJID) -> NeverJIDs :: [jid:literel_jid()], Result :: any(). mam_set_prefs(HookServer, ArchiveID, OwnerJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> - ejabberd_hooks:run_for_host_type(mam_set_prefs, HookServer, {error, not_implemented}, - [HookServer, ArchiveID, OwnerJID, - DefaultMode, AlwaysJIDs, NeverJIDs]). + run_hook_for_host_type(mam_set_prefs, HookServer, {error, not_implemented}, + [HookServer, ArchiveID, OwnerJID, + DefaultMode, AlwaysJIDs, NeverJIDs]). %%% @doc The `mam_get_prefs' hook is called to read %%% the archive settings for a given user. @@ -968,8 +964,8 @@ mam_set_prefs(HookServer, ArchiveID, OwnerJID, DefaultMode, AlwaysJIDs, NeverJI Result :: mod_mam:preference() | {error, Reason :: term()}. mam_get_prefs(HookServer, DefaultMode, ArchiveID, OwnerJID) -> InitialAccValue = {DefaultMode, [], []}, %% mod_mam:preference() type - ejabberd_hooks:run_for_host_type(mam_get_prefs, HookServer, InitialAccValue, - [HookServer, ArchiveID, OwnerJID]). + run_hook_for_host_type(mam_get_prefs, HookServer, InitialAccValue, + [HookServer, ArchiveID, OwnerJID]). %%% @doc The `mam_remove_archive' hook is called in order to %%% remove the entire archive for a particular user. @@ -978,8 +974,8 @@ mam_get_prefs(HookServer, DefaultMode, ArchiveID, OwnerJID) -> ArchiveID :: undefined | mod_mam:archive_id(), OwnerJID :: jid:jid(). mam_remove_archive(HookServer, ArchiveID, OwnerJID) -> - ejabberd_hooks:run_for_host_type(mam_remove_archive, HookServer, ok, - [HookServer, ArchiveID, OwnerJID]). + run_hook_for_host_type(mam_remove_archive, HookServer, ok, + [HookServer, ArchiveID, OwnerJID]). %%% @doc The `mam_lookup_messages' hook is to retrieve %%% archived messages for given search parameters. @@ -989,9 +985,8 @@ mam_remove_archive(HookServer, ArchiveID, OwnerJID) -> Result :: {ok, mod_mam:lookup_result()}. mam_lookup_messages(HookServer, Params) -> InitialLookupValue = {0, 0, []}, %% mod_mam:lookup_result() type - ejabberd_hooks:run_for_host_type(mam_lookup_messages, HookServer, - {ok, InitialLookupValue}, - [HookServer, Params]). + run_hook_for_host_type(mam_lookup_messages, HookServer, {ok, InitialLookupValue}, + [HookServer, Params]). %%% @doc The `mam_archive_message' hook is called in order %%% to store the message in the archive. @@ -1001,8 +996,7 @@ mam_lookup_messages(HookServer, Params) -> Params :: mod_mam:archive_message_params(), Result :: ok | {error, timeout}. mam_archive_message(HookServer, Params) -> - ejabberd_hooks:run_for_host_type(mam_archive_message, HookServer, ok, - [HookServer, Params]). + run_hook_for_host_type(mam_archive_message, HookServer, ok, [HookServer, Params]). %% MAM MUC related hooks @@ -1023,8 +1017,8 @@ mam_archive_message(HookServer, Params) -> OwnerJID :: jid:jid(), Result :: undefined | mod_mam:archive_id(). mam_muc_archive_id(HookServer, OwnerJID) -> - ejabberd_hooks:run_for_host_type(mam_muc_archive_id, HookServer, undefined, - [HookServer, OwnerJID]). + run_hook_for_host_type(mam_muc_archive_id, HookServer, undefined, + [HookServer, OwnerJID]). %%% @doc The `mam_muc_archive_size' hook is called to determine %%% the archive size for a given room. @@ -1034,8 +1028,8 @@ mam_muc_archive_id(HookServer, OwnerJID) -> RoomJID :: jid:jid(), Result :: integer(). mam_muc_archive_size(HostType, ArchiveID, RoomJID) -> - ejabberd_hooks:run_for_host_type(mam_muc_archive_size, HostType, 0, - [HostType, ArchiveID, RoomJID]). + run_hook_for_host_type(mam_muc_archive_size, HostType, 0, + [HostType, ArchiveID, RoomJID]). %%% @doc The `mam_muc_get_behaviour' hooks is called to determine if a message should %%% be archived or not based on the given room and user JIDs. @@ -1048,8 +1042,8 @@ mam_muc_archive_size(HostType, ArchiveID, RoomJID) -> Result :: mod_mam:archive_behaviour(). mam_muc_get_behaviour(HostType, ArchiveID, RoomJID, RemoteJID) -> DefaultBehaviour = always, %% mod_mam:archive_behaviour() type - ejabberd_hooks:run_for_host_type(mam_muc_get_behaviour, HostType, DefaultBehaviour, - [HostType, ArchiveID, RoomJID, RemoteJID]). + run_hook_for_host_type(mam_muc_get_behaviour, HostType, DefaultBehaviour, + [HostType, ArchiveID, RoomJID, RemoteJID]). %%% @doc The `mam_muc_set_prefs' hook is called to set a room's archive preferences. %%% @@ -1065,9 +1059,9 @@ mam_muc_get_behaviour(HostType, ArchiveID, RoomJID, RemoteJID) -> Result :: any(). mam_muc_set_prefs(HostType, ArchiveID, RoomJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> InitialAcc = {error, not_implemented}, - ejabberd_hooks:run_for_host_type(mam_muc_set_prefs, HostType, InitialAcc, - [HostType, ArchiveID, RoomJID, DefaultMode, - AlwaysJIDs, NeverJIDs]). + run_hook_for_host_type(mam_muc_set_prefs, HostType, InitialAcc, + [HostType, ArchiveID, RoomJID, DefaultMode, + AlwaysJIDs, NeverJIDs]). %%% @doc The `mam_muc_get_prefs' hook is called to read %%% the archive settings for a given room. @@ -1079,8 +1073,8 @@ mam_muc_set_prefs(HostType, ArchiveID, RoomJID, DefaultMode, AlwaysJIDs, NeverJI Result :: mod_mam:preference() | {error, Reason :: term()}. mam_muc_get_prefs(HostType, DefaultMode, ArchiveID, RoomJID) -> InitialAcc = {DefaultMode, [], []}, %% mod_mam:preference() type - ejabberd_hooks:run_for_host_type(mam_muc_get_prefs, HostType, InitialAcc, - [HostType, ArchiveID, RoomJID]). + run_hook_for_host_type(mam_muc_get_prefs, HostType, InitialAcc, + [HostType, ArchiveID, RoomJID]). %%% @doc The `mam_muc_remove_archive' hook is called in order to remove the entire %%% archive for a particular user. @@ -1089,8 +1083,8 @@ mam_muc_get_prefs(HostType, DefaultMode, ArchiveID, RoomJID) -> ArchiveID :: undefined | mod_mam:archive_id(), RoomJID :: jid:jid(). mam_muc_remove_archive(HostType, ArchiveID, RoomJID) -> - ejabberd_hooks:run_for_host_type(mam_muc_remove_archive, HostType, ok, - [HostType, ArchiveID, RoomJID]). + run_hook_for_host_type(mam_muc_remove_archive, HostType, ok, + [HostType, ArchiveID, RoomJID]). %%% @doc The `mam_muc_lookup_messages' hook is to retrieve archived %%% MUC messages for any given search parameters. @@ -1100,9 +1094,8 @@ mam_muc_remove_archive(HostType, ArchiveID, RoomJID) -> Result :: {ok, mod_mam:lookup_result()}. mam_muc_lookup_messages(HostType, Params) -> InitialLookupValue = {0, 0, []}, %% mod_mam:lookup_result() type - ejabberd_hooks:run_for_host_type(mam_muc_lookup_messages, HostType, - {ok, InitialLookupValue}, - [HostType, Params]). + run_hook_for_host_type(mam_muc_lookup_messages, HostType, {ok, InitialLookupValue}, + [HostType, Params]). %%% @doc The `mam_muc_archive_message' hook is called in order %%% to store the MUC message in the archive. @@ -1111,16 +1104,15 @@ mam_muc_lookup_messages(HostType, Params) -> Params :: mod_mam:archive_message_params(), Result :: ok | {error, timeout}. mam_muc_archive_message(HostType, Params) -> - ejabberd_hooks:run_for_host_type(mam_muc_archive_message, HostType, ok, - [HostType, Params]). + run_hook_for_host_type(mam_muc_archive_message, HostType, ok, [HostType, Params]). %%% @doc The `mam_muc_flush_messages' hook is run after the async bulk write %%% happens for MUC messages despite the result of the write. -spec mam_muc_flush_messages(HookServer :: jid:lserver(), MessageCount :: integer()) -> ok. mam_muc_flush_messages(HookServer, MessageCount) -> - ejabberd_hooks:run_for_host_type(mam_muc_flush_messages, HookServer, ok, - [HookServer, MessageCount]). + run_hook_for_host_type(mam_muc_flush_messages, HookServer, ok, + [HookServer, MessageCount]). %% GDPR related hooks @@ -1131,8 +1123,7 @@ mam_muc_flush_messages(HookServer, MessageCount) -> JID :: jid:jid(), Result :: ejabberd_gen_mam_archive:mam_pm_gdpr_data(). get_mam_pm_gdpr_data(HostType, JID) -> - ejabberd_hooks:run_for_host_type(get_mam_pm_gdpr_data, HostType, [], - [HostType, JID]). + run_hook_for_host_type(get_mam_pm_gdpr_data, HostType, [], [HostType, JID]). %%% @doc `get_mam_muc_gdpr_data' hook is called to provide %%% a user's archive for GDPR purposes. @@ -1141,8 +1132,7 @@ get_mam_pm_gdpr_data(HostType, JID) -> JID :: jid:jid(), Result :: ejabberd_gen_mam_archive:mam_muc_gdpr_data(). get_mam_muc_gdpr_data(HostType, JID) -> - ejabberd_hooks:run_for_host_type(get_mam_muc_gdpr_data, HostType, [], - [HostType, JID]). + run_hook_for_host_type(get_mam_muc_gdpr_data, HostType, [], [HostType, JID]). %%% @doc `get_personal_data' hook is called to retrieve %%% a user's personal data for GDPR purposes. @@ -1151,7 +1141,7 @@ get_mam_muc_gdpr_data(HostType, JID) -> JID :: jid:jid(), Result :: gdpr:personal_data(). get_personal_data(HostType, JID) -> - ejabberd_hooks:run_for_host_type(get_personal_data, HostType, [], [HostType, JID]). + run_hook_for_host_type(get_personal_data, HostType, [], [HostType, JID]). %% S2S related hooks @@ -1162,7 +1152,7 @@ get_personal_data(HostType, JID) -> Server :: jid:server(), Result :: any(). find_s2s_bridge(Name, Server) -> - ejabberd_hooks:run_global(find_s2s_bridge, undefined, [Name, Server]). + run_global_hook(find_s2s_bridge, undefined, [Name, Server]). %%% @doc `s2s_allow_host' hook is called to check whether a server %%% should be allowed to be connected to. @@ -1174,7 +1164,7 @@ find_s2s_bridge(Name, Server) -> S2SHost :: jid:server(), Result :: allow | deny. s2s_allow_host(MyHost, S2SHost) -> - ejabberd_hooks:run_global(s2s_allow_host, allow, [MyHost, S2SHost]). + run_global_hook(s2s_allow_host, allow, [MyHost, S2SHost]). %%% @doc `s2s_connect_hook' hook is called when a s2s connection is established. -spec s2s_connect_hook(Name, Server) -> Result when @@ -1182,7 +1172,7 @@ s2s_allow_host(MyHost, S2SHost) -> Server :: jid:server(), Result :: any(). s2s_connect_hook(Name, Server) -> - ejabberd_hooks:run_global(s2s_connect_hook, ok, [Name, Server]). + run_global_hook(s2s_connect_hook, ok, [Name, Server]). %%% @doc `s2s_send_packet' hook is called when a message is routed. -spec s2s_send_packet(Acc, From, To, Packet) -> Result when @@ -1192,7 +1182,7 @@ s2s_connect_hook(Name, Server) -> Packet :: exml:element(), Result :: mongoose_acc:t(). s2s_send_packet(Acc, From, To, Packet) -> - ejabberd_hooks:run_global(s2s_send_packet, Acc, [From, To, Packet]). + run_global_hook(s2s_send_packet, Acc, [From, To, Packet]). %%% @doc `s2s_stream_features' hook is used to extract %%% the stream management features supported by the server. @@ -1201,7 +1191,7 @@ s2s_send_packet(Acc, From, To, Packet) -> LServer :: jid:lserver(), Result :: [exml:element()]. s2s_stream_features(HostType, LServer) -> - ejabberd_hooks:run_for_host_type(s2s_stream_features, HostType, [], [HostType, LServer]). + run_hook_for_host_type(s2s_stream_features, HostType, [], [HostType, LServer]). %%% @doc `s2s_receive_packet' hook is called when %%% an incoming stanza is routed by the server. @@ -1209,54 +1199,55 @@ s2s_stream_features(HostType, LServer) -> Acc :: mongoose_acc:t(), Result :: mongoose_acc:t(). s2s_receive_packet(Acc) -> - ejabberd_hooks:run_global(s2s_receive_packet, Acc, []). + run_global_hook(s2s_receive_packet, Acc, []). %% Discovery related hooks %%% @doc `disco_local_identity' hook is called to get the identity of the server. --spec disco_local_identity(mongoose_disco:identity_acc()) -> mongoose_disco:identity_acc(). +-spec disco_local_identity(mongoose_disco:identity_acc()) -> + mongoose_disco:identity_acc(). disco_local_identity(Acc = #{host_type := HostType}) -> - ejabberd_hooks:run_for_host_type(disco_local_identity, HostType, Acc, []). + run_hook_for_host_type(disco_local_identity, HostType, Acc, []). %%% @doc `disco_sm_identity' hook is called to get the identity of the %%% client when a discovery IQ gets to session management. -spec disco_sm_identity(mongoose_disco:identity_acc()) -> mongoose_disco:identity_acc(). disco_sm_identity(Acc = #{host_type := HostType}) -> - ejabberd_hooks:run_for_host_type(disco_sm_identity, HostType, Acc, []). + run_hook_for_host_type(disco_sm_identity, HostType, Acc, []). %%% @doc `disco_local_items' hook is called to extract items associated with the server. -spec disco_local_items(mongoose_disco:item_acc()) -> mongoose_disco:item_acc(). disco_local_items(Acc = #{host_type := HostType}) -> - ejabberd_hooks:run_for_host_type(disco_local_items, HostType, Acc, []). + run_hook_for_host_type(disco_local_items, HostType, Acc, []). %%% @doc `disco_sm_items' hook is called to get the items associated %%% with the client when a discovery IQ gets to session management. -spec disco_sm_items(mongoose_disco:item_acc()) -> mongoose_disco:item_acc(). disco_sm_items(Acc = #{host_type := HostType}) -> - ejabberd_hooks:run_for_host_type(disco_sm_items, HostType, Acc, []). + run_hook_for_host_type(disco_sm_items, HostType, Acc, []). %%% @doc `disco_local_features' hook is called to extract features %%% offered by the server. -spec disco_local_features(mongoose_disco:feature_acc()) -> mongoose_disco:feature_acc(). disco_local_features(Acc = #{host_type := HostType}) -> - ejabberd_hooks:run_for_host_type(disco_local_features, HostType, Acc, []). + run_hook_for_host_type(disco_local_features, HostType, Acc, []). %%% @doc `disco_sm_features' hook is called to get the features of the client %%% when a discovery IQ gets to session management. -spec disco_sm_features(mongoose_disco:feature_acc()) -> mongoose_disco:feature_acc(). disco_sm_features(Acc = #{host_type := HostType}) -> - ejabberd_hooks:run_for_host_type(disco_sm_features, HostType, Acc, []). + run_hook_for_host_type(disco_sm_features, HostType, Acc, []). %%% @doc `disco_muc_features' hook is called to get the features %%% supported by the MUC (Light) service. -spec disco_muc_features(mongoose_disco:feature_acc()) -> mongoose_disco:feature_acc(). disco_muc_features(Acc = #{host_type := HostType}) -> - ejabberd_hooks:run_for_host_type(disco_muc_features, HostType, Acc, []). + run_hook_for_host_type(disco_muc_features, HostType, Acc, []). %%% @doc `disco_info' hook is called to extract information about the server. -spec disco_info(mongoose_disco:info_acc()) -> mongoose_disco:info_acc(). disco_info(Acc = #{host_type := HostType}) -> - ejabberd_hooks:run_for_host_type(disco_info, HostType, Acc, []). + run_hook_for_host_type(disco_info, HostType, Acc, []). %% AMP related hooks @@ -1269,8 +1260,7 @@ disco_info(Acc = #{host_type := HostType}) -> Result :: mod_amp:amp_match_result(). amp_check_condition(Server, Strategy, Rule) -> InitialAcc = no_match, %% mod_amp:amp_match_result() type - ejabberd_hooks:run_for_host_type(amp_check_condition, Server, InitialAcc, - [Strategy, Rule]). + run_hook_for_host_type(amp_check_condition, Server, InitialAcc, [Strategy, Rule]). %%% @doc The `amp_determine_strategy' hook is called when checking to determine %%% which strategy will be chosen when executing AMP rules. @@ -1283,8 +1273,8 @@ amp_check_condition(Server, Strategy, Rule) -> Result :: mod_amp:amp_strategy(). amp_determine_strategy(Server, From, To, Packet, Event) -> DefaultStrategy = amp_strategy:null_strategy(), - ejabberd_hooks:run_for_host_type(amp_determine_strategy, Server, DefaultStrategy, - [From, To, Packet, Event]). + run_hook_for_host_type(amp_determine_strategy, Server, DefaultStrategy, + [From, To, Packet, Event]). %%% @doc The `amp_error_action_triggered' hook is called to inform %%% that the `error' action has been triggered. @@ -1292,7 +1282,7 @@ amp_determine_strategy(Server, From, To, Packet, Event) -> Server :: jid:server(), Result :: any(). amp_error_action_triggered(Server) -> - ejabberd_hooks:run_for_host_type(amp_error_action_triggered, Server, ok, [Server]). + run_hook_for_host_type(amp_error_action_triggered, Server, ok, [Server]). %%% @doc The `amp_notify_action_triggered' hook is called to inform %%% that the `notify' action has been triggered. @@ -1300,8 +1290,7 @@ amp_error_action_triggered(Server) -> Server :: jid:server(), Result :: any(). amp_notify_action_triggered(Server) -> - ejabberd_hooks:run_for_host_type(amp_notify_action_triggered, Server, ok, - [Server]). + run_hook_for_host_type(amp_notify_action_triggered, Server, ok, [Server]). %%% @doc The `amp_verify_support' hook is called when checking %%% whether the host supports given AMP rules. @@ -1310,7 +1299,7 @@ amp_notify_action_triggered(Server) -> Rules :: mod_amp:amp_rules(), Result :: [mod_amp:amp_rule_support()]. amp_verify_support(Server, Rules) -> - ejabberd_hooks:run_for_host_type(amp_verify_support, Server, [], [Rules]). + run_hook_for_host_type(amp_verify_support, Server, [], [Rules]). %% MUC and MUC Light related hooks @@ -1320,7 +1309,7 @@ amp_verify_support(Server, Rules) -> EventData :: mod_muc:room_event_data(), Result :: exml:element(). filter_room_packet(HostType, Packet, EventData) -> - ejabberd_hooks:run_for_host_type(filter_room_packet, HostType, Packet, [HostType, EventData]). + run_hook_for_host_type(filter_room_packet, HostType, Packet, [HostType, EventData]). %%% @doc The `forget_room' hook is called when a room is removed from the database. -spec forget_room(HostType, MucHost, Room) -> Result when @@ -1329,7 +1318,7 @@ filter_room_packet(HostType, Packet, EventData) -> Room :: jid:luser(), Result :: any(). forget_room(HostType, MucHost, Room) -> - ejabberd_hooks:run_for_host_type(forget_room, HostType, ok, [HostType, MucHost, Room]). + run_hook_for_host_type(forget_room, HostType, ok, [HostType, MucHost, Room]). -spec invitation_sent(HookServer, Host, RoomJID, From, To, Reason) -> Result when HookServer :: jid:server(), @@ -1340,8 +1329,8 @@ forget_room(HostType, MucHost, Room) -> Reason :: binary(), Result :: any(). invitation_sent(HookServer, Host, RoomJID, From, To, Reason) -> - ejabberd_hooks:run_for_host_type(invitation_sent, HookServer, ok, - [HookServer, Host, RoomJID, From, To, Reason]). + run_hook_for_host_type(invitation_sent, HookServer, ok, + [HookServer, Host, RoomJID, From, To, Reason]). %%% @doc The `join_room' hook is called when a user joins a MUC room. -spec join_room(HookServer, Room, Host, JID, MucJID) -> Result when @@ -1352,8 +1341,8 @@ invitation_sent(HookServer, Host, RoomJID, From, To, Reason) -> MucJID :: jid:jid(), Result :: any(). join_room(HookServer, Room, Host, JID, MucJID) -> - ejabberd_hooks:run_for_host_type(join_room, HookServer, ok, - [HookServer, Room, Host, JID, MucJID]). + run_hook_for_host_type(join_room, HookServer, ok, + [HookServer, Room, Host, JID, MucJID]). %%% @doc The `leave_room' hook is called when a user joins a MUC room. -spec leave_room(HookServer, Room, Host, JID, MucJID) -> Result when @@ -1364,8 +1353,8 @@ join_room(HookServer, Room, Host, JID, MucJID) -> MucJID :: jid:jid(), Result :: any(). leave_room(HookServer, Room, Host, JID, MucJID) -> - ejabberd_hooks:run_for_host_type(leave_room, HookServer, ok, - [HookServer, Room, Host, JID, MucJID]). + run_hook_for_host_type(leave_room, HookServer, ok, + [HookServer, Room, Host, JID, MucJID]). %%% @doc The `room_packet' hook is called when a message is added to room's history. -spec room_packet(Server, FromNick, FromJID, JID, Packet) -> Result when @@ -1376,15 +1365,14 @@ leave_room(HookServer, Room, Host, JID, MucJID) -> Packet :: exml:element(), Result :: any(). room_packet(Server, FromNick, FromJID, JID, Packet) -> - ejabberd_hooks:run_for_host_type(room_packet, Server, ok, - [FromNick, FromJID, JID, Packet]). + run_hook_for_host_type(room_packet, Server, ok, [FromNick, FromJID, JID, Packet]). -spec update_inbox_for_muc(HostType, Info) -> Result when HostType :: mongooseim:host_type(), Info :: mod_muc_room:update_inbox_for_muc_payload(), Result :: mod_muc_room:update_inbox_for_muc_payload(). update_inbox_for_muc(HostType, Info) -> - ejabberd_hooks:run_for_host_type(update_inbox_for_muc, HostType, Info, []). + run_hook_for_host_type(update_inbox_for_muc, HostType, Info, []). %% Caps related hooks @@ -1396,8 +1384,7 @@ update_inbox_for_muc(HostType, Info) -> Result :: mongoose_acc:t(). caps_recognised(Acc, From, Pid, Features) -> HostType = mongoose_acc:host_type(Acc), - ejabberd_hooks:run_for_host_type(caps_recognised, HostType, Acc, - [From, Pid, Features]). + run_hook_for_host_type(caps_recognised, HostType, Acc, [From, Pid, Features]). %% PubSub related hooks @@ -1411,8 +1398,8 @@ caps_recognised(Acc, From, Pid, Features) -> NodeOptions :: list(), Result :: any(). pubsub_create_node(Server, PubSubHost, NodeId, Nidx, NodeOptions) -> - ejabberd_hooks:run_for_host_type(pubsub_create_node, Server, ok, - [Server, PubSubHost, NodeId, Nidx, NodeOptions]). + run_hook_for_host_type(pubsub_create_node, Server, ok, + [Server, PubSubHost, NodeId, Nidx, NodeOptions]). %%% @doc The `pubsub_delete_node' hook is called to inform %%% that a pubsub node is deleted. @@ -1423,8 +1410,8 @@ pubsub_create_node(Server, PubSubHost, NodeId, Nidx, NodeOptions) -> Nidx :: mod_pubsub:nodeIdx(), Result :: any(). pubsub_delete_node(Server, PubSubHost, NodeId, Nidx) -> - ejabberd_hooks:run_for_host_type(pubsub_delete_node, Server, ok, - [Server, PubSubHost, NodeId, Nidx]). + run_hook_for_host_type(pubsub_delete_node, Server, ok, + [Server, PubSubHost, NodeId, Nidx]). %%% @doc The `pubsub_publish_item' hook is called to inform %%% that a pubsub item is published. @@ -1438,9 +1425,9 @@ pubsub_delete_node(Server, PubSubHost, NodeId, Nidx) -> BrPayload :: mod_pubsub:payload(), Result :: any(). pubsub_publish_item(Server, NodeId, Publisher, ServiceJID, ItemId, BrPayload) -> - ejabberd_hooks:run_for_host_type(pubsub_publish_item, Server, ok, - [Server, NodeId, Publisher, ServiceJID, - ItemId, BrPayload]). + run_hook_for_host_type(pubsub_publish_item, Server, ok, + [Server, NodeId, Publisher, ServiceJID, + ItemId, BrPayload]). %% Global distribution related hooks @@ -1453,8 +1440,8 @@ pubsub_publish_item(Server, NodeId, Publisher, ServiceJID, ItemId, BrPayload) -> LocalHost :: jid:server(), Result :: any(). mod_global_distrib_known_recipient(GlobalHost, From, To, LocalHost) -> - ejabberd_hooks:run_for_host_type(mod_global_distrib_known_recipient, - GlobalHost, ok, [From, To, LocalHost]). + run_hook_for_host_type(mod_global_distrib_known_recipient, GlobalHost, ok, + [From, To, LocalHost]). %%% @doc The `mod_global_distrib_unknown_recipient' hook is called when %%% the recipient is unknown to `global_distrib'. @@ -1463,5 +1450,31 @@ mod_global_distrib_known_recipient(GlobalHost, From, To, LocalHost) -> Info :: filter_packet_acc(), Result :: any(). mod_global_distrib_unknown_recipient(GlobalHost, Info) -> - ejabberd_hooks:run_for_host_type(mod_global_distrib_unknown_recipient, - GlobalHost, Info, []). + run_hook_for_host_type(mod_global_distrib_unknown_recipient, GlobalHost, Info, []). + + +%%%---------------------------------------------------------------------- +%%% Internal functions +%%%---------------------------------------------------------------------- +run_global_hook(HookName, Acc, Params) when is_map(Params) -> + run_fold(HookName, global, Acc, Params); +run_global_hook(HookName, Acc, Args) when is_list(Args) -> + ParamsWithLegacyArgs = ejabberd_hooks:add_args(#{}, Args), + run_fold(HookName, global, Acc, ParamsWithLegacyArgs). + +run_hook_for_host_type(HookName, undefined, Acc, Args) -> + ?LOG_ERROR(#{what => undefined_host_type, + text => <<"Running hook for an undefined host type">>, + hook_name => HookName, hook_acc => Acc, hook_args => Args}), + Acc; +run_hook_for_host_type(HookName, HostType, Acc, Params) when is_binary(HostType), + is_map(Params) -> + run_fold(HookName, HostType, Acc, Params); +run_hook_for_host_type(HookName, HostType, Acc, Args) when is_binary(HostType), + is_list(Args) -> + ParamsWithLegacyArgs = ejabberd_hooks:add_args(#{}, Args), + run_fold(HookName, HostType, Acc, ParamsWithLegacyArgs). + +run_fold(HookName, HostType, Acc, Params) when is_map(Params) -> + {_, RetValue} = gen_hook:run_fold(HookName, HostType, Acc, Params), + RetValue. diff --git a/test/auth_tokens_SUITE.erl b/test/auth_tokens_SUITE.erl index efed59b9ca..325c4a45ed 100644 --- a/test/auth_tokens_SUITE.erl +++ b/test/auth_tokens_SUITE.erl @@ -52,7 +52,7 @@ init_per_testcase(Test, Config) Test =:= validation_property; Test =:= choose_key_by_token_type -> mock_mongoose_metrics(), - Config1 = async_helper:start(Config, [{ejabberd_hooks, start_link, []}, + Config1 = async_helper:start(Config, [{gen_hook, start_link, []}, {gen_mod, start, []}]), mock_keystore(), mock_rdbms_backend(), @@ -64,13 +64,13 @@ init_per_testcase(validity_period_test, Config) -> mock_gen_iq_handler(), mock_ejabberd_commands(), async_helper:start(Config, [{gen_mod, start, []}, - {ejabberd_hooks, start_link, []}]); + {gen_hook, start_link, []}]); init_per_testcase(revoked_token_is_not_valid, Config) -> mock_mongoose_metrics(), mock_tested_backend(), Config1 = async_helper:start(Config, [{gen_mod, start, []}, - {ejabberd_hooks, start_link, []}]), + {gen_hook, start_link, []}]), mock_keystore(), Config1; diff --git a/test/commands_SUITE.erl b/test/commands_SUITE.erl index e845238604..6868d5b1b8 100644 --- a/test/commands_SUITE.erl +++ b/test/commands_SUITE.erl @@ -583,7 +583,7 @@ ec_holder() -> mc_holder() -> % we have to do it here to avoid race condition and random failures - {ok, Pid} = ejabberd_hooks:start_link(), + {ok, Pid} = gen_hook:start_link(), mongoose_commands:init(), mongoose_commands:register(commands_new()), receive diff --git a/test/commands_backend_SUITE.erl b/test/commands_backend_SUITE.erl index a125854fe6..013a610993 100644 --- a/test/commands_backend_SUITE.erl +++ b/test/commands_backend_SUITE.erl @@ -97,11 +97,11 @@ groups() -> setup(Module) -> meck:unload(), meck:new(supervisor, [unstick, passthrough, no_link]), - meck:new(ejabberd_hooks, []), + meck:new(gen_hook, []), meck:new(ejabberd_auth, []), %% you have to meck some stuff to get it working.... - meck:expect(ejabberd_hooks, add, fun(_, _, _, _, _) -> ok end), - meck:expect(ejabberd_hooks, run_global, fun(_, _, _) -> ok end), + meck:expect(gen_hook, add_handler, fun(_, _, _, _, _) -> ok end), + meck:expect(gen_hook, run_fold, fun(_, _, _, _) -> {ok, ok} end), meck:expect(ejabberd_config, get_local_option, fun(_) -> undefined end), spawn(fun mc_holder/0), meck:expect(supervisor, start_child, @@ -120,7 +120,7 @@ teardown() -> mongoose_commands:unregister(commands_new()), meck:unload(ejabberd_config), meck:unload(ejabberd_auth), - meck:unload(ejabberd_hooks), + meck:unload(gen_hook), meck:unload(supervisor), mc_holder_proc ! stop, ok. diff --git a/test/component_reg_SUITE.erl b/test/component_reg_SUITE.erl index 382411d9d1..a8758817c4 100644 --- a/test/component_reg_SUITE.erl +++ b/test/component_reg_SUITE.erl @@ -24,12 +24,12 @@ init_per_suite(C) -> meck:expect(mongoose_domain_api, get_host_type, fun(_) -> {error, not_found} end), application:ensure_all_started(exometer_core), - ejabberd_hooks:start_link(), + gen_hook:start_link(), ejabberd_router:start_link(), C. init_per_testcase(_, C) -> - ejabberd_hooks:start_link(), + gen_hook:start_link(), C. end_per_suite(_C) -> @@ -48,7 +48,7 @@ registering(_C) -> ok. registering_with_local(_C) -> - ejabberd_hooks:start_link(), + gen_hook:start_link(), Dom = <<"aaa.bbb.com">>, ThisNode = node(), AnotherNode = 'another@nohost', diff --git a/test/ejabberd_c2s_SUITE_mocks.erl b/test/ejabberd_c2s_SUITE_mocks.erl index 70f9e0ff3f..aa9cee18cf 100644 --- a/test/ejabberd_c2s_SUITE_mocks.erl +++ b/test/ejabberd_c2s_SUITE_mocks.erl @@ -35,9 +35,8 @@ setup() -> undefined end), meck:expect(mongoose_credentials, get, fun mcred_get/2), - meck:new(ejabberd_hooks), - meck:expect(ejabberd_hooks, run_global, fun hookfold/3), - meck:expect(ejabberd_hooks, run_for_host_type, fun hookfold/4), + meck:new(gen_hook), + meck:expect(gen_hook, run_fold, fun hookfold/4), meck:new(ejabberd_config), meck:expect(ejabberd_config, get_local_option, @@ -71,15 +70,14 @@ default_global_option(language) -> <<"en">>. mcred_get(dummy_creds, username) -> <<"cosmic_hippo">>; mcred_get(dummy_creds, auth_module) -> auuuthmodule. -hookfold(check_bl_c2s, _, _) -> false. - -hookfold(roster_get_versioning_feature, _, _, _) -> []; -hookfold(roster_get_subscription_lists, _, A, _) -> A; -hookfold(privacy_get_user_list, _, A, _) -> A; -hookfold(session_opening_allowed_for_user, _, _, _) -> allow; -hookfold(c2s_stream_features, _, _, _) -> []; -hookfold(xmpp_send_element, _, A, _) -> A; -hookfold(privacy_check_packet, _, _, _) -> allow. +hookfold(check_bl_c2s, _, _, _) -> {ok, false}; +hookfold(roster_get_versioning_feature, _, _, _) -> {ok, []}; +hookfold(roster_get_subscription_lists, _, A, _) -> {ok, A}; +hookfold(privacy_get_user_list, _, A, _) -> {ok, A}; +hookfold(session_opening_allowed_for_user, _, _, _) -> {ok, allow}; +hookfold(c2s_stream_features, _, _, _) -> {ok, []}; +hookfold(xmpp_send_element, _, A, _) -> {ok, A}; +hookfold(privacy_check_packet, _, _, _) -> {ok, allow}. get_host_type(<<"localhost">>) -> {ok, <<"localhost">>}; get_host_type(_) -> {error, not_found}. diff --git a/test/ejabberd_hooks_SUITE.erl b/test/ejabberd_hooks_SUITE.erl index 65264462fa..5b36665247 100644 --- a/test/ejabberd_hooks_SUITE.erl +++ b/test/ejabberd_hooks_SUITE.erl @@ -4,9 +4,8 @@ -define(HOST, <<"localhost">>). all() -> - [ a_fun_can_be_added, + [ a_module_fun_can_be_added, - a_fun_can_be_removed, a_module_fun_can_be_removed, hooks_run_launches_nullary_fun, @@ -32,21 +31,6 @@ init_per_suite(C) -> end_per_suite(_C) -> application:stop(exometer_core). -init_per_testcase(_, C) -> - ejabberd_hooks:start_link(), - catch ets:new(local_config, [named_table]), - C. - -a_fun_can_be_added(_) -> - given_hooks_started(), - - % when - ejabberd_hooks:add(test_run_hook, ?HOST, fun(_) -> ok end, 1), - - % then - [{{test_run_hook,<<"localhost">>}, [{1,undefined,_}]}] = get_hooks(). - - a_module_fun_can_be_added(_) -> given_hooks_started(), given_module(hook_mod, fun_a, fun(_) -> ok end), @@ -55,19 +39,10 @@ a_module_fun_can_be_added(_) -> ejabberd_hooks:add(test_run_hook, ?HOST, hook_mod, fun_a, 1), % then - [{{test_run_hook,<<"localhost">>}, [{1,hook_mod,fun_a}]}] = get_hooks(). - - -a_fun_can_be_removed(_) -> - given_hooks_started(), - GivenFun = fun(_) -> ok end, - ejabberd_hooks:add(test_run_hook, ?HOST, GivenFun, 1), - - % when - ejabberd_hooks:delete(test_run_hook, ?HOST, GivenFun, 1), - - % then - [{{test_run_hook,<<"localhost">>}, []}] = get_hooks(). + [{{test_run_hook,<<"localhost">>}, + [{hook_handler, {test_run_hook,<<"localhost">>}, 1, + ejabberd_hooks, gen_hook_fn_wrapper, + #{function := fun_a, module := hook_mod}}]}] = get_hooks(). a_module_fun_can_be_removed(_) -> given_hooks_started(), @@ -87,7 +62,7 @@ hooks_run_launches_nullary_fun(_) -> given_hook_added(test_run_hook, hook_mod, fun_nullary, 1), %% when - ejabberd_hooks:run_for_host_type(test_run_hook, ?HOST, ok, []), + run_fold(test_run_hook, ?HOST, ok, []), %% then H = meck:history(hook_mod), @@ -99,7 +74,7 @@ hooks_run_launches_unary_fun(_) -> given_hook_added(test_run_hook, hook_mod, fun_onearg, 1), %% when - ejabberd_hooks:run_for_host_type(test_run_hook, ?HOST, ok, [oneval]), + run_fold(test_run_hook, ?HOST, ok, [oneval]), %% then [{_,{hook_mod,fun_onearg,[ok, oneval]}, oneval}] = meck:history(hook_mod). @@ -113,7 +88,7 @@ hooks_run_ignores_different_arity_funs(_) -> given_hook_added(test_run_hook, hook_mod, fun_twoarg, 1), %% when - ejabberd_hooks:run_for_host_type(test_run_hook, ?HOST, ok, [one, two]), + run_fold(test_run_hook, ?HOST, ok, [one, two]), %% then [{_,{hook_mod, fun_twoarg, [ok, one, two]}, success2}] = meck:history(hook_mod). @@ -127,7 +102,7 @@ hooks_run_stops_when_fun_returns_stop(_) -> given_hook_added(test_run_hook, hook_mod, another_fun, 2), %% when - ejabberd_hooks:run_for_host_type(test_run_hook, ?HOST, ok, []), + run_fold(test_run_hook, ?HOST, ok, []), %% then [{_,{hook_mod,a_fun,[ok]}, stop}] = meck:history(hook_mod). @@ -139,7 +114,7 @@ hooks_run_fold_folds_with_unary_fun(_) -> given_hook_added(test_fold_hook, hook_mod, unary_folder, 1), %% when - ejabberd_hooks:run_for_host_type(test_fold_hook, ?HOST, initial, []), + run_fold(test_fold_hook, ?HOST, initial, []), %% then [{_,{hook_mod,unary_folder,[initial]}, done}] = meck:history(hook_mod). @@ -150,7 +125,7 @@ hooks_run_fold_folds_with_binary_fun(_) -> given_hook_added(test_fold_hook, hook_mod, binary_folder, 1), %% when - ejabberd_hooks:run_for_host_type(test_fold_hook, ?HOST, initial, [arg1]), + run_fold(test_fold_hook, ?HOST, initial, [arg1]), %% then [{_,{hook_mod,binary_folder,[initial, arg1]}, done}] = meck:history(hook_mod). @@ -164,7 +139,7 @@ hooks_run_fold_passes_acc_along(_) -> given_hook_added(test_fold_hook, hook_mod2, second_folder, 2), %% when - R = ejabberd_hooks:run_for_host_type(test_fold_hook, ?HOST, 0, [10]), + R = run_fold(test_fold_hook, ?HOST, 0, [10]), %% then -10 = R. @@ -178,7 +153,7 @@ hooks_run_fold_stops_when_fun_returns_stop(_) -> given_hook_added(test_fold_hook, hook_mod2, folder, 2), %% when - R = ejabberd_hooks:run_for_host_type(test_fold_hook, ?HOST, continue, []), + R = run_fold(test_fold_hook, ?HOST, continue, []), %% then [{_,{hook_mod1,stopper,[continue]}, stop}] = meck:history(hook_mod1), @@ -195,7 +170,7 @@ hooks_run_fold_preserves_order(_) -> given_hook_added(test_fold_hook, hook_mod2, second_folder, 2), %% when - R = ejabberd_hooks:run_for_host_type(test_fold_hook, ?HOST, 0, []), + R = run_fold(test_fold_hook, ?HOST, 0, []), %% then 2 = R. @@ -211,7 +186,7 @@ error_in_run_fold_is_ignored(_) -> given_hook_added(test_fold_hook, working_mod, good, 2), %% when - R = ejabberd_hooks:run_for_host_type(test_fold_hook, ?HOST, initial, []), + R = run_fold(test_fold_hook, ?HOST, initial, []), %% then i_was_run = R, @@ -229,7 +204,7 @@ throw_in_run_fold_is_ignored(_) -> given_hook_added(test_fold_hook, working_mod, good, 2), %% when - R = ejabberd_hooks:run_for_host_type(test_fold_hook, ?HOST, initial, []), + R = run_fold(test_fold_hook, ?HOST, initial, []), %% then initial = R, @@ -248,7 +223,7 @@ exit_in_run_fold_is_ignored(_) -> given_hook_added(test_fold_hook, working_mod, good, 2), %% when - R = ejabberd_hooks:run_for_host_type(test_fold_hook, ?HOST, initial, []), + R = run_fold(test_fold_hook, ?HOST, initial, []), %% then initial = R, @@ -262,10 +237,9 @@ exit_in_run_fold_is_ignored(_) -> const(N) -> fun(_) -> N end. given_hooks_started() -> - error_logger:tty(false), Fun = fun(all_metrics_are_global) -> false end, given_module(ejabberd_config, get_local_option, Fun), - ejabberd_hooks:start_link(). + gen_hook:start_link(). given_hook_added(HookName, ModName, FunName, Prio) -> ejabberd_hooks:add(HookName, ?HOST, ModName, FunName, Prio). @@ -278,6 +252,10 @@ given_module(ModName, FunName, Fun) -> given_fun(ModName, FunName, Fun) -> meck:expect(ModName, FunName, Fun). +run_fold(HookName, HostType, Acc, Args) -> + Params = ejabberd_hooks:add_args(#{}, Args), + {_, RetValue} = gen_hook:run_fold(HookName, HostType, Acc, Params), + RetValue. get_hooks() -> - ets:tab2list(hooks). + ets:tab2list(gen_hook). diff --git a/test/ejabberd_sm_SUITE.erl b/test/ejabberd_sm_SUITE.erl index 3e2be1ee5f..6074dc4c1b 100644 --- a/test/ejabberd_sm_SUITE.erl +++ b/test/ejabberd_sm_SUITE.erl @@ -367,7 +367,7 @@ unique_count_while_removing_entries(C) -> unload_meck() -> meck:unload(acl), meck:unload(ejabberd_config), - meck:unload(ejabberd_hooks), + meck:unload(gen_hook), meck:unload(ejabberd_commands), meck:unload(mongoose_domain_api). @@ -376,8 +376,8 @@ set_test_case_meck(MaxUserSessions) -> meck:expect(ejabberd_config, get_local_option, fun(_) -> undefined end), meck:new(acl, []), meck:expect(acl, match_rule, fun(_, _, _) -> MaxUserSessions end), - meck:new(ejabberd_hooks, []), - meck:expect(ejabberd_hooks, run_for_host_type, fun(_, _, Acc, _) -> Acc end), + meck:new(gen_hook, []), + meck:expect(gen_hook, run_fold, fun(_, _, Acc, _) -> {ok, Acc} end), meck:new(mongoose_domain_api, []), meck:expect(mongoose_domain_api, get_domain_host_type, fun(H) -> {ok, H} end). @@ -606,8 +606,7 @@ set_meck(SMBackend) -> (host_types, Default) -> Default end), meck:expect(ejabberd_config, get_local_option, fun(_) -> undefined end), - meck:expect(ejabberd_hooks, add, fun(_) -> ok end), - meck:expect(ejabberd_hooks, add, fun(_, _, _, _, _) -> ok end), + meck:expect(gen_hook, add_handler, fun(_, _, _, _, _) -> ok end), meck:new(ejabberd_commands, []), meck:expect(ejabberd_commands, register_commands, fun(_) -> ok end), diff --git a/test/gen_hook_SUITE.erl b/test/gen_hook_SUITE.erl new file mode 100644 index 0000000000..13123a91b9 --- /dev/null +++ b/test/gen_hook_SUITE.erl @@ -0,0 +1,342 @@ +-module(gen_hook_SUITE). +-compile([export_all]). + +-include_lib("eunit/include/eunit.hrl"). + +-define(HOOK_TAG1, global). +-define(HOOK_TAG2, <<"some tag">>). + +-define(assertEqualLists(L1, L2), ?assertEqual(lists:sort(L1), lists:sort(L2))). + +all() -> + [single_handler_can_be_added_and_removed, + multiple_handlers_can_be_added_and_removed, + + local_fun_references_causes_error, + anonymous_fun_references_causes_error, + not_exported_external_fun_references_causes_error, + invalid_hook_handler_parameters_causes_error, + + run_fold_executes_handlers_in_the_right_order, + run_fold_stops_when_handler_returns_stop, + + errors_in_handlers_are_reported_but_ignored]. + +init_per_suite(Config) -> + application:ensure_all_started(exometer_core), + Config. + +end_per_suite(Config) -> + application:stop(exometer_core), + Config. + +init_per_testcase(_, Config) -> + Fun = fun(all_metrics_are_global) -> false end, + meck:new(ejabberd_config), + meck:expect(ejabberd_config, get_local_option, Fun), + gen_hook:start_link(), + Config. + +end_per_testcase(_, Config) -> + meck:unload(), + Config. + + +%%---------------------------------------------------------------- +%% test cases +%%---------------------------------------------------------------- +single_handler_can_be_added_and_removed(_) -> + meck:new([mod1, mod2], [non_strict]), + PlusHandlerFn = get_hook_handler(mod1, plus, fun hook_handler_plus/3), + MultiplyHandlerFn = get_hook_handler(mod2, multiply, fun hook_handler_multiply/3), + %% check that there are no hook handlers added yet + ?assertEqual([], get_handlers_for_all_hooks()), + %% add various hook handlers + ?assertEqual(ok, gen_hook:add_handler(calculate, ?HOOK_TAG1, MultiplyHandlerFn, + #{id => 2}, 2)), + ?assertEqual(ok, gen_hook:add_handler(calculate, ?HOOK_TAG1, PlusHandlerFn, + #{id => 1}, 1)), + ?assertEqual(ok, gen_hook:add_handler(calculate, ?HOOK_TAG2, PlusHandlerFn, + #{id => 1}, 1)), + %% check that hook handlers are added + Tag1Handlers = [%% this list must be sorted by priority + {hook_handler, {calculate, ?HOOK_TAG1}, 1, mod1, plus, + #{hook_name => calculate, hook_tag => ?HOOK_TAG1, id => 1}}, + {hook_handler, {calculate, ?HOOK_TAG1}, 2, mod2, multiply, + #{hook_name => calculate, hook_tag => ?HOOK_TAG1, id => 2}}], + AllHandlers = [{{calculate, ?HOOK_TAG1}, Tag1Handlers}, + {{calculate, ?HOOK_TAG2}, + [{hook_handler, {calculate, ?HOOK_TAG2}, 1, mod1, plus, + #{hook_name => calculate, hook_tag => ?HOOK_TAG2, + host_type =>?HOOK_TAG2, id => 1}}]}], + ?assertEqualLists(AllHandlers, get_handlers_for_all_hooks()), + %% try to add some hook handler second time and check that nothing has changed + ?assertEqual(ok, gen_hook:add_handler(calculate, ?HOOK_TAG1, MultiplyHandlerFn, + #{id => 2}, 2)), + ?assertEqualLists(AllHandlers, get_handlers_for_all_hooks()), + %% try to remove hook handler for ?HOOK_TAG2 and check that it's removed + ?assertEqual(ok, gen_hook:delete_handler(calculate, ?HOOK_TAG2, PlusHandlerFn, + #{id => 1}, 1)), + ?assertEqualLists([{{calculate, ?HOOK_TAG1}, Tag1Handlers}, + {{calculate, ?HOOK_TAG2}, []}], + get_handlers_for_all_hooks()), + %% try to remove hook handler for ?HOOK_TAG2 second time + %% and check that nothing has changed + ?assertEqual(ok, gen_hook:delete_handler(calculate, ?HOOK_TAG2, PlusHandlerFn, + #{id => 1}, 1)), + ?assertEqualLists([{{calculate, ?HOOK_TAG1}, Tag1Handlers}, + {{calculate, ?HOOK_TAG2}, []}], + get_handlers_for_all_hooks()), + %% try to remove hook handlers for ?HOOK_TAG1 and check that they are removed + ?assertEqual(ok, gen_hook:delete_handler(calculate, ?HOOK_TAG1, MultiplyHandlerFn, + #{id => 2}, 2)), + ?assertEqual(ok, gen_hook:delete_handler(calculate, ?HOOK_TAG1, PlusHandlerFn, + #{id => 1}, 1)), + ?assertEqualLists([{{calculate, ?HOOK_TAG1}, []}, {{calculate, ?HOOK_TAG2}, []}], + get_handlers_for_all_hooks()). + +multiple_handlers_can_be_added_and_removed(_) -> + meck:new([mod1, mod2], [non_strict]), + PlusHandlerFn = get_hook_handler(mod1, plus, fun hook_handler_plus/3), + MultiplyHandlerFn = get_hook_handler(mod2, multiply, fun hook_handler_multiply/3), + %% check that there are no hook handlers added yet + ?assertEqual([], get_handlers_for_all_hooks()), + %% add various hook handlers + HookHandlers = [{calculate, ?HOOK_TAG1, MultiplyHandlerFn, #{id => 2}, 2}, + {calculate, ?HOOK_TAG2, PlusHandlerFn, #{id => 1}, 1}, + {calculate, ?HOOK_TAG1, PlusHandlerFn, #{id => 1}, 1}], + ?assertEqual(ok, gen_hook:add_handlers(HookHandlers)), + %% check that hook handlers are added + Tag1Handlers = [%% this list must be sorted by priority + {hook_handler, {calculate, ?HOOK_TAG1}, 1, mod1, plus, + #{hook_name => calculate, hook_tag => ?HOOK_TAG1, id => 1}}, + {hook_handler, {calculate, ?HOOK_TAG1}, 2, mod2, multiply, + #{hook_name => calculate, hook_tag => ?HOOK_TAG1, id => 2}}], + AllHandlers = [{{calculate, ?HOOK_TAG1}, Tag1Handlers}, + {{calculate, ?HOOK_TAG2}, + [{hook_handler, {calculate, ?HOOK_TAG2}, 1, mod1, plus, + #{hook_name => calculate, hook_tag => ?HOOK_TAG2, + host_type =>?HOOK_TAG2, id => 1}}]}], + ?assertEqualLists(AllHandlers, get_handlers_for_all_hooks()), + %% try to add hook handlers second time and check that nothing has changed + ?assertEqual(ok, gen_hook:add_handlers(HookHandlers)), + ?assertEqualLists(AllHandlers, get_handlers_for_all_hooks()), + %% try to remove hook handlers and check that they are removed + ?assertEqual(ok, gen_hook:delete_handlers(HookHandlers)), + ?assertEqualLists([{{calculate, ?HOOK_TAG1}, []}, {{calculate, ?HOOK_TAG2}, []}], + get_handlers_for_all_hooks()), + %% try to remove hook handlers second time and check that nothing has changed + ?assertEqual(ok, gen_hook:delete_handlers(HookHandlers)), + ?assertEqualLists([{{calculate, ?HOOK_TAG1}, []}, {{calculate, ?HOOK_TAG2}, []}], + get_handlers_for_all_hooks()). + +local_fun_references_causes_error(_) -> + meck:new([mod1, mod2], [non_strict]), + PlusHandlerFn = get_hook_handler(mod1, plus, fun hook_handler_plus/3), + MultiplyHandlerFn = get_hook_handler(mod2, multiply, fun hook_handler_multiply/3), + %% check that there are no hook handlers added yet + ?assertEqual([], get_handlers_for_all_hooks()), + %% try to add multiple hook handlers, when one of them uses local function reference + LocalFunctionReference = fun hook_handler_plus/3, + HookHandlers = [{calculate, ?HOOK_TAG1, MultiplyHandlerFn, #{id => 2}, 2}, + {calculate, ?HOOK_TAG2, LocalFunctionReference, #{id => 1}, 1}, + {calculate, ?HOOK_TAG1, PlusHandlerFn, #{id => 1}, 1}], + ?assertError(#{what := only_external_function_references_allowed, + function := LocalFunctionReference}, + gen_hook:add_handlers(HookHandlers)), + %% check that handlers in the list are partially added (till error occurs) + ?assertEqual([{{calculate, ?HOOK_TAG1}, + [{hook_handler, {calculate, ?HOOK_TAG1}, 2, mod2, multiply, + #{hook_name => calculate, hook_tag => ?HOOK_TAG1, id => 2}}]}], + get_handlers_for_all_hooks()), + %% try to remove the same list of handlers + ?assertError(#{what := only_external_function_references_allowed, + function := LocalFunctionReference}, + gen_hook:delete_handlers(HookHandlers)), + %% check that partially added handlers are removed + ?assertEqual([{{calculate, ?HOOK_TAG1}, []}], get_handlers_for_all_hooks()). + +anonymous_fun_references_causes_error(_) -> + %% check that there are no hook handlers added yet + ?assertEqual([], get_handlers_for_all_hooks()), + %% try to add hook handler using anonymous function reference + AnonymousFunctionReference = fun(Acc, _, _) -> {ok, Acc} end, + ?assertError(#{what := only_external_function_references_allowed, + function := AnonymousFunctionReference}, + gen_hook:add_handler(calculate, ?HOOK_TAG1, AnonymousFunctionReference, + #{id => 2}, 2)), + %% check that nothing is added + ?assertEqual([], get_handlers_for_all_hooks()). + +not_exported_external_fun_references_causes_error(_) -> + %% check that there are no hook handlers added yet + ?assertEqual([], get_handlers_for_all_hooks()), + %% try to add hook handler using function reference for a missing module + NotExportedExternalFunctionReference1 = fun missing_module:missing_function/3, + ?assertError(#{what := module_is_not_loaded, module := missing_module}, + gen_hook:add_handler(calculate, ?HOOK_TAG1, + NotExportedExternalFunctionReference1, + #{id => 2}, 2)), + %% try to add hook handler using function reference for a missing module + NotExportedExternalFunctionReference2 = fun ?MODULE:missing_function/3, + ?assertError(#{what := function_is_not_exported, + function := NotExportedExternalFunctionReference2}, + gen_hook:add_handler(calculate, ?HOOK_TAG1, + NotExportedExternalFunctionReference2, + #{id => 2}, 2)), + %% check that nothing is added + ?assertEqual([], get_handlers_for_all_hooks()). + +invalid_hook_handler_parameters_causes_error(_) -> + %% check that there are no hook handlers added yet + ?assertEqual([], get_handlers_for_all_hooks()), + HandlerFn = fun ?MODULE:hook_handler_stop/3, + InvalidHookHandlers = [{calculate, ?HOOK_TAG1, HandlerFn, invalid_extra_param, 2}, + {<<"invalid hook name">>, ?HOOK_TAG1, HandlerFn, #{}, 2}, + {calculate, ?HOOK_TAG1, HandlerFn, #{}, invalid_priority}, + {calculate, invalid_hook_tag, HandlerFn, #{}, 2}], + [?assertError(function_clause, gen_hook:add_handlers([HookHandler])) + || HookHandler <- InvalidHookHandlers], + ?assertEqual([], get_handlers_for_all_hooks()). + +run_fold_executes_handlers_in_the_right_order(_) -> + meck:new(mod1, [non_strict]), + PlusHandlerFn = get_hook_handler(mod1, plus, fun hook_handler_plus/3), + MultiplyHandlerFn = get_hook_handler(mod1, multiply, fun hook_handler_multiply/3), + %% check that there are no hook handlers added yet + ?assertEqual([], get_handlers_for_all_hooks()), + %% add various hook handlers + HookHandlers = [{calculate, ?HOOK_TAG1, MultiplyHandlerFn, #{n => 5}, 5}, + {calculate, ?HOOK_TAG1, MultiplyHandlerFn, #{}, 2}, + {calculate, ?HOOK_TAG1, PlusHandlerFn, #{n => 3}, 1}, + {calculate, ?HOOK_TAG1, PlusHandlerFn, #{}, 4}], + ?assertEqual(ok, gen_hook:add_handlers(HookHandlers)), + %% run the hook + N = (((0 + 3) * 2) + 2) * 5, %% 40 + ?assertEqual({ok, N}, gen_hook:run_fold(calculate, ?HOOK_TAG1, 0, #{n => 2})), + %% check hook handlers execution sequence + Self = self(), + ?assertEqual([{Self, + {mod1, plus, [0, #{n => 2}, #{hook_name => calculate, n => 3, + hook_tag => ?HOOK_TAG1}]}, + {ok, 3}}, + {Self, + {mod1, multiply, [3, #{n => 2}, #{hook_name => calculate, + hook_tag => ?HOOK_TAG1}]}, + {ok, 6}}, + {Self, + {mod1, plus, [6, #{n => 2}, #{hook_name => calculate, + hook_tag => ?HOOK_TAG1}]}, + {ok, 8}}, + {Self, + {mod1, multiply, [8, #{n => 2}, #{hook_name => calculate, n => 5, + hook_tag => ?HOOK_TAG1}]}, + {ok, 40}}], + meck:history(mod1)). + +run_fold_stops_when_handler_returns_stop(_) -> + meck:new(mod1, [non_strict]), + PlusHandlerFn = get_hook_handler(mod1, plus, fun hook_handler_plus/3), + StopHandlerFn = get_hook_handler(mod1, stop, fun hook_handler_stop/3), + MultiplyHandlerFn = get_hook_handler(mod1, multiply, fun hook_handler_multiply/3), + %% check that there are no hook handlers added yet + ?assertEqual([], get_handlers_for_all_hooks()), + %% add various hook handlers + HookHandlers = [{calculate, ?HOOK_TAG1, MultiplyHandlerFn, #{n => 5}, 5}, + {calculate, ?HOOK_TAG1, MultiplyHandlerFn, #{}, 2}, + {calculate, ?HOOK_TAG1, PlusHandlerFn, #{n => 3}, 1}, + {calculate, ?HOOK_TAG1, PlusHandlerFn, #{}, 4}, + {calculate, ?HOOK_TAG1, StopHandlerFn, #{}, 3}], + ?assertEqual(ok, gen_hook:add_handlers(HookHandlers)), + %% run the hook + N = ((0 + 3) * 2), %% 6 + ?assertEqual({stop, N}, gen_hook:run_fold(calculate, ?HOOK_TAG1, 0, #{n => 2})), + %% check hook handlers execution sequence + Self = self(), + ?assertEqual([{Self, + {mod1, plus, [0, #{n => 2}, #{hook_name => calculate, n => 3, + hook_tag => ?HOOK_TAG1}]}, + {ok, 3}}, + {Self, + {mod1, multiply, [3, #{n => 2}, #{hook_name => calculate, + hook_tag => ?HOOK_TAG1}]}, + {ok, 6}}, + {Self, + {mod1, stop, [6, #{n => 2}, #{hook_name => calculate, + hook_tag => ?HOOK_TAG1}]}, + {stop, 6}}], + meck:history(mod1)). + +errors_in_handlers_are_reported_but_ignored(_) -> + meck:new(mod1, [non_strict]), + meck:new(gen_hook, [passthrough]), + PlusHandlerFn = get_hook_handler(mod1, plus, fun hook_handler_plus/3), + ErrorHandlerFn = get_hook_handler(mod1, error, fun hook_handler_error/3), + MultiplyHandlerFn = get_hook_handler(mod1, multiply, fun hook_handler_multiply/3), + %% check that there are no hook handlers added yet + ?assertEqual([], get_handlers_for_all_hooks()), + %% add various hook handlers + HookHandlers = [{calculate, ?HOOK_TAG1, MultiplyHandlerFn, #{n => 5}, 5}, + {calculate, ?HOOK_TAG1, MultiplyHandlerFn, #{}, 2}, + {calculate, ?HOOK_TAG1, PlusHandlerFn, #{n => 3}, 1}, + {calculate, ?HOOK_TAG1, PlusHandlerFn, #{}, 4}, + {calculate, ?HOOK_TAG1, ErrorHandlerFn, #{}, 3}], + ?assertEqual(ok, gen_hook:add_handlers(HookHandlers)), + %% run the hook + N = (((0 + 3) * 2) + 2) * 5, %% 40 + ?assertEqual({ok, N}, gen_hook:run_fold(calculate, ?HOOK_TAG1, 0, #{n => 2})), + %% check that error is reported + ?assertEqual(true, meck:called(gen_hook, error_running_hook, + [{some_error, '_'}, + {hook_handler, {calculate, ?HOOK_TAG1}, 3, mod1, error, + #{hook_name => calculate, hook_tag => ?HOOK_TAG1}}, + 6, #{n => 2}])), + %% check hook handlers execution sequence + Self = self(), + ?assertMatch([{Self, + {mod1, plus, [0, #{n := 2}, #{hook_name := calculate, n := 3, + hook_tag := ?HOOK_TAG1}]}, + {ok, 3}}, + {Self, + {mod1, multiply, [3, #{n := 2}, #{hook_name := calculate, + hook_tag := ?HOOK_TAG1}]}, + {ok, 6}}, + {Self, + {mod1, error, [6, #{n := 2}, #{hook_name := calculate, + hook_tag := ?HOOK_TAG1}]}, + error, some_error, _}, + {Self, + {mod1, plus, [6, #{n := 2}, #{hook_name := calculate, + hook_tag := ?HOOK_TAG1}]}, + {ok, 8}}, + {Self, + {mod1, multiply, [8, #{n := 2}, #{hook_name := calculate, n := 5, + hook_tag := ?HOOK_TAG1}]}, + {ok, 40}}], + meck:history(mod1)). + +%%---------------------------------------------------------------- +%% helper functions +%%---------------------------------------------------------------- +hook_handler_plus(Acc, _, #{n := N}) -> + {ok, Acc + N}; +hook_handler_plus(Acc, #{n := N}, _) -> + {ok, Acc + N}. + +hook_handler_multiply(Acc, _, #{n := N}) -> + {ok, Acc * N}; +hook_handler_multiply(Acc, #{n := N}, _) -> + {ok, Acc * N}. + +hook_handler_stop(Acc, _, _) -> + {stop, Acc}. + +hook_handler_error(_, _, _) -> + error(some_error). + +get_hook_handler(ModName, FunName, Fun) when is_function(Fun, 3) -> + meck:expect(ModName, FunName, Fun), + fun ModName:FunName/3. + +get_handlers_for_all_hooks() -> + ets:tab2list(gen_hook). diff --git a/test/keystore_SUITE.erl b/test/keystore_SUITE.erl index 2eddb63b8f..9196084c29 100644 --- a/test/keystore_SUITE.erl +++ b/test/keystore_SUITE.erl @@ -28,7 +28,7 @@ end_per_suite(C) -> init_per_testcase(_, Config) -> mock_mongoose_metrics(), - async_helper:start(Config, ejabberd_hooks, start_link, []). + async_helper:start(Config, gen_hook, start_link, []). end_per_testcase(module_startup_non_unique_key_ids, C) -> clean_after_testcase(C); diff --git a/test/mongoose_cleanup_SUITE.erl b/test/mongoose_cleanup_SUITE.erl index a957047d41..8ecd2b9e70 100644 --- a/test/mongoose_cleanup_SUITE.erl +++ b/test/mongoose_cleanup_SUITE.erl @@ -6,7 +6,7 @@ -export([all/0, init_per_suite/1, end_per_suite/1, init_per_testcase/2, end_per_testcase/2]). --export([cleaner_runs_hook_on_nodedown/1]). +-export([cleaner_runs_hook_on_nodedown/1, notify_self_hook/3]). -export([auth_anonymous/1, last/1, stream_management/1, @@ -45,7 +45,7 @@ end_per_suite(Config) -> Config. init_per_testcase(T, Config) -> - {ok, _HooksServer} = ejabberd_hooks:start_link(), + {ok, _HooksServer} = gen_hook:start_link(), setup_meck(meck_mods(T)), Config. @@ -63,11 +63,11 @@ meck_mods(_) -> [exometer, ejabberd_sm, ejabberd_local, ejabberd_config]. %% ----------------------------------------------------- cleaner_runs_hook_on_nodedown(_Config) -> - meck:expect(ejabberd_hooks, error_running_hook, fun(_, _, _) -> ok end), + meck:expect(gen_hook, error_running_hook, fun(_, _, _, _) -> ok end), {ok, Cleaner} = mongoose_cleaner:start_link(), - Self = self(), - NotifySelf = fun(Acc, Node) -> Self ! {got_nodedown, Node}, Acc end, - ejabberd_hooks:add(node_cleanup, global, undefined, NotifySelf, 50), + gen_hook:add_handler(node_cleanup, global, + fun ?MODULE:notify_self_hook/3, + #{self => self()}, 50), FakeNode = fakename@fakehost, Cleaner ! {nodedown, FakeNode}, @@ -77,8 +77,12 @@ cleaner_runs_hook_on_nodedown(_Config) -> after timer:seconds(1) -> ct:fail({timeout, got_nodedown}) end, - ?assertEqual(false, meck:called(ejabberd_hooks, error_running_hook, - ['_', {node_cleanup, global}, '_'])). + ?assertEqual(false, meck:called(gen_hook, error_running_hook, + ['_', '_', '_', '_'])). + +notify_self_hook(Acc, #{node := Node}, #{self := Self}) -> + Self ! {got_nodedown, Node}, + {ok, Acc}. auth_anonymous(_Config) -> {U, S, R, JID, SID} = get_fake_session(), diff --git a/test/mongooseim_metrics_SUITE.erl b/test/mongooseim_metrics_SUITE.erl index 24a1a337b0..a967995331 100644 --- a/test/mongooseim_metrics_SUITE.erl +++ b/test/mongooseim_metrics_SUITE.erl @@ -37,12 +37,12 @@ init_per_suite(C) -> Sup = spawn(fun() -> mim_ct_sup:start_link(ejabberd_sup), Hooks = - {ejabberd_hooks, - {ejabberd_hooks, start_link, []}, + {gen_hook, + {gen_hook, start_link, []}, permanent, brutal_kill, worker, - [ejabberd_hooks]}, + [gen_hook]}, C2SSupervisor = {ejabberd_c2s_sup, {ejabberd_tmp_sup, start_link, [ejabberd_c2s_sup, ejabberd_c2s]}, diff --git a/test/muc_light_SUITE.erl b/test/muc_light_SUITE.erl index 993919db26..23cafbbcfa 100644 --- a/test/muc_light_SUITE.erl +++ b/test/muc_light_SUITE.erl @@ -65,7 +65,7 @@ init_per_testcase(codec_calls, Config) -> ok = mnesia:start(), {ok, _} = application:ensure_all_started(exometer_core), ets:new(local_config, [named_table]), - ejabberd_hooks:start_link(), + gen_hook:start_link(), ejabberd_router:start_link(), mim_ct_sup:start_link(ejabberd_sup), gen_mod:start(), @@ -117,10 +117,11 @@ codec_calls(_Config) -> Sender = jid:from_binary(<<"bob@localhost/bbb">>), RoomUS = {<<"pokoik">>, <<"localhost">>}, HandleFun = fun(_, _, _) -> count_call(handler) end, - ejabberd_hooks:add(filter_room_packet, - <<"localhost">>, - fun(Acc, _HostType, _EvData) -> count_call(hook), Acc end, - 50), + gen_hook:add_handler(filter_room_packet, + <<"localhost">>, + fun ?MODULE:filter_room_packet_handler/3, + #{}, + 50), % count_call/1 should've been called twice - by handler fun (for each affiliated user, % we have one) and by a filter_room_packet hook handler. @@ -153,6 +154,9 @@ codec_calls(_Config) -> check_count(1, 2), ok. +filter_room_packet_handler(Acc, _Params, _Extra) -> + count_call(hook), + {ok, Acc}. %% ----------------- Room config schema ---------------------- simple_config_items_are_parsed(_Config) -> diff --git a/test/privacy_SUITE.erl b/test/privacy_SUITE.erl index 6873eb66dd..546e3fb8b1 100644 --- a/test/privacy_SUITE.erl +++ b/test/privacy_SUITE.erl @@ -41,7 +41,7 @@ init_per_suite(C) -> init_per_testcase(_, C) -> catch ets:new(local_config, [named_table]), - ejabberd_hooks:start_link(), + gen_hook:start_link(), mod_privacy:start(<<"localhost">>, []), C. diff --git a/test/roster_SUITE.erl b/test/roster_SUITE.erl index 6679a536de..b3b4fcd3d6 100644 --- a/test/roster_SUITE.erl +++ b/test/roster_SUITE.erl @@ -58,7 +58,7 @@ end_per_suite(C) -> init_per_testcase(_TC, C) -> init_ets(), - ejabberd_hooks:start_link(), + gen_hook:start_link(), gen_mod:start(), gen_mod:start_module(host_type(), mod_roster, []), C. diff --git a/test/router_SUITE.erl b/test/router_SUITE.erl index 4cfdb16af2..ae773bbe89 100644 --- a/test/router_SUITE.erl +++ b/test/router_SUITE.erl @@ -48,7 +48,7 @@ init_per_group(routing, Config) -> (_) -> undefined end), - ejabberd_hooks:start_link(), + gen_hook:start_link(), ejabberd_router:start_link(), Config; init_per_group(schema, Config) ->