From edfee35f8f869f5f2485f5096d96920f47c829b8 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 11 May 2021 15:59:49 +0200 Subject: [PATCH 01/73] HostTypy mod_mam --- src/mam/mod_mam.erl | 343 ++++++++++++++++++++++++-------------------- 1 file changed, 184 insertions(+), 159 deletions(-) diff --git a/src/mam/mod_mam.erl b/src/mam/mod_mam.erl index 97e3c3819c9..6026995c270 100644 --- a/src/mam/mod_mam.erl +++ b/src/mam/mod_mam.erl @@ -105,6 +105,7 @@ %% Datetime types %% Microseconds from 01.01.1970 -type unix_timestamp() :: non_neg_integer(). +-type host_type() :: mongooseim:host_type(). %% ---------------------------------------------------------------------- %% Other types @@ -160,9 +161,10 @@ %% API -spec get_personal_data(gdpr:personal_data(), jid:jid()) -> gdpr:personal_data(). -get_personal_data(Acc, #jid{ lserver = LServer } = JID) -> +get_personal_data(Acc, #jid{} = ArcJID) -> + HostType = jid_to_host_type(ArcJID), Schema = ["id", "from", "message"], - Entries = mongoose_hooks:get_mam_pm_gdpr_data(LServer, JID), + Entries = mongoose_hooks:get_mam_pm_gdpr_data(HostType, ArcJID), [{mam_pm, Schema, Entries} | Acc]. -spec delete_archive(jid:server(), jid:user()) -> 'ok'. @@ -170,54 +172,44 @@ delete_archive(Server, User) when is_binary(Server), is_binary(User) -> ?LOG_DEBUG(#{what => mam_delete_archive, user => User, server => Server}), ArcJID = jid:make(User, Server, <<>>), - Host = server_host(ArcJID), - ArcID = archive_id_int(Host, ArcJID), - remove_archive_hook(Host, ArcID, ArcJID), + HostType = jid_to_host_type(ArcJID), + ArcID = archive_id_int(HostType, ArcJID), + remove_archive_hook(HostType, ArcID, ArcJID), ok. - -spec archive_size(jid:server(), jid:user()) -> integer(). archive_size(Server, User) when is_binary(Server), is_binary(User) -> ArcJID = jid:make(User, Server, <<>>), - Host = server_host(ArcJID), - ArcID = archive_id_int(Host, ArcJID), - archive_size(Host, ArcID, ArcJID). - + HostType = jid_to_host_type(ArcJID), + ArcID = archive_id_int(HostType, ArcJID), + archive_size(HostType, ArcID, ArcJID). -spec archive_id(jid:server(), jid:user()) -> integer() | undefined. archive_id(Server, User) when is_binary(Server), is_binary(User) -> ArcJID = jid:make(User, Server, <<>>), - Host = server_host(ArcJID), - archive_id_int(Host, ArcJID). + HostType = jid_to_host_type(ArcJID), + archive_id_int(HostType, ArcJID). %% gen_mod callbacks %% Starting and stopping functions for users' archives --spec start(Host :: jid:server(), Opts :: list()) -> any(). -start(Host, Opts) -> +-spec start(HostType :: jid:server(), Opts :: list()) -> any(). +start(HostType, Opts) -> ?LOG_INFO(#{what => mam_starting}), - - %% `parallel' is the only one recommended here. - IQDisc = gen_mod:get_opt(iqdisc, Opts, parallel), %% Type - [mod_disco:register_feature(Host, Feature) || Feature <- features(?MODULE, Host)], - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_04, - ?MODULE, process_mam_iq, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_MAM_06, - ?MODULE, process_mam_iq, IQDisc), - ejabberd_hooks:add(hooks(Host)), - ensure_metrics(Host), + ensure_metrics(HostType), + ejabberd_hooks:add(hooks(HostType)), + add_id_handlers(HostType, Opts), + register_features(HostType), ok. - --spec stop(Host :: jid:server()) -> any(). -stop(Host) -> +-spec stop(HostType :: jid:server()) -> any(). +stop(HostType) -> ?LOG_INFO(#{what => mam_stopping}), - ejabberd_hooks:delete(hooks(Host)), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_04), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_MAM_06), - [mod_disco:unregister_feature(Host, Feature) || Feature <- features(?MODULE, Host)], + unregister_features(HostType), + ejabberd_hooks:delete(hooks(HostType)), + remove_iq_handlers(HostType), ok. %% ---------------------------------------------------------------------- @@ -230,28 +222,28 @@ stop(Host) -> %% (i.e `To.luser'). -spec process_mam_iq(From :: jid:jid(), To :: jid:jid(), Acc :: mongoose_acc:t(), IQ :: jlib:iq()) -> {mongoose_acc:t(), jlib:iq() | ignore}. -process_mam_iq(From=#jid{lserver=Host}, To, Acc, IQ) -> +process_mam_iq(From, To, Acc, IQ) -> + HostType = mongoose_acc:host_type(Acc), mod_mam_utils:maybe_log_deprecation(IQ), Action = mam_iq:action(IQ), - case is_action_allowed(Action, From, To) of + case is_action_allowed(HostType, Action, From, To) of true -> - case mod_mam_utils:wait_shaper(Host, Action, From) of + case mod_mam_utils:wait_shaper(HostType, Action, From) of ok -> - handle_error_iq(Host, Acc, To, Action, - handle_mam_iq(Action, From, To, IQ)); + handle_error_iq(HostType, Acc, To, Action, + handle_mam_iq(Action, From, To, IQ, Acc)); {error, max_delay_reached} -> ?LOG_WARNING(#{what => mam_max_delay_reached, text => <<"Return max_delay_reached error IQ from MAM">>, action => Action, acc => Acc}), - mongoose_metrics:update(Host, modMamDroppedIQ, 1), + mongoose_metrics:update(HostType, modMamDroppedIQ, 1), {Acc, return_max_delay_reached_error_iq(IQ)} end; false -> - mongoose_metrics:update(Host, modMamDroppedIQ, 1), + mongoose_metrics:update(HostType, modMamDroppedIQ, 1), {Acc, return_action_not_allowed_error_iq(IQ)} end. - %% @doc Handle an outgoing message. %% %% Note: for outgoing messages, the server MUST use the value of the 'to' @@ -264,7 +256,6 @@ user_send_packet(Acc, From, To, Packet) -> {_, Acc2} = handle_package(outgoing, true, From, To, From, Packet, Acc), Acc2. - %% @doc Handle an incoming message. %% %% Note: For incoming messages, the server MUST use the value of the @@ -293,7 +284,7 @@ filter_packet({From, To = #jid{lserver = LServer}, Acc1, Packet}) -> {MessID, Acc2} -> Packet2 = maybe_add_arcid_elems( To, MessID, Packet, - mod_mam_params:add_stanzaid_element(?MODULE, LServer)), + mod_mam_params:add_stanzaid_element(?MODULE, HostType)), {archived, Packet2, Acc2} end end, @@ -322,14 +313,29 @@ sm_filter_offline_message(Other, _From, _To, _Packet) -> %% ---------------------------------------------------------------------- %% Internal functions --spec server_host(jid:jid()) -> jid:lserver(). -server_host(#jid{lserver=LServer}) -> - LServer. +-spec jid_to_host_type(jid:jid()) -> host_type(). +jid_to_host_type(#jid{lserver=LServer}) -> + case mongoose_domain_api:get_host_type(LServer) of + {ok, HostType} -> + HostType; + {error, not_found} -> + LServer + end. + +-spec acc_to_host_type(mongoose_acc:t()) -> host_type(). +acc_to_host_type(Acc) -> + case mongoose_acc:host_type(Acc) of + undefined -> + mongoose_acc:lserver(Acc); + HostType -> + HostType + end. --spec is_action_allowed(Action :: mam_iq:action(), From :: jid:jid(), +-spec is_action_allowed(HostType :: host_type(), + Action :: mam_iq:action(), From :: jid:jid(), To :: jid:jid()) -> boolean(). -is_action_allowed(Action, From, To=#jid{lserver=Host}) -> - case acl:match_rule(Host, Action, From, default) of +is_action_allowed(HostType, Action, From, To) -> + case acl:match_rule(HostType, Action, From, default) of allow -> true; deny -> false; default -> is_action_allowed_by_default(Action, From, To) @@ -340,36 +346,35 @@ is_action_allowed(Action, From, To=#jid{lserver=Host}) -> is_action_allowed_by_default(_Action, From, To) -> compare_bare_jids(From, To). - -spec compare_bare_jids(jid:simple_jid() | jid:jid(), jid:simple_jid() | jid:jid()) -> boolean(). compare_bare_jids(JID1, JID2) -> jid:to_bare(JID1) =:= jid:to_bare(JID2). -spec handle_mam_iq(mam_iq:action(), From :: jid:jid(), To :: jid:jid(), - IQ :: jlib:iq()) -> jlib:iq() | {error, term(), jlib:iq()}. -handle_mam_iq(Action, From, To, IQ) -> + IQ :: jlib:iq(), Acc :: mongoose_acc:t()) -> + jlib:iq() | {error, term(), jlib:iq()}. +handle_mam_iq(Action, From, To, IQ, Acc) -> case Action of mam_get_prefs -> - handle_get_prefs(To, IQ); + handle_get_prefs(To, IQ, Acc); mam_set_prefs -> - handle_set_prefs(To, IQ); + handle_set_prefs(To, IQ, Acc); mam_set_message_form -> - handle_set_message_form(From, To, IQ); + handle_set_message_form(From, To, IQ, Acc); mam_get_message_form -> - handle_get_message_form(From, To, IQ) + handle_get_message_form(From, To, IQ, Acc) end. --spec handle_set_prefs(jid:jid(), jlib:iq()) -> +-spec handle_set_prefs(jid:jid(), jlib:iq(), mongoose_acc:t()) -> jlib:iq() | {error, term(), jlib:iq()}. -handle_set_prefs(ArcJID=#jid{}, - IQ=#iq{sub_el = PrefsEl}) -> +handle_set_prefs(ArcJID=#jid{}, IQ=#iq{sub_el = PrefsEl}, Acc) -> {DefaultMode, AlwaysJIDs, NeverJIDs} = parse_prefs(PrefsEl), ?LOG_DEBUG(#{what => mam_set_prefs, default_mode => DefaultMode, always_jids => AlwaysJIDs, never_jids => NeverJIDs, iq => IQ}), - Host = server_host(ArcJID), - ArcID = archive_id_int(Host, ArcJID), - Res = set_prefs(Host, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs), + HostType = mongoose_acc:host_type(Acc), + ArcID = archive_id_int(HostType, ArcJID), + Res = set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs), handle_set_prefs_result(Res, DefaultMode, AlwaysJIDs, NeverJIDs, IQ). handle_set_prefs_result(ok, DefaultMode, AlwaysJIDs, NeverJIDs, IQ) -> @@ -380,13 +385,12 @@ handle_set_prefs_result({error, Reason}, _DefaultMode, _AlwaysJIDs, _NeverJIDs, IQ) -> return_error_iq(IQ, Reason). - --spec handle_get_prefs(jid:jid(), IQ :: jlib:iq()) -> +-spec handle_get_prefs(jid:jid(), IQ :: jlib:iq(), Acc :: mongoose_acc:t()) -> jlib:iq() | {error, term(), jlib:iq()}. -handle_get_prefs(ArcJID=#jid{}, IQ=#iq{}) -> - Host = server_host(ArcJID), - ArcID = archive_id_int(Host, ArcJID), - Res = get_prefs(Host, ArcID, ArcJID, always), +handle_get_prefs(ArcJID=#jid{}, IQ=#iq{}, Acc) -> + HostType = mongoose_acc:host_type(Acc), + ArcID = archive_id_int(HostType, ArcJID), + Res = get_prefs(HostType, ArcID, ArcJID, always), handle_get_prefs_result(Res, IQ). handle_get_prefs_result({DefaultMode, AlwaysJIDs, NeverJIDs}, IQ) -> @@ -399,24 +403,23 @@ handle_get_prefs_result({error, Reason}, IQ) -> return_error_iq(IQ, Reason). -spec handle_set_message_form(From :: jid:jid(), ArcJID :: jid:jid(), - IQ :: jlib:iq()) -> + IQ :: jlib:iq(), Acc :: mongoose_acc:t()) -> jlib:iq() | ignore | {error, term(), jlib:iq()}. handle_set_message_form(#jid{} = From, #jid{} = ArcJID, - #iq{xmlns=MamNs, sub_el = QueryEl} = IQ) -> - Host = server_host(ArcJID), - ArcID = archive_id_int(Host, ArcJID), + #iq{xmlns=MamNs, sub_el = QueryEl} = IQ, + Acc) -> + HostType = mongoose_acc:host_type(Acc), + ArcID = archive_id_int(HostType, ArcJID), QueryID = exml_query:attr(QueryEl, <<"queryid">>, <<>>), - Params0 = mam_iq:form_to_lookup_params(IQ, mod_mam_params:max_result_limit(?MODULE, Host), - mod_mam_params:default_result_limit(?MODULE, Host), - mod_mam_params:extra_params_module(?MODULE, Host)), + Params0 = iq_to_lookup_params(HostType, IQ), Params = mam_iq:lookup_params_with_archive_details(Params0, ArcID, ArcJID, From), - case lookup_messages(Host, Params) of + case lookup_messages(HostType, Params) of {error, Reason} -> report_issue(Reason, mam_lookup_failed, ArcJID, IQ), return_error_iq(IQ, Reason); {ok, {TotalCount, Offset, MessageRows}} -> %% Forward messages - {FirstMessID, LastMessID} = forward_messages(From, ArcJID, MamNs, + {FirstMessID, LastMessID} = forward_messages(HostType, From, ArcJID, MamNs, QueryID, MessageRows, true), %% Make fin iq IsComplete = is_complete_result_page(TotalCount, Offset, MessageRows, Params), @@ -426,7 +429,13 @@ handle_set_message_form(#jid{} = From, #jid{} = ArcJID, IQ#iq{type = result, sub_el = [FinElem]} end. -forward_messages(From, ArcJID, MamNs, QueryID, MessageRows, SetClientNs) -> +iq_to_lookup_params(HostType, IQ) -> + Max = mod_mam_params:max_result_limit(?MODULE, HostType), + Def = mod_mam_params:default_result_limit(?MODULE, HostType), + Ext = mod_mam_params:extra_params_module(?MODULE, HostType), + mam_iq:form_to_lookup_params(IQ, Max, Def, Ext). + +forward_messages(HostType, From, ArcJID, MamNs, QueryID, MessageRows, SetClientNs) -> %% Forward messages {FirstMessID, LastMessID} = case MessageRows of @@ -434,8 +443,7 @@ forward_messages(From, ArcJID, MamNs, QueryID, MessageRows, SetClientNs) -> [_|_] -> {message_row_to_ext_id(hd(MessageRows)), message_row_to_ext_id(lists:last(MessageRows))} end, - Host = ArcJID#jid.lserver, - SendModule = mod_mam_params:send_message_mod(?MODULE, Host), + SendModule = mod_mam_params:send_message_mod(?MODULE, HostType), [send_message(SendModule, Row, ArcJID, From, message_row_to_xml(MamNs, Row, QueryID, SetClientNs)) || Row <- MessageRows], @@ -444,16 +452,16 @@ forward_messages(From, ArcJID, MamNs, QueryID, MessageRows, SetClientNs) -> send_message(SendModule, Row, ArcJID, From, Packet) -> mam_send_message:call_send_message(SendModule, Row, ArcJID, From, Packet). --spec handle_get_message_form(jid:jid(), jid:jid(), jlib:iq()) -> +-spec handle_get_message_form(jid:jid(), jid:jid(), jlib:iq(), mongoose_acc:t()) -> jlib:iq(). -handle_get_message_form(_From=#jid{lserver = Host}, _ArcJID=#jid{}, IQ=#iq{}) -> - return_message_form_iq(Host, IQ). - +handle_get_message_form(_From=#jid{}, _ArcJID=#jid{}, IQ=#iq{}, Acc) -> + HostType = mongoose_acc:host_type(Acc), + return_message_form_iq(HostType, IQ). determine_amp_strategy(Strategy = #amp_strategy{deliver = Deliver}, FromJID, ToJID, Packet, initial_check) -> - #jid{lserver = LServer} = ToJID, - ShouldBeStored = is_archivable_message(LServer, incoming, Packet) + HostType = jid_to_host_type(ToJID), + ShouldBeStored = is_archivable_message(HostType, incoming, Packet) andalso is_interesting(ToJID, FromJID) andalso ejabberd_auth:does_user_exist(ToJID), case ShouldBeStored of @@ -474,13 +482,13 @@ handle_package(Dir, ReturnMessID, LocJID = #jid{}, RemJID = #jid{}, SrcJID = #jid{}, Packet, Acc) -> - Host = server_host(LocJID), - case is_archivable_message(Host, Dir, Packet) - andalso should_archive_if_groupchat(Host, exml_query:attr(Packet, <<"type">>)) of + HostType = mongoose_acc:host_type(Acc), + case is_archivable_message(HostType, Dir, Packet) + andalso should_archive_if_groupchat(HostType, exml_query:attr(Packet, <<"type">>)) of true -> - ArcID = archive_id_int(Host, LocJID), + ArcID = archive_id_int(HostType, LocJID), OriginID = mod_mam_utils:get_origin_id(Packet), - case is_interesting(Host, LocJID, RemJID, ArcID) of + case is_interesting(HostType, LocJID, RemJID, ArcID) of true -> MessID = generate_message_id(), Params = #{message_id => MessID, @@ -491,7 +499,7 @@ handle_package(Dir, ReturnMessID, origin_id => OriginID, direction => Dir, packet => Packet}, - Result = archive_message(Host, Params), + Result = archive_message(HostType, Params), ExtMessId = return_external_message_id_if_ok(ReturnMessID, Result, MessID), {ExtMessId, mongoose_acc:set(mam, mam_id, ExtMessId, Acc)}; false -> @@ -501,8 +509,8 @@ handle_package(Dir, ReturnMessID, {undefined, Acc} end. -should_archive_if_groupchat(Host, <<"groupchat">>) -> - gen_mod:get_module_opt(Host, ?MODULE, archive_groupchats, false); +should_archive_if_groupchat(HostType, <<"groupchat">>) -> + gen_mod:get_module_opt(HostType, ?MODULE, archive_groupchats, false); should_archive_if_groupchat(_, _) -> true. @@ -513,12 +521,12 @@ return_external_message_id_if_ok(true, ok, MessID) -> mess_id_to_external_binary return_external_message_id_if_ok(_, _, _MessID) -> undefined. is_interesting(LocJID, RemJID) -> - Host = server_host(LocJID), - ArcID = archive_id_int(Host, LocJID), - is_interesting(Host, LocJID, RemJID, ArcID). + HostType = jid_to_host_type(LocJID), + ArcID = archive_id_int(HostType, LocJID), + is_interesting(HostType, LocJID, RemJID, ArcID). -is_interesting(Host, LocJID, RemJID, ArcID) -> - case get_behaviour(Host, ArcID, LocJID, RemJID) of +is_interesting(HostType, LocJID, RemJID, ArcID) -> + case get_behaviour(HostType, ArcID, LocJID, RemJID) of always -> true; never -> false; roster -> is_jid_in_user_roster(LocJID, RemJID) @@ -527,73 +535,71 @@ is_interesting(Host, LocJID, RemJID, ArcID) -> %% ---------------------------------------------------------------------- %% Backend wrappers --spec archive_id_int(jid:server(), jid:jid()) -> +-spec archive_id_int(host_type(), jid:jid()) -> non_neg_integer() | undefined. -archive_id_int(Host, ArcJID=#jid{}) -> - mongoose_hooks:mam_archive_id(Host, ArcJID). - +archive_id_int(HostType, ArcJID=#jid{}) -> + mongoose_hooks:mam_archive_id(HostType, ArcJID). --spec archive_size(jid:server(), archive_id(), jid:jid()) -> integer(). -archive_size(Host, ArcID, ArcJID=#jid{}) -> - mongoose_hooks:mam_archive_size(Host, ArcID, ArcJID). +-spec archive_size(host_type(), archive_id(), jid:jid()) -> integer(). +archive_size(HostType, ArcID, ArcJID=#jid{}) -> + mongoose_hooks:mam_archive_size(HostType, ArcID, ArcJID). - --spec get_behaviour(jid:server(), archive_id(), LocJID :: jid:jid(), +-spec get_behaviour(host_type(), archive_id(), LocJID :: jid:jid(), RemJID :: jid:jid()) -> atom(). -get_behaviour(Host, ArcID, LocJID=#jid{}, RemJID=#jid{}) -> - mongoose_hooks:mam_get_behaviour(Host, ArcID, LocJID, RemJID). - +get_behaviour(HostType, ArcID, LocJID=#jid{}, RemJID=#jid{}) -> + mongoose_hooks:mam_get_behaviour(HostType, ArcID, LocJID, RemJID). --spec set_prefs(jid:server(), archive_id(), ArcJID :: jid:jid(), +-spec set_prefs(host_type(), archive_id(), ArcJID :: jid:jid(), DefaultMode :: atom(), AlwaysJIDs :: [jid:literal_jid()], NeverJIDs :: [jid:literal_jid()]) -> any(). -set_prefs(Host, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> - mongoose_hooks:mam_set_prefs(Host, ArcID, ArcJID, DefaultMode, +set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> + mongoose_hooks:mam_set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs). - %% @doc Load settings from the database. --spec get_prefs(Host :: jid:server(), ArcID :: archive_id(), +-spec get_prefs(HostType :: host_type(), ArcID :: archive_id(), ArcJID :: jid:jid(), GlobalDefaultMode :: archive_behaviour() ) -> preference() | {error, Reason :: term()}. -get_prefs(Host, ArcID, ArcJID, GlobalDefaultMode) -> - mongoose_hooks:mam_get_prefs(Host, GlobalDefaultMode, ArcID, ArcJID). +get_prefs(HostType, ArcID, ArcJID, GlobalDefaultMode) -> + mongoose_hooks:mam_get_prefs(HostType, GlobalDefaultMode, ArcID, ArcJID). --spec remove_archive_hook(jid:server(), archive_id(), jid:jid()) -> 'ok'. -remove_archive_hook(Host, ArcID, ArcJID=#jid{}) -> - mongoose_hooks:mam_remove_archive(Host, ArcID, ArcJID), +-spec remove_archive_hook(host_type(), archive_id(), jid:jid()) -> 'ok'. +remove_archive_hook(HostType, ArcID, ArcJID=#jid{}) -> + mongoose_hooks:mam_remove_archive(HostType, ArcID, ArcJID), ok. --spec lookup_messages(Host :: jid:server(), - Params :: map()) -> +-spec lookup_messages(HostType :: host_type(), Params :: map()) -> {ok, mod_mam:lookup_result()} | {error, 'policy-violation'} | {error, Reason :: term()}. -lookup_messages(Host, Params) -> - Result = lookup_messages_without_policy_violation_check(Host, Params), +lookup_messages(HostType, Params) -> + Result = lookup_messages_without_policy_violation_check(HostType, Params), %% If a query returns a number of stanzas greater than this limit and the %% client did not specify a limit using RSM then the server should return %% a policy-violation error to the client. mod_mam_utils:check_result_for_policy_violation(Params, Result). -lookup_messages_without_policy_violation_check(Host, #{search_text := SearchText} = Params) -> - case SearchText /= undefined andalso not mod_mam_params:has_full_text_search(?MODULE, Host) of +lookup_messages_without_policy_violation_check( + HostType, #{search_text := SearchText} = Params) -> + case SearchText /= undefined andalso + not mod_mam_params:has_full_text_search(?MODULE, HostType) of true -> %% Use of disabled full text search {error, 'not-supported'}; false -> StartT = erlang:monotonic_time(microsecond), - R = mongoose_hooks:mam_lookup_messages(Host, Params), + R = mongoose_hooks:mam_lookup_messages(HostType, Params), Diff = erlang:monotonic_time(microsecond) - StartT, - mongoose_metrics:update(Host, [backends, ?MODULE, lookup], Diff), + mongoose_metrics:update(HostType, [backends, ?MODULE, lookup], Diff), R end. --spec archive_message(jid:server(), mod_mam:archive_message_params()) -> ok | {error, timeout}. -archive_message(Host, Params) -> +-spec archive_message(host_type(), mod_mam:archive_message_params()) -> + ok | {error, timeout}. +archive_message(HostType, Params) -> StartT = erlang:monotonic_time(microsecond), - R = mongoose_hooks:mam_archive_message(Host, Params), + R = mongoose_hooks:mam_archive_message(HostType, Params), Diff = erlang:monotonic_time(microsecond) - StartT, - mongoose_metrics:update(Host, [backends, ?MODULE, archive], Diff), + mongoose_metrics:update(HostType, [backends, ?MODULE, archive], Diff), R. %% ---------------------------------------------------------------------- @@ -614,8 +620,8 @@ message_row_to_xml(MamNs, #{id := MessID, jid := SrcJID, packet := Packet}, message_row_to_ext_id(#{id := MessID}) -> mess_id_to_external_binary(MessID). -handle_error_iq(Host, Acc, _To, _Action, {error, _Reason, IQ}) -> - mongoose_metrics:update(Host, modMamDroppedIQ, 1), +handle_error_iq(HostType, Acc, _To, _Action, {error, _Reason, IQ}) -> + mongoose_metrics:update(HostType, modMamDroppedIQ, 1), {Acc, IQ}; handle_error_iq(_Host, Acc, _To, _Action, IQ) -> {Acc, IQ}. @@ -633,7 +639,6 @@ return_max_delay_reached_error_iq(IQ) -> <<"en">>, <<"The action is cancelled because of flooding.">>), IQ#iq{type = error, sub_el = [ErrorEl]}. - -spec return_error_iq(jlib:iq(), Reason :: term()) -> {error, term(), jlib:iq()}. return_error_iq(IQ, {Reason, {stacktrace, _Stacktrace}}) -> return_error_iq(IQ, Reason); @@ -646,8 +651,8 @@ return_error_iq(IQ, not_implemented) -> return_error_iq(IQ, Reason) -> {error, Reason, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:internal_server_error()]}}. -return_message_form_iq(Host, IQ) -> - IQ#iq{type = result, sub_el = [message_form(?MODULE, Host, IQ#iq.xmlns)]}. +return_message_form_iq(HostType, IQ) -> + IQ#iq{type = result, sub_el = [message_form(?MODULE, HostType, IQ#iq.xmlns)]}. report_issue({Reason, {stacktrace, Stacktrace}}, Issue, ArcJID, IQ) -> report_issue(Reason, Stacktrace, Issue, ArcJID, IQ); @@ -665,36 +670,56 @@ report_issue(Reason, Stacktrace, Issue, #jid{lserver=LServer, luser=LUser}, IQ) issue => Issue, server => LServer, user => LUser, reason => Reason, iq => IQ, stacktrace => Stacktrace}). --spec is_archivable_message(Host :: jid:lserver(), Dir :: incoming | outgoing, +-spec is_archivable_message(HostType :: host_type(), + Dir :: incoming | outgoing, Packet :: exml:element()) -> boolean(). -is_archivable_message(Host, Dir, Packet) -> - {M, F} = mod_mam_params:is_archivable_message_fun(?MODULE, Host), - ArchiveChatMarkers = mod_mam_params:archive_chat_markers(?MODULE, Host), +is_archivable_message(HostType, Dir, Packet) -> + {M, F} = mod_mam_params:is_archivable_message_fun(?MODULE, HostType), + ArchiveChatMarkers = mod_mam_params:archive_chat_markers(?MODULE, HostType), erlang:apply(M, F, [?MODULE, Dir, Packet, ArchiveChatMarkers]). -config_metrics(Host) -> +config_metrics(HostType) -> OptsToReport = [{backend, rdbms}], %list of tuples {option, default_value} - mongoose_module_metrics:opts_for_module(Host, ?MODULE, OptsToReport). + mongoose_module_metrics:opts_for_module(HostType, ?MODULE, OptsToReport). -spec hooks(jid:lserver()) -> [ejabberd_hooks:hook()]. -hooks(Host) -> - [{user_send_packet, Host, ?MODULE, user_send_packet, 60}, - {rest_user_send_packet, Host, ?MODULE, user_send_packet, 60}, - {filter_local_packet, Host, ?MODULE, filter_packet, 90}, - {remove_user, Host, ?MODULE, remove_user, 50}, - {anonymous_purge_hook, Host, ?MODULE, remove_user, 50}, - {amp_determine_strategy, Host, ?MODULE, determine_amp_strategy, 20}, - {sm_filter_offline_message, Host, ?MODULE, sm_filter_offline_message, 50}, - {get_personal_data, Host, ?MODULE, get_personal_data, 50} - | mongoose_metrics_mam_hooks:get_mam_hooks(Host)]. - - -ensure_metrics(Host) -> - mongoose_metrics:ensure_metric(Host, [backends, ?MODULE, lookup], histogram), - mongoose_metrics:ensure_metric(Host, [Host, modMamLookups, simple], spiral), - mongoose_metrics:ensure_metric(Host, [backends, ?MODULE, archive], histogram), +hooks(HostType) -> + [{user_send_packet, HostType, ?MODULE, user_send_packet, 60}, + {rest_user_send_packet, HostType, ?MODULE, user_send_packet, 60}, + {filter_local_packet, HostType, ?MODULE, filter_packet, 90}, + {remove_user, HostType, ?MODULE, remove_user, 50}, + {anonymous_purge_hook, HostType, ?MODULE, remove_user, 50}, + {amp_determine_strategy, HostType, ?MODULE, determine_amp_strategy, 20}, + {sm_filter_offline_message, HostType, ?MODULE, sm_filter_offline_message, 50}, + {get_personal_data, HostType, ?MODULE, get_personal_data, 50} + | mongoose_metrics_mam_hooks:get_mam_hooks(HostType)]. + +unregister_features(HostType) -> + [mod_disco:unregister_feature(HostType, Feature) || Feature <- features(?MODULE, HostType)], + ok. + +register_features(HostType) -> + [mod_disco:register_feature(HostType, Feature) || Feature <- features(?MODULE, HostType)], + ok. + +add_id_handlers(HostType, Opts) -> + %% `parallel' is the only one recommended here. + IQDisc = gen_mod:get_opt(iqdisc, Opts, parallel), %% Type + gen_iq_handler:add_iq_handler(ejabberd_sm, HostType, ?NS_MAM_04, + ?MODULE, process_mam_iq, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_sm, HostType, ?NS_MAM_06, + ?MODULE, process_mam_iq, IQDisc). + +remove_iq_handlers(HostType) -> + gen_iq_handler:remove_iq_handler(ejabberd_sm, HostType, ?NS_MAM_04), + gen_iq_handler:remove_iq_handler(ejabberd_sm, HostType, ?NS_MAM_06). + +ensure_metrics(HostType) -> + mongoose_metrics:ensure_metric(HostType, [backends, ?MODULE, lookup], histogram), + mongoose_metrics:ensure_metric(HostType, [HostType, modMamLookups, simple], spiral), + mongoose_metrics:ensure_metric(HostType, [backends, ?MODULE, archive], histogram), lists:foreach(fun(Name) -> - mongoose_metrics:ensure_metric(Host, Name, spiral) + mongoose_metrics:ensure_metric(HostType, Name, spiral) end, spirals()). From d5e0ce0034c3f0f07cce19530aa8bbb8b9b485bf Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 12 May 2021 14:04:55 +0200 Subject: [PATCH 02/73] Fix failing test-running autocomplete for muc_SUITE --- big_tests/tests/ct_helper.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/big_tests/tests/ct_helper.erl b/big_tests/tests/ct_helper.erl index 3564c488799..49d03e19313 100644 --- a/big_tests/tests/ct_helper.erl +++ b/big_tests/tests/ct_helper.erl @@ -88,4 +88,12 @@ all_repeat_modes() -> repeat_until_any_fail]. sensible_maximum_repeats() -> - ct:get_config(sensible_maximum_repeats, 100). + case is_ct_started() of + true -> + ct:get_config(sensible_maximum_repeats, 100); + false -> %% In case it's called from test-runner-complete script + 100 + end. + +is_ct_started() -> + lists:keymember(common_test, 1, application:which_applications()). From a7e60fa2459741e34e3321c959170e19376260b7 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 12 May 2021 14:06:33 +0200 Subject: [PATCH 03/73] Fix failing muc_SUITE, if executed in locally without other suites --- big_tests/tests/mongoose_helper.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index d3f0c82a903..77efe884aab 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -239,6 +239,8 @@ stop_online_rooms() -> ok. forget_persistent_rooms() -> + %% To avoid `binary_to_existing_atom(<<"maygetmemberlist">>, utf8)' failing + rpc(mim(), mod_muc_room, module_info, []), Host = ct:get_config({hosts, mim, domain}), {ok, Rooms} = rpc(mim(), mod_muc_db_backend, get_rooms, [Host, muc_helper:muc_host()]), lists:foreach( From 62b586c59dd81fceb63e55c80e97b13503e426f5 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 12 May 2021 14:09:00 +0200 Subject: [PATCH 04/73] Use acc_to_host_type in mod_mam --- src/mam/mod_mam.erl | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/mam/mod_mam.erl b/src/mam/mod_mam.erl index 6026995c270..7f5d51a3531 100644 --- a/src/mam/mod_mam.erl +++ b/src/mam/mod_mam.erl @@ -372,7 +372,7 @@ handle_set_prefs(ArcJID=#jid{}, IQ=#iq{sub_el = PrefsEl}, Acc) -> {DefaultMode, AlwaysJIDs, NeverJIDs} = parse_prefs(PrefsEl), ?LOG_DEBUG(#{what => mam_set_prefs, default_mode => DefaultMode, always_jids => AlwaysJIDs, never_jids => NeverJIDs, iq => IQ}), - HostType = mongoose_acc:host_type(Acc), + HostType = acc_to_host_type(Acc), ArcID = archive_id_int(HostType, ArcJID), Res = set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs), handle_set_prefs_result(Res, DefaultMode, AlwaysJIDs, NeverJIDs, IQ). @@ -388,7 +388,7 @@ handle_set_prefs_result({error, Reason}, -spec handle_get_prefs(jid:jid(), IQ :: jlib:iq(), Acc :: mongoose_acc:t()) -> jlib:iq() | {error, term(), jlib:iq()}. handle_get_prefs(ArcJID=#jid{}, IQ=#iq{}, Acc) -> - HostType = mongoose_acc:host_type(Acc), + HostType = acc_to_host_type(Acc), ArcID = archive_id_int(HostType, ArcJID), Res = get_prefs(HostType, ArcID, ArcJID, always), handle_get_prefs_result(Res, IQ). @@ -408,7 +408,7 @@ handle_get_prefs_result({error, Reason}, IQ) -> handle_set_message_form(#jid{} = From, #jid{} = ArcJID, #iq{xmlns=MamNs, sub_el = QueryEl} = IQ, Acc) -> - HostType = mongoose_acc:host_type(Acc), + HostType = acc_to_host_type(Acc), ArcID = archive_id_int(HostType, ArcJID), QueryID = exml_query:attr(QueryEl, <<"queryid">>, <<>>), Params0 = iq_to_lookup_params(HostType, IQ), @@ -455,7 +455,7 @@ send_message(SendModule, Row, ArcJID, From, Packet) -> -spec handle_get_message_form(jid:jid(), jid:jid(), jlib:iq(), mongoose_acc:t()) -> jlib:iq(). handle_get_message_form(_From=#jid{}, _ArcJID=#jid{}, IQ=#iq{}, Acc) -> - HostType = mongoose_acc:host_type(Acc), + HostType = acc_to_host_type(Acc), return_message_form_iq(HostType, IQ). determine_amp_strategy(Strategy = #amp_strategy{deliver = Deliver}, @@ -479,10 +479,8 @@ amp_deliver_strategy([direct, none]) -> [direct, stored, none]. Packet :: exml:element(), Acc :: mongoose_acc:t()) -> {MaybeMessID :: binary() | undefined, Acc :: mongoose_acc:t()}. handle_package(Dir, ReturnMessID, - LocJID = #jid{}, - RemJID = #jid{}, - SrcJID = #jid{}, Packet, Acc) -> - HostType = mongoose_acc:host_type(Acc), + LocJID = #jid{}, RemJID = #jid{}, SrcJID = #jid{}, Packet, Acc) -> + HostType = acc_to_host_type(Acc), case is_archivable_message(HostType, Dir, Packet) andalso should_archive_if_groupchat(HostType, exml_query:attr(Packet, <<"type">>)) of true -> From 21666af77cc536ed1e53657eae1712eee1be5d6c Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:42:28 +0200 Subject: [PATCH 05/73] Add error text 'Room already exists' to mod_muc_light --- src/muc_light/mod_muc_light.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index 1c176e8bae6..51ebba39a64 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -493,7 +493,8 @@ create_room(Acc, From, FromUS, To, Create0, OrigPacket) -> mod_muc_light_codec_backend:encode({set, Details, To#jid.luser == <<>>}, From, FinalRoomUS, fun ejabberd_router:route/3); {error, exists} -> - mod_muc_light_codec_backend:encode_error({error, conflict}, From, To, OrigPacket, + mod_muc_light_codec_backend:encode_error({error, {conflict, <<"Room already exists">>}}, + From, To, OrigPacket, make_handler_fun(Acc)); {error, bad_request} -> mod_muc_light_codec_backend:encode_error({error, bad_request}, From, To, OrigPacket, From d71cdbf73b4975deda01dd37becb804440687208 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:43:25 +0200 Subject: [PATCH 06/73] Call mod_muc_light_db_backend:force_clear() from a helper in tests, not directly --- big_tests/tests/muc_light_legacy_SUITE.erl | 2 +- big_tests/tests/push_SUITE.erl | 2 +- big_tests/tests/push_integration_SUITE.erl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/big_tests/tests/muc_light_legacy_SUITE.erl b/big_tests/tests/muc_light_legacy_SUITE.erl index ff5f1d340aa..18d84021fb3 100644 --- a/big_tests/tests/muc_light_legacy_SUITE.erl +++ b/big_tests/tests/muc_light_legacy_SUITE.erl @@ -204,7 +204,7 @@ create_room(RoomU, MUCHost, Owner, Members, Config) -> {ok, _RoomUS} = rpc(?BACKEND, create_room, [RoomUS, DefaultConfig, AffUsers, <<"-">>]). clear_db() -> - rpc(?BACKEND, force_clear, []). + muc_light_helper:clear_db(). %%-------------------------------------------------------------------- %% MUC light tests diff --git a/big_tests/tests/push_SUITE.erl b/big_tests/tests/push_SUITE.erl index 421f8336b1e..5187fc02ebd 100644 --- a/big_tests/tests/push_SUITE.erl +++ b/big_tests/tests/push_SUITE.erl @@ -112,7 +112,7 @@ init_per_group(muclight_msg_notifications, Config0) -> [{host, subhost_pattern(?MUCHOST)}, {backend, mongoose_helper:mnesia_or_rdbms_backend()}, {rooms_in_rosters, true}]}]), - rpc(mod_muc_light_db_backend, force_clear, []), + muc_light_helper:clear_db(), Config; init_per_group(_, Config) -> ensure_pusher_module_and_save_old_mods(Config). diff --git a/big_tests/tests/push_integration_SUITE.erl b/big_tests/tests/push_integration_SUITE.erl index 4febbd56723..d3d5243b6ac 100644 --- a/big_tests/tests/push_integration_SUITE.erl +++ b/big_tests/tests/push_integration_SUITE.erl @@ -157,7 +157,7 @@ init_per_group(G, Config) when G =:= pm_notifications_with_inbox; init_per_group(G, Config) -> %% Some cleaning up C = init_modules(G, Config), - catch rpc(?RPC_SPEC, mod_muc_light_db_backend, force_clear, []), + muc_light_helper:clear_db(), C. end_per_group(_, Config) -> From 30de8a554b5fe8bce6bb8c936095ad20a133f50f Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:44:23 +0200 Subject: [PATCH 07/73] Execute filter_room_packet and forget_room using Host not MucHost Add host_type field in the state of MUC room gen servers Pass Acc around in muc-light code (to encode/decode functions of the codecs) --- doc/migrations/4.2.0_4.3.0.md | 4 + include/mod_muc_room.hrl | 1 + src/mam/mod_mam_muc.erl | 46 +++---- src/mod_muc.erl | 12 +- src/mod_muc_room.erl | 18 ++- src/mongoose_hooks.erl | 18 +-- src/muc_light/mod_muc_light.erl | 101 +++++++++------- src/muc_light/mod_muc_light_codec.erl | 14 ++- src/muc_light/mod_muc_light_codec_legacy.erl | 32 ++--- src/muc_light/mod_muc_light_codec_modern.erl | 119 +++++++++++-------- src/muc_light/mod_muc_light_commands.erl | 13 +- src/muc_light/mod_muc_light_db_mnesia.erl | 3 +- src/muc_light/mod_muc_light_room.erl | 75 +++++++----- src/muc_light/mod_muc_light_utils.erl | 51 ++++++-- test/muc_light_SUITE.erl | 2 +- 15 files changed, 307 insertions(+), 202 deletions(-) diff --git a/doc/migrations/4.2.0_4.3.0.md b/doc/migrations/4.2.0_4.3.0.md index f362c8f5e74..2c753854ec0 100644 --- a/doc/migrations/4.2.0_4.3.0.md +++ b/doc/migrations/4.2.0_4.3.0.md @@ -85,6 +85,10 @@ GO - `filter_room_packet` hook uses a map instead of a proplist for the event data information. - `room_send_packet` hook has been removed. Use `filter_room_packet` instead. +- `filter_room_packet` is called for HostType (was for MucHost). +- `forget_room` is called for HostType (was for MucHost). +- `forget_room` takes an extra argument HostType. +- `filter_room_packetz takes an extra argument HostType. ## Metrics REST API (obsolete) diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index b09b2cb86e5..dbb47a8ddc4 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -70,6 +70,7 @@ -record(state, {room :: mod_muc:room(), host :: jid:server(), + host_type :: mongooseim:host_type(), server_host :: jid:server(), access :: mod_muc:access(), jid :: jid:jid(), diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index b2ea20b00f4..3621b01efcd 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -43,10 +43,9 @@ -export([start/2, stop/1]). %% ejabberd room handlers --export([filter_room_packet/2, +-export([filter_room_packet/3, room_process_mam_iq/4, - forget_room/2, - forget_room/3]). + forget_room/4]). %% gdpr callback -export([get_personal_data/2]). @@ -156,7 +155,7 @@ start(Host, Opts) -> ?MODULE, room_process_mam_iq, IQDisc), gen_iq_handler:add_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_06, ?MODULE, room_process_mam_iq, IQDisc), - ejabberd_hooks:add(hooks(Host, MUCHost)), + ejabberd_hooks:add(hooks(Host)), ensure_metrics(Host), ok. @@ -165,7 +164,7 @@ start(Host, Opts) -> stop(Host) -> MUCHost = gen_mod:get_module_opt_subhost(Host, mod_mam_muc, mod_muc:default_host()), ?LOG_DEBUG(#{what => mam_muc_stopping}), - ejabberd_hooks:delete(hooks(Host, MUCHost)), + ejabberd_hooks:delete(hooks(Host)), gen_iq_handler:remove_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_04), gen_iq_handler:remove_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_06), [mod_disco:unregister_feature(MUCHost, Feature) || Feature <- features(?MODULE, Host)], @@ -175,14 +174,12 @@ stop(Host) -> %% hooks and handlers for MUC %% @doc Handle public MUC-message. --spec filter_room_packet(Packet :: packet(), +-spec filter_room_packet(Packet :: packet(), HostType :: mongooseim:host_type(), EventData :: mod_muc:room_event_data()) -> packet(). -filter_room_packet(Packet, EventData = #{ - room_jid := #jid{lserver = LServer} - }) -> +filter_room_packet(Packet, HostType, EventData = #{}) -> ?LOG_DEBUG(#{what => mam_room_packet, text => <<"Incoming room packet">>, packet => Packet, event_data => EventData}), - IsArchivable = is_archivable_message(LServer, incoming, Packet), + IsArchivable = is_archivable_message(HostType, incoming, Packet), case IsArchivable of true -> #{from_nick := FromNick, from_jid := FromJID, room_jid := RoomJID, @@ -260,16 +257,11 @@ room_process_mam_iq(From = #jid{lserver = Host}, To, Acc, IQ) -> %% #rh %% @doc This hook is called from `mod_muc:forget_room(Host, Name)'. --spec forget_room(map(), jid:lserver(), binary()) -> map(). -forget_room(Acc, LServer, RoomName) -> - forget_room(LServer, RoomName), +-spec forget_room(map(), mongooseim:host_type(), jid:lserver(), binary()) -> map(). +forget_room(Acc, _HostType, MucServer, RoomName) -> + delete_archive(MucServer, RoomName), Acc. -%% @doc This hook is called from `mod_muc:forget_room(Host, Name)'. --spec forget_room(jid:lserver(), binary()) -> 'ok'. -forget_room(LServer, RoomName) -> - delete_archive(LServer, RoomName). - %% ---------------------------------------------------------------------- %% Internal functions @@ -594,18 +586,18 @@ report_issue(Reason, Stacktrace, Issue, #jid{lserver = LServer, luser = LUser}, ?LOG_ERROR(#{what => mam_muc_error, issue => Issue, reason => Reason, user => LUser, server => LServer, iq => IQ, stacktrace => Stacktrace}). --spec is_archivable_message(MUCHost :: jid:lserver(), Dir :: incoming | outgoing, +-spec is_archivable_message(HostType :: mongooseim:host_type(), + Dir :: incoming | outgoing, Packet :: exml:element()) -> boolean(). -is_archivable_message(MUCHost, Dir, Packet) -> - {ok, Host} = mongoose_subhosts:get_host(MUCHost), - {M, F} = mod_mam_params:is_archivable_message_fun(?MODULE, Host), - ArchiveChatMarkers = mod_mam_params:archive_chat_markers(?MODULE, Host), +is_archivable_message(HostType, Dir, Packet) -> + {M, F} = mod_mam_params:is_archivable_message_fun(?MODULE, HostType), + ArchiveChatMarkers = mod_mam_params:archive_chat_markers(?MODULE, HostType), erlang:apply(M, F, [?MODULE, Dir, Packet, ArchiveChatMarkers]). --spec hooks(jid:lserver(), jid:lserver()) -> [ejabberd_hooks:hook()]. -hooks(Host, MUCHost) -> - [{filter_room_packet, MUCHost, ?MODULE, filter_room_packet, 60}, - {forget_room, MUCHost, ?MODULE, forget_room, 90}, +-spec hooks(jid:lserver()) -> [ejabberd_hooks:hook()]. +hooks(Host) -> + [{filter_room_packet, Host, ?MODULE, filter_room_packet, 60}, + {forget_room, Host, ?MODULE, forget_room, 90}, {get_personal_data, Host, ?MODULE, get_personal_data, 50} | mongoose_metrics_mam_hooks:get_mam_muc_hooks(Host)]. diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 5d1656cc607..d0154c83cd6 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -322,8 +322,9 @@ restore_room(ServerHost, Host, Name) -> -spec forget_room(jid:server(), jid:server(), room()) -> ok | {error, term()}. forget_room(ServerHost, Host, Name) -> + HostType = server_host_to_host_type(ServerHost), %% Removes room from DB, even if it's already removed. - Result = mod_muc_db_backend:forget_room(ServerHost, Host, Name), + Result = mod_muc_db_backend:forget_room(HostType, Host, Name), case Result of ok -> %% TODO this hook should be refactored to be executed on ServerHost, not Host. @@ -1323,3 +1324,12 @@ ensure_metrics(_Host) -> config_metrics(Host) -> OptsToReport = [{backend, mnesia}], %list of tuples {option, defualt_value} mongoose_module_metrics:opts_for_module(Host, ?MODULE, OptsToReport). + +-spec server_host_to_host_type(jid:lserver()) -> mongooseim:host_type(). +server_host_to_host_type(ServerHost) -> + case mongoose_domain_api:get_host_type(ServerHost) of + {ok, HostType} -> + HostType; + {error, not_found} -> + ServerHost + end. diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 6292fe3d388..c52bee57314 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -273,9 +273,10 @@ can_access_identity(RoomJID, UserJID) -> init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Creator, _Nick, DefRoomOpts]) when is_list(DefRoomOpts) -> process_flag(trap_exit, true), + HostType = server_host_to_host_type(ServerHost), Shaper = shaper:new(RoomShaper), State = set_affiliation(Creator, owner, - #state{host = Host, + #state{host = Host, host_type = HostType, server_host = ServerHost, access = Access, room = Room, @@ -303,8 +304,9 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, %% @doc A room is restored init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts]) -> process_flag(trap_exit, true), + HostType = server_host_to_host_type(ServerHost), Shaper = shaper:new(RoomShaper), - State = set_opts(Opts, #state{host = Host, + State = set_opts(Opts, #state{host = Host, host_type = HostType, server_host = ServerHost, access = Access, room = Room, @@ -880,7 +882,8 @@ broadcast_room_packet(From, FromNick, Role, Packet, StateData) -> EventData = #{from_nick => FromNick, from_jid => From, room_jid => StateData#state.jid, role => Role, affiliation => Affiliation}, - FilteredPacket = mongoose_hooks:filter_room_packet(StateData#state.host, Packet, EventData), + FilteredPacket = mongoose_hooks:filter_room_packet( + StateData#state.host_type, Packet, EventData), RouteFrom = jid:replace_resource(StateData#state.jid, FromNick), RoomJid = StateData#state.jid, @@ -4765,3 +4768,12 @@ notify_users_modified(#state{server_host = Host, jid = JID, users = Users} = Sta ls(LogMap, State) -> maps:merge(LogMap, #{room => State#state.room, sub_host => State#state.host}). + +-spec server_host_to_host_type(jid:lserver()) -> mongooseim:host_type(). +server_host_to_host_type(ServerHost) -> + case mongoose_domain_api:get_host_type(ServerHost) of + {ok, HostType} -> + HostType; + {error, not_found} -> + ServerHost + end. diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index 77c2a1d7c74..3641aae0a13 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -1379,22 +1379,22 @@ amp_verify_support(Server, Rules) -> %% MUC and MUC Light related hooks --spec filter_room_packet(Server, Packet, EventData) -> Result when - Server :: jid:lserver(), +-spec filter_room_packet(HostType, Packet, EventData) -> Result when + HostType :: mongooseim:host_type(), Packet :: exml:element(), EventData :: mod_muc:room_event_data(), Result :: exml:element(). -filter_room_packet(Server, Packet, EventData) -> - ejabberd_hooks:run_for_host_type(filter_room_packet, Server, Packet, [EventData]). +filter_room_packet(HostType, Packet, EventData) -> + ejabberd_hooks:run_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(HookServer, Host, Room) -> Result when - HookServer :: jid:server(), - Host :: jid:server(), +-spec forget_room(HostType, MucHost, Room) -> Result when + HostType :: jid:server(), + MucHost :: jid:server(), Room :: jid:luser(), Result :: any(). -forget_room(HookServer, Host, Room) -> - ejabberd_hooks:run_for_host_type(forget_room, HookServer, ok, [Host, Room]). +forget_room(HostType, MucHost, Room) -> + ejabberd_hooks:run_for_host_type(forget_room, HostType, ok, [HostType, MucHost, Room]). -spec invitation_sent(HookServer, Host, RoomJID, From, To, Reason) -> Result when HookServer :: jid:server(), diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index 51ebba39a64..d0e4abe21a4 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -29,7 +29,7 @@ %% For Administration API -export([try_to_create_room/3, - change_room_config/4, + change_room_config/5, delete_room/1]). %% gen_mod callbacks @@ -114,16 +114,18 @@ try_to_create_room(CreatorUS, RoomJID, #create{raw_config = RawConfig} = Creatio end. -spec change_room_config(UserJid :: jid:jid(), RoomID :: jid:resource(), - MUCLightDomain :: jid:server(), ConfigReq :: config_req_props()) -> + MUCLightDomain :: jid:server(), + ConfigReq :: config_req_props(), + Acc :: mongoose_acc:t()) -> {ok, jid:jid(), config_req_props()} | {error, validation_error() | bad_request | not_allowed}. -change_room_config(UserJid, RoomID, MUCLightDomain, ConfigReq) -> +change_room_config(UserJid, RoomID, MUCLightDomain, ConfigReq, Acc) -> R = {RoomID, MUCLightDomain}, RoomJID = jid:make(RoomID, MUCLightDomain, <<>>), RoomUS = jid:to_lus(RoomJID), AffUsersRes = mod_muc_light_db_backend:get_aff_users(RoomUS), - case mod_muc_light_room:process_request(UserJid, R, {set, ConfigReq}, AffUsersRes) of + case mod_muc_light_room:process_request(UserJid, R, {set, ConfigReq}, AffUsersRes, Acc) of {set, ConfigResp, _} -> {ok, RoomJID, ConfigResp}; {error, _Reason} = E -> @@ -265,7 +267,7 @@ hooks(Host, MUCHost) -> -spec process_packet(Acc :: mongoose_acc:t(), From ::jid:jid(), To ::jid:jid(), El :: exml:element(), Extra :: map()) -> any(). process_packet(Acc, From, To, El, _Extra) -> - process_decoded_packet(From, To, mod_muc_light_codec_backend:decode(From, To, El), Acc, El). + process_decoded_packet(From, To, mod_muc_light_codec_backend:decode(From, To, El, Acc), Acc, El). -spec process_decoded_packet(From :: jid:jid(), To :: jid:jid(), DecodedPacket :: mod_muc_light_codec:decode_result(), @@ -280,15 +282,15 @@ process_decoded_packet(From, To, {ok, {set, #create{} = Create}}, Acc, OrigPacke mod_muc_light_codec_backend:encode_error( {error, bad_request}, From, To, OrigPacket, make_handler_fun(Acc)) end; -process_decoded_packet(From, To, {ok, {get, #disco_info{} = DI}}, _Acc, _OrigPacket) -> - handle_disco_info_get(From, To, DI); -process_decoded_packet(From, To, {ok, {get, #disco_items{} = DI}}, _Acc, OrigPacket) -> - handle_disco_items_get(From, To, DI, OrigPacket); +process_decoded_packet(From, To, {ok, {get, #disco_info{} = DI}}, Acc, _OrigPacket) -> + handle_disco_info_get(From, To, DI, Acc); +process_decoded_packet(From, To, {ok, {get, #disco_items{} = DI}}, Acc, OrigPacket) -> + handle_disco_items_get(Acc, From, To, DI, OrigPacket); process_decoded_packet(From, To, {ok, {_, #blocking{}} = Blocking}, Acc, OrigPacket) -> RouteFun = make_handler_fun(Acc), case gen_mod:get_module_opt_by_subhost(To#jid.lserver, ?MODULE, blocking, ?DEFAULT_BLOCKING) of true -> - case handle_blocking(From, To, Blocking) of + case handle_blocking(Acc, From, To, Blocking) of {error, _} = Res -> mod_muc_light_codec_backend:encode_error(Res, From, To, OrigPacket, RouteFun); _ -> @@ -362,8 +364,8 @@ remove_user(Acc, User, Server) -> ?LOG_ERROR(#{what => muc_remove_user_failed, reason => Reason, acc => Acc}); AffectedRooms -> - bcast_removed_user(UserUS, AffectedRooms, Version), - maybe_forget_rooms(AffectedRooms), + bcast_removed_user(Acc, UserUS, AffectedRooms, Version), + maybe_forget_rooms(Acc, AffectedRooms), Acc end. @@ -404,13 +406,14 @@ add_rooms_to_roster(Acc, UserJID) -> {stop, mongoose_acc:t()} | mongoose_acc:t(). process_iq_get(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ, _ActiveList) -> MUCHost = gen_mod:get_module_opt_subhost(FromS, ?MODULE, default_host()), - case {mod_muc_light_codec_backend:decode(From, To, IQ), + case {mod_muc_light_codec_backend:decode(From, To, IQ, Acc), gen_mod:get_module_opt_by_subhost(MUCHost, ?MODULE, blocking, ?DEFAULT_BLOCKING)} of {{ok, {get, #blocking{} = Blocking}}, true} -> Items = mod_muc_light_db_backend:get_blocking(jid:to_lus(From), MUCHost), mod_muc_light_codec_backend:encode( {get, Blocking#blocking{ items = Items }}, From, jid:to_lus(To), - fun(_, _, Packet) -> put(encode_res, Packet) end), + fun(_, _, Packet) -> put(encode_res, Packet) end, + Acc), #xmlel{ children = ResponseChildren } = erase(encode_res), Result = {result, ResponseChildren}, {stop, mongoose_acc:set(hook, result, Result, Acc)}; @@ -427,7 +430,7 @@ process_iq_get(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ, _ActiveList) {stop, mongoose_acc:t()} | mongoose_acc:t(). process_iq_set(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ) -> MUCHost = gen_mod:get_module_opt_subhost(FromS, ?MODULE, default_host()), - case {mod_muc_light_codec_backend:decode(From, To, IQ), + case {mod_muc_light_codec_backend:decode(From, To, IQ, Acc), gen_mod:get_module_opt_by_subhost(MUCHost, ?MODULE, blocking, ?DEFAULT_BLOCKING)} of {{ok, {set, #blocking{ items = Items }} = Blocking}, true} -> RouteFun = fun(_, _, Packet) -> put(encode_res, Packet) end, @@ -438,7 +441,7 @@ process_iq_set(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ) -> {error, mongoose_xmpp_errors:bad_request()}, Acc)}; false -> ok = mod_muc_light_db_backend:set_blocking(jid:to_lus(From), MUCHost, Items), - mod_muc_light_codec_backend:encode(Blocking, From, jid:to_lus(To), RouteFun), + mod_muc_light_codec_backend:encode(Blocking, From, jid:to_lus(To), RouteFun, Acc), #xmlel{ children = ResponseChildren } = erase(encode_res), {stop, mongoose_acc:set(hook, result, {result, ResponseChildren}, Acc)} end; @@ -491,10 +494,10 @@ create_room(Acc, From, FromUS, To, Create0, OrigPacket) -> case try_to_create_room(FromUS, To, Create0) of {ok, FinalRoomUS, Details} -> mod_muc_light_codec_backend:encode({set, Details, To#jid.luser == <<>>}, From, - FinalRoomUS, fun ejabberd_router:route/3); + FinalRoomUS, fun ejabberd_router:route/3, Acc); {error, exists} -> mod_muc_light_codec_backend:encode_error({error, {conflict, <<"Room already exists">>}}, - From, To, OrigPacket, + From, To, OrigPacket, make_handler_fun(Acc)); {error, bad_request} -> mod_muc_light_codec_backend:encode_error({error, bad_request}, From, To, OrigPacket, @@ -533,14 +536,18 @@ process_create_aff_users(Creator, AffUsers, EqualOccupants) -> creator_aff(true) -> member; creator_aff(false) -> owner. --spec handle_disco_info_get(From ::jid:jid(), To ::jid:jid(), DiscoInfo :: disco_info_req_props()) -> ok. -handle_disco_info_get(From, To, DiscoInfo) -> +-spec handle_disco_info_get(From ::jid:jid(), To ::jid:jid(), + DiscoInfo :: disco_info_req_props(), + Acc :: mongoose_acc:t()) -> ok. +handle_disco_info_get(From, To, DiscoInfo, Acc) -> mod_muc_light_codec_backend:encode({get, DiscoInfo}, From, jid:to_lus(To), - fun ejabberd_router:route/3). + fun ejabberd_router:route/3, Acc). --spec handle_disco_items_get(From ::jid:jid(), To ::jid:jid(), DiscoItems :: disco_items_req_props(), +-spec handle_disco_items_get(Acc :: mongoose_acc:t(), + From ::jid:jid(), To ::jid:jid(), + DiscoItems :: disco_items_req_props(), OrigPacket :: exml:element()) -> ok. -handle_disco_items_get(From, To, DiscoItems0, OrigPacket) -> +handle_disco_items_get(Acc, From, To, DiscoItems0, OrigPacket) -> case catch mod_muc_light_db_backend:get_user_rooms(jid:to_lus(From), To#jid.lserver) of {error, Error} -> ?LOG_ERROR(#{what => muc_get_user_rooms_failed, @@ -557,8 +564,9 @@ handle_disco_items_get(From, To, DiscoItems0, OrigPacket) -> page_service_limit(DiscoItems0#disco_items.rsm, RoomsPerPage)) of {ok, RoomsInfoSlice, RSMOut} -> DiscoItems = DiscoItems0#disco_items{ rooms = RoomsInfoSlice, rsm = RSMOut }, - mod_muc_light_codec_backend:encode({get, DiscoItems}, From, jid:to_lus(To), - RouteFun); + mod_muc_light_codec_backend:encode({get, DiscoItems}, + From, jid:to_lus(To), + RouteFun, Acc); {error, item_not_found} -> mod_muc_light_codec_backend:encode_error({error, item_not_found}, From, To, OrigPacket, RouteFun) @@ -641,38 +649,38 @@ find_room_pos(RoomUS, [{RoomUS, _, _} | _], Pos) -> Pos; find_room_pos(RoomUS, [_ | RRooms], Pos) -> find_room_pos(RoomUS, RRooms, Pos + 1); find_room_pos(_, [], _) -> {error, item_not_found}. --spec handle_blocking(From :: jid:jid(), To :: jid:jid(), +-spec handle_blocking(Acc :: mongoose_acc:t(), From :: jid:jid(), To :: jid:jid(), BlockingReq :: {get | set, blocking_req_props()}) -> {error, bad_request} | ok. -handle_blocking(From, To, {get, #blocking{} = Blocking}) -> +handle_blocking(Acc, From, To, {get, #blocking{} = Blocking}) -> BlockingItems = mod_muc_light_db_backend:get_blocking(jid:to_lus(From), To#jid.lserver), mod_muc_light_codec_backend:encode({get, Blocking#blocking{ items = BlockingItems }}, - From, jid:to_lus(To), fun ejabberd_router:route/3); -handle_blocking(From, To, {set, #blocking{ items = Items }} = BlockingReq) -> + From, jid:to_lus(To), fun ejabberd_router:route/3, Acc); +handle_blocking(Acc, From, To, {set, #blocking{ items = Items }} = BlockingReq) -> case lists:any(fun({_, _, {WhoU, WhoS}}) -> WhoU =:= <<>> orelse WhoS =:= <<>> end, Items) of true -> {error, bad_request}; false -> ok = mod_muc_light_db_backend:set_blocking(jid:to_lus(From), To#jid.lserver, Items), mod_muc_light_codec_backend:encode( - BlockingReq, From, jid:to_lus(To), fun ejabberd_router:route/3), + BlockingReq, From, jid:to_lus(To), fun ejabberd_router:route/3, Acc), ok end. --spec bcast_removed_user(UserUS :: jid:simple_bare_jid(), +-spec bcast_removed_user(Acc :: mongoose_acc:t(), UserUS :: jid:simple_bare_jid(), AffectedRooms :: mod_muc_light_db:remove_user_return(), Version :: binary()) -> ok. -bcast_removed_user({UserU, UserS}, AffectedRooms, Version) -> - bcast_removed_user(jid:make_noprep(UserU, UserS, <<>>), AffectedRooms, +bcast_removed_user(Acc, {UserU, UserS}, AffectedRooms, Version) -> + bcast_removed_user(Acc, jid:make_noprep(UserU, UserS, <<>>), AffectedRooms, Version, mongoose_bin:gen_from_timestamp()). --spec bcast_removed_user(UserJID :: jid:jid(), +-spec bcast_removed_user(Acc :: mongoose_acc:t(), UserJID :: jid:jid(), AffectedRooms :: mod_muc_light_db:remove_user_return(), Version :: binary(), PacketID :: binary()) -> ok. -bcast_removed_user(_UserJID, [], _Version, _ID) -> +bcast_removed_user(_Acc, _UserJID, [], _Version, _ID) -> ok; -bcast_removed_user(UserJID, +bcast_removed_user(Acc, UserJID, [{RoomUS, {ok, OldAffUsers, NewAffUsers, AffUsersChanged, PrevVersion}} | RAffected], Version, ID) -> Affiliations = #affiliations{ @@ -681,21 +689,22 @@ bcast_removed_user(UserJID, version = Version, aff_users = AffUsersChanged }, - mod_muc_light_codec_backend:encode({set, Affiliations, OldAffUsers, NewAffUsers}, - UserJID, RoomUS, fun ejabberd_router:route/3), - bcast_removed_user(UserJID, RAffected, Version, ID); -bcast_removed_user(UserJID, [{{RoomU, RoomS} = RoomUS, Error} | RAffected], Version, ID) -> + Cmd = {set, Affiliations, OldAffUsers, NewAffUsers}, + mod_muc_light_codec_backend:encode(Cmd, UserJID, RoomUS, fun ejabberd_router:route/3, Acc), + bcast_removed_user(Acc, UserJID, RAffected, Version, ID); +bcast_removed_user(Acc, UserJID, [{{RoomU, RoomS} = RoomUS, Error} | RAffected], Version, ID) -> ?LOG_ERROR(#{what => muc_remove_user_failed, user_jid => jid:to_binary(UserJID), room => RoomU, sub_host => RoomS, reason => Error}), - bcast_removed_user(UserJID, RAffected, Version, ID). + bcast_removed_user(Acc, UserJID, RAffected, Version, ID). --spec maybe_forget_rooms(AffectedRooms :: mod_muc_light_db:remove_user_return()) -> ok. -maybe_forget_rooms([]) -> +-spec maybe_forget_rooms(Acc :: mongoose_acc:t(), + AffectedRooms :: mod_muc_light_db:remove_user_return()) -> ok. +maybe_forget_rooms(_Acc, []) -> ok; -maybe_forget_rooms([{RoomUS, {ok, _, NewAffUsers, _, _}} | RAffectedRooms]) -> - mod_muc_light_room:maybe_forget(RoomUS, NewAffUsers), - maybe_forget_rooms(RAffectedRooms). +maybe_forget_rooms(Acc, [{RoomUS, {ok, _, NewAffUsers, _, _}} | RAffectedRooms]) -> + mod_muc_light_room:maybe_forget(Acc, RoomUS, NewAffUsers), + maybe_forget_rooms(Acc, RAffectedRooms). make_handler_fun(Acc) -> fun(From, To, Packet) -> ejabberd_router:route(From, To, Acc, Packet) end. diff --git a/src/muc_light/mod_muc_light_codec.erl b/src/muc_light/mod_muc_light_codec.erl index 8d3094cefa8..0d2cad61f99 100644 --- a/src/muc_light/mod_muc_light_codec.erl +++ b/src/muc_light/mod_muc_light_codec.erl @@ -26,12 +26,14 @@ %% Behaviour callbacks %%==================================================================== --callback decode(From :: jid:jid(), To :: jid:jid(), Stanza :: exml:element()) -> +-callback decode(From :: jid:jid(), To :: jid:jid(), Stanza :: exml:element(), + Acc :: mongoose_acc:t()) -> decode_result(). -callback encode(Request :: muc_light_encode_request(), OriginalSender :: jid:jid(), RoomUS :: jid:simple_bare_jid(), % may be just service domain - HandleFun :: encoded_packet_handler()) -> any(). + HandleFun :: encoded_packet_handler(), + Acc :: mongoose_acc:t()) -> any(). -callback encode_error(ErrMsg :: tuple(), OrigFrom :: jid:jid(), OrigTo :: jid:jid(), OrigPacket :: exml:element(), HandleFun :: encoded_packet_handler()) -> @@ -53,11 +55,13 @@ encode_error(ErrMsg, ExtraChildren, OrigFrom, OrigTo, OrigPacket, HandleFun) -> make_error_elem({error, not_allowed}) -> mongoose_xmpp_errors:not_allowed(); make_error_elem({error, bad_request}) -> - mongoose_xmpp_errors:bad_request(); + mongoose_xmpp_errors:bad_request(<<"en">>, <<"Uncategorized bad request">>); make_error_elem({error, item_not_found}) -> mongoose_xmpp_errors:item_not_found(); -make_error_elem({error, conflict}) -> - mongoose_xmpp_errors:conflict(); +make_error_elem({error, {conflict, Text}}) -> + mongoose_xmpp_errors:conflict(<<"en">>, Text); +make_error_elem({error, {bad_request, Text}}) -> + make_error_elem({error, bad_request, Text}); make_error_elem({error, bad_request, Text}) -> mongoose_xmpp_errors:bad_request(<<"en">>, iolist_to_binary(Text)); make_error_elem({error, feature_not_implemented}) -> diff --git a/src/muc_light/mod_muc_light_codec_legacy.erl b/src/muc_light/mod_muc_light_codec_legacy.erl index b64ac380266..4d04c356546 100644 --- a/src/muc_light/mod_muc_light_codec_legacy.erl +++ b/src/muc_light/mod_muc_light_codec_legacy.erl @@ -11,7 +11,7 @@ -behaviour(mod_muc_light_codec). %% API --export([decode/3, encode/4, encode_error/5]). +-export([decode/4, encode/5, encode_error/5]). -include("mongoose.hrl"). -include("jlib.hrl"). @@ -22,8 +22,9 @@ %%==================================================================== -spec decode(From :: jid:jid(), To :: jid:jid(), - Stanza :: jlib:iq() | exml:element()) -> mod_muc_light_codec:decode_result(). -decode(_From, #jid{ luser = ToU } = _To, #xmlel{ name = <<"presence">> } = Stanza) + Stanza :: jlib:iq() | exml:element(), + Acc :: mongoose_acc:t()) -> mod_muc_light_codec:decode_result(). +decode(_From, #jid{ luser = ToU } = _To, #xmlel{ name = <<"presence">> } = Stanza, _Acc) when ToU =/= <<>> -> case {exml_query:path(Stanza, [{element, <<"x">>}, {attr, <<"xmlns">>}]), exml_query:attr(Stanza, <<"type">>)} of @@ -31,21 +32,22 @@ decode(_From, #jid{ luser = ToU } = _To, #xmlel{ name = <<"presence">> } = Stanz Available =:= <<"available">> -> {ok, {set, #create{}}}; _ -> ignore end; -decode(_From, #jid{ lresource = Resource }, _Stanza) when Resource =/= <<>> -> +decode(_From, #jid{ lresource = Resource }, _Stanza, _Acc) when Resource =/= <<>> -> {error, bad_request}; -decode(_From, _To, #xmlel{ name = <<"message">> } = Stanza) -> +decode(_From, _To, #xmlel{ name = <<"message">> } = Stanza, _Acc) -> decode_message(Stanza); -decode(From, _To, #xmlel{ name = <<"iq">> } = Stanza) -> +decode(From, _To, #xmlel{ name = <<"iq">> } = Stanza, _Acc) -> decode_iq(From, jlib:iq_query_info(Stanza)); -decode(From, _To, #iq{} = IQ) -> +decode(From, _To, #iq{} = IQ, _Acc) -> decode_iq(From, IQ); -decode(_, _, _) -> +decode(_, _, _, _Acc) -> {error, bad_request}. --spec encode(Request :: muc_light_encode_request(), OriginalSender :: jid:jid(), - RoomUS :: jid:simple_bare_jid(), - HandleFun :: mod_muc_light_codec:encoded_packet_handler()) -> any(). -encode({#msg{} = Msg, AffUsers}, Sender, {RoomU, RoomS} = RoomUS, HandleFun) -> +-spec encode(Request :: muc_light_encode_request(), + OriginalSender :: jid:jid(), RoomUS :: jid:simple_bare_jid(), + HandleFun :: mod_muc_light_codec:encoded_packet_handler(), + Acc :: mongoose_acc:t()) -> any(). +encode({#msg{} = Msg, AffUsers}, Sender, {RoomU, RoomS} = RoomUS, HandleFun, Acc) -> US = jid:to_lus(Sender), Aff = get_sender_aff(AffUsers, US), FromNick = jid:to_binary(jid:to_lus(Sender)), @@ -61,13 +63,14 @@ encode({#msg{} = Msg, AffUsers}, Sender, {RoomU, RoomS} = RoomUS, HandleFun) -> room_jid => jid:make_noprep(RoomU, RoomS, <<>>), affiliation => Aff, role => mod_muc_light_utils:light_aff_to_muc_role(Aff)}, + HostType = mod_muc_light_utils:acc_to_host_type(Acc), #xmlel{ children = Children } - = mongoose_hooks:filter_room_packet(RoomS, MsgForArch, EventData), + = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData), lists:foreach( fun({{U, S}, _}) -> send_to_aff_user(RoomJID, U, S, <<"message">>, Attrs, Children, HandleFun) end, AffUsers); -encode(OtherCase, Sender, RoomUS, HandleFun) -> +encode(OtherCase, Sender, RoomUS, HandleFun, _Acc) -> {RoomJID, RoomBin} = jids_from_room_with_resource(RoomUS, <<>>), case encode_meta(OtherCase, RoomJID, Sender, HandleFun) of {iq_reply, ID} -> @@ -490,4 +493,3 @@ get_sender_aff(Users, US) -> {US, Aff} -> Aff; _ -> undefined end. - diff --git a/src/muc_light/mod_muc_light_codec_modern.erl b/src/muc_light/mod_muc_light_codec_modern.erl index 4d1b152aa04..7c44e6ad1ae 100644 --- a/src/muc_light/mod_muc_light_codec_modern.erl +++ b/src/muc_light/mod_muc_light_codec_modern.erl @@ -11,7 +11,7 @@ -behaviour(mod_muc_light_codec). %% API --export([decode/3, encode/4, encode_error/5]). +-export([decode/4, encode/5, encode_error/5]). -include("mongoose.hrl"). -include("jlib.hrl"). @@ -29,25 +29,29 @@ class => Class, reason => Reason, stacktrace => Stacktrace}), {error, bad_request}). +-type bad_request() :: bad_request | {bad_request, binary()}. + %%==================================================================== %% API %%==================================================================== --spec decode(From :: jid:jid(), To :: jid:jid(), Stanza :: exml:element()) -> +-spec decode(From :: jid:jid(), To :: jid:jid(), Stanza :: exml:element(), + Acc :: mongoose_acc:t()) -> mod_muc_light_codec:decode_result(). -decode(_From, #jid{ lresource = Resource }, _Stanza) when Resource =/= <<>> -> - {error, bad_request}; -decode(_From, _To, #xmlel{ name = <<"message">> } = Stanza) -> +decode(_From, #jid{ lresource = Resource }, _Stanza, _Acc) when Resource =/= <<>> -> + {error, {bad_request, <<"Resource expected to be empty">>}}; +decode(_From, _To, #xmlel{ name = <<"message">> } = Stanza, _Acc) -> decode_message(Stanza); -decode(From, _To, #xmlel{ name = <<"iq">> } = Stanza) -> +decode(From, _To, #xmlel{ name = <<"iq">> } = Stanza, _Acc) -> decode_iq(From, jlib:iq_query_info(Stanza)); -decode(_, _, _) -> - {error, bad_request}. +decode(_, _, _, _) -> + {error, {bad_request, <<"Failed to decode unknown format">>}}. -spec encode(Request :: muc_light_encode_request(), OriginalSender :: jid:jid(), RoomUS :: jid:simple_bare_jid(), - HandleFun :: mod_muc_light_codec:encoded_packet_handler()) -> any(). -encode({#msg{} = Msg, AffUsers}, Sender, {RoomU, RoomS} = RoomUS, HandleFun) -> + HandleFun :: mod_muc_light_codec:encoded_packet_handler(), + Acc :: mongoose_acc:t()) -> any(). +encode({#msg{} = Msg, AffUsers}, Sender, {RoomU, RoomS} = RoomUS, HandleFun, Acc) -> US = jid:to_lus(Sender), FromNick = jid:to_binary(US), Aff = get_sender_aff(AffUsers, US), @@ -63,15 +67,16 @@ encode({#msg{} = Msg, AffUsers}, Sender, {RoomU, RoomS} = RoomUS, HandleFun) -> room_jid => jid:make_noprep(RoomU, RoomS, <<>>), affiliation => Aff, role => mod_muc_light_utils:light_aff_to_muc_role(Aff)}, + HostType = mod_muc_light_utils:acc_to_host_type(Acc), #xmlel{ children = Children } - = mongoose_hooks:filter_room_packet(RoomS, MsgForArch, EventData), + = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData), lists:foreach( fun({{U, S}, _}) -> msg_to_aff_user(RoomJID, U, S, Attrs, Children, HandleFun) end, AffUsers); -encode(OtherCase, Sender, RoomUS, HandleFun) -> +encode(OtherCase, Sender, RoomUS, HandleFun, Acc) -> {RoomJID, RoomBin} = jids_from_room_with_resource(RoomUS, <<>>), - case encode_iq(OtherCase, Sender, RoomJID, RoomBin, HandleFun) of + case encode_iq(OtherCase, Sender, RoomJID, RoomBin, HandleFun, Acc) of {reply, ID} -> IQRes = make_iq_result(RoomBin, jid:to_binary(Sender), ID, <<>>, undefined), HandleFun(RoomJID, Sender, IQRes); @@ -126,7 +131,7 @@ ensure_id({_, Id}) -> Id. %%==================================================================== -spec decode_iq(From :: jid:jid(), IQ :: jlib:iq()) -> - {ok, muc_light_packet() | muc_light_disco() | jlib:iq()} | {error, bad_request} | ignore. + {ok, muc_light_packet() | muc_light_disco() | jlib:iq()} | {error, bad_request()} | ignore. decode_iq(_From, #iq{ xmlns = ?NS_MUC_LIGHT_CONFIGURATION, type = get, sub_el = QueryEl, id = ID }) -> Version = exml_query:path(QueryEl, [{element, <<"version">>}, cdata], <<>>), @@ -187,21 +192,21 @@ decode_iq(_From, #iq{ type = error }) -> decode_iq(_From, #iq{} = IQ) -> {ok, IQ}; decode_iq(_, _) -> - {error, bad_request}. + {error, {bad_request, <<"Unknown IQ format">>}}. %% ------------------ Parsers ------------------ -spec parse_config(Els :: [jlib:xmlch()]) -> {ok, mod_muc_light_room_config:binary_kv()} - | {error, bad_request}. + | {error, bad_request()}. parse_config(Els) -> parse_config(Els, []). -spec parse_config(Els :: [jlib:xmlch()], ConfigAcc :: mod_muc_light_room_config:binary_kv()) -> - {ok, mod_muc_light_room_config:binary_kv()} | {error, bad_request}. + {ok, mod_muc_light_room_config:binary_kv()} | {error, bad_request()}. parse_config([], ConfigAcc) -> {ok, ConfigAcc}; parse_config([#xmlel{ name = <<"version">> } | _], _) -> - {error, bad_request}; + {error, {bad_request, <<"Version element not allowed">>}}; parse_config([#xmlel{ name = Key, children = [ #xmlcdata{ content = Value } ] } | REls], ConfigAcc) -> parse_config(REls, [{Key, Value} | ConfigAcc]); @@ -209,12 +214,12 @@ parse_config([_ | REls], ConfigAcc) -> parse_config(REls, ConfigAcc). -spec parse_aff_users(Els :: [jlib:xmlch()]) -> - {ok, aff_users()} | {error, bad_request}. + {ok, aff_users()} | {error, bad_request()}. parse_aff_users(Els) -> parse_aff_users(Els, []). -spec parse_aff_users(Els :: [jlib:xmlch()], AffUsersAcc :: aff_users()) -> - {ok, aff_users()} | {error, bad_request}. + {ok, aff_users()} | {error, bad_request()}. parse_aff_users([], AffUsersAcc) -> {ok, AffUsersAcc}; parse_aff_users([#xmlcdata{} | RItemsEls], AffUsersAcc) -> @@ -226,7 +231,7 @@ parse_aff_users([#xmlel{ name = <<"user">>, attrs = [{<<"affiliation">>, AffBin} Aff = mod_muc_light_utils:b2aff(AffBin), parse_aff_users(RItemsEls, [{jid:to_lus(JID), Aff} | AffUsersAcc]); parse_aff_users(_, _) -> - {error, bad_request}. + {error, {bad_request, <<"Failed to parse affiliations">>}}. -spec parse_blocking_list(Els :: [jlib:xmlch()]) -> {ok, [blocking_item()]} | {error, bad_request}. parse_blocking_list(ItemsEls) -> @@ -250,7 +255,8 @@ parse_blocking_list(_, _) -> %% Encoding %%==================================================================== -encode_iq({get, #disco_info{ id = ID }}, Sender, RoomJID, _RoomBin, _HandleFun) -> +encode_iq({get, #disco_info{ id = ID }}, + Sender, RoomJID, _RoomBin, _HandleFun, _Acc) -> {result, RegisteredFeatures} = mod_disco:get_local_features(empty, Sender, RoomJID, <<>>, <<>>), DiscoEls = [#xmlel{name = <<"identity">>, attrs = [{<<"category">>, <<"conference">>}, @@ -260,7 +266,7 @@ encode_iq({get, #disco_info{ id = ID }}, Sender, RoomJID, _RoomBin, _HandleFun) [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, URN}]} || {{URN, _Host}} <- RegisteredFeatures], {reply, ?NS_DISCO_INFO, DiscoEls, ID}; encode_iq({get, #disco_items{ rooms = Rooms, id = ID, rsm = RSMOut }}, - _Sender, _RoomJID, _RoomBin, _HandleFun) -> + _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) -> DiscoEls = [ #xmlel{ name = <<"item">>, attrs = [{<<"jid">>, <>}, {<<"name">>, RoomName}, @@ -268,23 +274,26 @@ encode_iq({get, #disco_items{ rooms = Rooms, id = ID, rsm = RSMOut }}, || {{RoomU, RoomS}, RoomName, RoomVersion} <- Rooms ], {reply, ?NS_DISCO_ITEMS, jlib:rsm_encode(RSMOut) ++ DiscoEls, ID}; encode_iq({get, #config{ prev_version = SameVersion, version = SameVersion, id = ID }}, - _Sender, _RoomJID, _RoomBin, _HandleFun) -> + _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) -> {reply, ID}; -encode_iq({get, #config{} = Config}, _Sender, _RoomJID, _RoomBin, _HandleFun) -> +encode_iq({get, #config{} = Config}, + _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) -> ConfigEls = [ kv_to_el(Field) || Field <- [{<<"version">>, Config#config.version} | Config#config.raw_config] ], {reply, ?NS_MUC_LIGHT_CONFIGURATION, ConfigEls, Config#config.id}; encode_iq({get, #affiliations{ prev_version = SameVersion, version = SameVersion, id = ID }}, - _Sender, _RoomJID, _RoomBin, _HandleFun) -> + _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) -> {reply, ID}; -encode_iq({get, #affiliations{ version = Version } = Affs}, _Sender, _RoomJID, _RoomBin, _HandleFun) -> +encode_iq({get, #affiliations{ version = Version } = Affs}, + _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) -> AffEls = [ aff_user_to_el(AffUser) || AffUser <- Affs#affiliations.aff_users ], {reply, ?NS_MUC_LIGHT_AFFILIATIONS, [kv_to_el(<<"version">>, Version) | AffEls], Affs#affiliations.id}; encode_iq({get, #info{ prev_version = SameVersion, version = SameVersion, id = ID }}, - _Sender, _RoomJID, _RoomBin, _HandleFun) -> + _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) -> {reply, ID}; -encode_iq({get, #info{ version = Version } = Info}, _Sender, _RoomJID, _RoomBin, _HandleFun) -> +encode_iq({get, #info{ version = Version } = Info}, + _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) -> ConfigEls = [ kv_to_el(Field) || Field <- Info#info.raw_config ], AffEls = [ aff_user_to_el(AffUser) || AffUser <- Info#info.aff_users ], InfoEls = [ @@ -293,7 +302,8 @@ encode_iq({get, #info{ version = Version } = Info}, _Sender, _RoomJID, _RoomBin, #xmlel{ name = <<"occupants">>, children = AffEls } ], {reply, ?NS_MUC_LIGHT_INFO, InfoEls, Info#info.id}; -encode_iq({set, #affiliations{} = Affs, OldAffUsers, NewAffUsers}, _Sender, RoomJID, RoomBin, HandleFun) -> +encode_iq({set, #affiliations{} = Affs, OldAffUsers, NewAffUsers}, + _Sender, RoomJID, RoomBin, HandleFun, Acc) -> Attrs = [ {<<"id">>, Affs#affiliations.id}, {<<"type">>, <<"groupchat">>}, @@ -307,20 +317,24 @@ encode_iq({set, #affiliations{} = Affs, OldAffUsers, NewAffUsers}, _Sender, Room children = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS, NotifForCurrentNoPrevVersion) }, EventData = room_event(RoomJID), + HostType = mod_muc_light_utils:acc_to_host_type(Acc), #xmlel{children = FinalChildrenForCurrentNoPrevVersion} - = mongoose_hooks:filter_room_packet(RoomJID#jid.lserver, MsgForArch, EventData), + = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData), FinalChildrenForCurrent = inject_prev_version(FinalChildrenForCurrentNoPrevVersion, Affs#affiliations.prev_version), bcast_aff_messages(RoomJID, OldAffUsers, NewAffUsers, Attrs, VersionEl, FinalChildrenForCurrent, HandleFun), {reply, Affs#affiliations.id}; -encode_iq({get, #blocking{} = Blocking}, _Sender, _RoomJID, _RoomBin, _HandleFun) -> +encode_iq({get, #blocking{} = Blocking}, + _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) -> BlockingEls = [ blocking_to_el(BlockingItem) || BlockingItem <- Blocking#blocking.items ], {reply, ?NS_MUC_LIGHT_BLOCKING, BlockingEls, Blocking#blocking.id}; -encode_iq({set, #blocking{ id = ID }}, _Sender, _RoomJID, _RoomBin, _HandleFun) -> +encode_iq({set, #blocking{ id = ID }}, + _Sender, _RoomJID, _RoomBin, _HandleFun, _Acc) -> {reply, ID}; -encode_iq({set, #create{} = Create, UniqueRequested}, _Sender, RoomJID, RoomBin, HandleFun) -> +encode_iq({set, #create{} = Create, UniqueRequested}, + _Sender, RoomJID, RoomBin, HandleFun, Acc) -> Attrs = [ {<<"id">>, Create#create.id}, {<<"type">>, <<"groupchat">>}, @@ -334,7 +348,8 @@ encode_iq({set, #create{} = Create, UniqueRequested}, _Sender, RoomJID, RoomBin, MsgForArch = #xmlel{ name = <<"message">>, attrs = Attrs, children = msg_envelope(?NS_MUC_LIGHT_AFFILIATIONS, AllAffsEls) }, EventData = room_event(RoomJID), - mongoose_hooks:filter_room_packet(RoomJID#jid.lserver, MsgForArch, EventData), + HostType = mod_muc_light_utils:acc_to_host_type(Acc), + mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData), %% IQ reply "from" %% Sent from service JID when unique room was requested @@ -345,7 +360,8 @@ encode_iq({set, #create{} = Create, UniqueRequested}, _Sender, RoomJID, RoomBin, false -> {RoomJID, RoomBin} end, {reply, ResFromJID, ResFromBin, <<>>, undefined, Create#create.id}; -encode_iq({set, #destroy{ id = ID }, AffUsers}, _Sender, RoomJID, RoomBin, HandleFun) -> +encode_iq({set, #destroy{ id = ID }, AffUsers}, + _Sender, RoomJID, RoomBin, HandleFun, _Acc) -> Attrs = [ {<<"id">>, ID}, {<<"type">>, <<"groupchat">>}, @@ -363,7 +379,22 @@ encode_iq({set, #destroy{ id = ID }, AffUsers}, _Sender, RoomJID, RoomBin, Handl end, AffUsers), {reply, ID}; -encode_iq({set, #config{} = Config, AffUsers}, _Sender, RoomJID, RoomBin, HandleFun) -> +encode_iq({set, #config{} = Config, AffUsers}, + _Sender, RoomJID, RoomBin, HandleFun, Acc) -> + MsgForArch = encode_set_config(Config, RoomBin), + EventData = room_event(RoomJID), + HostType = mod_muc_light_utils:acc_to_host_type(Acc), + #xmlel{ children = FinalConfigNotif } + = mongoose_hooks:filter_room_packet(HostType, MsgForArch, EventData), + + lists:foreach( + fun({{U, S}, _}) -> + msg_to_aff_user(RoomJID, U, S, MsgForArch#xmlel.attrs, FinalConfigNotif, HandleFun) + end, AffUsers), + + {reply, Config#config.id}. + +encode_set_config(Config, RoomBin) -> Attrs = [ {<<"id">>, Config#config.id}, {<<"type">>, <<"groupchat">>}, @@ -373,18 +404,8 @@ encode_iq({set, #config{} = Config, AffUsers}, _Sender, RoomJID, RoomBin, Handle ConfigNotif = [ kv_to_el(<<"prev-version">>, Config#config.prev_version), kv_to_el(<<"version">>, Config#config.version) | ConfigEls ], - MsgForArch = #xmlel{ name = <<"message">>, attrs = Attrs, - children = msg_envelope(?NS_MUC_LIGHT_CONFIGURATION, ConfigNotif) }, - EventData = room_event(RoomJID), - #xmlel{ children = FinalConfigNotif } - = mongoose_hooks:filter_room_packet(RoomJID#jid.lserver, MsgForArch, EventData), - - lists:foreach( - fun({{U, S}, _}) -> - msg_to_aff_user(RoomJID, U, S, Attrs, FinalConfigNotif, HandleFun) - end, AffUsers), - - {reply, Config#config.id}. + #xmlel{name = <<"message">>, attrs = Attrs, + children = msg_envelope(?NS_MUC_LIGHT_CONFIGURATION, ConfigNotif) }. %% --------------------------- Helpers --------------------------- diff --git a/src/muc_light/mod_muc_light_commands.erl b/src/muc_light/mod_muc_light_commands.erl index f6be9eed574..56f0b2532f6 100644 --- a/src/muc_light/mod_muc_light_commands.erl +++ b/src/muc_light/mod_muc_light_commands.erl @@ -193,18 +193,29 @@ change_affiliation(Domain, RoomID, Sender, Recipient0, Affiliation) -> <<"set">>, [Changes])). change_room_config(Domain, RoomID, RoomName, User, Subject) -> + LServer = jid:nameprep(Domain), + HostType = lserver_to_host_type(LServer), MUCLightDomain = gen_mod:get_module_opt_subhost( Domain, mod_muc_light, mod_muc_light:default_host()), UserUS = jid:binary_to_bare(User), ConfigReq = #config{ raw_config = [{<<"roomname">>, RoomName}, {<<"subject">>, Subject}]}, - case mod_muc_light:change_room_config(UserUS, RoomID, MUCLightDomain, ConfigReq) of + Acc = mongoose_acc:new(#{location => ?LOCATION, lserver => LServer, host_type => HostType}), + case mod_muc_light:change_room_config(UserUS, RoomID, MUCLightDomain, ConfigReq, Acc) of {ok, _RoomJID, _} -> ok; {error, Reason} -> {error, internal, Reason} end. +lserver_to_host_type(LServer) -> + case mongoose_domain_api:get_host_type(LServer) of + {ok, HostType} -> + HostType; + {error, not_found} -> + LServer + end. + send_message(Domain, RoomName, Sender, Message) -> Body = #xmlel{name = <<"body">>, children = [ #xmlcdata{ content = Message } ] diff --git a/src/muc_light/mod_muc_light_db_mnesia.erl b/src/muc_light/mod_muc_light_db_mnesia.erl index 3286eceb91e..1bb0437e598 100644 --- a/src/muc_light/mod_muc_light_db_mnesia.erl +++ b/src/muc_light/mod_muc_light_db_mnesia.erl @@ -234,7 +234,8 @@ get_info(RoomUS) -> -spec force_clear() -> ok. force_clear() -> - lists:foreach(fun({RoomU, RoomS}) -> mongoose_hooks:forget_room(RoomS, RoomS, RoomU) end, + %% XXX This is supported only by Mnesia backend! + lists:foreach(fun(RoomUS) -> mod_muc_light_utils:run_forget_room_hook(RoomUS) end, mnesia:dirty_all_keys(muc_light_room)), lists:foreach(fun mnesia:clear_table/1, [muc_light_room, muc_light_user_room, muc_light_blocking]). diff --git a/src/muc_light/mod_muc_light_room.erl b/src/muc_light/mod_muc_light_room.erl index 2c0f01208ca..e5531f1fa4e 100644 --- a/src/muc_light/mod_muc_light_room.erl +++ b/src/muc_light/mod_muc_light_room.erl @@ -24,7 +24,7 @@ -author('piotr.nosek@erlang-solutions.com'). %% API --export([handle_request/5, maybe_forget/2, process_request/4]). +-export([handle_request/5, maybe_forget/3, process_request/5]). %% Callbacks -export([participant_limit_check/2]). @@ -44,14 +44,16 @@ handle_request(From, To, OrigPacket, Request, Acc) -> RoomUS = jid:to_lus(To), AffUsersRes = mod_muc_light_db_backend:get_aff_users(RoomUS), - Response = process_request(From, RoomUS, Request, AffUsersRes), + Response = process_request(From, RoomUS, Request, AffUsersRes, Acc), send_response(From, To, RoomUS, OrigPacket, Response, Acc). --spec maybe_forget(RoomUS :: jid:simple_bare_jid(), NewAffUsers :: aff_users()) -> any(). -maybe_forget({RoomU, RoomS} = RoomUS, []) -> - mongoose_hooks:forget_room(RoomS, RoomS, RoomU), +-spec maybe_forget(Acc :: mongoose_acc:t(), + RoomUS :: jid:simple_bare_jid(), NewAffUsers :: aff_users()) -> any(). +maybe_forget(Acc, {RoomU, RoomS} = RoomUS, []) -> + HostType = mod_muc_light_utils:acc_to_host_type(Acc), + mongoose_hooks:forget_room(HostType, RoomS, RoomU), mod_muc_light_db_backend:destroy_room(RoomUS); -maybe_forget(_, _) -> +maybe_forget(_Acc, _, _) -> my_room_will_go_on. %%==================================================================== @@ -76,48 +78,55 @@ participant_limit_check({_, MUCServer} = _RoomUS, NewAffUsers) -> -spec process_request(From :: jid:jid(), RoomUS :: jid:simple_bare_jid(), Request :: muc_light_packet(), - AffUsersRes :: {ok, aff_users(), binary()} | {error, term()}) -> + AffUsersRes :: {ok, aff_users(), binary()} | {error, term()}, + Acc :: mongoose_acc:t()) -> packet_processing_result(). -process_request(_From, _RoomUS, _Request, {error, _} = Error) -> +process_request(_From, _RoomUS, _Request, {error, _} = Error, _Acc) -> Error; -process_request(From, RoomUS, Request, {ok, AffUsers, _Ver}) -> +process_request(From, RoomUS, Request, {ok, AffUsers, _Ver}, Acc) -> UserUS = jid:to_lus(From), Auth = lists:keyfind(UserUS, 1, AffUsers), - process_request(Request, From, UserUS, RoomUS, Auth, AffUsers). + process_request(Request, From, UserUS, RoomUS, Auth, AffUsers, Acc). -spec process_request(Request :: muc_light_packet(), From ::jid:jid(), UserUS :: jid:simple_bare_jid(), RoomUS :: jid:simple_bare_jid(), Auth :: false | aff_user(), - AffUsers :: aff_users()) -> + AffUsers :: aff_users(), + Acc :: mongoose_acc:t()) -> packet_processing_result(). -process_request(_Request, _From, _UserUS, _RoomUS, false, _AffUsers) -> +process_request(_Request, _From, _UserUS, _RoomUS, false, _AffUsers, _Acc) -> {error, item_not_found}; -process_request(#msg{} = Msg, _From, _UserUS, _RoomUS, _Auth, AffUsers) -> +process_request(#msg{} = Msg, _From, _UserUS, _RoomUS, _Auth, AffUsers, _Acc) -> {Msg, AffUsers}; -process_request({get, #config{} = ConfigReq}, _From, _UserUS, RoomUS, _Auth, _AffUsers) -> +process_request({get, #config{} = ConfigReq}, + _From, _UserUS, RoomUS, _Auth, _AffUsers, _Acc) -> {_, RoomS} = RoomUS, {ok, Config, RoomVersion} = mod_muc_light_db_backend:get_config(RoomUS), RawConfig = mod_muc_light_room_config:to_binary_kv(Config, mod_muc_light:config_schema(RoomS)), {get, ConfigReq#config{ version = RoomVersion, raw_config = RawConfig }}; -process_request({get, #affiliations{} = AffReq}, _From, _UserUS, RoomUS, _Auth, _AffUsers) -> +process_request({get, #affiliations{} = AffReq}, + _From, _UserUS, RoomUS, _Auth, _AffUsers, _Acc) -> {ok, AffUsers, RoomVersion} = mod_muc_light_db_backend:get_aff_users(RoomUS), {get, AffReq#affiliations{ version = RoomVersion, aff_users = AffUsers }}; -process_request({get, #info{} = InfoReq}, _From, _UserUS, {_, RoomS} = RoomUS, _Auth, _AffUsers) -> +process_request({get, #info{} = InfoReq}, + _From, _UserUS, {_, RoomS} = RoomUS, _Auth, _AffUsers, _Acc) -> {ok, Config, AffUsers, RoomVersion} = mod_muc_light_db_backend:get_info(RoomUS), RawConfig = mod_muc_light_room_config:to_binary_kv(Config, mod_muc_light:config_schema(RoomS)), {get, InfoReq#info{ version = RoomVersion, aff_users = AffUsers, raw_config = RawConfig }}; -process_request({set, #config{} = ConfigReq}, _From, _UserUS, {_, MUCServer} = RoomUS, - {_, UserAff}, AffUsers) -> +process_request({set, #config{} = ConfigReq}, + _From, _UserUS, {_, MUCServer} = RoomUS, + {_, UserAff}, AffUsers, _Acc) -> AllCanConfigure = gen_mod:get_module_opt_by_subhost( MUCServer, mod_muc_light, all_can_configure, ?DEFAULT_ALL_CAN_CONFIGURE), process_config_set(ConfigReq, RoomUS, UserAff, AffUsers, AllCanConfigure); -process_request({set, #affiliations{} = AffReq}, _From, UserUS, {_, MUCServer} = RoomUS, - {_, UserAff}, AffUsers) -> +process_request({set, #affiliations{} = AffReq}, + _From, UserUS, {_, MUCServer} = RoomUS, + {_, UserAff}, AffUsers, Acc) -> OwnerUS = case lists:keyfind(owner, 2, AffUsers) of false -> undefined; {OwnerUS0, _} -> OwnerUS0 @@ -133,14 +142,16 @@ process_request({set, #affiliations{} = AffReq}, _From, UserUS, {_, MUCServer} = validate_aff_changes_by_member( AffReq#affiliations.aff_users, [], UserUS, OwnerUS, RoomUS, AllCanInvite) end, - process_aff_set(AffReq, RoomUS, ValidateResult); -process_request({set, #destroy{} = DestroyReq}, _From, _UserUS, RoomUS, {_, owner}, AffUsers) -> + process_aff_set(AffReq, RoomUS, ValidateResult, Acc); +process_request({set, #destroy{} = DestroyReq}, + _From, _UserUS, RoomUS, {_, owner}, AffUsers, Acc) -> ok = mod_muc_light_db_backend:destroy_room(RoomUS), - maybe_forget(RoomUS, []), + maybe_forget(Acc, RoomUS, []), {set, DestroyReq, AffUsers}; -process_request({set, #destroy{}}, _From, _UserUS, _RoomUS, _Auth, _AffUsers) -> +process_request({set, #destroy{}}, + _From, _UserUS, _RoomUS, _Auth, _AffUsers, _Acc) -> {error, not_allowed}; -process_request(_UnknownReq, _From, _UserUS, _RoomUS, _Auth, _AffUsers) -> +process_request(_UnknownReq, _From, _UserUS, _RoomUS, _Auth, _AffUsers, _Acc) -> {error, bad_request}. %% --------- Config set --------- @@ -195,17 +206,18 @@ validate_aff_changes_by_member(_AffUsersChanges, _Acc, _UserUS, _OwnerUS, _RoomU -spec process_aff_set(AffReq :: affiliations_req_props(), RoomUS :: jid:simple_bare_jid(), - ValidateResult :: {ok, aff_users()} | {error, not_allowed}) -> + ValidateResult :: {ok, aff_users()} | {error, not_allowed}, + Acc :: mongoose_acc:t()) -> {set, affiliations_req_props(), OldAffUsers :: aff_users(), NewAffUsers :: aff_users()} | {error, not_allowed}. -process_aff_set(AffReq, _RoomUS, {ok, []}) -> % It seems that all users blocked this request +process_aff_set(AffReq, _RoomUS, {ok, []}, _Acc) -> % It seems that all users blocked this request {set, AffReq, [], []}; % Just return result to the user, don't change or broadcast anything -process_aff_set(AffReq, RoomUS, {ok, FilteredAffUsers}) -> +process_aff_set(AffReq, RoomUS, {ok, FilteredAffUsers}, Acc) -> NewVersion = mongoose_bin:gen_from_timestamp(), case mod_muc_light_db_backend:modify_aff_users(RoomUS, FilteredAffUsers, fun ?MODULE:participant_limit_check/2, NewVersion) of {ok, OldAffUsers, NewAffUsers, AffUsersChanged, OldVersion} -> - maybe_forget(RoomUS, NewAffUsers), + maybe_forget(Acc, RoomUS, NewAffUsers), {set, AffReq#affiliations{ prev_version = OldVersion, version = NewVersion, @@ -214,7 +226,7 @@ process_aff_set(AffReq, RoomUS, {ok, FilteredAffUsers}) -> Error -> Error end; -process_aff_set(_AffReq, _RoomUS, Error) -> +process_aff_set(_AffReq, _RoomUS, Error, _Acc) -> Error. %%==================================================================== @@ -228,7 +240,8 @@ send_response(From, RoomJID, _RoomUS, OrigPacket, {error, _} = Err, _Acc) -> mod_muc_light_codec_backend:encode_error( Err, From, RoomJID, OrigPacket, fun ejabberd_router:route/3); send_response(From, _RoomJID, RoomUS, _OriginalPacket, Response, Acc) -> - mod_muc_light_codec_backend:encode(Response, From, RoomUS, make_handler_fun(Acc)). + F = make_handler_fun(Acc), + mod_muc_light_codec_backend:encode(Response, From, RoomUS, F, Acc). %%==================================================================== %% Internal functions diff --git a/src/muc_light/mod_muc_light_utils.erl b/src/muc_light/mod_muc_light_utils.erl index 2fb4b7680e5..f9548556761 100644 --- a/src/muc_light/mod_muc_light_utils.erl +++ b/src/muc_light/mod_muc_light_utils.erl @@ -29,6 +29,8 @@ -export([light_aff_to_muc_role/1]). -export([room_limit_reached/2]). -export([filter_out_prevented/3]). +-export([acc_to_host_type/1]). +-export([run_forget_room_hook/1]). -include("jlib.hrl"). -include("mongoose.hrl"). @@ -45,18 +47,20 @@ -export_type([change_aff_success/0]). +-type bad_request() :: bad_request | {bad_request, binary()}. + %%==================================================================== %% API %%==================================================================== -spec change_aff_users(CurrentAffUsers :: aff_users(), AffUsersChangesAssorted :: aff_users()) -> - change_aff_success() | {error, bad_request}. + change_aff_success() | {error, bad_request()}. change_aff_users(AffUsers, AffUsersChangesAssorted) -> case {lists:keyfind(owner, 2, AffUsers), lists:keyfind(owner, 2, AffUsersChangesAssorted)} of {false, false} -> % simple, no special cases apply_aff_users_change(AffUsers, AffUsersChangesAssorted); {false, {_, _}} -> % ownerless room! - {error, bad_request}; + {error, {bad_request, <<"Ownerless room">>}}; _ -> lists:foldl(fun(F, Acc) -> F(Acc) end, apply_aff_users_change(AffUsers, AffUsersChangesAssorted), @@ -144,8 +148,8 @@ filter_out_loop(_FromUS, _MUCServer, _BlockingQuery, _RoomsPerUser, []) -> %% ---------------- Affiliations manipulation ---------------- --spec maybe_select_new_owner(ChangeResult :: change_aff_success() | {error, bad_request}) -> - change_aff_success() | {error, bad_request}. +-spec maybe_select_new_owner(ChangeResult :: change_aff_success() | {error, bad_request()}) -> + change_aff_success() | {error, bad_request()}. maybe_select_new_owner({ok, AU, AUC, JoiningUsers, LeavingUsers} = _AffRes) -> {AffUsers, AffUsersChanged} = case is_new_owner_needed(AU) andalso find_new_owner(AU, AUC, JoiningUsers) of @@ -196,8 +200,8 @@ select_promotion(_OldMembers, _JoiningUsers, [U | _]) -> select_promotion(_, _, _) -> false. --spec maybe_demote_old_owner(ChangeResult :: change_aff_success() | {error, bad_request}) -> - change_aff_success() | {error, bad_request}. +-spec maybe_demote_old_owner(ChangeResult :: change_aff_success() | {error, bad_request()}) -> + change_aff_success() | {error, bad_request()}. maybe_demote_old_owner({ok, AU, AUC, JoiningUsers, LeavingUsers}) -> Owners = [U || {U, owner} <- AU], PromotedOwners = [U || {U, owner} <- AUC], @@ -210,14 +214,14 @@ maybe_demote_old_owner({ok, AU, AUC, JoiningUsers, LeavingUsers}) -> NewAUC = [{OldOwner, member} | AUC], {ok, NewAU, NewAUC, JoiningUsers, LeavingUsers}; _ -> - {error, bad_request} + {error, {bad_request, <<"Failed to demote old owner">>}} end; maybe_demote_old_owner(Error) -> Error. -spec apply_aff_users_change(AffUsers :: aff_users(), AffUsersChanges :: aff_users()) -> - change_aff_success() | {error, bad_request}. + change_aff_success() | {error, bad_request()}. apply_aff_users_change(AU, AUC) -> JoiningUsers = proplists:get_keys(AUC) -- proplists:get_keys(AU), AffAndNewUsers = lists:sort(AU ++ [{U, none} || U <- JoiningUsers]), @@ -234,16 +238,15 @@ apply_aff_users_change(AU, AUC) -> NewAffUsers :: aff_users(), AffUsersChanges :: aff_users(), ChangesDone :: aff_users()) -> - change_aff_success_without_users() | {error, bad_request}. + change_aff_success_without_users() | {error, bad_request()}. apply_aff_users_change([], NAU, [], CD) -> %% User list must be sorted ascending but acc is currently sorted descending {ok, lists:reverse(NAU), CD}; apply_aff_users_change(_AU, _NAU, [{User, _}, {User, _} | _RAUC], _CD) -> - %% Cannot change affiliation for the same user twice in the same request - {error, bad_request}; + {error, {bad_request, <<"Cannot change affiliation for the same user " + "twice in the same request">>}}; apply_aff_users_change([AffUser | _], _NAU, [AffUser | _], _CD) -> - %% Meaningless change - {error, bad_request}; + {error, {bad_request, <<"Meaningless change">>}}; apply_aff_users_change([{User, _} | RAU], NAU, [{User, none} | RAUC], CD) -> %% removing user from the room apply_aff_users_change(RAU, NAU, RAUC, [{User, none} | CD]); @@ -260,6 +263,28 @@ apply_aff_users_change([OldUser | RAU], NAU, AUC, CD) -> %% keep user affiliation unchanged apply_aff_users_change(RAU, [OldUser | NAU], AUC, CD). +-spec acc_to_host_type(mongoose_acc:t()) -> mongooseim:host_type(). +acc_to_host_type(Acc) -> + case mongoose_acc:host_type(Acc) of + undefined -> + MucHost = mongoose_acc:lserver(Acc), + muc_host_to_host_type(MucHost); + HostType -> + HostType + end. +server_host_to_host_type(LServer) -> + case mongoose_domain_api:get_host_type(LServer) of + {ok, HostType} -> + HostType; + {error, not_found} -> + LServer + end. +muc_host_to_host_type(MucHost) -> + {ok, ServerHost} = mongoose_subhosts:get_host(MucHost), + server_host_to_host_type(ServerHost). +run_forget_room_hook({Room, MucHost}) -> + HostType = muc_host_to_host_type(MucHost), + mongoose_hooks:forget_room(HostType, MucHost, Room). diff --git a/test/muc_light_SUITE.erl b/test/muc_light_SUITE.erl index ab63c00a419..6b370a18f4e 100644 --- a/test/muc_light_SUITE.erl +++ b/test/muc_light_SUITE.erl @@ -118,7 +118,7 @@ codec_calls(_Config) -> HandleFun = fun(_, _, _) -> count_call(handler) end, ejabberd_hooks:add(filter_room_packet, <<"localhost">>, - fun(Acc, _EvData) -> count_call(hook), Acc end, + fun(Acc, _HostType, _EvData) -> count_call(hook), Acc end, 50), % count_call/1 should've been called twice - by handler fun (for each affiliated user, From a2fe66078637ba7d12cc481d2436d80ea2af6d2e Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 17 May 2021 11:58:53 +0200 Subject: [PATCH 08/73] Run is_muc_room_owner for host_type --- doc/migrations/4.2.0_4.3.0.md | 4 +++- src/mod_muc.erl | 11 ++++++----- src/mongoose_hooks.erl | 10 +++++----- src/muc_light/mod_muc_light.erl | 9 +++++---- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/doc/migrations/4.2.0_4.3.0.md b/doc/migrations/4.2.0_4.3.0.md index 2c753854ec0..293fbb0d3ff 100644 --- a/doc/migrations/4.2.0_4.3.0.md +++ b/doc/migrations/4.2.0_4.3.0.md @@ -88,7 +88,9 @@ GO - `filter_room_packet` is called for HostType (was for MucHost). - `forget_room` is called for HostType (was for MucHost). - `forget_room` takes an extra argument HostType. -- `filter_room_packetz takes an extra argument HostType. +- `filter_room_packet` takes an extra argument HostType. +- `is_muc_room_owner` is called for HostType (was for MucHost). +- `is_muc_room_owner` takes an extra argument HostType. ## Metrics REST API (obsolete) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index d0154c83cd6..81083ebae6b 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -59,7 +59,7 @@ -export([process_packet/5]). %% Hooks handlers --export([is_room_owner/3, +-export([is_muc_room_owner/4, muc_room_pid/2, can_access_room/3, can_access_identity/3]). @@ -413,7 +413,7 @@ init([Host, Opts]) -> hibernated_room_check_interval = CheckInterval, hibernated_room_timeout = HibernatedTimeout}, - ejabberd_hooks:add(is_muc_room_owner, MyHost, ?MODULE, is_room_owner, 50), + ejabberd_hooks:add(is_muc_room_owner, Host, ?MODULE, is_muc_room_owner, 50), ejabberd_hooks:add(muc_room_pid, MyHost, ?MODULE, muc_room_pid, 50), ejabberd_hooks:add(can_access_room, MyHost, ?MODULE, can_access_room, 50), ejabberd_hooks:add(can_access_identity, MyHost, ?MODULE, can_access_identity, 50), @@ -452,7 +452,7 @@ set_persistent_rooms_timer(#state{hibernated_room_check_interval = Timeout}) -> %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call(stop, _From, State) -> - ejabberd_hooks:delete(is_muc_room_owner, State#state.host, ?MODULE, is_room_owner, 50), + ejabberd_hooks:delete(is_muc_room_owner, State#state.server_host, ?MODULE, is_muc_room_owner, 50), ejabberd_hooks:delete(muc_room_pid, State#state.host, ?MODULE, muc_room_pid, 50), ejabberd_hooks:delete(can_access_room, State#state.host, ?MODULE, can_access_room, 50), ejabberd_hooks:delete(can_access_identity, State#state.host, ?MODULE, can_access_identity, 50), @@ -1246,8 +1246,9 @@ clean_table_from_bad_node(Node, Host) -> %% Hooks handlers %%==================================================================== --spec is_room_owner(Acc :: boolean(), Room :: jid:jid(), User :: jid:jid()) -> boolean(). -is_room_owner(_, Room, User) -> +-spec is_muc_room_owner(Acc :: boolean(), HostType :: mongooseim:host_type(), + Room :: jid:jid(), User :: jid:jid()) -> boolean(). +is_muc_room_owner(_, _HostType, Room, User) -> mod_muc_room:is_room_owner(Room, User) =:= {ok, true}. -spec muc_room_pid(Acc :: any(), Room :: jid:jid()) -> {ok, pid()} | {error, not_found}. diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index 3641aae0a13..e55ff9c4df0 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -886,14 +886,14 @@ roster_set(LServer, From, To, SubEl) -> %%% `Acc', `Room', `User'. %%% The arguments and the return value types correspond to the %%% following spec. --spec is_muc_room_owner(HookServer, Room, User) -> Result when - HookServer :: jid:lserver(), +-spec is_muc_room_owner(HostType, Room, User) -> Result when + HostType :: mongooseim:host_type(), Room :: jid:jid(), User :: jid:jid(), Result :: boolean(). -is_muc_room_owner(HookServer, Room, User) -> - ejabberd_hooks:run_for_host_type(is_muc_room_owner, HookServer, false, - [Room, User]). +is_muc_room_owner(HostType, Room, User) -> + ejabberd_hooks:run_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. diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index d0e4abe21a4..7601e7ec004 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -49,7 +49,7 @@ add_rooms_to_roster/2, process_iq_get/5, process_iq_set/4, - is_room_owner/3, + is_muc_room_owner/4, can_access_room/3, can_access_identity/3, muc_room_pid/2]). @@ -250,7 +250,7 @@ process_config_schema_value([{integer_value, Val}]) -> {Val, integer}; process_config_schema_value([{float_value, Val}]) -> {Val, float}. hooks(Host, MUCHost) -> - [{is_muc_room_owner, MUCHost, ?MODULE, is_room_owner, 50}, + [{is_muc_room_owner, Host, ?MODULE, is_room_owner, 50}, {muc_room_pid, MUCHost, ?MODULE, muc_room_pid, 50}, {can_access_room, MUCHost, ?MODULE, can_access_room, 50}, {can_access_identity, MUCHost, ?MODULE, can_access_identity, 50}, @@ -452,8 +452,9 @@ process_iq_set(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ) -> mongoose_acc:set(hook, result, {error, mongoose_xmpp_errors:bad_request()}, Acc) end. --spec is_room_owner(Acc :: boolean(), Room :: jid:jid(), User :: jid:jid()) -> boolean(). -is_room_owner(_, Room, User) -> +-spec is_muc_room_owner(Acc :: boolean(), HostType :: mongooseim:host_type(), + Room :: jid:jid(), User :: jid:jid()) -> boolean(). +is_muc_room_owner(_, _HostType, Room, User) -> owner == get_affiliation(Room, User). -spec muc_room_pid(Acc :: any(), Room :: jid:jid()) -> {ok, processless}. From 422d0b533d8fdb6c062b3fb0064e03e05c27934f Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 17 May 2021 16:32:00 +0200 Subject: [PATCH 09/73] Refactor mod_mam_muc_cache_user to be MUC-light friendly Also, allows to do cleaning on module stop --- src/mam/mod_mam_muc_cache_user.erl | 265 ++++++++++------------------- 1 file changed, 86 insertions(+), 179 deletions(-) diff --git a/src/mam/mod_mam_muc_cache_user.erl b/src/mam/mod_mam_muc_cache_user.erl index c39caaa1405..c3589e77d9c 100644 --- a/src/mam/mod_mam_muc_cache_user.erl +++ b/src/mam/mod_mam_muc_cache_user.erl @@ -1,18 +1,3 @@ -%%%------------------------------------------------------------------- -%%% @author Uvarov Michael -%%% @copyright (C) 2013, Uvarov Michael -%%% @doc Stores cache using ETS-table (special version for MUC). -%%% This module is a proxy for `mod_mam_rdbms_user' (it should be started). -%%% -%%% There are 2 hooks for `mam_archive_id': -%%% `cached_archive_id/3' and `store_archive_id/3'. -%%% -%%% The differencies from `mod_mam_rdbms_user' are: -%%% -%%% - This module deletes cached data after room deletion. -%%% -%%% @end -%%%------------------------------------------------------------------- -module(mod_mam_muc_cache_user). %% gen_mod handlers @@ -23,12 +8,7 @@ store_archive_id/3, remove_archive/4]). -%% API --export([clean_cache/1]). - -%% Internal exports --export([start_link/0]). - +-export([start_link/1]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -36,79 +16,66 @@ -include("mongoose.hrl"). -include("jlib.hrl"). +-define(TABLE, mam_muc_user_cache). -record(state, {}). +-record(mam_muc_user_cache, {key, room_id}). -%% @private -srv_name() -> - mod_mam_muc_cache. - -tbl_name_archive_id() -> - mod_mam_muc_cache_table_archive_id. - -tbl_name_monitor() -> - mod_mam_muc_cache_table_monitor. - -su_key(#jid{lserver = LServer, luser = LUser}) -> - {LServer, LUser}. +make_key(HostType, #jid{lserver = LServer, luser = LUser}) -> + {HostType, LServer, LUser}. -room_pid(RoomJID=#jid{}) -> - mongoose_hooks:muc_room_pid(RoomJID#jid.lserver, RoomJID). +make_record(HostType, ArcJID, UserID) -> + #mam_muc_user_cache{key = make_key(HostType, ArcJID), room_id = UserID}. %% ---------------------------------------------------------------------- %% gen_mod callbacks %% Starting and stopping functions for users' archives -start(Host, Opts) -> - start_server(Host), - start_muc(Host, Opts). - -stop(Host) -> - stop_muc(Host), - stop_server(Host). - -writer_child_spec() -> - {?MODULE, - {?MODULE, start_link, []}, - permanent, - 5000, - worker, - [?MODULE]}. - -start_server(_Host) -> - %% TODO make per host server to be consistent with the way - %% other modules start servers - %% This is a global server - supervisor:start_child(ejabberd_sup, writer_child_spec()). - -stop_server(_Host) -> - %% There is only one server for all hosts. Cannot stop it. +start(HostType, _Opts) -> + init_mnesia(), + start_server(HostType), + start_hooks(HostType), + ok. + +stop(HostType) -> + stop_hooks(HostType), + stop_server(HostType), + clean_mnesia(HostType), ok. %% ---------------------------------------------------------------------- -%% Add hooks for mod_mam_muc +%% Init -start_muc(Host, _Opts) -> - ejabberd_hooks:add(mam_muc_archive_id, Host, ?MODULE, cached_archive_id, 30), - ejabberd_hooks:add(mam_muc_archive_id, Host, ?MODULE, store_archive_id, 70), - ejabberd_hooks:add(mam_muc_remove_archive, Host, ?MODULE, remove_archive, 100), - ok. +start_hooks(HostType) -> + ejabberd_hooks:add(mam_muc_archive_id, HostType, ?MODULE, cached_archive_id, 30), + ejabberd_hooks:add(mam_muc_archive_id, HostType, ?MODULE, store_archive_id, 70), + ejabberd_hooks:add(mam_muc_remove_archive, HostType, ?MODULE, remove_archive, 100). -stop_muc(Host) -> - ejabberd_hooks:delete(mam_muc_archive_id, Host, ?MODULE, cached_archive_id, 30), - ejabberd_hooks:delete(mam_muc_archive_id, Host, ?MODULE, store_archive_id, 70), - ejabberd_hooks:delete(mam_muc_remove_archive, Host, ?MODULE, remove_archive, 100), - ok. +stop_hooks(HostType) -> + ejabberd_hooks:delete(mam_muc_archive_id, HostType, ?MODULE, cached_archive_id, 30), + ejabberd_hooks:delete(mam_muc_archive_id, HostType, ?MODULE, store_archive_id, 70), + ejabberd_hooks:delete(mam_muc_remove_archive, HostType, ?MODULE, remove_archive, 100). + +start_server(HostType) -> + supervisor:start_child(ejabberd_sup, writer_child_spec(HostType)). +stop_server(HostType) -> + Proc = srv_name(HostType), + supervisor:terminate_child(mod_mam_sup, Proc), + supervisor:delete_child(mod_mam_sup, Proc). -%%==================================================================== +writer_child_spec(HostType) -> + Id = {?MODULE, HostType}, + MFA = {?MODULE, start_link, [HostType]}, + {Id, MFA, permanent, 5000, worker, [?MODULE]}. + +%% ---------------------------------------------------------------------- %% API -%%==================================================================== -start_link() -> - gen_server:start_link({local, srv_name()}, ?MODULE, [], []). +start_link(HostType) -> + gen_server:start_link({local, srv_name(HostType)}, ?MODULE, [], []). -cached_archive_id(undefined, _Host, ArcJID) -> - case lookup_archive_id(ArcJID) of +cached_archive_id(undefined, HostType, ArcJID) -> + case lookup_archive_id(HostType, ArcJID) of not_found -> put(mam_not_cached_flag, true), undefined; @@ -116,140 +83,80 @@ cached_archive_id(undefined, _Host, ArcJID) -> UserID end. -store_archive_id(UserID, _Host, ArcJID) -> - maybe_cache_archive_id(ArcJID, UserID), +store_archive_id(UserID, HostType, ArcJID) -> + maybe_cache_archive_id(HostType, ArcJID, UserID), UserID. -%% #rh -remove_archive(Acc, _Host, _UserID, ArcJID) -> - clean_cache(ArcJID), +remove_archive(Acc, HostType, _UserID, ArcJID) -> + clean_cache(HostType, ArcJID), Acc. -%%==================================================================== +%% ---------------------------------------------------------------------- %% Internal functions -%%==================================================================== - -maybe_cache_archive_id(ArcJID, UserID) -> - case erase(mam_not_cached_flag) of - undefined -> - UserID; - true -> - cache_archive_id(ArcJID, UserID) - end. -%% @doc Put the user id into cache. -%% @private -cache_archive_id(ArcJID, UserID) -> - case room_pid(ArcJID) of - {error, not_found} -> - ok; - RoomPid -> - gen_server:call(srv_name(), {cache_archive_id, ArcJID, UserID, RoomPid}) +srv_name(HostType) -> + gen_mod:get_module_proc(HostType, ?MODULE). + +init_mnesia() -> + %% Replicates data across other nodes + mnesia:create_table(?TABLE, table_opts()), + mnesia:add_table_copy(?TABLE, node(), ram_copies). + +table_opts() -> + [{ram_copies, [node()]}, {type, ordered_set}, + {attributes, record_info(fields, ?TABLE)}]. + +clean_mnesia(HostType) -> + %% Clean mnesia locally + %% We don't want to replicate this across the cluster for performance reasons + catch ets:match_delete(?TABLE, {?TABLE, {HostType, '_', '_'}, '_'}). + +maybe_cache_archive_id(HostType, ArcJID, UserID) -> + case {erase(mam_not_cached_flag), is_integer(UserID)} of + {true, true} -> + Rec = make_record(HostType, ArcJID, UserID), + gen_server:cast(srv_name(HostType), {cache_archive_id, Rec}); + _ -> + ok end. -lookup_archive_id(ArcJID) -> +lookup_archive_id(HostType, ArcJID) -> try - ets:lookup_element(tbl_name_archive_id(), su_key(ArcJID), 2) + ets:lookup_element(?TABLE, make_key(HostType, ArcJID), #mam_muc_user_cache.room_id) catch error:badarg -> not_found end. -clean_cache(ArcJID) -> - %% For each room all operations are executed on a single node only. - gen_server:cast(srv_name(), {remove_user, ArcJID}). +clean_cache(HostType, ArcJID) -> + Key = make_key(HostType, ArcJID), + gen_server:cast(srv_name(HostType), {clean_cache, Key}). -%%==================================================================== +%% ---------------------------------------------------------------------- %% gen_server callbacks -%%==================================================================== - -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- + init([]) -> - TOpts = [named_table, protected, - {write_concurrency, false}, - {read_concurrency, true}], - ets:new(tbl_name_archive_id(), TOpts), - ets:new(tbl_name_monitor(), TOpts), {ok, #state{}}. -%%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} -%% Description: Handling call messages -%%-------------------------------------------------------------------- -handle_call({cache_archive_id, ArcJID, UserID, RoomPid}, _From, State) -> - Key = su_key(ArcJID), - case RoomPid of - {ok, processless} -> - ok; - {ok, Pid} -> - MonRef = erlang:monitor(process, Pid), - ets:insert(tbl_name_monitor(), {MonRef, Key}) - end, - ets:insert(tbl_name_archive_id(), {Key, UserID}), +handle_call(Msg, From, State) -> + ?LOG_WARNING(#{what => unexpected_call, msg => Msg, call_from => From}), {reply, ok, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling cast messages -%%-------------------------------------------------------------------- - -handle_cast({remove_user, ArcJID}, State) -> - ets:delete(tbl_name_archive_id(), su_key(ArcJID)), +handle_cast({cache_archive_id, Rec}, State) -> + mnesia:dirty_write(Rec), + {noreply, State}; +handle_cast({clean_cache, Key}, State) -> + mnesia:dirty_delete({?TABLE, Key}), {noreply, State}; handle_cast(Msg, State) -> ?UNEXPECTED_CAST(Msg), {noreply, State}. -%%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling all non call/cast messages -%%-------------------------------------------------------------------- - - -handle_info({'DOWN', MonRef, process, Pid, Reason}, State) -> - case ets:lookup(tbl_name_archive_id(), MonRef) of - [] -> - ?LOG_WARNING(#{what => unknown_monitor, - monitor_ref => MonRef, monitor_pid => Pid, - reason => Reason, state => State}); - [{MonRef, Key}] -> - ets:delete(tbl_name_archive_id(), MonRef), - ets:delete(tbl_name_monitor(), Key) - end, - {noreply, State}; handle_info(Msg, State) -> ?UNEXPECTED_INFO(Msg), {noreply, State}. -%%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() -%% Description: This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any necessary -%% cleaning up. When it returns, the gen_server terminates with Reason. -%% The return value is ignored. -%%-------------------------------------------------------------------- terminate(_Reason, _State) -> ok. -%%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: Convert process state when code is changed -%%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. - From 38078eff9579fc56e89e1d06cfe65d371cbe933b Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 17 May 2021 16:35:26 +0200 Subject: [PATCH 10/73] Removed muc_room_pid hook --- doc/developers-guide/hooks_description.md | 1 - .../mod_muc_light_developers_guide.md | 2 +- doc/migrations/4.2.0_4.3.0.md | 1 + src/mod_muc.erl | 7 ------- src/mongoose_hooks.erl | 11 +---------- src/muc_light/mod_muc_light.erl | 10 ++-------- 6 files changed, 5 insertions(+), 27 deletions(-) diff --git a/doc/developers-guide/hooks_description.md b/doc/developers-guide/hooks_description.md index 8bfadaa14de..71de3dbdc79 100644 --- a/doc/developers-guide/hooks_description.md +++ b/doc/developers-guide/hooks_description.md @@ -223,7 +223,6 @@ This is the perfect place to plug in custom security control. * mam_set_prefs * mod_global_distrib_known_recipient * mod_global_distrib_unknown_recipient -* muc_room_pid * offline_groupchat_message_hook * offline_message_hook * packet_to_component diff --git a/doc/developers-guide/mod_muc_light_developers_guide.md b/doc/developers-guide/mod_muc_light_developers_guide.md index 8ccbc467a68..1edcf64ae68 100644 --- a/doc/developers-guide/mod_muc_light_developers_guide.md +++ b/doc/developers-guide/mod_muc_light_developers_guide.md @@ -111,7 +111,7 @@ Injects room items to the user's roster. These callbacks handle blocking settings when legacy mode is enabled. -* `is_muc_room_owner`, `muc_room_pid`, `can_access_room`, `can_access_identity` used by `mod_muc_light:is_room_owner/3`, `mod_muc_light:muc_room_pid/2`, `mod_muc_light:can_access_room/3` and `mod_muc_light:can_access_identity/3` respectively +* `is_muc_room_owner`, `can_access_room`, `can_access_identity` used by `mod_muc_light:is_room_owner/3`, `mod_muc_light:can_access_room/3` and `mod_muc_light:can_access_identity/3` respectively Callbacks that provide essential data for the `mod_mam_muc` extension. diff --git a/doc/migrations/4.2.0_4.3.0.md b/doc/migrations/4.2.0_4.3.0.md index 293fbb0d3ff..9ebcb0ca403 100644 --- a/doc/migrations/4.2.0_4.3.0.md +++ b/doc/migrations/4.2.0_4.3.0.md @@ -91,6 +91,7 @@ GO - `filter_room_packet` takes an extra argument HostType. - `is_muc_room_owner` is called for HostType (was for MucHost). - `is_muc_room_owner` takes an extra argument HostType. +- `muc_room_pid` hook removed. ## Metrics REST API (obsolete) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 81083ebae6b..7a00214ca6a 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -60,7 +60,6 @@ %% Hooks handlers -export([is_muc_room_owner/4, - muc_room_pid/2, can_access_room/3, can_access_identity/3]). @@ -414,7 +413,6 @@ init([Host, Opts]) -> hibernated_room_timeout = HibernatedTimeout}, ejabberd_hooks:add(is_muc_room_owner, Host, ?MODULE, is_muc_room_owner, 50), - ejabberd_hooks:add(muc_room_pid, MyHost, ?MODULE, muc_room_pid, 50), ejabberd_hooks:add(can_access_room, MyHost, ?MODULE, can_access_room, 50), ejabberd_hooks:add(can_access_identity, MyHost, ?MODULE, can_access_identity, 50), @@ -453,7 +451,6 @@ set_persistent_rooms_timer(#state{hibernated_room_check_interval = Timeout}) -> %%-------------------------------------------------------------------- handle_call(stop, _From, State) -> ejabberd_hooks:delete(is_muc_room_owner, State#state.server_host, ?MODULE, is_muc_room_owner, 50), - ejabberd_hooks:delete(muc_room_pid, State#state.host, ?MODULE, muc_room_pid, 50), ejabberd_hooks:delete(can_access_room, State#state.host, ?MODULE, can_access_room, 50), ejabberd_hooks:delete(can_access_identity, State#state.host, ?MODULE, can_access_identity, 50), @@ -1251,10 +1248,6 @@ clean_table_from_bad_node(Node, Host) -> is_muc_room_owner(_, _HostType, Room, User) -> mod_muc_room:is_room_owner(Room, User) =:= {ok, true}. --spec muc_room_pid(Acc :: any(), Room :: jid:jid()) -> {ok, pid()} | {error, not_found}. -muc_room_pid(_, Room) -> - room_jid_to_pid(Room). - -spec can_access_room(Acc :: boolean(), Room :: jid:jid(), User :: jid:jid()) -> boolean(). can_access_room(_, Room, User) -> diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index e55ff9c4df0..eb2a14fb90f 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -84,8 +84,7 @@ -export([is_muc_room_owner/3, can_access_identity/3, - can_access_room/3, - muc_room_pid/2]). + can_access_room/3]). -export([mam_archive_id/2, mam_archive_size/3, @@ -906,14 +905,6 @@ can_access_identity(HookServer, Room, User) -> ejabberd_hooks:run_for_host_type(can_access_identity, HookServer, false, [Room, User]). -%%% @doc The `muc_room_pid' hooks is called to get the pid for a given room's JID --spec muc_room_pid(HookServer, Room) -> Result when - HookServer :: jid:lserver(), - Room :: jid:jid(), - Result :: undefined | {ok, processless | pid()} | {error, not_found}. -muc_room_pid(HookServer, Room) -> - ejabberd_hooks:run_for_host_type(muc_room_pid, HookServer, undefined, [Room]). - %%% @doc The `can_access_room' hook is called to determine %%% if a given user can access a room. -spec can_access_room(HookServer, Room, User) -> Result when diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index 7601e7ec004..84359890c27 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -51,8 +51,7 @@ process_iq_set/4, is_muc_room_owner/4, can_access_room/3, - can_access_identity/3, - muc_room_pid/2]). + can_access_identity/3]). %% For propEr -export([apply_rsm/3]). @@ -250,8 +249,7 @@ process_config_schema_value([{integer_value, Val}]) -> {Val, integer}; process_config_schema_value([{float_value, Val}]) -> {Val, float}. hooks(Host, MUCHost) -> - [{is_muc_room_owner, Host, ?MODULE, is_room_owner, 50}, - {muc_room_pid, MUCHost, ?MODULE, muc_room_pid, 50}, + [{is_muc_room_owner, Host, ?MODULE, is_muc_room_owner, 50}, {can_access_room, MUCHost, ?MODULE, can_access_room, 50}, {can_access_identity, MUCHost, ?MODULE, can_access_identity, 50}, @@ -457,10 +455,6 @@ process_iq_set(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ) -> is_muc_room_owner(_, _HostType, Room, User) -> owner == get_affiliation(Room, User). --spec muc_room_pid(Acc :: any(), Room :: jid:jid()) -> {ok, processless}. -muc_room_pid(_, _) -> - {ok, processless}. - -spec can_access_room(Acc :: boolean(), Room :: jid:jid(), User :: jid:jid()) -> boolean(). can_access_room(_, Room, User) -> From c43a45fe2ebcbbfd0a6a4f3b9155f9e02d8e578e Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 17 May 2021 16:52:31 +0200 Subject: [PATCH 11/73] Handle remove_domain in mod_mam_muc_cache_user --- src/mam/mod_mam_muc_cache_user.erl | 55 ++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/src/mam/mod_mam_muc_cache_user.erl b/src/mam/mod_mam_muc_cache_user.erl index c3589e77d9c..a3d9bb313ff 100644 --- a/src/mam/mod_mam_muc_cache_user.erl +++ b/src/mam/mod_mam_muc_cache_user.erl @@ -6,7 +6,8 @@ %% ejabberd handlers -export([cached_archive_id/3, store_archive_id/3, - remove_archive/4]). + remove_archive/4, + remove_domain/3]). -export([start_link/1]). %% gen_server callbacks @@ -23,6 +24,12 @@ make_key(HostType, #jid{lserver = LServer, luser = LUser}) -> {HostType, LServer, LUser}. +make_first_key(HostType, LServer) -> + {HostType, LServer, <<>>}. + +simplify_key({HostType, LServer, _}) -> + {HostType, LServer, <<>>}. + make_record(HostType, ArcJID, UserID) -> #mam_muc_user_cache{key = make_key(HostType, ArcJID), room_id = UserID}. @@ -46,14 +53,16 @@ stop(HostType) -> %% Init start_hooks(HostType) -> - ejabberd_hooks:add(mam_muc_archive_id, HostType, ?MODULE, cached_archive_id, 30), - ejabberd_hooks:add(mam_muc_archive_id, HostType, ?MODULE, store_archive_id, 70), - ejabberd_hooks:add(mam_muc_remove_archive, HostType, ?MODULE, remove_archive, 100). + ejabberd_hooks:add(hooks(HostType)). stop_hooks(HostType) -> - ejabberd_hooks:delete(mam_muc_archive_id, HostType, ?MODULE, cached_archive_id, 30), - ejabberd_hooks:delete(mam_muc_archive_id, HostType, ?MODULE, store_archive_id, 70), - ejabberd_hooks:delete(mam_muc_remove_archive, HostType, ?MODULE, remove_archive, 100). + ejabberd_hooks:delete(hooks(HostType)). + +hooks(HostType) -> + [{mam_muc_archive_id, HostType, ?MODULE, cached_archive_id, 30}, + {mam_muc_archive_id, HostType, ?MODULE, store_archive_id, 70}, + {mam_muc_remove_archive, HostType, ?MODULE, remove_archive, 100}, + {remove_domain, HostType, ?MODULE, remove_domain, 100}]. start_server(HostType) -> supervisor:start_child(ejabberd_sup, writer_child_spec(HostType)). @@ -91,6 +100,13 @@ remove_archive(Acc, HostType, _UserID, ArcJID) -> clean_cache(HostType, ArcJID), Acc. +-spec remove_domain(mongoose_hooks:simple_acc(), + mongooseim:host_type(), jid:lserver()) -> + mongoose_hooks:simple_acc(). +remove_domain(Acc, HostType, Domain) -> + gen_server:cast(srv_name(HostType), {remove_domain, HostType, Domain}), + Acc. + %% ---------------------------------------------------------------------- %% Internal functions @@ -147,6 +163,9 @@ handle_cast({cache_archive_id, Rec}, State) -> handle_cast({clean_cache, Key}, State) -> mnesia:dirty_delete({?TABLE, Key}), {noreply, State}; +handle_cast({remove_domain, HostType, Domain}, State) -> + handle_remove_domain(HostType, Domain), + {noreply, State}; handle_cast(Msg, State) -> ?UNEXPECTED_CAST(Msg), {noreply, State}. @@ -160,3 +179,25 @@ terminate(_Reason, _State) -> code_change(_OldVsn, State, _Extra) -> {ok, State}. + +%% ---------------------------------------------------------------------- +%% gen_server side + +handle_remove_domain(HostType, Domain) -> + FirstKey = make_first_key(HostType, Domain), + find_and_remove(FirstKey, FirstKey). + +find_and_remove(FirstKey, Key) -> + NextKey = mnesia:dirty_next(?TABLE, Key), + mnesia:dirty_delete({?TABLE, Key}), + case NextKey of + '$end_of_table' -> + ok; + _ -> + case simplify_key(NextKey) of + FirstKey -> + find_and_remove(FirstKey, NextKey); + _ -> %% Reached another domain + ok + end + end. From 7acb4b37da61ebd316f20f715de6043793e709b2 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 19 May 2021 10:02:32 +0200 Subject: [PATCH 12/73] Refactor ACL for MAM MUC Add a prefix muc_ to the action names --- big_tests/tests/mam_helper.erl | 2 +- big_tests/tests/rest_helper.erl | 2 +- doc/advanced-configuration/access.md | 18 + src/mam/mod_mam.erl | 4 +- src/mam/mod_mam_muc.erl | 337 ++++++++++-------- .../mongoose_client_api_rooms_messages.erl | 6 +- src/mongoose_hooks.erl | 79 ++-- src/muc_light/mod_muc_light_utils.erl | 5 + 8 files changed, 249 insertions(+), 204 deletions(-) diff --git a/big_tests/tests/mam_helper.erl b/big_tests/tests/mam_helper.erl index eef64b663bd..e13643882d0 100644 --- a/big_tests/tests/mam_helper.erl +++ b/big_tests/tests/mam_helper.erl @@ -959,7 +959,7 @@ put_muc_msgs(Msgs) -> archive_muc_msg(Host, {{MsgID, _}, {_RoomBin, RoomJID, RoomArcID}, {_FromBin, FromJID, SrcJID}, _, Packet}) -> - rpc_apply(mod_mam_muc, archive_message, [Host, #{message_id => MsgID, + rpc_apply(mod_mam_muc, archive_message_for_ct, [#{message_id => MsgID, archive_id => RoomArcID, local_jid => RoomJID, remote_jid => FromJID, diff --git a/big_tests/tests/rest_helper.erl b/big_tests/tests/rest_helper.erl index fd13ec64d8a..140594bc368 100644 --- a/big_tests/tests/rest_helper.erl +++ b/big_tests/tests/rest_helper.erl @@ -446,7 +446,7 @@ put_room_msg({{_, MsgID}, {_, ToJID, ToArcID}, {_, SrcJID, _}, Msg}) -> Host = ct:get_config({hosts, mim, domain}), - ok = mam_helper:rpc_apply(mod_mam_muc, archive_message, [Host, #{message_id => MsgID, + ok = mam_helper:rpc_apply(mod_mam_muc, archive_message_for_ct, [#{message_id => MsgID, archive_id => ToArcID, local_jid => ToJID, remote_jid => FromJID, diff --git a/doc/advanced-configuration/access.md b/doc/advanced-configuration/access.md index 58d3e4a55de..628ae7283a1 100644 --- a/doc/advanced-configuration/access.md +++ b/doc/advanced-configuration/access.md @@ -138,6 +138,22 @@ These rules set the permissions for MAM operations triggered by IQ stanzas and h They can return `"allow"`, `"deny"` or `"default"`. The last value uses the default setting for the operation, which is to allow the operation when the sender and recipient JID's are the same. +MAM for MUC permissions has `muc_` prefix: + +```toml + muc_mam_set_prefs = [ + {acl = "all", value = "default"} + ] + + muc_mam_get_prefs = [ + {acl = "all", value = "default"} + ] + + muc_mam_lookup_messages = [ + {acl = "all", value = "default"} + ] +``` + ### MAM shapers These rules limit the rate of MAM operations triggered by IQ stanzas. @@ -175,6 +191,8 @@ For each operation there are two rules: The values returned by the rules (`mam_shaper`, `mam_global_shaper`) are shaper names, which need to be defined in the [`shaper` section](shaper.md#mam-shapers). +MAM for MUC shapers has `muc_` prefix. + ### Maximum number of sessions The `max_user_sessions` rule is used to determine the maximum number of sessions a user can open. diff --git a/src/mam/mod_mam.erl b/src/mam/mod_mam.erl index 7f5d51a3531..7270a39ffae 100644 --- a/src/mam/mod_mam.erl +++ b/src/mam/mod_mam.erl @@ -195,7 +195,7 @@ archive_id(Server, User) %% gen_mod callbacks %% Starting and stopping functions for users' archives --spec start(HostType :: jid:server(), Opts :: list()) -> any(). +-spec start(HostType :: host_type(), Opts :: list()) -> any(). start(HostType, Opts) -> ?LOG_INFO(#{what => mam_starting}), ensure_metrics(HostType), @@ -204,7 +204,7 @@ start(HostType, Opts) -> register_features(HostType), ok. --spec stop(HostType :: jid:server()) -> any(). +-spec stop(HostType :: host_type()) -> any(). stop(HostType) -> ?LOG_INFO(#{what => mam_stopping}), unregister_features(HostType), diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index 3621b01efcd..a57bb6d7344 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -51,11 +51,9 @@ -export([get_personal_data/2]). %% private --export([archive_message/2]). +-export([archive_message_for_ct/1]). -export([lookup_messages/2]). -export([archive_id_int/2]). --export([handle_set_message_form/3]). --export([handle_lookup_result/4]). %% ---------------------------------------------------------------------- %% Imports @@ -103,6 +101,8 @@ Offset :: non_neg_integer(), MessageRows :: [row()]}. -type row() :: mod_mam:message_row(). +-type host_type() :: mongooseim:host_type(). +-type muc_action() :: atom(). -export_type([row/0, row_batch/0]). @@ -115,66 +115,54 @@ get_personal_data(Acc, #jid{ lserver = LServer } = JID) -> Entries = mongoose_hooks:get_mam_muc_gdpr_data(LServer, JID), [{mam_muc, Schema, Entries} | Acc]. --spec delete_archive(jid:server(), jid:user()) -> 'ok'. -delete_archive(SubHost, RoomName) when is_binary(SubHost), is_binary(RoomName) -> - ?LOG_DEBUG(#{what => mam_delete_room, room => RoomName, sub_host => SubHost}), - ArcJID = jid:make(RoomName, SubHost, <<>>), - {ok, Host} = mongoose_subhosts:get_host(SubHost), - ArcID = archive_id_int(Host, ArcJID), - remove_archive(Host, ArcID, ArcJID), +-spec delete_archive(jid:server(), jid:user()) -> ok. +delete_archive(MucHost, RoomName) when is_binary(MucHost), is_binary(RoomName) -> + ?LOG_DEBUG(#{what => mam_delete_room, room => RoomName, sub_host => MucHost}), + ArcJID = jid:make(RoomName, MucHost, <<>>), + HostType = mod_muc_light_utils:room_jid_to_host_type(ArcJID), + ArcID = archive_id_int(HostType, ArcJID), + remove_archive(HostType, ArcID, ArcJID), ok. - -spec archive_size(jid:server(), jid:user()) -> integer(). - -archive_size(SubHost, RoomName) when is_binary(SubHost), is_binary(RoomName) -> - ArcJID = jid:make(RoomName, SubHost, <<>>), - {ok, Host} = mongoose_subhosts:get_host(SubHost), - ArcID = archive_id_int(Host, ArcJID), - archive_size(Host, ArcID, ArcJID). - +archive_size(MucHost, RoomName) when is_binary(MucHost), is_binary(RoomName) -> + ArcJID = jid:make(RoomName, MucHost, <<>>), + HostType = mod_muc_light_utils:room_jid_to_host_type(ArcJID), + ArcID = archive_id_int(HostType, ArcJID), + archive_size(HostType, ArcID, ArcJID). -spec archive_id(jid:server(), jid:user()) -> integer(). -archive_id(SubHost, RoomName) when is_binary(SubHost), is_binary(RoomName) -> - ArcJID = jid:make(RoomName, SubHost, <<>>), - {ok, Host} = mongoose_subhosts:get_host(SubHost), - archive_id_int(Host, ArcJID). +archive_id(MucHost, RoomName) when is_binary(MucHost), is_binary(RoomName) -> + ArcJID = jid:make(RoomName, MucHost, <<>>), + HostType = mod_muc_light_utils:room_jid_to_host_type(ArcJID), + archive_id_int(HostType, ArcJID). %% ---------------------------------------------------------------------- %% gen_mod callbacks %% Starting and stopping functions for MUC archives --spec start(Host :: jid:server(), Opts :: list()) -> any(). -start(Host, Opts) -> +-spec start(HostType :: host_type(), Opts :: list()) -> any(). +start(HostType, Opts) -> ?LOG_DEBUG(#{what => mam_muc_starting}), - %% MUC host. - MUCHost = gen_mod:get_opt_subhost(Host, Opts, mod_muc:default_host()), - IQDisc = gen_mod:get_opt(iqdisc, Opts, parallel), %% Type - [mod_disco:register_feature(MUCHost, Feature) || Feature <- features(?MODULE, Host)], - gen_iq_handler:add_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_04, - ?MODULE, room_process_mam_iq, IQDisc), - gen_iq_handler:add_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_06, - ?MODULE, room_process_mam_iq, IQDisc), - ejabberd_hooks:add(hooks(Host)), - ensure_metrics(Host), + ensure_metrics(HostType), + ejabberd_hooks:add(hooks(HostType)), + add_iq_handlers(HostType, Opts), + register_features(HostType), ok. --spec stop(Host :: jid:server()) -> any(). - -stop(Host) -> - MUCHost = gen_mod:get_module_opt_subhost(Host, mod_mam_muc, mod_muc:default_host()), +-spec stop(HostType :: host_type()) -> any(). +stop(HostType) -> ?LOG_DEBUG(#{what => mam_muc_stopping}), - ejabberd_hooks:delete(hooks(Host)), - gen_iq_handler:remove_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_04), - gen_iq_handler:remove_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_06), - [mod_disco:unregister_feature(MUCHost, Feature) || Feature <- features(?MODULE, Host)], + unregister_features(HostType), + ejabberd_hooks:delete(hooks(HostType)), + remove_iq_handlers(HostType), ok. %% ---------------------------------------------------------------------- %% hooks and handlers for MUC %% @doc Handle public MUC-message. --spec filter_room_packet(Packet :: packet(), HostType :: mongooseim:host_type(), +-spec filter_room_packet(Packet :: packet(), HostType :: host_type(), EventData :: mod_muc:room_event_data()) -> packet(). filter_room_packet(Packet, HostType, EventData = #{}) -> ?LOG_DEBUG(#{what => mam_room_packet, text => <<"Incoming room packet">>, @@ -184,22 +172,24 @@ filter_room_packet(Packet, HostType, EventData = #{}) -> true -> #{from_nick := FromNick, from_jid := FromJID, room_jid := RoomJID, role := Role, affiliation := Affiliation} = EventData, - archive_room_packet(Packet, FromNick, FromJID, RoomJID, Role, Affiliation); + archive_room_packet(HostType, Packet, FromNick, FromJID, + RoomJID, Role, Affiliation); false -> Packet end. %% @doc Archive without validation. --spec archive_room_packet(Packet :: packet(), FromNick :: jid:user(), +-spec archive_room_packet(HostType :: host_type(), + Packet :: packet(), FromNick :: jid:user(), FromJID :: jid:jid(), RoomJID :: jid:jid(), Role :: mod_muc:role(), Affiliation :: mod_muc:affiliation()) -> packet(). -archive_room_packet(Packet, FromNick, FromJID=#jid{}, RoomJID=#jid{}, Role, Affiliation) -> - {ok, Host} = mongoose_subhosts:get_host(RoomJID#jid.lserver), - ArcID = archive_id_int(Host, RoomJID), +archive_room_packet(HostType, Packet, FromNick, FromJID=#jid{}, + RoomJID=#jid{}, Role, Affiliation) -> + ArcID = archive_id_int(HostType, RoomJID), %% Occupant JID SrcJID = jid:replace_resource(RoomJID, FromNick), IsInteresting = - case get_behaviour(Host, ArcID, RoomJID, SrcJID) of + case get_behaviour(HostType, ArcID, RoomJID, SrcJID) of always -> true; never -> false; roster -> true @@ -217,14 +207,13 @@ archive_room_packet(Packet, FromNick, FromJID=#jid{}, RoomJID=#jid{}, Role, Affi origin_id => OriginID, direction => incoming, packet => Packet1}, - Result = archive_message(Host, Params), + Result = archive_message(HostType, Params), %% Packet2 goes to archive, Packet to other users case Result of ok -> - maybe_add_arcid_elems(RoomJID, - mess_id_to_external_binary(MessID), - Packet, - mod_mam_params:add_stanzaid_element(?MODULE, Host)); + ExtID = mess_id_to_external_binary(MessID), + ShouldAdd = mod_mam_params:add_stanzaid_element(?MODULE, HostType), + maybe_add_arcid_elems(RoomJID, ExtID, Packet, ShouldAdd); {error, _} -> Packet end; false -> Packet @@ -238,17 +227,19 @@ archive_room_packet(Packet, FromNick, FromJID=#jid{}, RoomJID=#jid{}, Role, Affi %% (i.e `To.luser'). -spec room_process_mam_iq(From :: jid:jid(), To :: jid:jid(), Acc :: mongoose_acc:t(), IQ :: jlib:iq()) -> {mongoose_acc:t(), jlib:iq() | ignore}. -room_process_mam_iq(From = #jid{lserver = Host}, To, Acc, IQ) -> +room_process_mam_iq(From, To, Acc, IQ) -> + HostType = mod_muc_light_utils:acc_to_host_type(Acc), mod_mam_utils:maybe_log_deprecation(IQ), Action = mam_iq:action(IQ), - case is_action_allowed(Action, From, To) of + MucAction = action_to_muc_action(Action), + case is_action_allowed(HostType, Action, MucAction, From, To) of true -> - case mod_mam_utils:wait_shaper(Host, Action, From) of + case mod_mam_utils:wait_shaper(HostType, MucAction, From) of ok -> - handle_error_iq(Acc, Host, To, Action, - handle_mam_iq(Action, From, To, IQ)); + handle_error_iq(Acc, HostType, To, Action, + handle_mam_iq(HostType, Action, From, To, IQ)); {error, max_delay_reached} -> - mongoose_metrics:update(Host, modMucMamDroppedIQ, 1), + mongoose_metrics:update(HostType, modMucMamDroppedIQ, 1), {Acc, return_max_delay_reached_error_iq(IQ)} end; false -> {Acc, return_action_not_allowed_error_iq(IQ)} @@ -257,7 +248,7 @@ room_process_mam_iq(From = #jid{lserver = Host}, To, Acc, IQ) -> %% #rh %% @doc This hook is called from `mod_muc:forget_room(Host, Name)'. --spec forget_room(map(), mongooseim:host_type(), jid:lserver(), binary()) -> map(). +-spec forget_room(map(), host_type(), jid:lserver(), binary()) -> map(). forget_room(Acc, _HostType, MucServer, RoomName) -> delete_archive(MucServer, RoomName), Acc. @@ -265,69 +256,73 @@ forget_room(Acc, _HostType, MucServer, RoomName) -> %% ---------------------------------------------------------------------- %% Internal functions --spec is_action_allowed(atom(), jid:jid(), jid:jid()) -> boolean(). -is_action_allowed(Action, From, To = #jid{lserver = Host}) -> - case acl:match_rule(Host, Action, From, default) of +-spec is_action_allowed(host_type(), mam_iq:action(), muc_action(), + jid:jid(), jid:jid()) -> boolean(). +is_action_allowed(HostType, Action, MucAction, From, To) -> + case acl:match_rule(HostType, MucAction, From, default) of allow -> true; deny -> false; - default -> is_action_allowed_by_default(Action, From, To) + default -> is_room_action_allowed_by_default(HostType, Action, From, To) end. --spec is_action_allowed_by_default(Action :: mam_iq:action(), From :: jid:jid(), - To :: jid:jid()) -> boolean(). -is_action_allowed_by_default(Action, From, To) -> - is_room_action_allowed_by_default(Action, From, To). +-spec action_to_muc_action(mam_iq:action()) -> atom(). +action_to_muc_action(Action) -> + list_to_atom("muc_" ++ atom_to_list(Action)). --spec is_room_action_allowed_by_default(Action :: mam_iq:action(), - From :: jid:jid(), To :: jid:jid()) -> boolean(). -is_room_action_allowed_by_default(Action, From, To) -> +-spec is_room_action_allowed_by_default(HostType :: host_type(), + Action :: mam_iq:action(), + From :: jid:jid(), + To :: jid:jid()) -> boolean(). +is_room_action_allowed_by_default(HostType, Action, From, To) -> case mam_iq:action_type(Action) of - set -> is_room_owner(From, To); - get -> can_access_room(From, To) + set -> is_room_owner(HostType, From, To); + get -> can_access_room(HostType, From, To) end. - --spec is_room_owner(User :: jid:jid(), Room :: jid:jid()) -> boolean(). -is_room_owner(User, Room) -> - mongoose_hooks:is_muc_room_owner(Room#jid.lserver, Room, User). - +-spec is_room_owner(HostType :: host_type(), + UserJid :: jid:jid(), RoomJid :: jid:jid()) -> boolean(). +is_room_owner(HostType, UserJid, RoomJid) -> + mongoose_hooks:is_muc_room_owner(HostType, UserJid, RoomJid). %% @doc Return true if user element should be removed from results --spec is_user_identity_hidden(User :: jid:jid(), Room :: jid:jid()) -> boolean(). -is_user_identity_hidden(User, Room) -> - case mongoose_hooks:can_access_identity(Room#jid.lserver, Room, User) of +-spec is_user_identity_hidden(HostType :: host_type(), + UserJid :: jid:jid(), + RoomJid :: jid:jid()) -> boolean(). +is_user_identity_hidden(HostType, UserJid, RoomJid) -> + case mongoose_hooks:can_access_identity(HostType, RoomJid, UserJid) of CanAccess when is_boolean(CanAccess) -> not CanAccess end. --spec can_access_room(User :: jid:jid(), Room :: jid:jid()) -> boolean(). -can_access_room(User, Room) -> - mongoose_hooks:can_access_room(Room#jid.lserver, Room, User). +-spec can_access_room(HostType :: host_type(), + UserJid :: jid:jid(), RoomJid :: jid:jid()) -> boolean(). +can_access_room(HostType, UserJid, RoomJid) -> + mongoose_hooks:can_access_room(HostType, RoomJid, UserJid). --spec handle_mam_iq(mam_iq:action(), From :: jid:jid(), jid:jid(), jlib:iq()) -> +-spec handle_mam_iq(HostType :: host_type(), mam_iq:action(), + From :: jid:jid(), jid:jid(), jlib:iq()) -> jlib:iq() | {error, any(), jlib:iq()} | ignore. -handle_mam_iq(Action, From, To, IQ) -> +handle_mam_iq(HostType, Action, From, To, IQ) -> case Action of mam_get_prefs -> - handle_get_prefs(To, IQ); + handle_get_prefs(HostType, To, IQ); mam_set_prefs -> - handle_set_prefs(To, IQ); + handle_set_prefs(HostType, To, IQ); mam_set_message_form -> - handle_set_message_form(From, To, IQ); + handle_set_message_form(HostType, From, To, IQ); mam_get_message_form -> - handle_get_message_form(From, To, IQ) + handle_get_message_form(HostType, From, To, IQ) end. --spec handle_set_prefs(jid:jid(), jlib:iq()) -> +-spec handle_set_prefs(host_type(), jid:jid(), jlib:iq()) -> jlib:iq() | {error, any(), jlib:iq()}. -handle_set_prefs(ArcJID = #jid{}, +handle_set_prefs(HostType, ArcJID = #jid{}, IQ = #iq{sub_el = PrefsEl}) -> {DefaultMode, AlwaysJIDs, NeverJIDs} = parse_prefs(PrefsEl), ?LOG_DEBUG(#{what => mam_muc_set_prefs, archive_jid => ArcJID, default_mode => DefaultMode, always_jids => AlwaysJIDs, never_jids => NeverJIDs, iq => IQ}), - {ok, Host} = mongoose_subhosts:get_host(ArcJID#jid.lserver), - ArcID = archive_id_int(Host, ArcJID), - Res = set_prefs(Host, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs), + ArcID = archive_id_int(HostType, ArcJID), + Res = set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs), handle_set_prefs_result(Res, DefaultMode, AlwaysJIDs, NeverJIDs, IQ). handle_set_prefs_result(ok, DefaultMode, AlwaysJIDs, NeverJIDs, IQ) -> @@ -338,12 +333,11 @@ handle_set_prefs_result({error, Reason}, return_error_iq(IQ, Reason). --spec handle_get_prefs(jid:jid(), jlib:iq()) -> +-spec handle_get_prefs(host_type(), jid:jid(), jlib:iq()) -> jlib:iq() | {error, any(), jlib:iq()}. -handle_get_prefs(ArcJID=#jid{}, IQ=#iq{}) -> - {ok, Host} = mongoose_subhosts:get_host(ArcJID#jid.lserver), - ArcID = archive_id_int(Host, ArcJID), - Res = get_prefs(Host, ArcID, ArcJID, always), +handle_get_prefs(HostType, ArcJID=#jid{}, IQ=#iq{}) -> + ArcID = archive_id_int(HostType, ArcJID), + Res = get_prefs(HostType, ArcID, ArcJID, always), handle_get_prefs_result(ArcJID, Res, IQ). handle_get_prefs_result(ArcJID, {DefaultMode, AlwaysJIDs, NeverJIDs}, IQ) -> @@ -355,37 +349,38 @@ handle_get_prefs_result(ArcJID, {DefaultMode, AlwaysJIDs, NeverJIDs}, IQ) -> handle_get_prefs_result(_ArcJID, {error, Reason}, IQ) -> return_error_iq(IQ, Reason). --spec handle_set_message_form(From :: jid:jid(), ArcJID :: jid:jid(), +-spec handle_set_message_form(HostType :: host_type(), + From :: jid:jid(), ArcJID :: jid:jid(), IQ :: jlib:iq()) -> jlib:iq() | ignore | {error, term(), jlib:iq()}. -handle_set_message_form(#jid{} = From, #jid{} = ArcJID, IQ) -> - {ok, Host} = mongoose_subhosts:get_host(ArcJID#jid.lserver), - ArcID = archive_id_int(Host, ArcJID), - Params0 = mam_iq:form_to_lookup_params(IQ, mod_mam_params:max_result_limit(?MODULE, Host), - mod_mam_params:default_result_limit(?MODULE, Host), - mod_mam_params:extra_params_module(?MODULE, Host)), +handle_set_message_form(HostType, #jid{} = From, #jid{} = ArcJID, IQ) -> + ArcID = archive_id_int(HostType, ArcJID), + ResLimit = mod_mam_params:max_result_limit(?MODULE, HostType), + DefLimit = mod_mam_params:default_result_limit(?MODULE, HostType), + ExtMod = mod_mam_params:extra_params_module(?MODULE, HostType), + Params0 = mam_iq:form_to_lookup_params(IQ, ResLimit, DefLimit, ExtMod), Params = mam_iq:lookup_params_with_archive_details(Params0, ArcID, ArcJID, From), - Result = lookup_messages(Host, Params), - handle_lookup_result(Result, From, IQ, Params). + Result = lookup_messages(HostType, Params), + handle_lookup_result(Result, HostType, From, IQ, Params). --spec handle_lookup_result({ok, mod_mam:lookup_result()} | {error, term()}, jid:jid(), - jlib:iq(), map()) -> +-spec handle_lookup_result({ok, mod_mam:lookup_result()} | {error, term()}, + host_type(), jid:jid(), jlib:iq(), map()) -> jlib:iq() | ignore | {error, term(), jlib:iq()}. -handle_lookup_result(Result, From, IQ, #{owner_jid := ArcJID} = Params) -> +handle_lookup_result(Result, HostType, From, IQ, #{owner_jid := ArcJID} = Params) -> case Result of {error, Reason} -> report_issue(Reason, mam_muc_lookup_failed, ArcJID, IQ), return_error_iq(IQ, Reason); {ok, Res} -> - send_messages_and_iq_result(Res, From, IQ, Params) + send_messages_and_iq_result(Res, HostType, From, IQ, Params) end. -send_messages_and_iq_result({TotalCount, Offset, MessageRows}, From, +send_messages_and_iq_result({TotalCount, Offset, MessageRows}, HostType, From, #iq{xmlns = MamNs, sub_el = QueryEl} = IQ, #{owner_jid := ArcJID} = Params) -> %% Forward messages QueryID = exml_query:attr(QueryEl, <<"queryid">>, <<>>), - {FirstMessID, LastMessID} = forward_messages(From, ArcJID, MamNs, + {FirstMessID, LastMessID} = forward_messages(HostType, From, ArcJID, MamNs, QueryID, MessageRows, true), %% Make fin iq @@ -395,17 +390,16 @@ send_messages_and_iq_result({TotalCount, Offset, MessageRows}, From, FinElem = make_fin_element(IQ#iq.xmlns, IsComplete, IsStable, ResultSetEl), IQ#iq{type = result, sub_el = [FinElem]}. -forward_messages(From, ArcJID, MamNs, QueryID, MessageRows, SetClientNs) -> +forward_messages(HostType, From, ArcJID, MamNs, QueryID, MessageRows, SetClientNs) -> %% Forward messages {FirstMessID, LastMessID, HideUser} = case MessageRows of [] -> {undefined, undefined, undefined}; [_ | _] -> {message_row_to_ext_id(hd(MessageRows)), message_row_to_ext_id(lists:last(MessageRows)), - is_user_identity_hidden(From, ArcJID)} + is_user_identity_hidden(HostType, From, ArcJID)} end, - {ok, Host} = mongoose_subhosts:get_host(ArcJID#jid.lserver), - SendModule = mod_mam_params:send_message_mod(?MODULE, Host), + SendModule = mod_mam_params:send_message_mod(?MODULE, HostType), [send_message(SendModule, Row, ArcJID, From, message_row_to_xml(MamNs, From, HideUser, SetClientNs, Row, QueryID)) @@ -415,10 +409,11 @@ forward_messages(From, ArcJID, MamNs, QueryID, MessageRows, SetClientNs) -> send_message(SendModule, Row, ArcJID, From, Packet) -> mam_send_message:call_send_message(SendModule, Row, ArcJID, From, Packet). --spec handle_get_message_form(jid:jid(), jid:jid(), jlib:iq()) -> +-spec handle_get_message_form(host_type(), jid:jid(), jid:jid(), jlib:iq()) -> jlib:iq(). -handle_get_message_form(_From = #jid{lserver = Host}, _ArcJID = #jid{}, IQ = #iq{}) -> - return_message_form_iq(Host, IQ). +handle_get_message_form(HostType, + _From = #jid{}, _ArcJID = #jid{}, IQ = #iq{}) -> + return_message_form_iq(HostType, IQ). %% ---------------------------------------------------------------------- %% Backend wrappers @@ -433,60 +428,63 @@ archive_id_int(Host, ArcJID = #jid{}) -> archive_size(Host, ArcID, ArcJID = #jid{}) -> mongoose_hooks:mam_muc_archive_size(Host, ArcID, ArcJID). --spec get_behaviour(jid:server(), mod_mam:archive_id(), +-spec get_behaviour(HostType :: host_type(), ArcID :: mod_mam:archive_id(), LocJID :: jid:jid(), RemJID :: jid:jid()) -> any(). -get_behaviour(Host, ArcID, - LocJID = #jid{}, - RemJID = #jid{}) -> - mongoose_hooks:mam_muc_get_behaviour(Host, ArcID, LocJID, RemJID). - +get_behaviour(HostType, ArcID, LocJID = #jid{}, RemJID = #jid{}) -> + mongoose_hooks:mam_muc_get_behaviour(HostType, ArcID, LocJID, RemJID). --spec set_prefs(Host :: jid:server(), ArcID :: mod_mam:archive_id(), +-spec set_prefs(HostType :: host_type(), ArcID :: mod_mam:archive_id(), ArcJID :: jid:jid(), DefaultMode :: mod_mam:archive_behaviour(), AlwaysJIDs :: [jid:literal_jid()], NeverJIDs :: [jid:literal_jid()]) -> any(). -set_prefs(Host, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> - mongoose_hooks:mam_muc_set_prefs(Host, ArcID, ArcJID, DefaultMode, +set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> + mongoose_hooks:mam_muc_set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs). %% @doc Load settings from the database. --spec get_prefs(Host :: jid:server(), ArcID :: mod_mam:archive_id(), +-spec get_prefs(HostType :: host_type(), ArcID :: mod_mam:archive_id(), ArcJID :: jid:jid(), GlobalDefaultMode :: mod_mam:archive_behaviour()) -> mod_mam:preference() | {error, Reason :: term()}. -get_prefs(Host, ArcID, ArcJID, GlobalDefaultMode) -> - mongoose_hooks:mam_muc_get_prefs(Host, GlobalDefaultMode, ArcID, ArcJID). +get_prefs(HostType, ArcID, ArcJID, GlobalDefaultMode) -> + mongoose_hooks:mam_muc_get_prefs(HostType, GlobalDefaultMode, ArcID, ArcJID). --spec remove_archive(jid:server(), mod_mam:archive_id() | undefined, - jid:jid()) -> 'ok'. -remove_archive(Host, ArcID, ArcJID = #jid{}) -> - mongoose_hooks:mam_muc_remove_archive(Host, ArcID, ArcJID), +-spec remove_archive(host_type(), mod_mam:archive_id() | undefined, + jid:jid()) -> ok. +remove_archive(HostType, ArcID, ArcJID = #jid{}) -> + mongoose_hooks:mam_muc_remove_archive(HostType, ArcID, ArcJID), ok. %% See description in mod_mam. --spec lookup_messages(Host :: jid:server(), Params :: map()) -> +-spec lookup_messages(HostType :: host_type(), Params :: map()) -> {ok, mod_mam:lookup_result()} | {error, 'policy-violation'} - | {error, Reason :: term()}.%Result :: any(), -lookup_messages(Host, Params) -> - Result = lookup_messages_without_policy_violation_check(Host, Params), + | {error, Reason :: term()}. +lookup_messages(HostType, Params) -> + Result = lookup_messages_without_policy_violation_check(HostType, Params), %% If a query returns a number of stanzas greater than this limit and the %% client did not specify a limit using RSM then the server should return %% a policy-violation error to the client. mod_mam_utils:check_result_for_policy_violation(Params, Result). -lookup_messages_without_policy_violation_check(Host, #{search_text := SearchText} = Params) -> - case SearchText /= undefined andalso not mod_mam_params:has_full_text_search(?MODULE, Host) of +lookup_messages_without_policy_violation_check(HostType, + #{search_text := SearchText} = Params) -> + case SearchText /= undefined andalso + not mod_mam_params:has_full_text_search(?MODULE, HostType) of true -> %% Use of disabled full text search {error, 'not-supported'}; false -> - mongoose_hooks:mam_muc_lookup_messages(Host, Params) + mongoose_hooks:mam_muc_lookup_messages(HostType, Params) end. --spec archive_message(jid:server(), mod_mam:archive_message_params()) -> ok | {error, timeout}. -archive_message(Host, Params) -> - mongoose_hooks:mam_muc_archive_message(Host, Params). +archive_message_for_ct(Params = #{room_jid := RoomJid}) -> + HostType = mod_muc_light_utils:room_jid_to_host_type(RoomJid), + archive_message(HostType, Params). + +-spec archive_message(host_type(), mod_mam:archive_message_params()) -> ok | {error, timeout}. +archive_message(HostType, Params) -> + mongoose_hooks:mam_muc_archive_message(HostType, Params). %% ---------------------------------------------------------------------- %% Helpers @@ -564,8 +562,8 @@ return_max_delay_reached_error_iq(IQ) -> <<"en">>, <<"The action is cancelled because of flooding.">>), IQ#iq{type = error, sub_el = [ErrorEl]}. -return_message_form_iq(Host, IQ) -> - IQ#iq{type = result, sub_el = [message_form(?MODULE, Host, IQ#iq.xmlns)]}. +return_message_form_iq(HostType, IQ) -> + IQ#iq{type = result, sub_el = [message_form(?MODULE, HostType, IQ#iq.xmlns)]}. % the stacktrace is a big lie @@ -586,7 +584,7 @@ report_issue(Reason, Stacktrace, Issue, #jid{lserver = LServer, luser = LUser}, ?LOG_ERROR(#{what => mam_muc_error, issue => Issue, reason => Reason, user => LUser, server => LServer, iq => IQ, stacktrace => Stacktrace}). --spec is_archivable_message(HostType :: mongooseim:host_type(), +-spec is_archivable_message(HostType :: host_type(), Dir :: incoming | outgoing, Packet :: exml:element()) -> boolean(). is_archivable_message(HostType, Dir, Packet) -> @@ -594,12 +592,35 @@ is_archivable_message(HostType, Dir, Packet) -> ArchiveChatMarkers = mod_mam_params:archive_chat_markers(?MODULE, HostType), erlang:apply(M, F, [?MODULE, Dir, Packet, ArchiveChatMarkers]). --spec hooks(jid:lserver()) -> [ejabberd_hooks:hook()]. -hooks(Host) -> - [{filter_room_packet, Host, ?MODULE, filter_room_packet, 60}, - {forget_room, Host, ?MODULE, forget_room, 90}, - {get_personal_data, Host, ?MODULE, get_personal_data, 50} - | mongoose_metrics_mam_hooks:get_mam_muc_hooks(Host)]. +-spec hooks(host_type()) -> [ejabberd_hooks:hook()]. +hooks(HostType) -> + [{filter_room_packet, HostType, ?MODULE, filter_room_packet, 60}, + {forget_room, HostType, ?MODULE, forget_room, 90}, + {get_personal_data, HostType, ?MODULE, get_personal_data, 50} + | mongoose_metrics_mam_hooks:get_mam_muc_hooks(HostType)]. + +register_features(HostType) -> + MUCHost = gen_mod:get_module_opt_subhost(HostType, mod_mam_muc, mod_muc:default_host()), + [mod_disco:register_feature(MUCHost, Feature) || Feature <- features(?MODULE, HostType)], + ok. + +unregister_features(HostType) -> + MUCHost = gen_mod:get_module_opt_subhost(HostType, mod_mam_muc, mod_muc:default_host()), + [mod_disco:unregister_feature(MUCHost, Feature) || Feature <- features(?MODULE, HostType)], + ok. + +add_iq_handlers(HostType, Opts) -> + MUCHost = gen_mod:get_opt_subhost(HostType, Opts, mod_muc:default_host()), + IQDisc = gen_mod:get_opt(iqdisc, Opts, parallel), %% Type + gen_iq_handler:add_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_04, + ?MODULE, room_process_mam_iq, IQDisc), + gen_iq_handler:add_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_06, + ?MODULE, room_process_mam_iq, IQDisc). + +remove_iq_handlers(HostType) -> + MUCHost = gen_mod:get_module_opt_subhost(HostType, mod_mam_muc, mod_muc:default_host()), + gen_iq_handler:remove_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_04), + gen_iq_handler:remove_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_06). ensure_metrics(Host) -> lists:foreach(fun(Name) -> diff --git a/src/mongoose_client_api/mongoose_client_api_rooms_messages.erl b/src/mongoose_client_api/mongoose_client_api_rooms_messages.erl index 1d5f0684d0c..97466e28c0e 100644 --- a/src/mongoose_client_api/mongoose_client_api_rooms_messages.erl +++ b/src/mongoose_client_api/mongoose_client_api_rooms_messages.erl @@ -50,15 +50,15 @@ to_json(Req, #{role_in_room := none} = State) -> mongoose_client_api:forbidden_request(Req, State); to_json(Req, #{jid := UserJID, room := Room} = State) -> RoomJID = maps:get(jid, Room), - Server = UserJID#jid.server, + HostType = mod_muc_light_utils:room_jid_to_host_type(RoomJID), Now = os:system_time(microsecond), - ArchiveID = mod_mam_muc:archive_id_int(Server, RoomJID), + ArchiveID = mod_mam_muc:archive_id_int(HostType, RoomJID), QS = cowboy_req:parse_qs(Req), PageSize = maybe_integer(proplists:get_value(<<"limit">>, QS, <<"50">>)), Before = maybe_integer(proplists:get_value(<<"before">>, QS)), End = maybe_before_to_us(Before, Now), RSM = #rsm_in{direction = before, id = undefined}, - R = mod_mam_muc:lookup_messages(Server, + R = mod_mam_muc:lookup_messages(HostType, #{archive_id => ArchiveID, owner_jid => RoomJID, caller_jid => UserJID, diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index eb2a14fb90f..4b93281037f 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -896,24 +896,25 @@ is_muc_room_owner(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. --spec can_access_identity(HookServer, Room, User) -> Result when - HookServer :: jid:lserver(), +-spec can_access_identity(HostType, Room, User) -> Result when + HostType :: mongooseim:host_type(), Room :: jid:jid(), User :: jid:jid(), Result :: boolean(). -can_access_identity(HookServer, Room, User) -> - ejabberd_hooks:run_for_host_type(can_access_identity, HookServer, false, - [Room, User]). +can_access_identity(HostType, Room, User) -> + ejabberd_hooks:run_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. --spec can_access_room(HookServer, Room, User) -> Result when - HookServer :: jid:lserver(), +-spec can_access_room(HostType, Room, User) -> Result when + HostType :: mongooseim:host_type(), Room :: jid:jid(), User :: jid:jid(), Result :: boolean(). -can_access_room(HookServer, Room, User) -> - ejabberd_hooks:run_for_host_type(can_access_room, HookServer, false, [Room, User]). +can_access_room(HostType, Room, User) -> + ejabberd_hooks:run_for_host_type(can_access_room, HostType, false, + [HostType, Room, User]). %% MAM related hooks @@ -1058,80 +1059,80 @@ mam_muc_archive_size(HookServer, 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. --spec mam_muc_get_behaviour(HookServer, ArchiveID, +-spec mam_muc_get_behaviour(HostType, ArchiveID, RoomJID, RemoteJID) -> Result when - HookServer :: jid:lserver(), + HostType :: mongooseim:host_type(), ArchiveID :: undefined | mod_mam:archive_id(), RoomJID :: jid:jid(), RemoteJID :: jid:jid(), Result :: mod_mam:archive_behaviour(). -mam_muc_get_behaviour(HookServer, ArchiveID, RoomJID, RemoteJID) -> +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, HookServer, DefaultBehaviour, - [HookServer, ArchiveID, RoomJID, RemoteJID]). + ejabberd_hooks:run_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. %%% %%% It's possible to set which JIDs are always or never allowed in the archive --spec mam_muc_set_prefs(HookServer, ArchiveId, RoomJID, +-spec mam_muc_set_prefs(HostType, ArchiveId, RoomJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> Result when - HookServer :: jid:lserver(), + HostType :: mongooseim:host_type(), ArchiveId :: undefined | mod_mam:archive_id(), RoomJID :: jid:jid(), DefaultMode :: mod_mam:archive_behaviour(), AlwaysJIDs :: [jid:literal_jid()], NeverJIDs :: [jid:literel_jid()], Result :: any(). -mam_muc_set_prefs(HookServer, ArchiveID, RoomJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> +mam_muc_set_prefs(HostType, ArchiveID, RoomJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> InitialAcc = {error, not_implemented}, - ejabberd_hooks:run_for_host_type(mam_muc_set_prefs, HookServer, InitialAcc, - [HookServer, ArchiveID, RoomJID, DefaultMode, + ejabberd_hooks:run_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. --spec mam_muc_get_prefs(HookServer, DefaultMode, ArchiveID, RoomJID) -> Result when - HookServer :: jid:lserver(), +-spec mam_muc_get_prefs(HostType, DefaultMode, ArchiveID, RoomJID) -> Result when + HostType :: mongooseim:host_type(), DefaultMode :: mod_mam:archive_behaviour(), ArchiveID :: undefined | mod_mam:archive_id(), RoomJID :: jid:jid(), Result :: mod_mam:preference() | {error, Reason :: term()}. -mam_muc_get_prefs(HookServer, DefaultMode, ArchiveID, RoomJID) -> +mam_muc_get_prefs(HostType, DefaultMode, ArchiveID, RoomJID) -> InitialAcc = {DefaultMode, [], []}, %% mod_mam:preference() type - ejabberd_hooks:run_for_host_type(mam_muc_get_prefs, HookServer, InitialAcc, - [HookServer, ArchiveID, RoomJID]). + ejabberd_hooks:run_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. --spec mam_muc_remove_archive(HookServer, ArchiveID, RoomJID) -> any() when - HookServer :: jid:lserver(), +-spec mam_muc_remove_archive(HostType, ArchiveID, RoomJID) -> any() when + HostType :: mongooseim:host_type(), ArchiveID :: undefined | mod_mam:archive_id(), RoomJID :: jid:jid(). -mam_muc_remove_archive(HookServer, ArchiveID, RoomJID) -> - ejabberd_hooks:run_for_host_type(mam_muc_remove_archive, HookServer, ok, - [HookServer, ArchiveID, RoomJID]). +mam_muc_remove_archive(HostType, ArchiveID, RoomJID) -> + ejabberd_hooks:run_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. --spec mam_muc_lookup_messages(HookServer, Params) -> Result when - HookServer :: jid:lserver(), +-spec mam_muc_lookup_messages(HostType, Params) -> Result when + HostType :: mongooseim:host_type(), Params :: map(), Result :: {ok, mod_mam:lookup_result()}. -mam_muc_lookup_messages(HookServer, Params) -> +mam_muc_lookup_messages(HostType, Params) -> InitialLookupValue = {0, 0, []}, %% mod_mam:lookup_result() type - ejabberd_hooks:run_for_host_type(mam_muc_lookup_messages, HookServer, + ejabberd_hooks:run_for_host_type(mam_muc_lookup_messages, HostType, {ok, InitialLookupValue}, - [HookServer, Params]). + [HostType, Params]). %%% @doc The `mam_muc_archive_message' hook is called in order %%% to store the MUC message in the archive. --spec mam_muc_archive_message(HookServer, Params) -> Result when - HookServer :: jid:lserver(), +-spec mam_muc_archive_message(HostType, Params) -> Result when + HostType :: mongooseim:host_type(), Params :: mod_mam:archive_message_params(), Result :: ok | {error, timeout}. -mam_muc_archive_message(HookServer, Params) -> - ejabberd_hooks:run_for_host_type(mam_muc_archive_message, HookServer, ok, - [HookServer, Params]). +mam_muc_archive_message(HostType, Params) -> + ejabberd_hooks:run_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. diff --git a/src/muc_light/mod_muc_light_utils.erl b/src/muc_light/mod_muc_light_utils.erl index f9548556761..84094c624ee 100644 --- a/src/muc_light/mod_muc_light_utils.erl +++ b/src/muc_light/mod_muc_light_utils.erl @@ -30,6 +30,7 @@ -export([room_limit_reached/2]). -export([filter_out_prevented/3]). -export([acc_to_host_type/1]). +-export([room_jid_to_host_type/1]). -export([run_forget_room_hook/1]). -include("jlib.hrl"). @@ -273,6 +274,10 @@ acc_to_host_type(Acc) -> HostType end. +-spec room_jid_to_host_type(jid:jid()) -> mongooseim:host_type(). +room_jid_to_host_type(#jid{lserver = MucHost}) -> + muc_host_to_host_type(MucHost). + server_host_to_host_type(LServer) -> case mongoose_domain_api:get_host_type(LServer) of {ok, HostType} -> From dd170a968b9b7d2891720d36cab611e5328a1662 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 19 May 2021 10:41:36 +0200 Subject: [PATCH 13/73] Handle new can_access_room/4 and can_access_identity/4 hooks --- src/mam/mod_mam_muc.erl | 8 ++++++-- src/mod_muc.erl | 22 ++++++++++++---------- src/muc_light/mod_muc_light.erl | 19 ++++++++++--------- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index a57bb6d7344..9856b5da3a3 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -242,7 +242,11 @@ room_process_mam_iq(From, To, Acc, IQ) -> mongoose_metrics:update(HostType, modMucMamDroppedIQ, 1), {Acc, return_max_delay_reached_error_iq(IQ)} end; - false -> {Acc, return_action_not_allowed_error_iq(IQ)} + false -> + ?LOG_WARNING(#{what => action_not_allowed, + action => Action, acc => Acc, + can_access_room => can_access_room(HostType, From, To)}), + {Acc, return_action_not_allowed_error_iq(IQ)} end. @@ -478,7 +482,7 @@ lookup_messages_without_policy_violation_check(HostType, mongoose_hooks:mam_muc_lookup_messages(HostType, Params) end. -archive_message_for_ct(Params = #{room_jid := RoomJid}) -> +archive_message_for_ct(Params = #{local_jid := RoomJid}) -> HostType = mod_muc_light_utils:room_jid_to_host_type(RoomJid), archive_message(HostType, Params). diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 7a00214ca6a..1222dfaa834 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -60,8 +60,8 @@ %% Hooks handlers -export([is_muc_room_owner/4, - can_access_room/3, - can_access_identity/3]). + can_access_room/4, + can_access_identity/4]). %% Stats -export([online_rooms_number/0]). @@ -413,8 +413,8 @@ init([Host, Opts]) -> hibernated_room_timeout = HibernatedTimeout}, ejabberd_hooks:add(is_muc_room_owner, Host, ?MODULE, is_muc_room_owner, 50), - ejabberd_hooks:add(can_access_room, MyHost, ?MODULE, can_access_room, 50), - ejabberd_hooks:add(can_access_identity, MyHost, ?MODULE, can_access_identity, 50), + ejabberd_hooks:add(can_access_room, Host, ?MODULE, can_access_room, 50), + ejabberd_hooks:add(can_access_identity, Host, ?MODULE, can_access_identity, 50), ejabberd_router:register_route(MyHost, mongoose_packet_handler:new(?MODULE, #{state => State})), @@ -451,8 +451,8 @@ set_persistent_rooms_timer(#state{hibernated_room_check_interval = Timeout}) -> %%-------------------------------------------------------------------- handle_call(stop, _From, State) -> ejabberd_hooks:delete(is_muc_room_owner, State#state.server_host, ?MODULE, is_muc_room_owner, 50), - ejabberd_hooks:delete(can_access_room, State#state.host, ?MODULE, can_access_room, 50), - ejabberd_hooks:delete(can_access_identity, State#state.host, ?MODULE, can_access_identity, 50), + ejabberd_hooks:delete(can_access_room, State#state.server_host, ?MODULE, can_access_room, 50), + ejabberd_hooks:delete(can_access_identity, State#state.server_host, ?MODULE, can_access_identity, 50), {stop, normal, ok, State}; @@ -1248,17 +1248,19 @@ clean_table_from_bad_node(Node, Host) -> is_muc_room_owner(_, _HostType, Room, User) -> mod_muc_room:is_room_owner(Room, User) =:= {ok, true}. --spec can_access_room(Acc :: boolean(), Room :: jid:jid(), User :: jid:jid()) -> +-spec can_access_room(Acc :: boolean(), HostType :: mongooseim:host_type(), + Room :: jid:jid(), User :: jid:jid()) -> boolean(). -can_access_room(_, Room, User) -> +can_access_room(_, _HostType, Room, User) -> case mod_muc_room:can_access_room(Room, User) of {error, _} -> false; {ok, CanAccess} -> CanAccess end. --spec can_access_identity(Acc :: boolean(), Room :: jid:jid(), User :: jid:jid()) -> +-spec can_access_identity(Acc :: boolean(), HostType :: mongooseim:host_type(), + Room :: jid:jid(), User :: jid:jid()) -> boolean(). -can_access_identity(_, Room, User) -> +can_access_identity(_, _HostType, Room, User) -> case mod_muc_room:can_access_identity(Room, User) of {error, _} -> false; {ok, CanAccess} -> CanAccess diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index 84359890c27..b4bbc4ee18f 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -50,8 +50,8 @@ process_iq_get/5, process_iq_set/4, is_muc_room_owner/4, - can_access_room/3, - can_access_identity/3]). + can_access_room/4, + can_access_identity/4]). %% For propEr -export([apply_rsm/3]). @@ -250,9 +250,8 @@ process_config_schema_value([{float_value, Val}]) -> {Val, float}. hooks(Host, MUCHost) -> [{is_muc_room_owner, Host, ?MODULE, is_muc_room_owner, 50}, - {can_access_room, MUCHost, ?MODULE, can_access_room, 50}, - {can_access_identity, MUCHost, ?MODULE, can_access_identity, 50}, - + {can_access_room, Host, ?MODULE, can_access_room, 50}, + {can_access_identity, Host, ?MODULE, can_access_identity, 50}, {offline_groupchat_message_hook, Host, ?MODULE, prevent_service_unavailable, 90}, {remove_user, Host, ?MODULE, remove_user, 50}, {remove_domain, Host, ?MODULE, remove_domain, 50}, @@ -455,14 +454,16 @@ process_iq_set(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ) -> is_muc_room_owner(_, _HostType, Room, User) -> owner == get_affiliation(Room, User). --spec can_access_room(Acc :: boolean(), Room :: jid:jid(), User :: jid:jid()) -> +-spec can_access_room(Acc :: boolean(), HostType :: mongooseim:host_type(), + Room :: jid:jid(), User :: jid:jid()) -> boolean(). -can_access_room(_, Room, User) -> +can_access_room(_, _HostType, Room, User) -> none =/= get_affiliation(Room, User). --spec can_access_identity(Acc :: boolean(), Room :: jid:jid(), User :: jid:jid()) -> +-spec can_access_identity(Acc :: boolean(), HostType :: mongooseim:host_type(), + Room :: jid:jid(), User :: jid:jid()) -> boolean(). -can_access_identity(_Acc, _Room, _User) -> +can_access_identity(_Acc, _HostType, _Room, _User) -> %% User JIDs are explicit in MUC Light but this hook is about appending %% 0045 MUC element with user identity and we don't want it false. From 1857b251c0a88c197d7fea3e75011d12481be62d Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 19 May 2021 11:03:21 +0200 Subject: [PATCH 14/73] Rename last Host to HostType in mod_mam_muc --- src/mam/mod_mam_muc.erl | 42 +++++++++++++++++------------------------ 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index 9856b5da3a3..ebc05a3328f 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -177,7 +177,6 @@ filter_room_packet(Packet, HostType, EventData = #{}) -> false -> Packet end. - %% @doc Archive without validation. -spec archive_room_packet(HostType :: host_type(), Packet :: packet(), FromNick :: jid:user(), @@ -207,8 +206,9 @@ archive_room_packet(HostType, Packet, FromNick, FromJID=#jid{}, origin_id => OriginID, direction => incoming, packet => Packet1}, + %% Packet to be broadcasted and packet to be archived are + %% not 100% the same Result = archive_message(HostType, Params), - %% Packet2 goes to archive, Packet to other users case Result of ok -> ExtID = mess_id_to_external_binary(MessID), @@ -219,7 +219,6 @@ archive_room_packet(HostType, Packet, FromNick, FromJID=#jid{}, false -> Packet end. - %% @doc `To' is an account or server entity hosting the archive. %% Servers that archive messages on behalf of local users SHOULD expose archives %% to the user on their bare JID (i.e. `From.luser'), @@ -249,9 +248,6 @@ room_process_mam_iq(From, To, Acc, IQ) -> {Acc, return_action_not_allowed_error_iq(IQ)} end. - -%% #rh -%% @doc This hook is called from `mod_muc:forget_room(Host, Name)'. -spec forget_room(map(), host_type(), jid:lserver(), binary()) -> map(). forget_room(Acc, _HostType, MucServer, RoomName) -> delete_archive(MucServer, RoomName), @@ -336,7 +332,6 @@ handle_set_prefs_result({error, Reason}, _DefaultMode, _AlwaysJIDs, _NeverJIDs, IQ) -> return_error_iq(IQ, Reason). - -spec handle_get_prefs(host_type(), jid:jid(), jlib:iq()) -> jlib:iq() | {error, any(), jlib:iq()}. handle_get_prefs(HostType, ArcJID=#jid{}, IQ=#iq{}) -> @@ -422,15 +417,15 @@ handle_get_message_form(HostType, %% ---------------------------------------------------------------------- %% Backend wrappers --spec archive_id_int(jid:server(), jid:jid()) -> integer() | undefined. -archive_id_int(Host, ArcJID = #jid{}) -> - mongoose_hooks:mam_muc_archive_id(Host, ArcJID). +-spec archive_id_int(HostType :: host_type(), ArcJID :: jid:jid()) -> + integer() | undefined. +archive_id_int(HostType, ArcJID = #jid{}) -> + mongoose_hooks:mam_muc_archive_id(HostType, ArcJID). - --spec archive_size(jid:server(), mod_mam:archive_id(), jid:jid()) - -> integer(). -archive_size(Host, ArcID, ArcJID = #jid{}) -> - mongoose_hooks:mam_muc_archive_size(Host, ArcID, ArcJID). +-spec archive_size(HostType :: host_type(), ArcID :: mod_mam:archive_id(), + ArcJID ::jid:jid()) -> non_neg_integer(). +archive_size(HostType, ArcID, ArcJID = #jid{}) -> + mongoose_hooks:mam_muc_archive_size(HostType, ArcID, ArcJID). -spec get_behaviour(HostType :: host_type(), ArcID :: mod_mam:archive_id(), LocJID :: jid:jid(), RemJID :: jid:jid()) -> any(). @@ -445,7 +440,6 @@ set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs) -> mongoose_hooks:mam_muc_set_prefs(HostType, ArcID, ArcJID, DefaultMode, AlwaysJIDs, NeverJIDs). - %% @doc Load settings from the database. -spec get_prefs(HostType :: host_type(), ArcID :: mod_mam:archive_id(), ArcJID :: jid:jid(), GlobalDefaultMode :: mod_mam:archive_behaviour()) @@ -459,7 +453,6 @@ remove_archive(HostType, ArcID, ArcJID = #jid{}) -> mongoose_hooks:mam_muc_remove_archive(HostType, ArcID, ArcJID), ok. - %% See description in mod_mam. -spec lookup_messages(HostType :: host_type(), Params :: map()) -> {ok, mod_mam:lookup_result()} @@ -529,12 +522,12 @@ replace_from_to_attributes(SrcJID, Packet = #xmlel{attrs = Attrs}) -> message_row_to_ext_id(#{id := MessID}) -> mess_id_to_external_binary(MessID). --spec handle_error_iq(mongoose_acc:t(), jid:lserver(), jid:jid(), atom(), +-spec handle_error_iq(mongoose_acc:t(), host_type(), jid:jid(), atom(), {error, term(), jlib:iq()} | jlib:iq() | ignore) -> {mongoose_acc:t(), jlib:iq() | ignore}. -handle_error_iq(Acc, Host, _To, _Action, {error, _Reason, IQ}) -> - mongoose_metrics:update(Host, modMucMamDroppedIQ, 1), +handle_error_iq(Acc, HostType, _To, _Action, {error, _Reason, IQ}) -> + mongoose_metrics:update(HostType, modMucMamDroppedIQ, 1), {Acc, IQ}; -handle_error_iq(Acc, _Host, _To, _Action, IQ) -> +handle_error_iq(Acc, _HostType, _To, _Action, IQ) -> {Acc, IQ}. return_error_iq(IQ, {Reason, {stacktrace, _Stacktrace}}) -> @@ -569,7 +562,6 @@ return_max_delay_reached_error_iq(IQ) -> return_message_form_iq(HostType, IQ) -> IQ#iq{type = result, sub_el = [message_form(?MODULE, HostType, IQ#iq.xmlns)]}. - % the stacktrace is a big lie report_issue({Reason, {stacktrace, Stacktrace}}, Issue, ArcJID, IQ) -> report_issue(Reason, Stacktrace, Issue, ArcJID, IQ); @@ -615,7 +607,7 @@ unregister_features(HostType) -> add_iq_handlers(HostType, Opts) -> MUCHost = gen_mod:get_opt_subhost(HostType, Opts, mod_muc:default_host()), - IQDisc = gen_mod:get_opt(iqdisc, Opts, parallel), %% Type + IQDisc = gen_mod:get_opt(iqdisc, Opts, parallel), gen_iq_handler:add_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_04, ?MODULE, room_process_mam_iq, IQDisc), gen_iq_handler:add_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_06, @@ -626,9 +618,9 @@ remove_iq_handlers(HostType) -> gen_iq_handler:remove_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_04), gen_iq_handler:remove_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_06). -ensure_metrics(Host) -> +ensure_metrics(HostType) -> lists:foreach(fun(Name) -> - mongoose_metrics:ensure_metric(Host, Name, spiral) + mongoose_metrics:ensure_metric(HostType, Name, spiral) end, spirals()). From 99a7fe0703e746180ff1356167ef401e40f88556 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 19 May 2021 21:43:18 +0200 Subject: [PATCH 15/73] Use new mongoose_domain_api API for get_host_type --- src/mam/mod_mam.erl | 2 +- src/mod_muc.erl | 2 +- src/mod_muc_room.erl | 2 +- src/muc_light/mod_muc_light_commands.erl | 2 +- src/muc_light/mod_muc_light_utils.erl | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mam/mod_mam.erl b/src/mam/mod_mam.erl index 7270a39ffae..325149ef5a2 100644 --- a/src/mam/mod_mam.erl +++ b/src/mam/mod_mam.erl @@ -315,7 +315,7 @@ sm_filter_offline_message(Other, _From, _To, _Packet) -> -spec jid_to_host_type(jid:jid()) -> host_type(). jid_to_host_type(#jid{lserver=LServer}) -> - case mongoose_domain_api:get_host_type(LServer) of + case mongoose_domain_api:get_domain_host_type(LServer) of {ok, HostType} -> HostType; {error, not_found} -> diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 1222dfaa834..fbc5a0b71d8 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1323,7 +1323,7 @@ config_metrics(Host) -> -spec server_host_to_host_type(jid:lserver()) -> mongooseim:host_type(). server_host_to_host_type(ServerHost) -> - case mongoose_domain_api:get_host_type(ServerHost) of + case mongoose_domain_api:get_domain_host_type(ServerHost) of {ok, HostType} -> HostType; {error, not_found} -> diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index c52bee57314..066394073a7 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -4771,7 +4771,7 @@ ls(LogMap, State) -> -spec server_host_to_host_type(jid:lserver()) -> mongooseim:host_type(). server_host_to_host_type(ServerHost) -> - case mongoose_domain_api:get_host_type(ServerHost) of + case mongoose_domain_api:get_domain_host_type(ServerHost) of {ok, HostType} -> HostType; {error, not_found} -> diff --git a/src/muc_light/mod_muc_light_commands.erl b/src/muc_light/mod_muc_light_commands.erl index 56f0b2532f6..cffad3cfea8 100644 --- a/src/muc_light/mod_muc_light_commands.erl +++ b/src/muc_light/mod_muc_light_commands.erl @@ -209,7 +209,7 @@ change_room_config(Domain, RoomID, RoomName, User, Subject) -> end. lserver_to_host_type(LServer) -> - case mongoose_domain_api:get_host_type(LServer) of + case mongoose_domain_api:get_domain_host_type(LServer) of {ok, HostType} -> HostType; {error, not_found} -> diff --git a/src/muc_light/mod_muc_light_utils.erl b/src/muc_light/mod_muc_light_utils.erl index 84094c624ee..f1b642f03dc 100644 --- a/src/muc_light/mod_muc_light_utils.erl +++ b/src/muc_light/mod_muc_light_utils.erl @@ -287,8 +287,8 @@ server_host_to_host_type(LServer) -> end. muc_host_to_host_type(MucHost) -> - {ok, ServerHost} = mongoose_subhosts:get_host(MucHost), - server_host_to_host_type(ServerHost). + {ok, HostType} = mongoose_domain_api:get_subdomain_host_type(MucHost), + HostType. run_forget_room_hook({Room, MucHost}) -> HostType = muc_host_to_host_type(MucHost), From f3db2f86a86c9067829ea422c2f773e70eaf33d5 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:02:23 +0200 Subject: [PATCH 16/73] Pass Acc around in muc-light code (to encode/decode functions of the codecs) --- src/muc_light/mod_muc_light.erl | 359 ++++++++++++---------- src/muc_light/mod_muc_light_db.erl | 6 +- src/muc_light/mod_muc_light_db_mnesia.erl | 16 +- src/muc_light/mod_muc_light_db_rdbms.erl | 298 +++++++++--------- src/muc_light/mod_muc_light_room.erl | 27 +- src/muc_light/mod_muc_light_utils.erl | 70 +++-- 6 files changed, 417 insertions(+), 359 deletions(-) diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index b4bbc4ee18f..db740469c85 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -43,7 +43,7 @@ %% Hook handlers -export([prevent_service_unavailable/4, - get_muc_service/5, + disco_local_items/5, remove_user/3, remove_domain/3, add_rooms_to_roster/2, @@ -58,6 +58,9 @@ -export([config_metrics/1]). +-type muc_server() :: jid:lserver(). +-type host_type() :: mongooseim:host_type(). + %%==================================================================== %% API %%==================================================================== @@ -71,13 +74,24 @@ default_schema_definition() -> default_host() -> mongoose_subdomain_utils:make_subdomain_pattern(<<"muclight.@HOST@">>). --spec default_config(MUCServer :: jid:lserver()) -> mod_muc_light_room_config:kv(). +-spec default_config(MUCServer :: muc_server()) -> mod_muc_light_room_config:kv(). default_config(MUCServer) -> - gen_mod:get_module_opt_by_subhost(MUCServer, ?MODULE, default_config, []). + HostType = mod_muc_light_utils:muc_host_to_host_type(MUCServer), + default_config_for_host_type(HostType). --spec config_schema(MUCServer :: jid:lserver()) -> mod_muc_light_room_config:schema(). +-spec config_schema(MUCServer :: muc_server()) -> mod_muc_light_room_config:schema(). config_schema(MUCServer) -> - gen_mod:get_module_opt_by_subhost(MUCServer, ?MODULE, config_schema, undefined). + HostType = mod_muc_light_utils:muc_host_to_host_type(MUCServer), + config_schema_for_host_type(HostType). + +%% Internals +-spec default_config_for_host_type(host_type()) -> mod_muc_light_room_config:kv(). +default_config_for_host_type(HostType) -> + gen_mod:get_module_opt(HostType, ?MODULE, default_config, []). + +-spec config_schema_for_host_type(host_type()) -> mod_muc_light_room_config:schema(). +config_schema_for_host_type(HostType) -> + gen_mod:get_module_opt(HostType, ?MODULE, config_schema, undefined). %%==================================================================== %% Administration API @@ -86,17 +100,14 @@ config_schema(MUCServer) -> -spec try_to_create_room(CreatorUS :: jid:simple_bare_jid(), RoomJID :: jid:jid(), CreationCfg :: create_req_props()) -> {ok, jid:simple_bare_jid(), create_req_props()} - | {error, validation_error() | bad_request | exists}. + | {error, validation_error() | bad_request | exists | max_occupants_reached}. try_to_create_room(CreatorUS, RoomJID, #create{raw_config = RawConfig} = CreationCfg) -> - {_RoomU, RoomS} = RoomUS = jid:to_lus(RoomJID), - InitialAffUsers = mod_muc_light_utils:filter_out_prevented( - CreatorUS, RoomUS, CreationCfg#create.aff_users), - MaxOccupants = gen_mod:get_module_opt_by_subhost( - RoomJID#jid.lserver, ?MODULE, max_occupants, ?DEFAULT_MAX_OCCUPANTS), - case {mod_muc_light_room_config:apply_binary_kv( - RawConfig, default_config(RoomS), config_schema(RoomS)), - process_create_aff_users_if_valid(RoomS, CreatorUS, InitialAffUsers)} of - {{ok, Config0}, {ok, FinalAffUsers}} when length(FinalAffUsers) =< MaxOccupants -> + RoomUS = jid:to_lus(RoomJID), + HostType = mod_muc_light_utils:room_jid_to_host_type(RoomJID), + CfgRes = prepare_config(HostType, RawConfig), + AffRes = prepare_affs(HostType, CreatorUS, RoomUS, CreationCfg), + case {CfgRes, AffRes} of + {{ok, Config0}, {ok, FinalAffUsers}} -> Version = mongoose_bin:gen_from_timestamp(), case mod_muc_light_db_backend:create_room( RoomUS, lists:sort(Config0), FinalAffUsers, Version) of @@ -108,8 +119,8 @@ try_to_create_room(CreatorUS, RoomJID, #create{raw_config = RawConfig} = Creatio end; {{error, _} = Error, _} -> Error; - _ -> - {error, bad_request} + {_, {error, _} = Error} -> + Error end. -spec change_room_config(UserJid :: jid:jid(), RoomID :: jid:resource(), @@ -139,66 +150,59 @@ delete_room(RoomUS) -> %% gen_mod callbacks %%==================================================================== --spec start(Host :: jid:server(), Opts :: list()) -> ok. -start(Host, Opts) -> - %% Prevent sending service-unavailable on groupchat messages - - MUCHost = gen_mod:get_opt_subhost(Host, Opts, default_host()), - ejabberd_hooks:add(hooks(Host, MUCHost)), - - case gen_mod:get_opt(rooms_in_rosters, Opts, ?DEFAULT_ROOMS_IN_ROSTERS) of - false -> ignore; - true -> ejabberd_hooks:add(roster_get, Host, ?MODULE, add_rooms_to_roster, 50) - end, - - TrackedDBFuns = [create_room, destroy_room, room_exists, get_user_rooms, - remove_user, remove_domain, - get_config, set_config, get_blocking, set_blocking, - get_aff_users, modify_aff_users], - gen_mod:start_backend_module(mod_muc_light_db, Opts, TrackedDBFuns), - Codec = case gen_mod:get_opt(legacy_mode, Opts, ?DEFAULT_LEGACY_MODE) of - false -> - modern; - true -> - ejabberd_hooks:add(privacy_iq_get, Host, ?MODULE, process_iq_get, 1), - ejabberd_hooks:add(privacy_iq_set, Host, ?MODULE, process_iq_set, 1), - legacy - end, +-spec start(HostType :: host_type(), Opts :: list()) -> ok. +start(HostType, Opts) -> + set_dynamic_opts(HostType, Opts), + Codec = host_type_to_codec(HostType), + gen_mod:start_backend_module(mod_muc_light_db, Opts, tracked_db_funs()), gen_mod:start_backend_module(mod_muc_light_codec, [{backend, Codec}], []), + mod_muc_light_db_backend:start(HostType), + ejabberd_hooks:add(hooks(HostType)), + %% Handler + SubdomainPattern = subdomain_pattern(HostType), + PacketHandler = mongoose_packet_handler:new(?MODULE), + mongoose_domain_api:register_subdomain(HostType, SubdomainPattern, PacketHandler), + ok. - mod_muc_light_db_backend:start(Host, MUCHost), - mongoose_subhosts:register(Host, MUCHost), - ejabberd_router:register_route(MUCHost, mongoose_packet_handler:new(?MODULE)), - - %% Prepare config schema - ConfigSchema = mod_muc_light_room_config:schema_from_definition( - gen_mod:get_opt(config_schema, Opts, default_schema_definition())), - gen_mod:set_module_opt(Host, ?MODULE, config_schema, ConfigSchema), - - %% Prepare default config - DefaultConfig = mod_muc_light_room_config:default_from_schema(ConfigSchema), - gen_mod:set_module_opt(Host, ?MODULE, default_config, DefaultConfig), - +-spec stop(HostType :: host_type()) -> ok. +stop(HostType) -> + SubdomainPattern = subdomain_pattern(HostType), + mongoose_domain_api:unregister_subdomain(HostType, SubdomainPattern), + mod_muc_light_db_backend:stop(HostType), + ejabberd_hooks:delete(hooks(HostType)), ok. --spec stop(Host :: jid:server()) -> ok. -stop(Host) -> - MUCHost = gen_mod:get_module_opt_subhost(Host, ?MODULE, default_host()), - ejabberd_router:unregister_route(MUCHost), - mongoose_subhosts:unregister(MUCHost), +%% Init helpers +subdomain_pattern(HostType) -> + gen_mod:get_module_opt(HostType, ?MODULE, host, default_host()). - mod_muc_light_db_backend:stop(Host, MUCHost), +server_host_to_muc_host(HostType, ServerHost) -> + mongoose_subdomain_utils:get_fqdn(subdomain_pattern(HostType), ServerHost). - ejabberd_hooks:delete(hooks(Host, MUCHost)), +host_type_to_codec(HostType) -> + case gen_mod:get_module_opt(HostType, legacy_mode, ?MODULE, ?DEFAULT_LEGACY_MODE) of + false -> + modern; + true -> + legacy + end. - %% Hook for room in roster - ejabberd_hooks:delete(roster_get, Host, ?MODULE, add_rooms_to_roster, 50), - %% Hooks for legacy mode - ejabberd_hooks:delete(privacy_iq_get, Host, ?MODULE, process_iq_get, 1), - ejabberd_hooks:delete(privacy_iq_set, Host, ?MODULE, process_iq_set, 1), +tracked_db_funs() -> + [create_room, destroy_room, room_exists, get_user_rooms, + remove_user, remove_domain, + get_config, set_config, get_blocking, set_blocking, + get_aff_users, modify_aff_users]. - ok. +set_dynamic_opts(HostType, Opts) -> + %% Prepare config schema + Def = gen_mod:get_opt(config_schema, Opts, default_schema_definition()), + ConfigSchema = mod_muc_light_room_config:schema_from_definition(Def), + gen_mod:set_module_opt(HostType, ?MODULE, config_schema, ConfigSchema), + %% Prepare default config + DefaultConfig = mod_muc_light_room_config:default_from_schema(ConfigSchema), + gen_mod:set_module_opt(HostType, ?MODULE, default_config, DefaultConfig). +%% Config callbacks -spec config_spec() -> mongoose_config_spec:config_section(). config_spec() -> #section{ @@ -248,14 +252,28 @@ process_config_schema_value([{string_value, Val}]) -> {Val, binary}; process_config_schema_value([{integer_value, Val}]) -> {Val, integer}; process_config_schema_value([{float_value, Val}]) -> {Val, float}. -hooks(Host, MUCHost) -> - [{is_muc_room_owner, Host, ?MODULE, is_muc_room_owner, 50}, - {can_access_room, Host, ?MODULE, can_access_room, 50}, - {can_access_identity, Host, ?MODULE, can_access_identity, 50}, - {offline_groupchat_message_hook, Host, ?MODULE, prevent_service_unavailable, 90}, - {remove_user, Host, ?MODULE, remove_user, 50}, - {remove_domain, Host, ?MODULE, remove_domain, 50}, - {disco_local_items, Host, ?MODULE, get_muc_service, 50}]. +hooks(HostType) -> + Codec = host_type_to_codec(HostType), + Roster = gen_mod:get_module_opt(HostType, rooms_in_rosters, ?MODULE, ?DEFAULT_ROOMS_IN_ROSTERS), + [{is_muc_room_owner, HostType, ?MODULE, is_muc_room_owner, 50}, + {can_access_room, HostType, ?MODULE, can_access_room, 50}, + {can_access_identity, HostType, ?MODULE, can_access_identity, 50}, + %% Prevent sending service-unavailable on groupchat messages + {offline_groupchat_message_hook, HostType, ?MODULE, prevent_service_unavailable, 90}, + {remove_user, HostType, ?MODULE, remove_user, 50}, + {remove_domain, HostType, ?MODULE, remove_domain, 50}, + {disco_local_items, HostType, ?MODULE, disco_local_items, 50}] ++ + case Codec of + legacy -> + [{privacy_iq_get, HostType, ?MODULE, process_iq_get, 1}, + {privacy_iq_set, HostType, ?MODULE, process_iq_set, 1}]; + _ -> + [] + end ++ + case Roster of + false -> []; + true -> [{roster_get, HostType, ?MODULE, add_rooms_to_roster, 50}] + end. %%==================================================================== %% Routing @@ -264,60 +282,66 @@ hooks(Host, MUCHost) -> -spec process_packet(Acc :: mongoose_acc:t(), From ::jid:jid(), To ::jid:jid(), El :: exml:element(), Extra :: map()) -> any(). process_packet(Acc, From, To, El, _Extra) -> - process_decoded_packet(From, To, mod_muc_light_codec_backend:decode(From, To, El, Acc), Acc, El). + HostType = mod_muc_light_utils:acc_to_host_type(Acc), + DecodedPacket = mod_muc_light_codec_backend:decode(From, To, El, Acc), + process_decoded_packet(HostType, From, To, Acc, El, DecodedPacket). --spec process_decoded_packet(From :: jid:jid(), To :: jid:jid(), - DecodedPacket :: mod_muc_light_codec:decode_result(), +-spec process_decoded_packet( + HostType :: host_type(), + From :: jid:jid(), To :: jid:jid(), Acc :: mongoose_acc:t(), - OrigPacket :: exml:element()) -> any(). -process_decoded_packet(From, To, {ok, {set, #create{} = Create}}, Acc, OrigPacket) -> + OrigPacket :: exml:element(), + DecodedPacket :: mod_muc_light_codec:decode_result()) -> any(). +process_decoded_packet(HostType, From, To, Acc, El, + {ok, {set, #create{} = Create}}) -> FromUS = jid:to_lus(From), - case not mod_muc_light_utils:room_limit_reached(FromUS, To#jid.lserver) of - true -> - create_room(Acc, From, FromUS, To, Create, OrigPacket); - false -> - mod_muc_light_codec_backend:encode_error( - {error, bad_request}, From, To, OrigPacket, make_handler_fun(Acc)) + case not mod_muc_light_utils:room_limit_reached(FromUS, HostType) of + true -> create_room(Acc, From, FromUS, To, Create, El); + false -> make_err(From, To, El, Acc, {error, room_limit_reached}) end; -process_decoded_packet(From, To, {ok, {get, #disco_info{} = DI}}, Acc, _OrigPacket) -> +process_decoded_packet(_HostType, From, To, Acc, _El, + {ok, {get, #disco_info{} = DI}}) -> handle_disco_info_get(From, To, DI, Acc); -process_decoded_packet(From, To, {ok, {get, #disco_items{} = DI}}, Acc, OrigPacket) -> - handle_disco_items_get(Acc, From, To, DI, OrigPacket); -process_decoded_packet(From, To, {ok, {_, #blocking{}} = Blocking}, Acc, OrigPacket) -> - RouteFun = make_handler_fun(Acc), - case gen_mod:get_module_opt_by_subhost(To#jid.lserver, ?MODULE, blocking, ?DEFAULT_BLOCKING) of +process_decoded_packet(HostType, From, To, Acc, El, + {ok, {get, #disco_items{} = DI}}) -> + handle_disco_items_get(HostType, Acc, From, To, DI, El); +process_decoded_packet(HostType, From, To, Acc, El, + {ok, {_, #blocking{}} = Blocking}) -> + case gen_mod:get_module_opt(HostType, ?MODULE, blocking, ?DEFAULT_BLOCKING) of true -> case handle_blocking(Acc, From, To, Blocking) of - {error, _} = Res -> - mod_muc_light_codec_backend:encode_error(Res, From, To, OrigPacket, RouteFun); - _ -> - ok + {error, _} = Res -> make_err(From, To, El, Acc, Res); + _ -> ok end; - false -> mod_muc_light_codec_backend:encode_error( - {error, bad_request}, From, To, OrigPacket, RouteFun) + false -> make_err(From, To, El, Acc, {error, blocking_disabled}) end; -process_decoded_packet(From, To, {ok, #iq{} = IQ}, Acc, OrigPacket) -> - case mod_muc_iq:process_iq(To#jid.lserver, From, To, Acc, IQ) of +process_decoded_packet(HostType, From, To, Acc, El, + {ok, #iq{} = IQ}) -> + case mod_muc_iq:process_iq(HostType, From, To, Acc, IQ) of {Acc1, error} -> - mod_muc_light_codec_backend:encode_error( - {error, feature_not_implemented}, From, To, OrigPacket, make_handler_fun(Acc1)); + make_err(From, To, El, Acc1, {error, feature_not_implemented}); _ -> ok end; -process_decoded_packet(From, #jid{ luser = RoomU } = To, {ok, RequestToRoom}, Acc, OrigPacket) - when RoomU =/= <<>> -> +process_decoded_packet(_HostType, From, To, Acc, El, + {ok, RequestToRoom}) + when To#jid.luser =/= <<>> -> case mod_muc_light_db_backend:room_exists(jid:to_lus(To)) of - true -> mod_muc_light_room:handle_request(From, To, OrigPacket, RequestToRoom, Acc); - false -> mod_muc_light_codec_backend:encode_error( - {error, item_not_found}, From, To, OrigPacket, make_handler_fun(Acc)) + true -> mod_muc_light_room:handle_request(From, To, El, RequestToRoom, Acc); + false -> make_err(From, To, El, Acc, {error, item_not_found}) end; -process_decoded_packet(From, To, {error, _} = Err, Acc, OrigPacket) -> - mod_muc_light_codec_backend:encode_error( - Err, From, To, OrigPacket, make_handler_fun(Acc)); -process_decoded_packet(_From, _To, ignore, _Acc, _OrigPacket) -> +process_decoded_packet(_HostType, From, To, Acc, El, + {error, _} = Err) -> + make_err(From, To, El, Acc, Err); +process_decoded_packet(_HostType, _From, _To, _Acc, _El, ignore) -> ok; -process_decoded_packet(From, To, _InvalidReq, Acc, OrigPacket) -> - mod_muc_light_codec_backend:encode_error( - {error, bad_request}, From, To, OrigPacket, make_handler_fun(Acc)). +process_decoded_packet(_HostType, From, To, Acc, El, InvalidReq) -> + ?LOG_WARNING(#{what => muc_light_invalid_request, + acc => Acc, reason => InvalidReq}), + make_err(From, To, El, Acc, {error, bad_request}). + +make_err(From, To, El, Acc, Reason) -> + mod_muc_light_codec_backend:encode_error(Reason, From, To, El, + make_handler_fun(Acc)). %%==================================================================== %% Hook handlers @@ -331,24 +355,28 @@ prevent_service_unavailable(Acc, _From, _To, Packet) -> _Type -> Acc end. --spec get_muc_service(Acc :: {result, [exml:element()]} | empty | {error, any()}, +%% To is the host name, not subdomain. +-spec disco_local_items(Acc :: {result, [exml:element()]} | empty | {error, any()}, From :: jid:jid(), To :: jid:jid(), NS :: binary(), ejabberd:lang()) -> {result, [exml:element()]} | empty | {error, any()}. -get_muc_service({result, Nodes}, _From, #jid{lserver = LServer} = _To, <<"">>, _Lang) -> - XMLNS = case gen_mod:get_module_opt_by_subhost( - LServer, ?MODULE, legacy_mode, ?DEFAULT_LEGACY_MODE) of +disco_local_items({result, Nodes}, _From, #jid{lserver = ServerHost} = To, <<"">>, _Lang) -> + HostType = mod_muc_light_utils:room_jid_to_host_type(To), + XMLNS = case legacy_mode(HostType) of true -> ?NS_MUC; false -> ?NS_MUC_LIGHT end, - SubHost = gen_mod:get_module_opt_subhost(LServer, ?MODULE, default_host()), + MUCHost = server_host_to_muc_host(HostType, ServerHost), Item = [#xmlel{name = <<"item">>, - attrs = [{<<"jid">>, SubHost}, + attrs = [{<<"jid">>, MUCHost}, {<<"node">>, XMLNS}]}], {result, [Item | Nodes]}; -get_muc_service(Acc, _From, _To, _Node, _Lang) -> +disco_local_items(Acc, _From, _To, _Node, _Lang) -> Acc. +legacy_mode(HostType) -> + gen_mod:get_module_opt(HostType, ?MODULE, legacy_mode, ?DEFAULT_LEGACY_MODE). + -spec remove_user(Acc :: mongoose_acc:t(), User :: binary(), Server :: binary()) -> mongoose_acc:t(). remove_user(Acc, User, Server) -> @@ -357,9 +385,10 @@ remove_user(Acc, User, Server) -> UserUS = {LUser, LServer}, Version = mongoose_bin:gen_from_timestamp(), case mod_muc_light_db_backend:remove_user(UserUS, Version) of - {error, Reason} = Err -> + {error, Reason} -> ?LOG_ERROR(#{what => muc_remove_user_failed, - reason => Reason, acc => Acc}); + reason => Reason, acc => Acc}), + Acc; AffectedRooms -> bcast_removed_user(Acc, UserUS, AffectedRooms, Version), maybe_forget_rooms(Acc, AffectedRooms), @@ -370,8 +399,7 @@ remove_user(Acc, User, Server) -> mongooseim:host_type(), jid:lserver()) -> mongoose_hooks:simple_acc(). remove_domain(Acc, HostType, Domain) -> - MUCHost = gen_mod:get_module_opt_subhost(Domain, ?MODULE, default_host()), - ?LOG_ERROR(#{what => remove_domain_muc, host_type => HostType, domain => Domain}), + MUCHost = server_host_to_muc_host(HostType, Domain), mod_muc_light_db_backend:remove_domain(HostType, MUCHost, Domain), Acc. @@ -381,30 +409,29 @@ add_rooms_to_roster(Acc, UserJID) -> Items = mongoose_acc:get(roster, items, [], Acc), RoomList = mod_muc_light_db_backend:get_user_rooms(UserUS, undefined), Info = get_rooms_info(lists:sort(RoomList)), - NewItems = lists:foldl( - fun({{RoomU, RoomS}, RoomName, RoomVersion}, Items0) -> - JID = jid:make_noprep(RoomU, RoomS, <<>>), - Item = #roster{ - usj = {RoomU, RoomS, jid:to_lower(JID)}, - us = {RoomU, RoomS}, - jid = jid:to_lower(JID), - name = RoomName, - subscription = to, - groups = [?NS_MUC_LIGHT], - xs = [#xmlel{ name = <<"version">>, - children = [#xmlcdata{ content = RoomVersion }] }] - }, - [Item | Items0] - end, Items, Info), + NewItems = [make_roster_item(Item) || Item <- Info] ++ Items, mongoose_acc:set(roster, items, NewItems, Acc). +make_roster_item({{RoomU, RoomS}, RoomName, RoomVersion}) -> + JID = jid:make_noprep(RoomU, RoomS, <<>>), + VerEl = #xmlel{ name = <<"version">>, + children = [#xmlcdata{ content = RoomVersion }] }, + #roster{usj = {RoomU, RoomS, jid:to_lower(JID)}, + us = {RoomU, RoomS}, + jid = jid:to_lower(JID), + name = RoomName, + subscription = to, + groups = [?NS_MUC_LIGHT], + xs = [VerEl] }. + -spec process_iq_get(Acc :: mongoose_acc:t(), From :: jid:jid(), To :: jid:jid(), IQ :: jlib:iq(), ActiveList :: binary()) -> {stop, mongoose_acc:t()} | mongoose_acc:t(). process_iq_get(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ, _ActiveList) -> + HostType = mod_muc_light_utils:acc_to_host_type(Acc), MUCHost = gen_mod:get_module_opt_subhost(FromS, ?MODULE, default_host()), case {mod_muc_light_codec_backend:decode(From, To, IQ, Acc), - gen_mod:get_module_opt_by_subhost(MUCHost, ?MODULE, blocking, ?DEFAULT_BLOCKING)} of + gen_mod:get_module_opt(HostType, ?MODULE, blocking, ?DEFAULT_BLOCKING)} of {{ok, {get, #blocking{} = Blocking}}, true} -> Items = mod_muc_light_db_backend:get_blocking(jid:to_lus(From), MUCHost), mod_muc_light_codec_backend:encode( @@ -422,13 +449,15 @@ process_iq_get(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ, _ActiveList) mongoose_acc:set(hook, result, Result, Acc) end. +%% Blocking is done using your local domain -spec process_iq_set(Acc :: mongoose_acc:t(), From :: jid:jid(), To :: jid:jid(), IQ :: jlib:iq()) -> {stop, mongoose_acc:t()} | mongoose_acc:t(). process_iq_set(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ) -> - MUCHost = gen_mod:get_module_opt_subhost(FromS, ?MODULE, default_host()), + HostType = mod_muc_light_utils:acc_to_host_type(Acc), + MUCHost = server_host_to_muc_host(HostType, FromS), case {mod_muc_light_codec_backend:decode(From, To, IQ, Acc), - gen_mod:get_module_opt_by_subhost(MUCHost, ?MODULE, blocking, ?DEFAULT_BLOCKING)} of + gen_mod:get_module_opt(HostType, ?MODULE, blocking, ?DEFAULT_BLOCKING)} of {{ok, {set, #blocking{ items = Items }} = Blocking}, true} -> RouteFun = fun(_, _, Packet) -> put(encode_res, Packet) end, ConditionFun = fun({_, _, {WhoU, WhoS}}) -> WhoU =:= <<>> orelse WhoS =:= <<>> end, @@ -472,6 +501,26 @@ can_access_identity(_Acc, _HostType, _Room, _User) -> %% Internal functions %%==================================================================== +prepare_config(HostType, RawConfig) -> + DefConfig = default_config_for_host_type(HostType), + Schema = config_schema_for_host_type(HostType), + mod_muc_light_room_config:apply_binary_kv(RawConfig, DefConfig, Schema). + +prepare_affs(HostType, CreatorUS, RoomUS, #create{aff_users = AffUsers}) -> + InitialAffUsers = mod_muc_light_utils:filter_out_prevented(HostType, + CreatorUS, RoomUS, AffUsers), + Res = process_create_aff_users_if_valid(HostType, CreatorUS, InitialAffUsers), + MaxOccupants = max_occupants(HostType), + case Res of + {ok, FinalAffUsers} when length(FinalAffUsers) > MaxOccupants -> + {error, max_occupants_reached}; + _ -> + Res + end. + +max_occupants(HostType) -> + gen_mod:get_module_opt(HostType, ?MODULE, max_occupants, ?DEFAULT_MAX_OCCUPANTS). + get_affiliation(Room, User) -> case mod_muc_light_db_backend:get_aff_users(jid:to_lus(Room)) of {ok, AffUsers, _} -> @@ -504,21 +553,23 @@ create_room(Acc, From, FromUS, To, Create0, OrigPacket) -> {error, bad_request, ErrorText}, From, To, OrigPacket, make_handler_fun(Acc)) end. --spec process_create_aff_users_if_valid(MUCServer :: jid:lserver(), +-spec process_create_aff_users_if_valid(HostType :: host_type(), Creator :: jid:simple_bare_jid(), AffUsers :: aff_users()) -> {ok, aff_users()} | {error, bad_request}. -process_create_aff_users_if_valid(MUCServer, Creator, AffUsers) -> +process_create_aff_users_if_valid(HostType, Creator, AffUsers) -> case lists:any(fun ({User, _}) when User =:= Creator -> true; ({_, Aff}) -> Aff =:= none end, AffUsers) of false -> - process_create_aff_users( - Creator, AffUsers, gen_mod:get_module_opt_by_subhost( - MUCServer, ?MODULE, equal_occupants, ?DEFAULT_EQUAL_OCCUPANTS)); + process_create_aff_users(Creator, AffUsers, equal_occupants(HostType)); true -> {error, bad_request} end. +equal_occupants(HostType) -> + gen_mod:get_module_opt(HostType, ?MODULE, + equal_occupants, ?DEFAULT_EQUAL_OCCUPANTS). + -spec process_create_aff_users(Creator :: jid:simple_bare_jid(), AffUsers :: aff_users(), EqualOccupants :: boolean()) -> {ok, aff_users()} | {error, bad_request}. @@ -539,11 +590,12 @@ handle_disco_info_get(From, To, DiscoInfo, Acc) -> mod_muc_light_codec_backend:encode({get, DiscoInfo}, From, jid:to_lus(To), fun ejabberd_router:route/3, Acc). --spec handle_disco_items_get(Acc :: mongoose_acc:t(), +-spec handle_disco_items_get(HostType :: host_type(), + Acc :: mongoose_acc:t(), From ::jid:jid(), To ::jid:jid(), DiscoItems :: disco_items_req_props(), OrigPacket :: exml:element()) -> ok. -handle_disco_items_get(Acc, From, To, DiscoItems0, OrigPacket) -> +handle_disco_items_get(HostType, Acc, From, To, DiscoItems0, OrigPacket) -> case catch mod_muc_light_db_backend:get_user_rooms(jid:to_lus(From), To#jid.lserver) of {error, Error} -> ?LOG_ERROR(#{what => muc_get_user_rooms_failed, @@ -554,8 +606,7 @@ handle_disco_items_get(Acc, From, To, DiscoItems0, OrigPacket) -> Rooms -> RoomsInfo = get_rooms_info(lists:sort(Rooms)), RouteFun = fun ejabberd_router:route/3, - RoomsPerPage = gen_mod:get_module_opt_by_subhost( - To#jid.lserver, ?MODULE, rooms_per_page, ?DEFAULT_ROOMS_PER_PAGE), + RoomsPerPage = gen_mod:get_module_opt(HostType, ?MODULE, rooms_per_page, ?DEFAULT_ROOMS_PER_PAGE), case apply_rsm(RoomsInfo, length(RoomsInfo), page_service_limit(DiscoItems0#disco_items.rsm, RoomsPerPage)) of {ok, RoomsInfoSlice, RSMOut} -> @@ -688,7 +739,7 @@ bcast_removed_user(Acc, UserJID, Cmd = {set, Affiliations, OldAffUsers, NewAffUsers}, mod_muc_light_codec_backend:encode(Cmd, UserJID, RoomUS, fun ejabberd_router:route/3, Acc), bcast_removed_user(Acc, UserJID, RAffected, Version, ID); -bcast_removed_user(Acc, UserJID, [{{RoomU, RoomS} = RoomUS, Error} | RAffected], Version, ID) -> +bcast_removed_user(Acc, UserJID, [{{RoomU, RoomS} = _RoomUS, Error} | RAffected], Version, ID) -> ?LOG_ERROR(#{what => muc_remove_user_failed, user_jid => jid:to_binary(UserJID), room => RoomU, sub_host => RoomS, reason => Error}), diff --git a/src/muc_light/mod_muc_light_db.erl b/src/muc_light/mod_muc_light_db.erl index fc05f4047aa..928497fff05 100644 --- a/src/muc_light/mod_muc_light_db.erl +++ b/src/muc_light/mod_muc_light_db.erl @@ -25,9 +25,9 @@ %% ------------------------ Backend start/stop ------------------------ --callback start(Host :: jid:server(), MUCHost :: jid:server()) -> ok. +-callback start(HostType :: mongooseim:host_type()) -> ok. --callback stop(Host :: jid:server(), MUCHost :: jid:server()) -> ok. +-callback stop(HostType :: mongooseim:host_type()) -> ok. %% ------------------------ General room management ------------------------ @@ -44,7 +44,7 @@ [RoomUS :: jid:simple_bare_jid()]. -callback get_user_rooms_count(UserUS :: jid:simple_bare_jid(), - MUCServer :: jid:lserver()) -> + HostType :: mongooseim:host_type()) -> non_neg_integer(). -callback remove_user(UserUS :: jid:simple_bare_jid(), Version :: binary()) -> diff --git a/src/muc_light/mod_muc_light_db_mnesia.erl b/src/muc_light/mod_muc_light_db_mnesia.erl index 1bb0437e598..da94540e1a7 100644 --- a/src/muc_light/mod_muc_light_db_mnesia.erl +++ b/src/muc_light/mod_muc_light_db_mnesia.erl @@ -25,8 +25,8 @@ %% API -export([ - start/2, - stop/2, + start/1, + stop/1, create_room/4, destroy_room/1, @@ -85,12 +85,12 @@ %% ------------------------ Backend start/stop ------------------------ --spec start(Host :: jid:server(), MUCHost :: jid:server()) -> ok. -start(_Host, _MUCHost) -> +-spec start(Host :: jid:server()) -> ok. +start(_Host) -> init_tables(). --spec stop(Host :: jid:server(), MUCHost :: jid:server()) -> ok. -stop(_Host, _MUCHost) -> +-spec stop(Host :: jid:server()) -> ok. +stop(_Host) -> ok. %% ------------------------ General room management ------------------------ @@ -120,9 +120,9 @@ get_user_rooms(UserUS, _MUCHost) -> [ UserRoom#muc_light_user_room.room || UserRoom <- UsersRooms ]. -spec get_user_rooms_count(UserUS :: jid:simple_bare_jid(), - MUCServer :: jid:lserver()) -> + HostType :: mongooseim:host_type()) -> non_neg_integer(). -get_user_rooms_count(UserUS, _MUCServer) -> +get_user_rooms_count(UserUS, _HostType) -> length(mnesia:dirty_read(muc_light_user_room, UserUS)). -spec remove_user(UserUS :: jid:simple_bare_jid(), Version :: binary()) -> diff --git a/src/muc_light/mod_muc_light_db_rdbms.erl b/src/muc_light/mod_muc_light_db_rdbms.erl index 921c7db0687..8af59e1cc48 100644 --- a/src/muc_light/mod_muc_light_db_rdbms.erl +++ b/src/muc_light/mod_muc_light_db_rdbms.erl @@ -25,8 +25,8 @@ %% API -export([ - start/2, - stop/2, + start/1, + stop/1, create_room/4, destroy_room/1, @@ -71,13 +71,13 @@ %% ------------------------ Backend start/stop ------------------------ --spec start(Host :: jid:server(), MUCHost :: jid:server()) -> ok. -start(_Host, _MUCHost) -> +-spec start(Host :: jid:server()) -> ok. +start(_Host) -> prepare_queries(), ok. --spec stop(Host :: jid:server(), MUCHost :: jid:server()) -> ok. -stop(_Host, _MUCHost) -> +-spec stop(Host :: jid:server()) -> ok. +stop(_Host) -> ok. %% ------------------------ SQL ------------------------------------------- @@ -251,123 +251,123 @@ prepare_domain_removal_queries() -> %% ------------------------ Room SQL functions ------------------------ -select_room_id(MainHost, RoomU, RoomS) -> +select_room_id(HostType, RoomU, RoomS) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_room_id, [RoomU, RoomS]). + HostType, muc_light_select_room_id, [RoomU, RoomS]). -select_room_id_and_version(MainHost, RoomU, RoomS) -> +select_room_id_and_version(HostType, RoomU, RoomS) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_room_id_and_version, [RoomU, RoomS]). + HostType, muc_light_select_room_id_and_version, [RoomU, RoomS]). -select_user_rooms(MainHost, LUser, LServer) -> +select_user_rooms(HostType, LUser, LServer) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_user_rooms, [LUser, LServer]). + HostType, muc_light_select_user_rooms, [LUser, LServer]). -select_user_rooms_count(MainHost, LUser, LServer) -> +select_user_rooms_count(HostType, LUser, LServer) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_user_rooms_count, [LUser, LServer]). + HostType, muc_light_select_user_rooms_count, [LUser, LServer]). -insert_room(MainHost, RoomU, RoomS, Version) -> +insert_room(HostType, RoomU, RoomS, Version) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_insert_room, [RoomU, RoomS, Version]). + HostType, muc_light_insert_room, [RoomU, RoomS, Version]). -update_room_version(MainHost, RoomU, RoomS, Version) -> +update_room_version(HostType, RoomU, RoomS, Version) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_update_room_version, [Version, RoomU, RoomS]). + HostType, muc_light_update_room_version, [Version, RoomU, RoomS]). -delete_room(MainHost, RoomU, RoomS) -> +delete_room(HostType, RoomU, RoomS) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_delete_room, [RoomU, RoomS]). + HostType, muc_light_delete_room, [RoomU, RoomS]). %% ------------------------ Affiliation SQL functions ------------------------ %% Returns affiliations with a version -select_affs_by_us(MainHost, RoomU, RoomS) -> +select_affs_by_us(HostType, RoomU, RoomS) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_affs_by_us, [RoomU, RoomS]). + HostType, muc_light_select_affs_by_us, [RoomU, RoomS]). %% Returns affiliations without a version -select_affs_by_room_id(MainHost, RoomID) -> +select_affs_by_room_id(HostType, RoomID) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_affs_by_room_id, [RoomID]). + HostType, muc_light_select_affs_by_room_id, [RoomID]). -insert_aff(MainHost, RoomID, UserU, UserS, Aff) -> +insert_aff(HostType, RoomID, UserU, UserS, Aff) -> DbAff = aff_atom2db(Aff), mongoose_rdbms:execute_successfully( - MainHost, muc_light_insert_aff, [RoomID, UserU, UserS, DbAff]). + HostType, muc_light_insert_aff, [RoomID, UserU, UserS, DbAff]). -update_aff(MainHost, RoomID, UserU, UserS, Aff) -> +update_aff(HostType, RoomID, UserU, UserS, Aff) -> DbAff = aff_atom2db(Aff), mongoose_rdbms:execute_successfully( - MainHost, muc_light_update_aff, [DbAff, RoomID, UserU, UserS]). + HostType, muc_light_update_aff, [DbAff, RoomID, UserU, UserS]). -delete_affs(MainHost, RoomID) -> +delete_affs(HostType, RoomID) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_delete_affs, [RoomID]). + HostType, muc_light_delete_affs, [RoomID]). -delete_aff(MainHost, RoomID, UserU, UserS) -> +delete_aff(HostType, RoomID, UserU, UserS) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_delete_aff, [RoomID, UserU, UserS]). + HostType, muc_light_delete_aff, [RoomID, UserU, UserS]). %% ------------------------ Config SQL functions --------------------------- -select_config_by_room_id(MainHost, RoomID) -> +select_config_by_room_id(HostType, RoomID) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_config_by_room_id, [RoomID]). + HostType, muc_light_select_config_by_room_id, [RoomID]). -select_config_by_us(MainHost, RoomU, RoomS) -> +select_config_by_us(HostType, RoomU, RoomS) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_config_by_us, [RoomU, RoomS]). + HostType, muc_light_select_config_by_us, [RoomU, RoomS]). -insert_config(MainHost, RoomID, Key, Val) -> +insert_config(HostType, RoomID, Key, Val) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_insert_config, [RoomID, Key, Val]). + HostType, muc_light_insert_config, [RoomID, Key, Val]). -update_config(MainHost, RoomID, Key, Val) -> +update_config(HostType, RoomID, Key, Val) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_update_config, [Val, RoomID, Key]). + HostType, muc_light_update_config, [Val, RoomID, Key]). -delete_config(MainHost, RoomID) -> +delete_config(HostType, RoomID) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_delete_config, [RoomID]). + HostType, muc_light_delete_config, [RoomID]). %% ------------------------ Blocking SQL functions ------------------------- -select_blocking(MainHost, LUser, LServer) -> +select_blocking(HostType, LUser, LServer) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_blocking, [LUser, LServer]). + HostType, muc_light_select_blocking, [LUser, LServer]). -select_blocking_cnt(MainHost, LUser, LServer, [{What, Who}]) -> +select_blocking_cnt(HostType, LUser, LServer, [{What, Who}]) -> DbWhat = what_atom2db(What), DbWho = jid:to_binary(Who), mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_blocking_cnt, + HostType, muc_light_select_blocking_cnt, [LUser, LServer, DbWhat, DbWho]); -select_blocking_cnt(MainHost, LUser, LServer, [{What1, Who1}, {What2, Who2}]) -> +select_blocking_cnt(HostType, LUser, LServer, [{What1, Who1}, {What2, Who2}]) -> DbWhat1 = what_atom2db(What1), DbWhat2 = what_atom2db(What2), DbWho1 = jid:to_binary(Who1), DbWho2 = jid:to_binary(Who2), mongoose_rdbms:execute_successfully( - MainHost, muc_light_select_blocking_cnt2, + HostType, muc_light_select_blocking_cnt2, [LUser, LServer, DbWhat1, DbWho1, DbWhat2, DbWho2]). -insert_blocking(MainHost, LUser, LServer, What, Who) -> +insert_blocking(HostType, LUser, LServer, What, Who) -> DbWhat = what_atom2db(What), DbWho = jid:to_binary(Who), mongoose_rdbms:execute_successfully( - MainHost, muc_light_insert_blocking, + HostType, muc_light_insert_blocking, [LUser, LServer, DbWhat, DbWho]). -delete_blocking1(MainHost, LUser, LServer, What, Who) -> +delete_blocking1(HostType, LUser, LServer, What, Who) -> DbWhat = what_atom2db(What), DbWho = jid:to_binary(Who), mongoose_rdbms:execute_successfully( - MainHost, muc_light_delete_blocking1, + HostType, muc_light_delete_blocking1, [LUser, LServer, DbWhat, DbWho]). -delete_blocking(MainHost, UserU, UserS) -> +delete_blocking(HostType, UserU, UserS) -> mongoose_rdbms:execute_successfully( - MainHost, muc_light_delete_blocking, [UserU, UserS]). + HostType, muc_light_delete_blocking, [UserU, UserS]). %% ------------------------ General room management ------------------------ @@ -375,33 +375,33 @@ delete_blocking(MainHost, UserU, UserS) -> AffUsers :: aff_users(), Version :: binary()) -> {ok, FinalRoomUS :: jid:simple_bare_jid()} | {error, exists}. create_room({<<>>, RoomS} = RoomUS, Config, AffUsers, Version) -> - MainHost = main_host(RoomUS), - create_room_with_random_name(MainHost, RoomS, Config, AffUsers, Version, 10); + HostType = main_host(RoomUS), + create_room_with_random_name(HostType, RoomS, Config, AffUsers, Version, 10); create_room(RoomUS, Config, AffUsers, Version) -> - MainHost = main_host(RoomUS), - create_room_with_specified_name(MainHost, RoomUS, Config, AffUsers, Version). + HostType = main_host(RoomUS), + create_room_with_specified_name(HostType, RoomUS, Config, AffUsers, Version). -create_room_with_random_name(_MainHost, RoomS, _Config, _AffUsers, _Version, 0) -> +create_room_with_random_name(_HostType, RoomS, _Config, _AffUsers, _Version, 0) -> ?LOG_ERROR(#{what => muc_create_room_with_random_name_failed, sub_host => RoomS}), error(create_room_with_random_name_failed); -create_room_with_random_name(MainHost, RoomS, Config, AffUsers, Version, Retries) +create_room_with_random_name(HostType, RoomS, Config, AffUsers, Version, Retries) when Retries > 0 -> RoomU = mongoose_bin:gen_from_timestamp(), RoomUS = {RoomU, RoomS}, - F = fun() -> create_room_transaction(MainHost, RoomUS, Config, AffUsers, Version) end, - case mongoose_rdbms:sql_transaction(MainHost, F) of + F = fun() -> create_room_transaction(HostType, RoomUS, Config, AffUsers, Version) end, + case mongoose_rdbms:sql_transaction(HostType, F) of {atomic, ok} -> {ok, RoomUS}; Other -> ?LOG_ERROR(#{what => muc_create_room_with_random_name_retry, candidate_room => RoomU, sub_host => RoomS, reason => Other}), - create_room_with_random_name(MainHost, RoomS, Config, AffUsers, Version, Retries-1) + create_room_with_random_name(HostType, RoomS, Config, AffUsers, Version, Retries-1) end. -create_room_with_specified_name(MainHost, RoomUS, Config, AffUsers, Version) -> - F = fun() -> create_room_transaction(MainHost, RoomUS, Config, AffUsers, Version) end, - case mongoose_rdbms:sql_transaction(MainHost, F) of +create_room_with_specified_name(HostType, RoomUS, Config, AffUsers, Version) -> + F = fun() -> create_room_transaction(HostType, RoomUS, Config, AffUsers, Version) end, + case mongoose_rdbms:sql_transaction(HostType, F) of {atomic, ok} -> {ok, RoomUS}; Other -> @@ -418,37 +418,36 @@ create_room_with_specified_name(MainHost, RoomUS, Config, AffUsers, Version) -> -spec destroy_room(RoomUS :: jid:simple_bare_jid()) -> ok | {error, not_exists | not_empty}. destroy_room(RoomUS) -> - MainHost = main_host(RoomUS), - F = fun() -> destroy_room_transaction(MainHost, RoomUS) end, - {atomic, Res} = mongoose_rdbms:sql_transaction(MainHost, F), + HostType = main_host(RoomUS), + F = fun() -> destroy_room_transaction(HostType, RoomUS) end, + {atomic, Res} = mongoose_rdbms:sql_transaction(HostType, F), Res. -spec room_exists(RoomUS :: jid:simple_bare_jid()) -> boolean(). room_exists({RoomU, RoomS} = RoomUS) -> - MainHost = main_host(RoomUS), - {selected, Res} = select_room_id(MainHost, RoomU, RoomS), + HostType = main_host(RoomUS), + {selected, Res} = select_room_id(HostType, RoomU, RoomS), Res /= []. -spec get_user_rooms(UserUS :: jid:simple_bare_jid(), MUCServer :: jid:lserver() | undefined) -> [RoomUS :: jid:simple_bare_jid()]. -get_user_rooms({LUser, LServer}, undefined) -> - lists:usort(lists:flatmap( - fun(Host) -> - {selected, Rooms} = select_user_rooms(Host, LUser, LServer), - Rooms - end, ?MYHOSTS)); +get_user_rooms({LUser, LServer} = US, undefined) -> + %% Only one hosttype is handled here + %% It is used to be map over MYHOSTS + HostType = mod_muc_light_utils:server_host_to_host_type(LServer), + {selected, Rooms} = select_user_rooms(HostType, LUser, LServer), + Rooms; get_user_rooms({LUser, LServer}, MUCServer) -> - MainHost = main_host(MUCServer), - {selected, Rooms} = select_user_rooms(MainHost, LUser, LServer), + HostType = mod_muc_light_utils:muc_host_to_host_type(MUCServer), + {selected, Rooms} = select_user_rooms(HostType, LUser, LServer), Rooms. -spec get_user_rooms_count(UserUS :: jid:simple_bare_jid(), - MUCServer :: jid:lserver()) -> + HostType :: mongooseim:host_type()) -> non_neg_integer(). -get_user_rooms_count({LUser, LServer}, MUCServer) -> - MainHost = main_host(MUCServer), - {selected, [{Cnt}]} = select_user_rooms_count(MainHost, LUser, LServer), +get_user_rooms_count({LUser, LServer}, HostType) -> + {selected, [{Cnt}]} = select_user_rooms_count(HostType, LUser, LServer), mongoose_rdbms:result_to_integer(Cnt). -spec remove_user(UserUS :: jid:simple_bare_jid(), Version :: binary()) -> @@ -481,8 +480,8 @@ remove_domain(HostType, RoomS, LServer) -> -spec get_config(RoomUS :: jid:simple_bare_jid()) -> {ok, mod_muc_light_room_config:kv(), Version :: binary()} | {error, not_exists}. get_config({RoomU, RoomS} = RoomUS) -> - MainHost = main_host(RoomUS), - {selected, Result} = select_config_by_us(MainHost, RoomU, RoomS), + HostType = main_host(RoomUS), + {selected, Result} = select_config_by_us(HostType, RoomU, RoomS), case Result of [] -> {error, not_exists}; @@ -499,10 +498,10 @@ get_config({RoomU, RoomS} = RoomUS) -> Version :: binary()) -> {ok, PrevVersion :: binary()} | {error, not_exists}. set_config(RoomUS, ConfigChanges, Version) -> - MainHost = main_host(RoomUS), + HostType = main_host(RoomUS), {atomic, Res} = mongoose_rdbms:sql_transaction( - MainHost, fun() -> set_config_transaction(RoomUS, ConfigChanges, Version) end), + HostType, fun() -> set_config_transaction(RoomUS, ConfigChanges, Version) end), Res. -spec set_config(RoomUS :: jid:simple_bare_jid(), @@ -516,8 +515,8 @@ set_config(RoomJID, Key, Val, Version) -> -spec get_blocking(UserUS :: jid:simple_bare_jid(), MUCServer :: jid:lserver()) -> [blocking_item()]. get_blocking({LUser, LServer}, MUCServer) -> - MainHost = main_host(MUCServer), - {selected, WhatWhos} = select_blocking(MainHost, LUser, LServer), + HostType = main_host(MUCServer), + {selected, WhatWhos} = select_blocking(HostType, LUser, LServer), decode_blocking(WhatWhos). -spec get_blocking(UserUS :: jid:simple_bare_jid(), @@ -525,8 +524,8 @@ get_blocking({LUser, LServer}, MUCServer) -> WhatWhos :: [{blocking_what(), jid:simple_bare_jid()}]) -> blocking_action(). get_blocking({LUser, LServer}, MUCServer, WhatWhos) -> - MainHost = main_host(MUCServer), - {selected, [{Count}]} = select_blocking_cnt(MainHost, LUser, LServer, WhatWhos), + HostType = main_host(MUCServer), + {selected, [{Count}]} = select_blocking_cnt(HostType, LUser, LServer, WhatWhos), case mongoose_rdbms:result_to_integer(Count) of 0 -> allow; _ -> deny @@ -536,27 +535,27 @@ get_blocking({LUser, LServer}, MUCServer, WhatWhos) -> MUCServer :: jid:lserver(), BlockingItems :: [blocking_item()]) -> ok. set_blocking(UserUS, MUCServer, BlockingItems) -> - MainHost = main_host(MUCServer), - set_blocking_loop(MainHost, UserUS, MUCServer, BlockingItems). + HostType = main_host(MUCServer), + set_blocking_loop(HostType, UserUS, MUCServer, BlockingItems). -set_blocking_loop(_MainHost, _UserUS, _MUCServer, []) -> +set_blocking_loop(_HostType, _UserUS, _MUCServer, []) -> ok; -set_blocking_loop(MainHost, {LUser, LServer} = UserUS, MUCServer, +set_blocking_loop(HostType, {LUser, LServer} = UserUS, MUCServer, [{What, deny, Who} | RBlockingItems]) -> - {updated, _} = insert_blocking(MainHost, LUser, LServer, What, Who), - set_blocking_loop(MainHost, UserUS, MUCServer, RBlockingItems); -set_blocking_loop(MainHost, {LUser, LServer} = UserUS, MUCServer, + {updated, _} = insert_blocking(HostType, LUser, LServer, What, Who), + set_blocking_loop(HostType, UserUS, MUCServer, RBlockingItems); +set_blocking_loop(HostType, {LUser, LServer} = UserUS, MUCServer, [{What, allow, Who} | RBlockingItems]) -> - {updated, _} = delete_blocking1(MainHost, LUser, LServer, What, Who), - set_blocking_loop(MainHost, UserUS, MUCServer, RBlockingItems). + {updated, _} = delete_blocking1(HostType, LUser, LServer, What, Who), + set_blocking_loop(HostType, UserUS, MUCServer, RBlockingItems). %% ------------------------ Affiliations manipulation ------------------------ -spec get_aff_users(RoomUS :: jid:simple_bare_jid()) -> {ok, aff_users(), Version :: binary()} | {error, not_exists}. get_aff_users({RoomU, RoomS} = RoomUS) -> - MainHost = main_host(RoomUS), - case select_affs_by_us(MainHost, RoomU, RoomS) of + HostType = main_host(RoomUS), + case select_affs_by_us(HostType, RoomU, RoomS) of {selected, []} -> {error, not_exists}; {selected, [{Version, null, null, null}]} -> @@ -572,10 +571,10 @@ get_aff_users({RoomU, RoomS} = RoomUS) -> Version :: binary()) -> mod_muc_light_db:modify_aff_users_return(). modify_aff_users(RoomUS, AffUsersChanges, ExternalCheck, Version) -> - MainHost = main_host(RoomUS), - F = fun() -> modify_aff_users_transaction(MainHost, RoomUS, AffUsersChanges, + HostType = main_host(RoomUS), + F = fun() -> modify_aff_users_transaction(HostType, RoomUS, AffUsersChanges, ExternalCheck, Version) end, - {atomic, Res} = mongoose_rdbms:sql_transaction(MainHost, F), + {atomic, Res} = mongoose_rdbms:sql_transaction(HostType, F), Res. %% ------------------------ Misc ------------------------ @@ -584,13 +583,13 @@ modify_aff_users(RoomUS, AffUsersChanges, ExternalCheck, Version) -> {ok, mod_muc_light_room_config:kv(), aff_users(), Version :: binary()} | {error, not_exists}. get_info({RoomU, RoomS} = RoomUS) -> - MainHost = main_host(RoomUS), - case select_room_id_and_version(MainHost, RoomU, RoomS) of + HostType = main_host(RoomUS), + case select_room_id_and_version(HostType, RoomU, RoomS) of {selected, [{DbRoomID, Version}]} -> RoomID = mongoose_rdbms:result_to_integer(DbRoomID), - {selected, AffUsersDB} = select_affs_by_room_id(MainHost, RoomID), + {selected, AffUsersDB} = select_affs_by_room_id(HostType, RoomID), AffUsers = decode_affs(AffUsersDB), - {selected, ConfigDB} = select_config_by_room_id(MainHost, RoomID), + {selected, ConfigDB} = select_config_by_room_id(HostType, RoomID), {ok, Config} = mod_muc_light_room_config:apply_binary_kv( ConfigDB, [], mod_muc_light:config_schema(RoomS)), @@ -656,51 +655,51 @@ force_clear() -> %% ------------------------ General room management ------------------------ %% Expects config to have unique fields! --spec create_room_transaction(MainHost :: jid:lserver(), +-spec create_room_transaction(HostType :: mongooseim:host_type(), RoomUS :: jid:simple_bare_jid(), Config :: mod_muc_light_room_config:kv(), AffUsers :: aff_users(), Version :: binary()) -> ok. -create_room_transaction(MainHost, {RoomU, RoomS}, Config, AffUsers, Version) -> - insert_room(MainHost, RoomU, RoomS, Version), - RoomID = mongoose_rdbms:selected_to_integer(select_room_id(MainHost, RoomU, RoomS)), +create_room_transaction(HostType, {RoomU, RoomS}, Config, AffUsers, Version) -> + insert_room(HostType, RoomU, RoomS, Version), + RoomID = mongoose_rdbms:selected_to_integer(select_room_id(HostType, RoomU, RoomS)), Schema = mod_muc_light:config_schema(RoomS), ConfigFields = mod_muc_light_room_config:to_binary_kv(Config, Schema), - [insert_aff_tuple(MainHost, RoomID, AffUser) || AffUser <- AffUsers], - [insert_config_kv(MainHost, RoomID, KV) || KV <- ConfigFields], + [insert_aff_tuple(HostType, RoomID, AffUser) || AffUser <- AffUsers], + [insert_config_kv(HostType, RoomID, KV) || KV <- ConfigFields], ok. -insert_aff_tuple(MainHost, RoomID, {{UserU, UserS}, Aff}) -> - insert_aff(MainHost, RoomID, UserU, UserS, Aff). +insert_aff_tuple(HostType, RoomID, {{UserU, UserS}, Aff}) -> + insert_aff(HostType, RoomID, UserU, UserS, Aff). -insert_config_kv(MainHost, RoomID, {Key, Val}) -> - insert_config(MainHost, RoomID, Key, Val). +insert_config_kv(HostType, RoomID, {Key, Val}) -> + insert_config(HostType, RoomID, Key, Val). --spec destroy_room_transaction(MainHost :: jid:lserver(), +-spec destroy_room_transaction(HostType :: mongooseim:host_type(), RoomUS :: jid:simple_bare_jid()) -> ok | {error, not_exists}. -destroy_room_transaction(MainHost, {RoomU, RoomS}) -> - case select_room_id(MainHost, RoomU, RoomS) of +destroy_room_transaction(HostType, {RoomU, RoomS}) -> + case select_room_id(HostType, RoomU, RoomS) of {selected, [{DbRoomID}]} -> RoomID = mongoose_rdbms:result_to_integer(DbRoomID), - {updated, _} = delete_affs(MainHost, RoomID), - {updated, _} = delete_config(MainHost, RoomID), - {updated, _} = delete_room(MainHost, RoomU, RoomS), + {updated, _} = delete_affs(HostType, RoomID), + {updated, _} = delete_config(HostType, RoomID), + {updated, _} = delete_room(HostType, RoomU, RoomS), ok; {selected, []} -> {error, not_exists} end. --spec remove_user_transaction(MainHost :: jid:lserver(), +-spec remove_user_transaction(HostType :: mongooseim:host_type(), UserUS :: jid:simple_bare_jid(), Version :: binary()) -> mod_muc_light_db:remove_user_return(). -remove_user_transaction(MainHost, {UserU, UserS} = UserUS, Version) -> +remove_user_transaction(HostType, {UserU, UserS} = UserUS, Version) -> Rooms = get_user_rooms(UserUS, undefined), - {updated, _} = delete_blocking(MainHost, UserU, UserS), + {updated, _} = delete_blocking(HostType, UserU, UserS), lists:map( fun(RoomUS) -> - {RoomUS, modify_aff_users_transaction(MainHost, + {RoomUS, modify_aff_users_transaction(HostType, RoomUS, [{UserUS, none}], fun(_, _) -> ok end, Version)} end, Rooms). @@ -711,14 +710,14 @@ remove_user_transaction(MainHost, {UserU, UserS} = UserUS, Version) -> Version :: binary()) -> {ok, PrevVersion :: binary()} | {error, not_exists}. set_config_transaction({RoomU, RoomS} = RoomUS, ConfigChanges, Version) -> - MainHost = main_host(RoomUS), - case select_room_id_and_version(MainHost, RoomU, RoomS) of + HostType = main_host(RoomUS), + case select_room_id_and_version(HostType, RoomU, RoomS) of {selected, [{DbRoomID, PrevVersion}]} -> RoomID = mongoose_rdbms:result_to_integer(DbRoomID), - {updated, _} = update_room_version(MainHost, RoomU, RoomS, Version), + {updated, _} = update_room_version(HostType, RoomU, RoomS, Version), lists:foreach( fun({Key, Val}) -> - {updated, _} = update_config(MainHost, RoomID, Key, Val) + {updated, _} = update_config(HostType, RoomID, Key, Val) end, mod_muc_light_room_config:to_binary_kv( ConfigChanges, mod_muc_light:config_schema(RoomS))), {ok, PrevVersion}; @@ -730,24 +729,24 @@ set_config_transaction({RoomU, RoomS} = RoomUS, ConfigChanges, Version) -> %% ------------------------ Affiliations manipulation ------------------------ --spec modify_aff_users_transaction(MainHost :: jid:lserver(), +-spec modify_aff_users_transaction(HostType :: mongooseim:host_type(), RoomUS :: jid:simple_bare_jid(), AffUsersChanges :: aff_users(), CheckFun :: external_check_fun(), Version :: binary()) -> mod_muc_light_db:modify_aff_users_return(). -modify_aff_users_transaction(MainHost, {RoomU, RoomS} = RoomUS, +modify_aff_users_transaction(HostType, {RoomU, RoomS} = RoomUS, AffUsersChanges, CheckFun, Version) -> - case select_room_id_and_version(MainHost, RoomU, RoomS) of + case select_room_id_and_version(HostType, RoomU, RoomS) of {selected, [{DbRoomID, PrevVersion}]} -> RoomID = mongoose_rdbms:result_to_integer(DbRoomID), - modify_aff_users_transaction(MainHost, + modify_aff_users_transaction(HostType, RoomUS, RoomID, AffUsersChanges, CheckFun, PrevVersion, Version); {selected, []} -> {error, not_exists} end. --spec modify_aff_users_transaction(MainHost :: jid:lserver(), +-spec modify_aff_users_transaction(HostType :: mongooseim:host_type(), RoomUS :: jid:simple_bare_jid(), RoomID :: room_id(), AffUsersChanges :: aff_users(), @@ -755,17 +754,17 @@ modify_aff_users_transaction(MainHost, {RoomU, RoomS} = RoomUS, PrevVersion :: binary(), Version :: binary()) -> mod_muc_light_db:modify_aff_users_return(). -modify_aff_users_transaction(MainHost, RoomUS, RoomID, AffUsersChanges, +modify_aff_users_transaction(HostType, RoomUS, RoomID, AffUsersChanges, CheckFun, PrevVersion, Version) -> - {selected, AffUsersDB} = select_affs_by_room_id(MainHost, RoomID), + {selected, AffUsersDB} = select_affs_by_room_id(HostType, RoomID), AffUsers = decode_affs(AffUsersDB), case mod_muc_light_utils:change_aff_users(AffUsers, AffUsersChanges) of {ok, NewAffUsers, AffUsersChanged, JoiningUsers, _LeavingUsers} -> case CheckFun(RoomUS, NewAffUsers) of ok -> - apply_aff_users_transaction(MainHost, RoomID, AffUsersChanged, JoiningUsers), + apply_aff_users_transaction(HostType, RoomID, AffUsersChanged, JoiningUsers), {RoomU, RoomS} = RoomUS, - {updated, _} = update_room_version(MainHost, RoomU, RoomS, Version), + {updated, _} = update_room_version(HostType, RoomU, RoomS, Version), {ok, AffUsers, NewAffUsers, AffUsersChanged, PrevVersion}; Error -> Error @@ -774,28 +773,27 @@ modify_aff_users_transaction(MainHost, RoomUS, RoomID, AffUsersChanges, Error end. --spec apply_aff_users_transaction(MainHost :: jid:lserver(), +-spec apply_aff_users_transaction(HostType :: mongooseim:host_type(), RoomID :: room_id(), AffUsersChanges :: aff_users(), JoiningUsers :: [jid:simple_bare_jid()]) -> ok. -apply_aff_users_transaction(MainHost, RoomID, AffUsersChanged, JoiningUsers) -> +apply_aff_users_transaction(HostType, RoomID, AffUsersChanged, JoiningUsers) -> lists:foreach( fun({{UserU, UserS}, none}) -> - {updated, _} = delete_aff(MainHost, RoomID, UserU, UserS); + {updated, _} = delete_aff(HostType, RoomID, UserU, UserS); ({{UserU, UserS} = UserUS, Aff}) -> case lists:member(UserUS, JoiningUsers) of true -> - {updated, _} = insert_aff(MainHost, RoomID, UserU, UserS, Aff); + {updated, _} = insert_aff(HostType, RoomID, UserU, UserS, Aff); false -> - {updated, _} = update_aff(MainHost, RoomID, UserU, UserS, Aff) + {updated, _} = update_aff(HostType, RoomID, UserU, UserS, Aff) end end, AffUsersChanged). %% ------------------------ Common ------------------------ --spec main_host(JIDOrServer :: jid:simple_bare_jid() | binary()) -> jid:lserver(). +-spec main_host(JIDOrServer :: jid:simple_bare_jid() | binary()) -> mongooseim:host_type(). main_host({_, RoomS}) -> main_host(RoomS); main_host(MUCServer) -> - {ok, MainHost} = mongoose_subhosts:get_host(MUCServer), - MainHost. + mod_muc_light_utils:muc_host_to_host_type(MUCServer). diff --git a/src/muc_light/mod_muc_light_room.erl b/src/muc_light/mod_muc_light_room.erl index e5531f1fa4e..e88dea9febd 100644 --- a/src/muc_light/mod_muc_light_room.erl +++ b/src/muc_light/mod_muc_light_room.erl @@ -64,8 +64,9 @@ maybe_forget(_Acc, _, _) -> NewAffUsers :: aff_users()) -> ok | {error, occupant_limit_exceeded}. participant_limit_check({_, MUCServer} = _RoomUS, NewAffUsers) -> - MaxOccupants = gen_mod:get_module_opt_by_subhost( - MUCServer, mod_muc_light, max_occupants, ?DEFAULT_MAX_OCCUPANTS), + HostType = mod_muc_light_utils:muc_host_to_host_type(MUCServer), + MaxOccupants = gen_mod:get_module_opt( + HostType, mod_muc_light, max_occupants, ?DEFAULT_MAX_OCCUPANTS), case length(NewAffUsers) > MaxOccupants of true -> {error, occupant_limit_exceeded}; false -> ok @@ -119,14 +120,13 @@ process_request({get, #info{} = InfoReq}, {get, InfoReq#info{ version = RoomVersion, aff_users = AffUsers, raw_config = RawConfig }}; process_request({set, #config{} = ConfigReq}, - _From, _UserUS, {_, MUCServer} = RoomUS, - {_, UserAff}, AffUsers, _Acc) -> - AllCanConfigure = gen_mod:get_module_opt_by_subhost( - MUCServer, mod_muc_light, all_can_configure, ?DEFAULT_ALL_CAN_CONFIGURE), + _From, _UserUS, RoomUS, {_, UserAff}, AffUsers, Acc) -> + HostType = mod_muc_light_utils:acc_to_host_type(Acc), + AllCanConfigure = all_can_configure(HostType), process_config_set(ConfigReq, RoomUS, UserAff, AffUsers, AllCanConfigure); process_request({set, #affiliations{} = AffReq}, - _From, UserUS, {_, MUCServer} = RoomUS, - {_, UserAff}, AffUsers, Acc) -> + _From, UserUS, RoomUS, {_, UserAff}, AffUsers, Acc) -> + HostType = mod_muc_light_utils:acc_to_host_type(Acc), OwnerUS = case lists:keyfind(owner, 2, AffUsers) of false -> undefined; {OwnerUS0, _} -> OwnerUS0 @@ -134,11 +134,10 @@ process_request({set, #affiliations{} = AffReq}, ValidateResult = case UserAff of owner -> - {ok, mod_muc_light_utils:filter_out_prevented( + {ok, mod_muc_light_utils:filter_out_prevented(HostType, UserUS, RoomUS, AffReq#affiliations.aff_users)}; member -> - AllCanInvite = gen_mod:get_module_opt_by_subhost( - MUCServer, mod_muc_light, all_can_invite, ?DEFAULT_ALL_CAN_INVITE), + AllCanInvite = all_can_invite(HostType), validate_aff_changes_by_member( AffReq#affiliations.aff_users, [], UserUS, OwnerUS, RoomUS, AllCanInvite) end, @@ -154,6 +153,12 @@ process_request({set, #destroy{}}, process_request(_UnknownReq, _From, _UserUS, _RoomUS, _Auth, _AffUsers, _Acc) -> {error, bad_request}. +all_can_invite(HostType) -> + gen_mod:get_module_opt(HostType, mod_muc_light, all_can_invite, ?DEFAULT_ALL_CAN_INVITE). + +all_can_configure(HostType) -> + gen_mod:get_module_opt(HostType, mod_muc_light, all_can_configure, ?DEFAULT_ALL_CAN_CONFIGURE). + %% --------- Config set --------- -spec process_config_set(ConfigReq :: config_req_props(), diff --git a/src/muc_light/mod_muc_light_utils.erl b/src/muc_light/mod_muc_light_utils.erl index f1b642f03dc..46f8214beb5 100644 --- a/src/muc_light/mod_muc_light_utils.erl +++ b/src/muc_light/mod_muc_light_utils.erl @@ -28,9 +28,11 @@ -export([b2aff/1, aff2b/1]). -export([light_aff_to_muc_role/1]). -export([room_limit_reached/2]). --export([filter_out_prevented/3]). +-export([filter_out_prevented/4]). -export([acc_to_host_type/1]). -export([room_jid_to_host_type/1]). +-export([muc_host_to_host_type/1]). +-export([server_host_to_host_type/1]). -export([run_forget_room_hook/1]). -include("jlib.hrl"). @@ -84,21 +86,23 @@ light_aff_to_muc_role(owner) -> moderator; light_aff_to_muc_role(member) -> participant; light_aff_to_muc_role(none) -> none. --spec room_limit_reached(UserUS :: jid:simple_bare_jid(), RoomS :: jid:lserver()) -> +-spec room_limit_reached(UserUS :: jid:simple_bare_jid(), HostType :: mongooseim:host_type()) -> boolean(). -room_limit_reached(UserUS, RoomS) -> - room_limit_reached( - UserUS, RoomS, gen_mod:get_module_opt_by_subhost( - RoomS, mod_muc_light, rooms_per_user, ?DEFAULT_ROOMS_PER_USER)). - --spec filter_out_prevented(FromUS :: jid:simple_bare_jid(), - RoomUS :: jid:simple_bare_jid(), - AffUsers :: aff_users()) -> aff_users(). -filter_out_prevented(FromUS, {RoomU, MUCServer} = RoomUS, AffUsers) -> - RoomsPerUser = gen_mod:get_module_opt_by_subhost( - MUCServer, mod_muc_light, rooms_per_user, ?DEFAULT_ROOMS_PER_USER), - BlockingEnabled = gen_mod:get_module_opt_by_subhost(MUCServer, mod_muc_light, - blocking, ?DEFAULT_BLOCKING), +room_limit_reached(UserUS, HostType) -> + check_room_limit_reached(UserUS, HostType, rooms_per_user(HostType)). + +rooms_per_user(HostType) -> + gen_mod:get_module_opt(HostType, mod_muc_light, + rooms_per_user, ?DEFAULT_ROOMS_PER_USER). + +-spec filter_out_prevented(HostType :: mongooseim:host_type(), + FromUS :: jid:simple_bare_jid(), + RoomUS :: jid:simple_bare_jid(), + AffUsers :: aff_users()) -> aff_users(). +filter_out_prevented(HostType, FromUS, {RoomU, MUCServer} = RoomUS, AffUsers) -> + RoomsPerUser = rooms_per_user(HostType), + BlockingEnabled = gen_mod:get_module_opt(HostType, mod_muc_light, + blocking, ?DEFAULT_BLOCKING), BlockingQuery = case {BlockingEnabled, RoomU} of {true, <<>>} -> [{user, FromUS}]; {true, _} -> [{user, FromUS}, {room, RoomUS}]; @@ -106,7 +110,8 @@ filter_out_prevented(FromUS, {RoomU, MUCServer} = RoomUS, AffUsers) -> end, case BlockingQuery == undefined andalso RoomsPerUser == infinity of true -> AffUsers; - false -> filter_out_loop(FromUS, MUCServer, BlockingQuery, RoomsPerUser, AffUsers) + false -> filter_out_loop(HostType, FromUS, MUCServer, + BlockingQuery, RoomsPerUser, AffUsers) end. %%==================================================================== @@ -115,36 +120,39 @@ filter_out_prevented(FromUS, {RoomU, MUCServer} = RoomUS, AffUsers) -> %% ---------------- Checks ---------------- --spec room_limit_reached(UserUS :: jid:simple_bare_jid(), - RoomS :: jid:lserver(), +-spec check_room_limit_reached(UserUS :: jid:simple_bare_jid(), + HostType :: mongooseim:host_type(), RoomsPerUser :: infinity | pos_integer()) -> boolean(). -room_limit_reached(_UserUS, _RoomS, infinity) -> +check_room_limit_reached(_UserUS, _HostType, infinity) -> false; -room_limit_reached(UserUS, RoomS, RoomsPerUser) -> - mod_muc_light_db_backend:get_user_rooms_count(UserUS, RoomS) >= RoomsPerUser. +check_room_limit_reached(UserUS, HostType, RoomsPerUser) -> + mod_muc_light_db_backend:get_user_rooms_count(UserUS, HostType) >= RoomsPerUser. %% ---------------- Filter for blocking ---------------- --spec filter_out_loop(FromUS :: jid:simple_bare_jid(), +-spec filter_out_loop(HostType :: mongooseim:host_type(), + FromUS :: jid:simple_bare_jid(), MUCServer :: jid:lserver(), BlockingQuery :: [{blocking_what(), jid:simple_bare_jid()}], RoomsPerUser :: rooms_per_user(), AffUsers :: aff_users()) -> aff_users(). -filter_out_loop(FromUS, MUCServer, BlockingQuery, RoomsPerUser, +filter_out_loop(HostType, FromUS, MUCServer, BlockingQuery, RoomsPerUser, [{UserUS, _} = AffUser | RAffUsers]) -> NotBlocked = case (BlockingQuery == undefined orelse UserUS =:= FromUS) of false -> mod_muc_light_db_backend:get_blocking( UserUS, MUCServer, BlockingQuery) == allow; true -> true end, - case NotBlocked andalso not room_limit_reached(FromUS, MUCServer, RoomsPerUser) of + case NotBlocked andalso not check_room_limit_reached(FromUS, HostType, RoomsPerUser) of true -> - [AffUser | filter_out_loop(FromUS, MUCServer, BlockingQuery, RoomsPerUser, RAffUsers)]; + [AffUser | filter_out_loop(HostType, FromUS, MUCServer, + BlockingQuery, RoomsPerUser, RAffUsers)]; false -> - filter_out_loop(FromUS, MUCServer, BlockingQuery, RoomsPerUser, RAffUsers) + filter_out_loop(HostType, FromUS, MUCServer, + BlockingQuery, RoomsPerUser, RAffUsers) end; -filter_out_loop(_FromUS, _MUCServer, _BlockingQuery, _RoomsPerUser, []) -> +filter_out_loop(_HostType, _FromUS, _MUCServer, _BlockingQuery, _RoomsPerUser, []) -> []. %% ---------------- Affiliations manipulation ---------------- @@ -279,12 +287,8 @@ room_jid_to_host_type(#jid{lserver = MucHost}) -> muc_host_to_host_type(MucHost). server_host_to_host_type(LServer) -> - case mongoose_domain_api:get_host_type(LServer) of - {ok, HostType} -> - HostType; - {error, not_found} -> - LServer - end. + {ok, HostType} = mongoose_domain_api:get_domain_host_type(LServer), + HostType. muc_host_to_host_type(MucHost) -> {ok, HostType} = mongoose_domain_api:get_subdomain_host_type(MucHost), From 18f826370145e15064d4a00ec239a8124f207bf3 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:03:20 +0200 Subject: [PATCH 17/73] Remove gen_mod:get_module_opt_by_subhost/4 --- src/gen_mod.erl | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 5a2c23a1d17..571b0b069ca 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -64,8 +64,6 @@ get_module_opts/2, get_opt_subhost/3, get_module_opt_subhost/3, - % Get opts by subhost - get_module_opt_by_subhost/4, loaded_modules/0, loaded_modules/1, @@ -340,19 +338,6 @@ get_module_opts(HostType, Module) -> [#ejabberd_module{opts = Opts} | _] -> Opts end. - --spec get_module_opt_by_subhost( - SubHost :: domain_name(), - Module :: module(), - Opt :: term(), - Default :: term()) -> term(). -get_module_opt_by_subhost(SubHost, Module, Opt, Default) -> - %% TODO: try to get rid of this interface or at least - %% refactor it with mongoose_subhosts module - {ok, Host} = mongoose_subhosts:get_host(SubHost), - get_module_opt(Host, Module, Opt, Default). - - %% @doc use this function only on init stage %% Non-atomic! You have been warned. -spec set_module_opt(host_type(), module(), _Opt, _Value) -> boolean(). From 39fd37be89f74e7d460b02bd0e0764075c3108e4 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:04:00 +0200 Subject: [PATCH 18/73] Execute disco_local_items for host type in mod_disco --- src/mod_disco.erl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/mod_disco.erl b/src/mod_disco.erl index cc4cdfcceaf..930b7639ca9 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -191,9 +191,8 @@ process_local_iq_items(_From, _To, Acc, #iq{type = set, sub_el = SubEl} = IQ) -> {Acc, IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:not_allowed()]}}; process_local_iq_items(From, To, Acc, #iq{type = get, lang = Lang, sub_el = SubEl} = IQ) -> Node = xml:get_tag_attr_s(<<"node">>, SubEl), - Host = To#jid.lserver, - - case mongoose_hooks:disco_local_items(Host, From, To, Node, Lang) of + {ok, HostType} = mongoose_domain_api:get_domain_host_type(To#jid.lserver), + case mongoose_hooks:disco_local_items(HostType, From, To, Node, Lang) of {result, Items} -> ANode = make_node_attr(Node), {Acc, IQ#iq{type = result, @@ -209,6 +208,7 @@ process_local_iq_items(From, To, Acc, #iq{type = get, lang = Lang, sub_el = SubE process_local_iq_info(_From, _To, Acc, #iq{type = set, sub_el = SubEl} = IQ) -> {Acc, IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:not_allowed()]}}; process_local_iq_info(From, To, Acc, #iq{type = get, lang = Lang, sub_el = SubEl} = IQ) -> + %% Server or a subdomain Host = To#jid.lserver, Node = xml:get_tag_attr_s(<<"node">>, SubEl), Identity = mongoose_hooks:disco_local_identity(Host, From, To, Node, Lang), @@ -219,8 +219,7 @@ process_local_iq_info(From, To, Acc, #iq{type = get, lang = Lang, sub_el = SubEl {Acc, IQ#iq{type = result, sub_el = [#xmlel{name = <<"query">>, attrs = [{<<"xmlns">>, ?NS_DISCO_INFO} | ANode], - children = Identity ++ - Info ++ + children = Identity ++ Info ++ features_to_xml(Features)}]}}; {error, Error} -> {Acc, IQ#iq{type = error, sub_el = [SubEl, Error]}} From fcee9891fce974acfc63a51483066ee3f796f224 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:04:38 +0200 Subject: [PATCH 19/73] Fix spec for mod_vcard:get_local_features --- src/mod_vcard.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index d1b042b535d..b805eda3744 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -473,7 +473,7 @@ set_vcard({error, no_handler_defined}, From, VCARD) -> end; set_vcard({error, _} = E, _From, _VCARD) -> E. --spec get_local_features(Acc :: {result, [exml:element()]} | empty | {error, any()}, +-spec get_local_features(Acc :: {result, [XMLNS :: binary()]} | empty | {error, any()}, From :: jid:jid(), To :: jid:jid(), Node :: binary(), From 122e57eb5875582fef1c0a912033035eb45e620c Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:06:02 +0200 Subject: [PATCH 20/73] Use HostType instead of MucHost to register IQs in mod_muc_iq --- src/mod_muc_iq.erl | 78 ++++++++++------------------------------------ 1 file changed, 17 insertions(+), 61 deletions(-) diff --git a/src/mod_muc_iq.erl b/src/mod_muc_iq.erl index 8caab0feeef..b3bd238a48e 100644 --- a/src/mod_muc_iq.erl +++ b/src/mod_muc_iq.erl @@ -34,78 +34,52 @@ tbl_name() -> start_link() -> gen_server:start_link({local, srv_name()}, ?MODULE, [], []). - %% @doc Handle custom IQ. %% Called from mod_muc_room. --spec process_iq(jid:server(), jid:jid(), jid:jid(), mongoose_acc:t(), +-spec process_iq(mongooseim:host_type(), jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) -> mongoose_acc:t() | {mongoose_acc:t(), error}. -process_iq(Host, From, RoomJID, Acc, IQ = #iq{xmlns = XMLNS}) -> - case ets:lookup(tbl_name(), {XMLNS, Host}) of +process_iq(HostType, From, RoomJID, Acc, IQ = #iq{xmlns = XMLNS}) -> + case ets:lookup(tbl_name(), {XMLNS, HostType}) of [{_, IQHandler}] -> gen_iq_component:handle(IQHandler, Acc, From, RoomJID, IQ); [] -> {Acc, error} end. +-spec register_iq_handler(mongooseim:host_type(), binary(), mongoose_iq_handler:t()) -> ok. +register_iq_handler(HostType, XMLNS, IQHandler) -> + gen_server:cast(srv_name(), + {register_iq_handler, HostType, XMLNS, IQHandler}). --spec register_iq_handler(jid:server(), binary(), mongoose_iq_handler:t()) -> ok. -register_iq_handler(Host, XMLNS, IQHandler) -> +-spec unregister_iq_handler(mongooseim:host_type(), binary()) -> ok. +unregister_iq_handler(HostType, XMLNS) -> gen_server:cast(srv_name(), - {register_iq_handler, Host, XMLNS, IQHandler}). + {unregister_iq_handler, HostType, XMLNS}). -spec sync() -> ok. sync() -> gen_server:call(srv_name(), sync). --spec unregister_iq_handler(jid:server(), binary()) -> ok. -unregister_iq_handler(Host, XMLNS) -> - gen_server:cast(srv_name(), - {unregister_iq_handler, Host, XMLNS}). - %%==================================================================== %% gen_server callbacks %%==================================================================== -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- init([]) -> ets:new(tbl_name(), [named_table, protected]), {ok, #state{}}. -%%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} -%% Description: Handling call messages -%%-------------------------------------------------------------------- handle_call(sync, _From, State) -> {reply, ok, State}; handle_call(_Request, _From, State) -> - Reply = ok, - {reply, Reply, State}. - -%%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling cast messages -%%-------------------------------------------------------------------- - -handle_cast({register_iq_handler, Host, XMLNS, IQHandler}, State) -> - ets:insert(tbl_name(), {{XMLNS, Host}, IQHandler}), + {reply, ok, State}. + +handle_cast({register_iq_handler, HostType, XMLNS, IQHandler}, State) -> + ets:insert(tbl_name(), {{XMLNS, HostType}, IQHandler}), {noreply, State}; -handle_cast({unregister_iq_handler, Host, XMLNS}, State) -> - case ets:lookup(tbl_name(), {XMLNS, Host}) of +handle_cast({unregister_iq_handler, HostType, XMLNS}, State) -> + case ets:lookup(tbl_name(), {XMLNS, HostType}) of [{_, IQHandler}] -> gen_iq_component:stop_iq_handler(IQHandler), - ets:delete(tbl_name(), {XMLNS, Host}); + ets:delete(tbl_name(), {XMLNS, HostType}); _ -> ok end, @@ -113,30 +87,12 @@ handle_cast({unregister_iq_handler, Host, XMLNS}, State) -> handle_cast(_Msg, State) -> {noreply, State}. -%%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling all non call/cast messages -%%-------------------------------------------------------------------- - handle_info(_Msg, State) -> {noreply, State}. -%%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() -%% Description: This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any necessary -%% cleaning up. When it returns, the gen_server terminates with Reason. -%% The return value is ignored. -%%-------------------------------------------------------------------- terminate(_Reason, _State) -> ok. -%%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: Convert process state when code is changed -%%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. From 2f0218d1c0941be42bf31b633c76b8c62913ebd9 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:06:38 +0200 Subject: [PATCH 21/73] Register IQs for HostType in mod_mam_muc --- src/mam/mod_mam_muc.erl | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index ebc05a3328f..732b6d1086a 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -153,9 +153,9 @@ start(HostType, Opts) -> -spec stop(HostType :: host_type()) -> any(). stop(HostType) -> ?LOG_DEBUG(#{what => mam_muc_stopping}), - unregister_features(HostType), ejabberd_hooks:delete(hooks(HostType)), remove_iq_handlers(HostType), + unregister_features(HostType), ok. %% ---------------------------------------------------------------------- @@ -595,6 +595,7 @@ hooks(HostType) -> {get_personal_data, HostType, ?MODULE, get_personal_data, 50} | mongoose_metrics_mam_hooks:get_mam_muc_hooks(HostType)]. +%% TODO multitenancy register_features(HostType) -> MUCHost = gen_mod:get_module_opt_subhost(HostType, mod_mam_muc, mod_muc:default_host()), [mod_disco:register_feature(MUCHost, Feature) || Feature <- features(?MODULE, HostType)], @@ -605,18 +606,25 @@ unregister_features(HostType) -> [mod_disco:unregister_feature(MUCHost, Feature) || Feature <- features(?MODULE, HostType)], ok. +-spec default_host() -> mongoose_subdomain_utils:subdomain_pattern(). +default_host() -> + mod_muc:default_host(). + +subdomain_pattern(HostType) -> + gen_mod:get_module_opt(HostType, ?MODULE, host, default_host()). + add_iq_handlers(HostType, Opts) -> - MUCHost = gen_mod:get_opt_subhost(HostType, Opts, mod_muc:default_host()), IQDisc = gen_mod:get_opt(iqdisc, Opts, parallel), - gen_iq_handler:add_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_04, + gen_iq_handler:add_iq_handler(mod_muc_iq, HostType, ?NS_MAM_04, ?MODULE, room_process_mam_iq, IQDisc), - gen_iq_handler:add_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_06, - ?MODULE, room_process_mam_iq, IQDisc). + gen_iq_handler:add_iq_handler(mod_muc_iq, HostType, ?NS_MAM_06, + ?MODULE, room_process_mam_iq, IQDisc), + ok. remove_iq_handlers(HostType) -> - MUCHost = gen_mod:get_module_opt_subhost(HostType, mod_mam_muc, mod_muc:default_host()), - gen_iq_handler:remove_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_04), - gen_iq_handler:remove_iq_handler(mod_muc_iq, MUCHost, ?NS_MAM_06). + gen_iq_handler:remove_iq_handler(mod_muc_iq, HostType, ?NS_MAM_04), + gen_iq_handler:remove_iq_handler(mod_muc_iq, HostType, ?NS_MAM_06), + ok. ensure_metrics(HostType) -> lists:foreach(fun(Name) -> From f74128b04d89afc2d7bf5aaba2d7c16599f256a7 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:07:29 +0200 Subject: [PATCH 22/73] Remove muc_SUITE:load_already_registered_permanent_rooms testcase --- big_tests/tests/muc_SUITE.erl | 43 +++-------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/big_tests/tests/muc_SUITE.erl b/big_tests/tests/muc_SUITE.erl index 7c3737400e4..92809dbe880 100644 --- a/big_tests/tests/muc_SUITE.erl +++ b/big_tests/tests/muc_SUITE.erl @@ -34,7 +34,6 @@ unload_muc/0, muc_host/0, start_room/5, - create_instant_room/5, generate_rpc_jid/1, destroy_room/1, destroy_room/2, @@ -273,7 +272,6 @@ groups() -> deny_creation_of_http_password_protected_room_wrong_password ]}, {room_registration_race_condition, [], [ - load_already_registered_permanent_rooms, create_already_registered_room, check_presence_route_to_offline_room, check_message_route_to_offline_room @@ -462,10 +460,6 @@ end_per_group(_GroupName, Config) -> domain() -> ct:get_config({hosts, mim, domain}). -init_per_testcase(CaseName = load_already_registered_permanent_rooms, Config) -> - meck_room(), - meck_room_start(), - escalus:init_per_testcase(CaseName, Config); init_per_testcase(CaseName = create_already_registered_room, Config) -> meck_room(), meck_room_start(), @@ -546,7 +540,7 @@ meck_room() -> meck_room_start() -> rpc(mim(), meck, expect, [mod_muc_room, start, fun(Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts) -> - mod_muc:register_room(Host, Room, ?FAKEPID), + mod_muc:register_room(Host, Host, Room, ?FAKEPID), meck:passthrough([Host, ServerHost, Access, @@ -560,7 +554,7 @@ meck_room_start() -> rpc(mim(), meck, expect, [mod_muc_room, start, fun(Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Creator, Nick, DefRoomOpts) -> - mod_muc:register_room(Host, Room, ?FAKEPID), + mod_muc:register_room(Host, Host, Room, ?FAKEPID), meck:passthrough([Host, ServerHost, Access, @@ -585,9 +579,6 @@ meck_room_route() -> % destroy_room(Config), % escalus:end_per_testcase(CaseName, Config); -end_per_testcase(CaseName = load_already_registered_permanent_rooms, Config) -> - rpc(mim(), meck, unload, []), - escalus:end_per_testcase(CaseName, Config); end_per_testcase(CaseName = create_already_registered_room, Config) -> rpc(mim(), meck, unload, []), escalus:end_per_testcase(CaseName, Config); @@ -4534,34 +4525,6 @@ parse_result_query(#xmlel{name = <<"query">>, children = Children}) -> last = Last, count = Count}. -%%-------------------------------------------------------------------- -%% Room registration use cases -%% Tests for race condition that occurs when multiple users -%% attempt to start and/or register the same room at the same time -%%-------------------------------------------------------------------- -load_already_registered_permanent_rooms(_Config) -> - Room = <<"testroom1">>, - Host = <<"localhost">>, - ServerHost = <<"localhost">>, - Access = none, - HistorySize = 10, - RoomShaper = none, - HttpAuthPool = none, - - %% Write a permanent room - ok = rpc(mim(), mod_muc, store_room, [domain(), Host, Room, []]), - - % Load permanent rooms - rpc(mim(), mod_muc, load_permanent_rooms, - [Host, ServerHost, Access, HistorySize, RoomShaper, HttpAuthPool]), - - %% Read online room - RoomJID = mongoose_helper:make_jid(Room, Host, <<>>), - {ok, Pid} = rpc(mim(), mod_muc, room_jid_to_pid, [RoomJID]), - - %% Check if the pid read from mnesia matches the fake pid - ?assert_equal(?FAKEPID, Pid). - create_already_registered_room(Config) -> Room = <<"testroom2">>, Host = muc_host(), @@ -4744,7 +4707,7 @@ stanza_change_nick(Room, NewNick) -> start_rsm_rooms(Config, User, Nick) -> From = generate_rpc_jid(User), - [create_instant_room( + [muc_helper:create_instant_room( <<"localhost">>, generate_room_name(N), From, Nick, []) || N <- lists:seq(1, 15)], Config. From d13f0b599835e25c771692b84b2e319b1cb79902 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:08:01 +0200 Subject: [PATCH 23/73] Use HostType variable in mongoose_hooks:disco_local_items --- src/mongoose_hooks.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index 4b93281037f..92b46cfa7bc 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -1256,15 +1256,15 @@ disco_local_features(Server, From, To, Node, Lang) -> [From, To, Node, Lang]). %%% @doc `disco_local_items' hook is called to extract items associated with the server. --spec disco_local_items(Server, From, To, Node, Lang) -> Result when - Server :: jid:server(), +-spec disco_local_items(HostType, From, To, Node, Lang) -> Result when + HostType :: mongooseim:host_type(), From :: jid:jid(), To :: jid:jid(), Node :: binary(), Lang :: ejabberd:lang(), Result :: {result, [exml:element()]} | {error, any()}. -disco_local_items(Server, From, To, Node, Lang) -> - ejabberd_hooks:run_for_host_type(disco_local_items, Server, empty, +disco_local_items(HostType, From, To, Node, Lang) -> + ejabberd_hooks:run_for_host_type(disco_local_items, HostType, empty, [From, To, Node, Lang]). %%% @doc `disco_local_identity' hook is called to get the identity of the server. From fa4abce2d9606d75b4fa87d3387fc45874228f17 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:09:28 +0200 Subject: [PATCH 24/73] Add load_permanent_rooms_at_startup into migration docs --- doc/migrations/4.2.0_4.3.0.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/migrations/4.2.0_4.3.0.md b/doc/migrations/4.2.0_4.3.0.md index 9ebcb0ca403..f274e2846e1 100644 --- a/doc/migrations/4.2.0_4.3.0.md +++ b/doc/migrations/4.2.0_4.3.0.md @@ -92,6 +92,7 @@ GO - `is_muc_room_owner` is called for HostType (was for MucHost). - `is_muc_room_owner` takes an extra argument HostType. - `muc_room_pid` hook removed. +- `load_permanent_rooms_at_startup` option is ignored now. ## Metrics REST API (obsolete) From aa767383e76e7567359715c3198789006ab8ff4d Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:09:51 +0200 Subject: [PATCH 25/73] Add removal of gen_mod:get_module_opt_by_subhost into migration docs --- doc/migrations/4.2.0_4.3.0.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/migrations/4.2.0_4.3.0.md b/doc/migrations/4.2.0_4.3.0.md index f274e2846e1..aef6474bf1a 100644 --- a/doc/migrations/4.2.0_4.3.0.md +++ b/doc/migrations/4.2.0_4.3.0.md @@ -93,6 +93,7 @@ GO - `is_muc_room_owner` takes an extra argument HostType. - `muc_room_pid` hook removed. - `load_permanent_rooms_at_startup` option is ignored now. +- `gen_mod:get_module_opt_by_subhost` API removed. ## Metrics REST API (obsolete) From c429d2a74fb18f0ae4d12e1110a3cc54c1bf358c Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:11:01 +0200 Subject: [PATCH 26/73] Pass MucHost into mod_muc:create_instant_room from tests in mod_helper --- big_tests/tests/muc_helper.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/big_tests/tests/muc_helper.erl b/big_tests/tests/muc_helper.erl index a18f6544ca7..cf0a1144969 100644 --- a/big_tests/tests/muc_helper.erl +++ b/big_tests/tests/muc_helper.erl @@ -136,10 +136,10 @@ generate_rpc_jid({_,User}) -> LServer = escalus_utils:jid_to_lower(Server), {jid, Username, Server, <<"rpc">>, LUsername, LServer, <<"rpc">>}. -create_instant_room(Host, Room, From, Nick, Opts) -> +create_instant_room(ServerHost, Room, From, Nick, Opts) -> Room1 = rpc(mim(), jid, nodeprep, [Room]), rpc(mim(), mod_muc, create_instant_room, - [Host, Room1, From, Nick, Opts]). + [ServerHost, muc_host(), Room1, From, Nick, Opts]). destroy_room(Config) -> destroy_room(muc_host(), ?config(room, Config)). From 9dffbef3366e0492d1b6a554e10c1de97835695b Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:12:24 +0200 Subject: [PATCH 27/73] Remove load_permanent_rooms from mod_muc Use HostType argument in mod_muc --- include/mod_muc.hrl | 1 + src/mod_muc.erl | 527 ++++++++++++++++++++++---------------------- 2 files changed, 263 insertions(+), 265 deletions(-) diff --git a/include/mod_muc.hrl b/include/mod_muc.hrl index 3fd9eb38153..45d5a6ade7b 100644 --- a/include/mod_muc.hrl +++ b/include/mod_muc.hrl @@ -4,6 +4,7 @@ }). -record(muc_online_room, {name_host, + host_type, pid }). diff --git a/src/mod_muc.erl b/src/mod_muc.erl index fbc5a0b71d8..0c966b70ec0 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -37,19 +37,18 @@ stop/1, config_spec/0, process_room_affiliation/1, - room_destroyed/3, + room_destroyed/4, store_room/4, restore_room/3, forget_room/3, - create_instant_room/5, - process_iq_disco_items/4, + create_instant_room/6, broadcast_service_message/2, can_use_nick/4, room_jid_to_pid/1, default_host/0]). %% For testing purposes only --export([load_permanent_rooms/6, register_room/3]). +-export([register_room/4]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, @@ -61,7 +60,8 @@ %% Hooks handlers -export([is_muc_room_owner/4, can_access_room/4, - can_access_identity/4]). + can_access_identity/4, + disco_local_items/5]). %% Stats -export([online_rooms_number/0]). @@ -92,6 +92,9 @@ {From :: jid:jid(), To :: jid:jid(), Acc :: mongoose_acc:t(), Packet :: packet()}. -type access() :: {_AccessRoute, _AccessCreate, _AccessAdmin, _AccessPersistent}. +-type host_type() :: mongooseim:host_type(). +-type muc_host() :: jid:lserver(). +-type server_host() :: jid:lserver(). -include("mod_muc.hrl"). @@ -102,6 +105,7 @@ -type muc_online_room() :: #muc_online_room{ name_host :: room_host(), + host_type :: host_type(), pid :: pid() }. @@ -119,8 +123,8 @@ }. -export_type([room_event_data/0]). --record(state, {host :: jid:server(), - server_host :: jid:literal_jid(), +-record(state, {host_type :: jid:literal_jid(), + subdomain_pattern :: mongoose_subdomain_utils:subdomain_pattern(), access, history_size :: integer(), default_room_opts :: list(), @@ -144,37 +148,39 @@ %% Function: start_link() -> {ok, Pid} | ignore | {error, Error} %% Description: Starts the server %%-------------------------------------------------------------------- --spec start_link(jid:server(), list()) - -> 'ignore' | {'error', _} | {'ok', pid()}. -start_link(Host, Opts) -> - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:start_link({local, Proc}, ?MODULE, [Host, Opts], []). - - --spec start(jid:server(), _) -> - {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}. -start(Host, Opts) -> - ensure_metrics(Host), +-spec start_link(host_type(), list()) + -> ignore | {error, _} | {ok, pid()}. +start_link(HostType, Opts) -> + Proc = gen_mod:get_module_proc(HostType, ?PROCNAME), + gen_server:start_link({local, Proc}, ?MODULE, [HostType, Opts], []). + +-spec start(host_type(), _) -> ok. +start(HostType, Opts) -> + ensure_metrics(HostType), TrackedDBFuns = [store_room, restore_room, forget_room, get_rooms, can_use_nick, get_nick, set_nick, unset_nick], gen_mod:start_backend_module(mod_muc_db, Opts, TrackedDBFuns), - start_supervisor(Host), - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), + start_supervisor(HostType), + start_server(HostType, Opts), + ok. + +-spec stop(host_type()) -> ok. +stop(HostType) -> + stop_supervisor(HostType), + stop_gen_server(HostType), + ok. + +start_server(HostType, Opts) -> + Proc = gen_mod:get_module_proc(HostType, ?PROCNAME), ChildSpec = {Proc, - {?MODULE, start_link, [Host, Opts]}, + {?MODULE, start_link, [HostType, Opts]}, temporary, 1000, worker, [?MODULE]}, ejabberd_sup:start_child(ChildSpec). --spec stop(jid:server()) -> 'ok' - | {'error', 'not_found' | 'restarting' | 'running' | 'simple_one_for_one'}. -stop(Host) -> - stop_supervisor(Host), - stop_gen_server(Host). - -spec config_spec() -> mongoose_config_spec:config_section(). config_spec() -> #section{ @@ -278,61 +284,62 @@ process_room_affiliation(KVs) -> proplists:split(KVs, [user, server, resource, affiliation]), {{User, Server, Res}, Aff}. -stop_gen_server(Host) -> - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), +stop_gen_server(HostType) -> + Proc = gen_mod:get_module_proc(HostType, ?PROCNAME), gen_server:call(Proc, stop), %% Proc can still be alive because of a race condition ejabberd_sup:stop_child(Proc). - %% @doc This function is called by a room in three situations: %% A) The owner of the room destroyed it %% B) The only participant of a temporary room leaves it %% C) mod_muc:stop was called, and each room is being terminated %% In this case, the mod_muc process died before the room processes %% So the message sending must be catched --spec room_destroyed(jid:server(), room(), pid()) -> 'ok'. -room_destroyed(Host, Room, Pid) -> - F = fun() -> mnesia:delete_object(#muc_online_room{name_host = {Room, Host}, pid = Pid}) end, +-spec room_destroyed(host_type(), jid:server(), room(), pid()) -> 'ok'. +room_destroyed(HostType, MucHost, Room, Pid) -> + Obj = #muc_online_room{name_host = {Room, MucHost}, + host_type = HostType, pid = Pid}, + F = fun() -> mnesia:delete_object(Obj) end, {atomic, ok} = mnesia:transaction(F), ok. - %% @doc Create a room. %% If Opts = default, the default room options are used. %% Else use the passed options as defined in mod_muc_room. --spec create_instant_room(jid:server(), Name :: room(), +%% XXX Only used from tests. +-spec create_instant_room(jid:lserver(), MucHost :: jid:lserver(), Name :: room(), From :: jid:jid(), Nick :: nick(), Opts :: list()) -> any(). -create_instant_room(Host, Name, From, Nick, Opts) -> - Proc = gen_mod:get_module_proc(Host, ?PROCNAME), - gen_server:call(Proc, {create_instant, Name, From, Nick, Opts}). - +create_instant_room(ServerHost, MucHost, Name, From, Nick, Opts) -> + {ok, HostType} = mongoose_domain_api:get_domain_host_type(ServerHost), + Proc = gen_mod:get_module_proc(HostType, ?PROCNAME), + gen_server:call(Proc, {create_instant, ServerHost, MucHost, Name, From, Nick, Opts}). -spec store_room(jid:server(), jid:server(), room(), list()) -> {error, _} | ok. -store_room(ServerHost, Host, Name, Opts) -> - mod_muc_db_backend:store_room(ServerHost, Host, Name, Opts). +store_room(ServerHost, MucHost, Name, Opts) -> + mod_muc_db_backend:store_room(ServerHost, MucHost, Name, Opts). --spec restore_room(jid:server(), jid:server(), room()) -> +-spec restore_room(host_type(), jid:server(), room()) -> {error, _} | {ok, _}. -restore_room(ServerHost, Host, Name) -> - mod_muc_db_backend:restore_room(ServerHost, Host, Name). +restore_room(HostType, MucHost, Name) -> + mod_muc_db_backend:restore_room(HostType, MucHost, Name). -spec forget_room(jid:server(), jid:server(), room()) -> ok | {error, term()}. -forget_room(ServerHost, Host, Name) -> +forget_room(ServerHost, MucHost, Name) -> HostType = server_host_to_host_type(ServerHost), %% Removes room from DB, even if it's already removed. - Result = mod_muc_db_backend:forget_room(HostType, Host, Name), + Result = mod_muc_db_backend:forget_room(HostType, MucHost, Name), case Result of ok -> - %% TODO this hook should be refactored to be executed on ServerHost, not Host. + %% TODO this hook should be refactored to be executed on ServerHost, not MucHost. %% It also should be renamed to forget_room_hook. %% We also need to think how to remove stopped rooms %% (i.e. in case we want to expose room removal over REST or SQS). %% %% In some _rare_ cases this hook can be called more than once for the same room. - mongoose_hooks:forget_room(ServerHost, Host, Name); + mongoose_hooks:forget_room(ServerHost, MucHost, Name); _ -> %% Room is not removed or we don't know. %% XXX Handle this case better. @@ -340,35 +347,33 @@ forget_room(ServerHost, Host, Name) -> end, Result. --spec process_iq_disco_items(Host :: jid:server(), From :: jid:jid(), +%% For rooms +-spec process_iq_disco_items(MucHost :: jid:server(), From :: jid:jid(), To :: jid:jid(), jlib:iq()) -> mongoose_acc:t(). -process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) -> +process_iq_disco_items(MucHost, From, To, #iq{lang = Lang} = IQ) -> Rsm = jlib:rsm_decode(IQ), Res = IQ#iq{type = result, sub_el = [#xmlel{name = <<"query">>, attrs = [{<<"xmlns">>, ?NS_DISCO_ITEMS}], - children = iq_disco_items(Host, From, Lang, Rsm)}]}, - ejabberd_router:route(To, - From, - jlib:iq_to_xml(Res)). - + children = iq_disco_items(MucHost, From, Lang, Rsm)}]}, + ejabberd_router:route(To, From, jlib:iq_to_xml(Res)). -spec can_use_nick(jid:server(), jid:server(), jid:jid(), nick()) -> boolean(). can_use_nick(_ServerHost, _Host, _JID, <<>>) -> false; -can_use_nick(ServerHost, Host, JID, Nick) -> - mod_muc_db_backend:can_use_nick(ServerHost, Host, JID, Nick). +can_use_nick(ServerHost, MucHost, JID, Nick) -> + mod_muc_db_backend:can_use_nick(ServerHost, MucHost, JID, Nick). set_nick(_LServer, _Host, _From, <<>>) -> {error, should_not_be_empty}; -set_nick(LServer, Host, From, Nick) -> - mod_muc_db_backend:set_nick(LServer, Host, From, Nick). +set_nick(LServer, MucHost, From, Nick) -> + mod_muc_db_backend:set_nick(LServer, MucHost, From, Nick). -unset_nick(LServer, Host, From) -> - mod_muc_db_backend:unset_nick(LServer, Host, From). +unset_nick(LServer, MucHost, From) -> + mod_muc_db_backend:unset_nick(LServer, MucHost, From). -get_nick(LServer, Host, From) -> - mod_muc_db_backend:get_nick(LServer, Host, From). +get_nick(LServer, MucHost, From) -> + mod_muc_db_backend:get_nick(LServer, MucHost, From). %%==================================================================== %% gen_server callbacks @@ -381,16 +386,15 @@ get_nick(LServer, Host, From) -> %% {stop, Reason} %% Description: Initiates the server %%-------------------------------------------------------------------- --spec init([jid:server() | list(), ...]) -> {'ok', state()}. -init([Host, Opts]) -> - mod_muc_db_backend:init(Host, Opts), +-spec init([host_type() | list(), ...]) -> {'ok', state()}. +init([HostType, Opts]) -> + mod_muc_db_backend:init(HostType, Opts), mnesia:create_table(muc_online_room, [{ram_copies, [node()]}, {attributes, record_info(fields, muc_online_room)}]), mnesia:add_table_copy(muc_online_room, node(), ram_copies), catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]), - MyHost = gen_mod:get_opt_subhost(Host, Opts, default_host()), - clean_table_from_bad_node(node(), MyHost), + clean_table_from_bad_node(node(), HostType), mnesia:subscribe(system), Access = gen_mod:get_opt(access, Opts, all), AccessCreate = gen_mod:get_opt(access_create, Opts, all), @@ -402,8 +406,9 @@ init([Host, Opts]) -> RoomShaper = gen_mod:get_opt(room_shaper, Opts, none), CheckInterval = gen_mod:get_opt(hibernated_room_check_interval, Opts, infinity), HibernatedTimeout = gen_mod:get_opt(hibernated_room_timeout, Opts, infinity), - State = #state{host = MyHost, - server_host = Host, + SubdomainPattern = gen_mod:get_opt(host, Opts, default_host()), + State = #state{host_type = HostType, + subdomain_pattern = SubdomainPattern, access = {Access, AccessCreate, AccessAdmin, AccessPersistent}, default_room_opts = DefRoomOpts, history_size = HistorySize, @@ -411,26 +416,21 @@ init([Host, Opts]) -> http_auth_pool = HttpAuthPool, hibernated_room_check_interval = CheckInterval, hibernated_room_timeout = HibernatedTimeout}, - - ejabberd_hooks:add(is_muc_room_owner, Host, ?MODULE, is_muc_room_owner, 50), - ejabberd_hooks:add(can_access_room, Host, ?MODULE, can_access_room, 50), - ejabberd_hooks:add(can_access_identity, Host, ?MODULE, can_access_identity, 50), - - ejabberd_router:register_route(MyHost, mongoose_packet_handler:new(?MODULE, - #{state => State})), - mongoose_subhosts:register(Host, MyHost), - - case gen_mod:get_module_opt(Host, mod_muc, load_permanent_rooms_at_startup, false) of + %% Hooks + ejabberd_hooks:add(hooks(HostType)), + %% Handler + PacketHandler = mongoose_packet_handler:new(?MODULE, #{state => State}), + mongoose_domain_api:register_subdomain(HostType, SubdomainPattern, PacketHandler), + %% Loading + case gen_mod:get_module_opt(HostType, mod_muc, load_permanent_rooms_at_startup, false) of false -> ?LOG_INFO(#{what => load_permanent_rooms_at_startup, skip => true, text => <<"Skip loading permanent rooms at startup. " "Each room is loaded when someone access the room">>}); true -> - ?LOG_INFO(#{what => load_permanent_rooms_at_startup, skip => false, - text => <<"Loading permanent rooms at startup">>}), - load_permanent_rooms(MyHost, Host, - {Access, AccessCreate, AccessAdmin, AccessPersistent}, - HistorySize, RoomShaper, HttpAuthPool) + ?LOG_WARNING(#{what => load_permanent_rooms_at_startup_is_deprecated, skip => false, + text => <<"Loading permanent rooms at startup is deprecated. " + "The option is ignored.">>}) end, set_persistent_rooms_timer(State), {ok, State}. @@ -450,32 +450,28 @@ set_persistent_rooms_timer(#state{hibernated_room_check_interval = Timeout}) -> %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call(stop, _From, State) -> - ejabberd_hooks:delete(is_muc_room_owner, State#state.server_host, ?MODULE, is_muc_room_owner, 50), - ejabberd_hooks:delete(can_access_room, State#state.server_host, ?MODULE, can_access_room, 50), - ejabberd_hooks:delete(can_access_identity, State#state.server_host, ?MODULE, can_access_identity, 50), - + ejabberd_hooks:delete(hooks(State#state.host_type)), {stop, normal, ok, State}; -handle_call({create_instant, Room, From, Nick, Opts}, +handle_call({create_instant, ServerHost, MucHost, Room, From, Nick, Opts}, _From, - #state{host = Host, - server_host = ServerHost, + #state{host_type = HostType, access = Access, default_room_opts = DefOpts, history_size = HistorySize, room_shaper = RoomShaper, http_auth_pool = HttpAuthPool} = State) -> - ?LOG_DEBUG(#{what => muc_create_instant, room => Room, sub_host => Host}), + ?LOG_DEBUG(#{what => muc_create_instant, room => Room, sub_host => MucHost}), NewOpts = case Opts of default -> DefOpts; _ -> Opts end, {ok, Pid} = mod_muc_room:start( - Host, ServerHost, Access, + MucHost, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, From, Nick, [{instant, true}|NewOpts]), - register_room_or_stop_if_duplicate(Host, Room, Pid), + register_room_or_stop_if_duplicate(HostType, MucHost, Room, Pid), {reply, ok, State}. %%-------------------------------------------------------------------- @@ -498,7 +494,7 @@ handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> clean_table_from_bad_node(Node), {noreply, State}; handle_info(stop_hibernated_persistent_rooms, - #state{server_host = ServerHost, + #state{host_type = ServerHost, hibernated_room_timeout = Timeout} = State) when is_integer(Timeout) -> ?LOG_INFO(#{what => muc_stop_hibernated_persistent_rooms, server => ServerHost, text => <<"Closing hibernated persistent rooms">>}), @@ -541,9 +537,9 @@ stop_if_hibernated_for_specified_time(Pid, Now, Timeout, {hibernated, LastHibern %% cleaning up. When it returns, the gen_server terminates with Reason. %% The return value is ignored. %%-------------------------------------------------------------------- -terminate(_Reason, State) -> - mongoose_subhosts:unregister(State#state.host), - ejabberd_router:unregister_route(State#state.host), +terminate(_Reason, State = #state{host_type = HostType, + subdomain_pattern = SubdomainPattern}) -> + mongoose_domain_api:unregister_subdomain(HostType, SubdomainPattern), ok. %%-------------------------------------------------------------------- @@ -556,29 +552,28 @@ code_change(_OldVsn, State, _Extra) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- --spec start_supervisor(jid:server()) -> {'error', _} - | {'ok', 'undefined' | pid()} - | {'ok', 'undefined' | pid(), _}. -start_supervisor(Host) -> - Proc = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup), - ChildSpec = - {Proc, - {ejabberd_tmp_sup, start_link, - [Proc, mod_muc_room]}, - permanent, - infinity, - supervisor, - [ejabberd_tmp_sup]}, +-spec start_supervisor(host_type()) -> {error, _} + | {ok, undefined | pid()} + | {ok, undefined | pid(), _}. +start_supervisor(HostType) -> + ChildSpec = sup_spec(HostType), ejabberd_sup:start_child(ChildSpec). - --spec stop_supervisor(jid:server()) -> 'ok' - | {'error', 'not_found' | 'restarting' | 'running' | 'simple_one_for_one'}. -stop_supervisor(Host) -> - Proc = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup), +sup_spec(HostType) -> + Proc = gen_mod:get_module_proc(HostType, ejabberd_mod_muc_sup), + {Proc, + {ejabberd_tmp_sup, start_link, [Proc, mod_muc_room]}, + permanent, + infinity, + supervisor, + [ejabberd_tmp_sup]}. + +-spec stop_supervisor(jid:server()) -> ok | {error, Reason} + when Reason :: not_found | restarting | running | simple_one_for_one. +stop_supervisor(HostType) -> + Proc = gen_mod:get_module_proc(HostType, ejabberd_mod_muc_sup), ejabberd_sup:stop_child(Proc). - -spec process_packet(Acc :: mongoose_acc:t(), From :: jid:jid(), To :: jid:simple_jid() | jid:jid(), @@ -586,11 +581,11 @@ stop_supervisor(Host) -> #{state := state()}) -> ok | mongoose_acc:t(). process_packet(Acc, From, To, El, #{state := State}) -> {AccessRoute, _, _, _} = State#state.access, - ServerHost = State#state.server_host, + ServerHost = State#state.host_type, case acl:match_rule(ServerHost, AccessRoute, From) of allow -> - {Room, _, _} = jid:to_lower(To), - route_to_room(Room, {From, To, Acc, El}, State); + {Room, MucHost, _} = jid:to_lower(To), + route_to_room(MucHost, Room, {From, To, Acc, El}, State); _ -> #xmlel{attrs = Attrs} = El, Lang = xml:get_attr_s(<<"xml:lang">>, Attrs), @@ -600,14 +595,14 @@ process_packet(Acc, From, To, El, #{state := State}) -> end. --spec route_to_room(room(), from_to_packet(), state()) -> 'ok' | pid(). -route_to_room(<<>>, {_, To, _Acc, _} = Routed, State) -> +-spec route_to_room(jid:lserver(), room(), from_to_packet(), state()) -> ok | pid(). +route_to_room(_MucHost, <<>>, {_, To, _Acc, _} = Routed, State) -> {_, _, Nick} = jid:to_lower(To), route_by_nick(Nick, Routed, State); -route_to_room(Room, Routed, #state{host=Host} = State) -> - case mnesia:dirty_read(muc_online_room, {Room, Host}) of +route_to_room(MucHost, Room, Routed, #state{} = State) -> + case mnesia:dirty_read(muc_online_room, {Room, MucHost}) of [] -> - case get_registered_room_or_route_error(Room, Routed, State) of + case get_registered_room_or_route_error(MucHost, Room, Routed, State) of {ok, Pid} -> route_to_online_room(Pid, Routed); {route_error, _ErrText} -> @@ -623,41 +618,40 @@ route_to_online_room(Pid, {From, To, Acc, Packet}) -> {_, _, Nick} = jid:to_lower(To), ok = mod_muc_room:route(Pid, From, Nick, Acc, Packet). --spec get_registered_room_or_route_error(room(), from_to_packet(), state()) -> {ok, pid()} | {route_error, binary()}. -get_registered_room_or_route_error(Room, {From, To, Acc, Packet}, State) -> +-spec get_registered_room_or_route_error(muc_host(), room(), from_to_packet(), state()) -> {ok, pid()} | {route_error, binary()}. +get_registered_room_or_route_error(MucHost, Room, {From, To, Acc, Packet}, State) -> #xmlel{name = Name, attrs = Attrs} = Packet, Type = xml:get_attr_s(<<"type">>, Attrs), case {Name, Type} of {<<"presence">>, <<>>} -> - get_registered_room_or_route_error_from_presence(Room, From, To, Acc, Packet, State); + get_registered_room_or_route_error_from_presence(MucHost, Room, From, To, Acc, Packet, State); _ -> - get_registered_room_or_route_error_from_packet(Room, From, To, Acc, Packet, State) + get_registered_room_or_route_error_from_packet(MucHost, Room, From, To, Acc, Packet, State) end. -get_registered_room_or_route_error_from_presence(Room, From, To, Acc, Packet, - #state{server_host = ServerHost, - host = Host, +get_registered_room_or_route_error_from_presence(MucHost, Room, From, To, Acc, Packet, + #state{host_type = HostType, access = Access} = State) -> {_, AccessCreate, _, _} = Access, - case check_user_can_create_room(ServerHost, AccessCreate, - From, Room) of + case check_user_can_create_room(HostType, AccessCreate, From, Room) of true -> #state{history_size = HistorySize, room_shaper = RoomShaper, http_auth_pool = HttpAuthPool, default_room_opts = DefRoomOpts} = State, {_, _, Nick} = jid:to_lower(To), - Result = start_new_room(Host, ServerHost, Access, Room, + Result = start_new_room(HostType, MucHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, From, Nick, DefRoomOpts, Acc), case Result of {ok, Pid} -> - register_room_or_stop_if_duplicate(Host, Room, Pid); + register_room_or_stop_if_duplicate(HostType, MucHost, Room, Pid); {error, {failed_to_restore, Reason}} -> %% Notify user about our backend module error ?LOG_WARNING(#{what => muc_send_service_unavailable, text => <<"Failed to restore room">>, - room => Room, server => ServerHost, sub_host => Host, + host_type => HostType, + room => Room, sub_host => MucHost, reason => Reason, acc => Acc}), Lang = exml_query:attr(Packet, <<"xml:lang">>, <<>>), ErrText = <<"Service is temporary unavailable">>, @@ -679,12 +673,10 @@ get_registered_room_or_route_error_from_presence(Room, From, To, Acc, Packet, {route_error, ErrText} end. -get_registered_room_or_route_error_from_packet(Room, From, To, Acc, Packet, - #state{server_host = ServerHost, - host = Host, +get_registered_room_or_route_error_from_packet(MucHost, Room, From, To, Acc, Packet, + #state{host_type = HostType, access = Access} = State) -> - - case restore_room(ServerHost, Host, Room) of + case restore_room(HostType, MucHost, Room) of {error, room_not_found} -> Lang = exml_query:attr(Packet, <<"xml:lang">>, <<>>), ErrText = <<"Conference room does not exist">>, @@ -694,7 +686,7 @@ get_registered_room_or_route_error_from_packet(Room, From, To, Acc, Packet, {route_error, ErrText}; {error, Reason} -> ?LOG_WARNING(#{what => muc_send_service_unavailable, - room => Room, server => ServerHost, sub_host => Host, + room => Room, host_type => HostType, sub_host => MucHost, reason => Reason, acc => Acc}), Lang = exml_query:attr(Packet, <<"xml:lang">>, <<>>), ErrText = <<"Service is temporary unavailable">>, @@ -707,10 +699,10 @@ get_registered_room_or_route_error_from_packet(Room, From, To, Acc, Packet, #state{history_size = HistorySize, room_shaper = RoomShaper, http_auth_pool = HttpAuthPool} = State, - {ok, Pid} = mod_muc_room:start(Host, ServerHost, Access, + {ok, Pid} = mod_muc_room:start(MucHost, HostType, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts), - register_room_or_stop_if_duplicate(Host, Room, Pid) + register_room_or_stop_if_duplicate(HostType, MucHost, Room, Pid) end. -spec route_by_nick(room(), from_to_packet(), state()) -> 'ok' | pid(). @@ -729,10 +721,10 @@ route_by_nick(_Nick, {From, To, Acc, Packet}, _State) -> ejabberd_router:route(To, From, Acc1, Err) end. - -spec route_by_type(binary(), from_to_packet(), state()) -> 'ok' | pid(). -route_by_type(<<"iq">>, {From, To, Acc, Packet}, #state{host = Host} = State) -> - ServerHost = State#state.server_host, +route_by_type(<<"iq">>, {From, To, Acc, Packet}, #state{} = State) -> + ServerHost = State#state.host_type, + MucHost = To#jid.lserver, case jlib:iq_query_info(Packet) of #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS, lang = Lang} = IQ -> Info = mongoose_hooks:disco_info(ServerHost, ?MODULE, <<"">>, Lang), @@ -742,9 +734,9 @@ route_by_type(<<"iq">>, {From, To, Acc, Packet}, #state{host = Host} = State) -> children = iq_disco_info(Lang, From, To) ++ Info}]}, ejabberd_router:route(To, From, jlib:iq_to_xml(Res)); #iq{type = get, xmlns = ?NS_DISCO_ITEMS} = IQ -> - spawn(?MODULE, process_iq_disco_items, [Host, From, To, IQ]); + proc_lib:spawn(fun() -> process_iq_disco_items(MucHost, From, To, IQ) end); #iq{type = get, xmlns = ?NS_REGISTER = XMLNS, lang = Lang} = IQ -> - Result = iq_get_register_info(ServerHost, Host, From, Lang), + Result = iq_get_register_info(ServerHost, MucHost, From, Lang), Res = IQ#iq{type = result, sub_el = [#xmlel{name = <<"query">>, attrs = [{<<"xmlns">>, XMLNS}], @@ -754,7 +746,7 @@ route_by_type(<<"iq">>, {From, To, Acc, Packet}, #state{host = Host} = State) -> xmlns = ?NS_REGISTER = XMLNS, lang = Lang, sub_el = SubEl} = IQ -> - case process_iq_register_set(ServerHost, Host, From, SubEl, Lang) of + case process_iq_register_set(ServerHost, MucHost, From, SubEl, Lang) of {result, IQRes} -> Res = IQ#iq{type = result, sub_el = [#xmlel{name = <<"query">>, @@ -787,8 +779,9 @@ route_by_type(<<"iq">>, {From, To, Acc, Packet}, #state{host = Host} = State) -> ok end; route_by_type(<<"message">>, {From, To, Acc, Packet}, - #state{host = Host, server_host = ServerHost, + #state{host_type = ServerHost, access = {_, _, AccessAdmin, _}}) -> + MucHost = To#jid.lserver, #xmlel{attrs = Attrs} = Packet, case xml:get_attr_s(<<"type">>, Attrs) of <<"error">> -> @@ -797,7 +790,7 @@ route_by_type(<<"message">>, {From, To, Acc, Packet}, case acl:match_rule(ServerHost, AccessAdmin, From) of allow -> Msg = xml:get_path_s(Packet, [{elem, <<"body">>}, cdata]), - broadcast_service_message(Host, Msg); + broadcast_service_message(MucHost, Msg); _ -> Lang = xml:get_attr_s(<<"xml:lang">>, Attrs), ErrTxt = <<"Only service administrators are allowed to send service messages">>, @@ -809,64 +802,33 @@ route_by_type(<<"message">>, {From, To, Acc, Packet}, route_by_type(<<"presence">>, _Routed, _State) -> ok. - --spec check_user_can_create_room('global' | jid:server(), - 'allow' | atom(), jid:jid(), room()) -> boolean(). -check_user_can_create_room(ServerHost, AccessCreate, From, RoomID) -> - case acl:match_rule(ServerHost, AccessCreate, From) of +-spec check_user_can_create_room(global | host_type(), + allow | atom(), jid:jid(), room()) -> boolean(). +check_user_can_create_room(HostType, AccessCreate, From, RoomID) -> + case acl:match_rule(HostType, AccessCreate, From) of allow -> - (size(RoomID) =< gen_mod:get_module_opt(ServerHost, mod_muc, + (size(RoomID) =< gen_mod:get_module_opt(HostType, mod_muc, max_room_id, infinity)); _ -> false end. --spec load_permanent_rooms(Host :: jid:server(), Srv :: jid:server(), - Access :: access(), HistorySize :: 'undefined' | integer(), - RoomShaper :: shaper:shaper(), HttpAuthPool :: none | mongoose_http_client:pool()) -> 'ok'. -load_permanent_rooms(Host, ServerHost, Access, HistorySize, RoomShaper, HttpAuthPool) -> - RoomsToLoad = - case mod_muc_db_backend:get_rooms(ServerHost, Host) of - {ok, Rs} -> - Rs; - {error, Reason} -> - ?LOG_ERROR(#{what => muc_get_rooms_failed, - text => <<"Skip load_permanent_rooms">>, - reason => Reason, server => ServerHost, sub_host => Host}), - [] - end, - lists:foreach( - fun(R) -> - {Room, Host} = R#muc_room.name_host, - {ok, Pid} = mod_muc_room:start( - Host, - ServerHost, - Access, - Room, - HistorySize, - RoomShaper, - HttpAuthPool, - R#muc_room.opts), - register_room_or_stop_if_duplicate(Host, Room, Pid) - - end, RoomsToLoad). - --spec start_new_room(Host :: 'undefined' | jid:server(), - Srv :: jid:server(), Access :: access(), room(), +-spec start_new_room(HostType :: host_type(), MucHost :: muc_host(), + Access :: access(), room(), HistorySize :: 'undefined' | integer(), RoomShaper :: shaper:shaper(), HttpAuthPool :: none | mongoose_http_client:pool(), From :: jid:jid(), nick(), DefRoomOpts :: 'undefined' | [any()], Acc :: mongoose_acc:t()) -> {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}. -start_new_room(Host, ServerHost, Access, Room, +start_new_room(HostType, MucHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, From, Nick, DefRoomOpts, Acc) -> - case mod_muc_db_backend:restore_room(ServerHost, Host, Room) of + case mod_muc_db_backend:restore_room(HostType, MucHost, Room) of {error, room_not_found} -> ?LOG_DEBUG(#{what => muc_start_new_room, acc => Acc, - room => Room, server => ServerHost, sub_host => Host}), - mod_muc_room:start(Host, ServerHost, Access, + room => Room, host_type => HostType, sub_host => MucHost}), + mod_muc_room:start(MucHost, HostType, Access, Room, HistorySize, RoomShaper, HttpAuthPool, From, Nick, DefRoomOpts); @@ -874,14 +836,14 @@ start_new_room(Host, ServerHost, Access, Room, {error, {failed_to_restore, Reason}}; {ok, Opts} -> ?LOG_DEBUG(#{what => muc_restore_room, acc => Acc, room => Room, - server => ServerHost, sub_host => Host, room_opts => Opts}), - mod_muc_room:start(Host, ServerHost, Access, + host_type => HostType, sub_host => MucHost, room_opts => Opts}), + mod_muc_room:start(MucHost, HostType, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts) end. -register_room_or_stop_if_duplicate(Host, Room, Pid) -> - case register_room(Host, Room, Pid) of +register_room_or_stop_if_duplicate(HostType, MucHost, Room, Pid) -> + case register_room(HostType, MucHost, Room, Pid) of {_, ok} -> {ok, Pid}; {_, {exists, OldPid}} -> @@ -889,13 +851,15 @@ register_room_or_stop_if_duplicate(Host, Room, Pid) -> {ok, OldPid} end. --spec register_room('undefined' | jid:server(), room(), - 'undefined' | pid()) -> {'aborted', _} | {'atomic', _}. -register_room(Host, Room, Pid) -> +-spec register_room(HostType :: host_type(), jid:server(), room(), + pid()) -> {'aborted', _} | {'atomic', _}. +register_room(HostType, MucHost, Room, Pid) -> F = fun() -> - case mnesia:read(muc_online_room, {Room, Host}, write) of + case mnesia:read(muc_online_room, {Room, MucHost}, write) of [] -> - mnesia:write(#muc_online_room{name_host = {Room, Host}, pid = Pid}); + mnesia:write(#muc_online_room{name_host = {Room, MucHost}, + host_type = HostType, + pid = Pid}); [R] -> {exists, R#muc_online_room.pid} end @@ -933,48 +897,50 @@ iq_disco_info(Lang, From, To) -> [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, URN}]} || {{URN, _Host}} <- RegisteredFeatures]. --spec iq_disco_items(jid:server(), jid:jid(), ejabberd:lang(), +%% Disco for rooms +-spec iq_disco_items(muc_host(), jid:jid(), ejabberd:lang(), Rsm :: none | jlib:rsm_in()) -> any(). -iq_disco_items(Host, From, Lang, none) -> - Rooms = lists:ukeysort(1, lists:map( - fun({_,Room,PidOrEmptyList}) -> {Room, PidOrEmptyList} end, - get_vh_rooms(Host) ++ get_persistent_vh_rooms(Host))), - BareRooms = lists:filtermap(fun(Room) -> room_to_item(Room, Host, From, Lang) end, Rooms), +iq_disco_items(MucHost, From, Lang, none) -> + AllRooms = get_vh_rooms(MucHost) ++ get_persistent_vh_rooms(MucHost), + Rooms = lists:ukeysort(1, lists:map(fun record_to_simple/1, AllRooms)), + BareRooms = lists:filtermap(fun(Room) -> room_to_item(Room, MucHost, From, Lang) end, Rooms), lists:ukeysort(3, BareRooms); -iq_disco_items(Host, From, Lang, Rsm) -> - {Rooms, RsmO} = get_vh_rooms(Host, Rsm), +iq_disco_items(MucHost, From, Lang, Rsm) -> + {Rooms, RsmO} = get_vh_rooms(MucHost, Rsm), RsmOut = jlib:rsm_encode(RsmO), - lists:filtermap(fun(Room) -> room_to_item(Room, Host, From, Lang) end, Rooms) ++ RsmOut. + lists:filtermap(fun(Room) -> room_to_item(Room, MucHost, From, Lang) end, Rooms) ++ RsmOut. -room_to_item({{Name, _}, Pid}, Host, From, Lang) when is_pid(Pid) -> +room_to_item({{Name, _}, Pid}, MucHost, From, Lang) when is_pid(Pid) -> case catch gen_fsm_compat:sync_send_all_state_event( Pid, {get_disco_item, From, Lang}, 100) of {item, Desc} -> flush(), {true, #xmlel{name = <<"item">>, - attrs = [{<<"jid">>, jid:to_binary({Name, Host, <<>>})}, + attrs = [{<<"jid">>, jid:to_binary({Name, MucHost, <<>>})}, {<<"name">>, Desc}]}}; _ -> false end; - room_to_item({{ Name, _ }, _}, Host, _, _) -> + room_to_item({{ Name, _ }, _}, MucHost, _, _) -> {true, #xmlel{name = <<"item">>, - attrs = [{<<"jid">>, jid:to_binary({Name, Host, <<>>})}, + attrs = [{<<"jid">>, jid:to_binary({Name, MucHost, <<>>})}, {<<"name">>, Name}]} }. +record_to_simple(#muc_online_room{name_host = Room, pid = Pid}) -> + {Room, Pid}; +record_to_simple(#muc_room{name_host = Room, opts = Opts}) -> + {Room, Opts}. --spec get_vh_rooms(jid:server(), jlib:rsm_in()) -> {list(), jlib:rsm_out()}. -get_vh_rooms(Host, #rsm_in{max=Max, direction=Direction, id=I, index=Index}) -> +-spec get_vh_rooms(muc_host(), jlib:rsm_in()) -> {list(), jlib:rsm_out()}. +get_vh_rooms(MucHost, #rsm_in{max=Max, direction=Direction, id=I, index=Index}) -> NonUndefMax = case Max of undefined -> 134217728; _ -> Max end, - Rooms = get_vh_rooms(Host) ++ get_persistent_vh_rooms(Host), - BareSortedRooms = lists:ukeysort(1, lists:map( - fun({_,Room,PidOrEmptyList}) -> {Room, PidOrEmptyList} end, - Rooms)), + Rooms = get_vh_rooms(MucHost) ++ get_persistent_vh_rooms(MucHost), + BareSortedRooms = lists:ukeysort(1, lists:map(fun record_to_simple/1, Rooms)), Count = erlang:length(BareSortedRooms), L2 = case {Index, Direction} of {undefined, undefined} -> @@ -1066,9 +1032,9 @@ iq_get_unique(From) -> -spec iq_get_register_info(jid:server(), jid:server(), jid:simple_jid() | jid:jid(), ejabberd:lang()) -> [jlib:xmlel(), ...]. -iq_get_register_info(ServerHost, Host, From, Lang) -> +iq_get_register_info(ServerHost, MucHost, From, Lang) -> {Nick, Registered} = - case catch get_nick(ServerHost, Host, From) of + case catch get_nick(ServerHost, MucHost, From) of {'EXIT', _Reason} -> {<<>>, []}; {error, _} -> @@ -1084,7 +1050,7 @@ iq_get_register_info(ServerHost, Host, From, Lang) -> EnterNicknameEl = #xmlel{name = <<"instructions">>, children = [#xmlcdata{content = EnterNicknameText}]}, TitleText = <<(translate:translate(Lang, <<"Nickname Registration at ">>))/binary, - Host/binary>>, + MucHost/binary>>, TitleEl = #xmlel{name = <<"title">>, children = [#xmlcdata{content = TitleText}]}, Registered ++ [ClientReqEl, @@ -1097,8 +1063,8 @@ iq_get_register_info(ServerHost, Host, From, Lang) -> -spec iq_set_register_info(jid:server(), jid:server(), jid:simple_jid() | jid:jid(), nick(), ejabberd:lang()) -> {'error', jlib:xmlel()} | {'result', []}. -iq_set_register_info(ServerHost, Host, From, Nick, Lang) -> - case set_nick(ServerHost, Host, From, Nick) of +iq_set_register_info(ServerHost, MucHost, From, Nick, Lang) -> + case set_nick(ServerHost, MucHost, From, Nick) of ok -> {result, []}; {error, conflict} -> @@ -1109,7 +1075,7 @@ iq_set_register_info(ServerHost, Host, From, Nick, Lang) -> {error, mongoose_xmpp_errors:not_acceptable(Lang, ErrText)}; {error, ErrorReason} -> ?LOG_ERROR(#{what => muc_iq_set_register_info_failed, - server => ServerHost, sub_host => Host, + server => ServerHost, sub_host => MucHost, from_jid => jid:to_binary(From), nick => Nick, reason => ErrorReason}), {error, mongoose_xmpp_errors:internal_server_error()} @@ -1118,13 +1084,13 @@ iq_set_register_info(ServerHost, Host, From, Nick, Lang) -> -spec iq_set_unregister_info(jid:server(), jid:server(), jid:simple_jid() | jid:jid(), ejabberd:lang()) -> {'error', jlib:xmlel()} | {'result', []}. -iq_set_unregister_info(ServerHost, Host, From, _Lang) -> - case unset_nick(ServerHost, Host, From) of +iq_set_unregister_info(ServerHost, MucHost, From, _Lang) -> + case unset_nick(ServerHost, MucHost, From) of ok -> {result, []}; {error, ErrorReason} -> ?LOG_ERROR(#{what => muc_iq_set_unregister_info_failed, - server => ServerHost, sub_host => Host, + server => ServerHost, sub_host => MucHost, from_jid => jid:to_binary(From), reason => ErrorReason}), {error, mongoose_xmpp_errors:internal_server_error()} end. @@ -1132,7 +1098,7 @@ iq_set_unregister_info(ServerHost, Host, From, _Lang) -> -spec process_iq_register_set(jid:server(), jid:server(), jid:jid(), jlib:xmlel(), ejabberd:lang()) -> {'error', jlib:xmlel()} | {'result', []}. -process_iq_register_set(ServerHost, Host, From, SubEl, Lang) -> +process_iq_register_set(ServerHost, MucHost, From, SubEl, Lang) -> #xmlel{children = Els} = SubEl, case xml:get_subtag(SubEl, <<"remove">>) of false -> @@ -1140,21 +1106,21 @@ process_iq_register_set(ServerHost, Host, From, SubEl, Lang) -> [#xmlel{name = <<"x">>} = XEl] -> process_register(xml:get_tag_attr_s(<<"xmlns">>, XEl), xml:get_tag_attr_s(<<"type">>, XEl), - ServerHost, Host, From, Lang, XEl); + ServerHost, MucHost, From, Lang, XEl); _ -> {error, mongoose_xmpp_errors:bad_request()} end; _ -> - iq_set_unregister_info(ServerHost, Host, From, Lang) + iq_set_unregister_info(ServerHost, MucHost, From, Lang) end. -spec process_register(XMLNS :: binary(), Type :: binary(), - ServerHost :: jid:server(), Host :: jid:server(), + ServerHost :: jid:server(), MucHost :: jid:server(), From :: jid:jid(), Lang :: ejabberd:lang(), XEl :: exml:element()) -> {error, exml:element()} | {result, []}. process_register(?NS_XDATA, <<"cancel">>, _ServerHost, _Host, _From, _Lang, _XEl) -> {result, []}; -process_register(?NS_XDATA, <<"submit">>, ServerHost, Host, From, Lang, XEl) -> +process_register(?NS_XDATA, <<"submit">>, ServerHost, MucHost, From, Lang, XEl) -> XData = jlib:parse_xdata_submit(XEl), case XData of invalid -> @@ -1162,7 +1128,7 @@ process_register(?NS_XDATA, <<"submit">>, ServerHost, Host, From, Lang, XEl) -> _ -> case lists:keysearch(<<"nick">>, 1, XData) of {value, {_, [Nick]}} when Nick /= <<>> -> - iq_set_register_info(ServerHost, Host, From, Nick, Lang); + iq_set_register_info(ServerHost, MucHost, From, Nick, Lang); _ -> ErrText = <<"You must fill in field \"Nickname\" in the form">>, {error, mongoose_xmpp_errors:not_acceptable(Lang, ErrText)} @@ -1181,27 +1147,25 @@ iq_get_vcard(Lang) -> <<(translate:translate(Lang, <<"ejabberd MUC module">>))/binary, "\nCopyright (c) 2003-2011 ProcessOne">>}]}]. - --spec broadcast_service_message(jid:server(), binary() | string()) -> ok. -broadcast_service_message(Host, Msg) -> +-spec broadcast_service_message(muc_host(), binary() | string()) -> ok. +broadcast_service_message(MucHost, Msg) -> lists:foreach( fun(#muc_online_room{pid = Pid}) -> gen_fsm_compat:send_all_state_event( Pid, {service_message, Msg}) - end, get_vh_rooms(Host)). - + end, get_vh_rooms(MucHost)). --spec get_vh_rooms(jid:server()) -> [muc_online_room()]. -get_vh_rooms(Host) -> +-spec get_vh_rooms(muc_host()) -> [muc_online_room()]. +get_vh_rooms(MucHost) -> mnesia:dirty_select(muc_online_room, [{#muc_online_room{name_host = '$1', _ = '_'}, - [{'==', {element, 2, '$1'}, Host}], + [{'==', {element, 2, '$1'}, MucHost}], ['$_']}]). --spec get_persistent_vh_rooms(jid:server()) -> [muc_room()]. +-spec get_persistent_vh_rooms(muc_host()) -> [muc_room()]. get_persistent_vh_rooms(MucHost) -> - {ok, Host} = mongoose_subhosts:get_host(MucHost), - case mod_muc_db_backend:get_rooms(Host, MucHost) of + {ok, HostType} = mongoose_domain_api:get_subdomain_host_type(MucHost), + case mod_muc_db_backend:get_rooms(HostType, MucHost) of {ok, List} -> List; {error, _} -> @@ -1223,13 +1187,13 @@ clean_table_from_bad_node(Node) -> mnesia:async_dirty(F). --spec clean_table_from_bad_node(node(), jid:server()) -> any(). -clean_table_from_bad_node(Node, Host) -> +-spec clean_table_from_bad_node(node(), host_type()) -> any(). +clean_table_from_bad_node(Node, HostType) -> F = fun() -> Es = mnesia:select( muc_online_room, [{#muc_online_room{pid = '$1', - name_host = {'_', Host}, + host_type = HostType, _ = '_'}, [{'==', {node, '$1'}, Node}], ['$_']}]), @@ -1267,11 +1231,11 @@ can_access_identity(_, _HostType, Room, User) -> end. online_rooms_number() -> - lists:sum([online_rooms_number(Host) || Host <- ?MYHOSTS]). + lists:sum([online_rooms_number(MucHost) || MucHost <- ?MYHOSTS]). -online_rooms_number(Host) -> +online_rooms_number(MucHost) -> try - Supervisor = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup), + Supervisor = gen_mod:get_module_proc(MucHost, ejabberd_mod_muc_sup), Stats = supervisor:count_children(Supervisor), proplists:get_value(active, Stats) catch _:_ -> @@ -1279,21 +1243,21 @@ online_rooms_number(Host) -> end. hibernated_rooms_number() -> - lists:sum([hibernated_rooms_number(Host) || Host <- ?MYHOSTS]). + lists:sum([hibernated_rooms_number(MucHost) || MucHost <- ?MYHOSTS]). -hibernated_rooms_number(Host) -> +hibernated_rooms_number(MucHost) -> try - count_hibernated_rooms(Host) + count_hibernated_rooms(MucHost) catch _:_ -> 0 end. -count_hibernated_rooms(Host) -> - AllRooms = all_room_pids(Host), +count_hibernated_rooms(MucHost) -> + AllRooms = all_room_pids(MucHost), lists:foldl(fun count_hibernated_rooms/2, 0, AllRooms). -all_room_pids(Host) -> - Supervisor = gen_mod:get_module_proc(Host, ejabberd_mod_muc_sup), +all_room_pids(MucHost) -> + Supervisor = gen_mod:get_module_proc(MucHost, ejabberd_mod_muc_sup), [Pid || {undefined, Pid, worker, _} <- supervisor:which_children(Supervisor)]. @@ -1317,9 +1281,9 @@ ensure_metrics(_Host) -> {function, mod_muc, online_rooms_number, [], eval, ?EX_EVAL_SINGLE_VALUE}). -config_metrics(Host) -> +config_metrics(HostType) -> OptsToReport = [{backend, mnesia}], %list of tuples {option, defualt_value} - mongoose_module_metrics:opts_for_module(Host, ?MODULE, OptsToReport). + mongoose_module_metrics:opts_for_module(HostType, ?MODULE, OptsToReport). -spec server_host_to_host_type(jid:lserver()) -> mongooseim:host_type(). server_host_to_host_type(ServerHost) -> @@ -1329,3 +1293,36 @@ server_host_to_host_type(ServerHost) -> {error, not_found} -> ServerHost end. + +hooks(HostType) -> + [{is_muc_room_owner, HostType, ?MODULE, is_muc_room_owner, 50}, + {can_access_room, HostType, ?MODULE, can_access_room, 50}, + {can_access_identity, HostType, ?MODULE, can_access_identity, 50}, + {disco_local_items, HostType, ?MODULE, disco_local_items, 250}]. + +subdomain_pattern(HostType) -> + gen_mod:get_module_opt(HostType, ?MODULE, host, default_host()). + +server_host_to_muc_host(HostType, ServerHost) -> + mongoose_subdomain_utils:get_fqdn(subdomain_pattern(HostType), ServerHost). + +%% Exposes MUC-host, when querying the server host. +%% To is the host name, not subdomain. +-spec disco_local_items(Acc :: {result, [exml:element()]} | empty | {error, any()}, + From :: jid:jid(), To :: jid:jid(), + NS :: binary(), ejabberd:lang()) + -> {result, [exml:element()]} | empty | {error, any()}. +disco_local_items(Result, _From, #jid{lserver = ServerHost} = To, <<"">>, _Lang) -> + HostType = server_host_to_host_type(ServerHost), + MUCHost = server_host_to_muc_host(HostType, ServerHost), + Item = #xmlel{name = <<"item">>, + attrs = [{<<"jid">>, MUCHost}, + {<<"node">>, ?NS_MUC}]}, + case Result of + {result, Nodes} -> + {result, [Item | Nodes]}; + empty -> + {result, [Item]} + end; +disco_local_items(Acc, _From, _To, _Node, _Lang) -> + Acc. From 12057106bd19aaf01fab569790859611a238b7d1 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Thu, 20 May 2021 21:24:03 +0200 Subject: [PATCH 28/73] Fix some bugs in muc_light --- big_tests/tests/muc_light_helper.erl | 5 +++-- src/mod_muc.erl | 19 +++++----------- src/muc_light/mod_muc_light.erl | 28 +++++++++++++++--------- src/muc_light/mod_muc_light_db_rdbms.erl | 2 +- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/big_tests/tests/muc_light_helper.erl b/big_tests/tests/muc_light_helper.erl index aad5de66281..deea06a3a5d 100644 --- a/big_tests/tests/muc_light_helper.erl +++ b/big_tests/tests/muc_light_helper.erl @@ -259,8 +259,9 @@ ver(Int) -> -spec set_mod_config(K :: atom(), V :: any(), SubHost :: binary()) -> ok. set_mod_config(K, V, SubHost) -> - {ok, Host} = rpc(mim(), mongoose_subhosts, get_host, [SubHost]), - true = rpc(mim(), gen_mod, set_module_opt, [Host, mod_muc_light, K, V]). + {ok, HostType} = rpc(mim(), mongoose_domain_api, + get_subdomain_host_type, [SubHost]), + true = rpc(mim(), gen_mod, set_module_opt, [HostType, mod_muc_light, K, V]). assert_no_aff_duplicates(AffUsers) -> Users = [US || {US, _} <- AffUsers], diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 0c966b70ec0..12906cdca87 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -328,7 +328,7 @@ restore_room(HostType, MucHost, Name) -> -spec forget_room(jid:server(), jid:server(), room()) -> ok | {error, term()}. forget_room(ServerHost, MucHost, Name) -> - HostType = server_host_to_host_type(ServerHost), + HostType = mod_muc_light_utils:server_host_to_host_type(ServerHost), %% Removes room from DB, even if it's already removed. Result = mod_muc_db_backend:forget_room(HostType, MucHost, Name), case Result of @@ -1285,15 +1285,6 @@ config_metrics(HostType) -> OptsToReport = [{backend, mnesia}], %list of tuples {option, defualt_value} mongoose_module_metrics:opts_for_module(HostType, ?MODULE, OptsToReport). --spec server_host_to_host_type(jid:lserver()) -> mongooseim:host_type(). -server_host_to_host_type(ServerHost) -> - case mongoose_domain_api:get_domain_host_type(ServerHost) of - {ok, HostType} -> - HostType; - {error, not_found} -> - ServerHost - end. - hooks(HostType) -> [{is_muc_room_owner, HostType, ?MODULE, is_muc_room_owner, 50}, {can_access_room, HostType, ?MODULE, can_access_room, 50}, @@ -1312,8 +1303,10 @@ server_host_to_muc_host(HostType, ServerHost) -> From :: jid:jid(), To :: jid:jid(), NS :: binary(), ejabberd:lang()) -> {result, [exml:element()]} | empty | {error, any()}. +disco_local_items({error, _} = Acc, _From, _To, _Node, _Lang) -> + Acc; disco_local_items(Result, _From, #jid{lserver = ServerHost} = To, <<"">>, _Lang) -> - HostType = server_host_to_host_type(ServerHost), + HostType = mod_muc_light_utils:server_host_to_host_type(ServerHost), MUCHost = server_host_to_muc_host(HostType, ServerHost), Item = #xmlel{name = <<"item">>, attrs = [{<<"jid">>, MUCHost}, @@ -1323,6 +1316,4 @@ disco_local_items(Result, _From, #jid{lserver = ServerHost} = To, <<"">>, _Lang) {result, [Item | Nodes]}; empty -> {result, [Item]} - end; -disco_local_items(Acc, _From, _To, _Node, _Lang) -> - Acc. + end. diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index db740469c85..fc7fccdad2c 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -262,7 +262,7 @@ hooks(HostType) -> {offline_groupchat_message_hook, HostType, ?MODULE, prevent_service_unavailable, 90}, {remove_user, HostType, ?MODULE, remove_user, 50}, {remove_domain, HostType, ?MODULE, remove_domain, 50}, - {disco_local_items, HostType, ?MODULE, disco_local_items, 50}] ++ + {disco_local_items, HostType, ?MODULE, disco_local_items, 250}] ++ case Codec of legacy -> [{privacy_iq_get, HostType, ?MODULE, process_iq_get, 1}, @@ -360,19 +360,23 @@ prevent_service_unavailable(Acc, _From, _To, Packet) -> From :: jid:jid(), To :: jid:jid(), NS :: binary(), ejabberd:lang()) -> {result, [exml:element()]} | empty | {error, any()}. -disco_local_items({result, Nodes}, _From, #jid{lserver = ServerHost} = To, <<"">>, _Lang) -> - HostType = mod_muc_light_utils:room_jid_to_host_type(To), +disco_local_items({error, _} = Acc, _From, _To, _Node, _Lang) -> + Acc; +disco_local_items(Result, _From, #jid{lserver = ServerHost} = To, <<"">>, _Lang) -> + HostType = mod_muc_light_utils:server_host_to_host_type(ServerHost), XMLNS = case legacy_mode(HostType) of true -> ?NS_MUC; false -> ?NS_MUC_LIGHT end, MUCHost = server_host_to_muc_host(HostType, ServerHost), - Item = [#xmlel{name = <<"item">>, - attrs = [{<<"jid">>, MUCHost}, - {<<"node">>, XMLNS}]}], - {result, [Item | Nodes]}; -disco_local_items(Acc, _From, _To, _Node, _Lang) -> - Acc. + Item = #xmlel{name = <<"item">>, + attrs = [{<<"jid">>, MUCHost}, {<<"node">>, XMLNS}]}, + case Result of + {result, Nodes} -> + {result, [Item | Nodes]}; + empty -> + {result, [Item]} + end. legacy_mode(HostType) -> gen_mod:get_module_opt(HostType, ?MODULE, legacy_mode, ?DEFAULT_LEGACY_MODE). @@ -547,8 +551,12 @@ create_room(Acc, From, FromUS, To, Create0, OrigPacket) -> {error, bad_request} -> mod_muc_light_codec_backend:encode_error({error, bad_request}, From, To, OrigPacket, make_handler_fun(Acc)); - {error, Error} -> + {error, {_,_} = Error} -> ErrorText = io_lib:format("~s:~p", tuple_to_list(Error)), + mod_muc_light_codec_backend:encode_error( + {error, bad_request, ErrorText}, From, To, OrigPacket, make_handler_fun(Acc)); + {error, Error} -> + ErrorText = io_lib:format("~p", [Error]), mod_muc_light_codec_backend:encode_error( {error, bad_request, ErrorText}, From, To, OrigPacket, make_handler_fun(Acc)) end. diff --git a/src/muc_light/mod_muc_light_db_rdbms.erl b/src/muc_light/mod_muc_light_db_rdbms.erl index 8af59e1cc48..ac040ad3002 100644 --- a/src/muc_light/mod_muc_light_db_rdbms.erl +++ b/src/muc_light/mod_muc_light_db_rdbms.erl @@ -437,7 +437,7 @@ get_user_rooms({LUser, LServer} = US, undefined) -> %% It is used to be map over MYHOSTS HostType = mod_muc_light_utils:server_host_to_host_type(LServer), {selected, Rooms} = select_user_rooms(HostType, LUser, LServer), - Rooms; + lists:usort(Rooms); get_user_rooms({LUser, LServer}, MUCServer) -> HostType = mod_muc_light_utils:muc_host_to_host_type(MUCServer), {selected, Rooms} = select_user_rooms(HostType, LUser, LServer), From 804a4bc6be986dc0e28555ad614ec203f51684bf Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Fri, 21 May 2021 11:14:53 +0200 Subject: [PATCH 29/73] Fix wrong order of arguments in get_module_opt --- big_tests/tests/muc_helper.erl | 13 ++++++++++--- include/mongoose.hrl | 6 +++++- src/gen_mod.erl | 5 +++++ src/mongoose_hooks.erl | 2 +- src/muc_light/mod_muc_light.erl | 10 +++++----- src/muc_light/mod_muc_light_codec_legacy.erl | 2 +- src/muc_light/mod_muc_light_codec_modern.erl | 2 +- 7 files changed, 28 insertions(+), 12 deletions(-) diff --git a/big_tests/tests/muc_helper.erl b/big_tests/tests/muc_helper.erl index cf0a1144969..a370a2ce1c3 100644 --- a/big_tests/tests/muc_helper.erl +++ b/big_tests/tests/muc_helper.erl @@ -5,6 +5,8 @@ -include_lib("exml/include/exml.hrl"). -include_lib("escalus/include/escalus_xmlns.hrl"). +-include("mam_helper.hrl"). + -import(distributed_helper, [mim/0, subhost_pattern/1, rpc/4]). @@ -221,7 +223,7 @@ stanza_get_features() -> escalus_stanza:setattr(escalus_stanza:iq_get(?NS_DISCO_INFO, []), <<"to">>, muc_host()). -has_features(#xmlel{children = [ Query ]}, Features) -> +has_features(#xmlel{children = [ Query ]} = Iq, Features) -> %% %% %% + Loaded = rpc(mim(), gen_mod, loaded_modules_with_opts, [<<"localhost">>]), + ct:log("Loaded modules:~n~p", [Loaded]), + Identity = exml_query:subelement(Query, <<"identity">>), <<"conference">> = exml_query:attr(Identity, <<"category">>), - Features = exml_query:paths(Query, [{element, <<"feature">>}, - {attr, <<"var">>}]). + ?assert_equal_extra(Features, + exml_query:paths(Query, [{element, <<"feature">>}, + {attr, <<"var">>}]), + [Iq]). assert_valid_affiliation(<<"owner">>) -> ok; assert_valid_affiliation(<<"admin">>) -> ok; diff --git a/include/mongoose.hrl b/include/mongoose.hrl index f90f23e80c6..954df5b2cdc 100644 --- a/include/mongoose.hrl +++ b/include/mongoose.hrl @@ -36,4 +36,8 @@ %% Logging mechanism -include("mongoose_logger.hrl"). - +-ifdef(PROD_NODE). +-define(ASSERT_MODULE(M), ok). +-else. +-define(ASSERT_MODULE(M), M:module_info(module)). +-endif. diff --git a/src/gen_mod.erl b/src/gen_mod.erl index 571b0b069ca..f6b8b7a7ecd 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -326,7 +326,12 @@ set_opt(Opt, Opts, Value) -> lists:keystore(Opt, 1, Opts, {Opt, Value}). +-spec get_module_opt(mongooseim:host_type(), module(), atom(), term()) -> term(). get_module_opt(HostType, Module, Opt, Default) -> + %% Fail in dev builds. + %% It protects against passing something weird as a Module argument + %% or against wrong argument order. + ?ASSERT_MODULE(Module), ModuleOpts = get_module_opts(HostType, Module), get_opt(Opt, ModuleOpts, Default). diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index 92b46cfa7bc..85ea1e15d82 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -1381,7 +1381,7 @@ filter_room_packet(HostType, Packet, EventData) -> %%% @doc The `forget_room' hook is called when a room is removed from the database. -spec forget_room(HostType, MucHost, Room) -> Result when - HostType :: jid:server(), + HostType :: mongooseim:host_type(), MucHost :: jid:server(), Room :: jid:luser(), Result :: any(). diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index fc7fccdad2c..08806621dbb 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -180,11 +180,11 @@ server_host_to_muc_host(HostType, ServerHost) -> mongoose_subdomain_utils:get_fqdn(subdomain_pattern(HostType), ServerHost). host_type_to_codec(HostType) -> - case gen_mod:get_module_opt(HostType, legacy_mode, ?MODULE, ?DEFAULT_LEGACY_MODE) of - false -> - modern; + case gen_mod:get_module_opt(HostType, ?MODULE, legacy_mode, ?DEFAULT_LEGACY_MODE) of true -> - legacy + legacy; + false -> + modern end. tracked_db_funs() -> @@ -254,7 +254,7 @@ process_config_schema_value([{float_value, Val}]) -> {Val, float}. hooks(HostType) -> Codec = host_type_to_codec(HostType), - Roster = gen_mod:get_module_opt(HostType, rooms_in_rosters, ?MODULE, ?DEFAULT_ROOMS_IN_ROSTERS), + Roster = gen_mod:get_module_opt(HostType, ?MODULE, rooms_in_rosters, ?DEFAULT_ROOMS_IN_ROSTERS), [{is_muc_room_owner, HostType, ?MODULE, is_muc_room_owner, 50}, {can_access_room, HostType, ?MODULE, can_access_room, 50}, {can_access_identity, HostType, ?MODULE, can_access_identity, 50}, diff --git a/src/muc_light/mod_muc_light_codec_legacy.erl b/src/muc_light/mod_muc_light_codec_legacy.erl index 4d04c356546..33a94f3dba7 100644 --- a/src/muc_light/mod_muc_light_codec_legacy.erl +++ b/src/muc_light/mod_muc_light_codec_legacy.erl @@ -251,7 +251,7 @@ encode_meta({get, #disco_info{ id = ID }}, RoomJID, SenderJID, _HandleFun) -> DiscoEls = [#xmlel{name = <<"identity">>, attrs = [{<<"category">>, <<"conference">>}, {<<"type">>, <<"text">>}, - {<<"name">>, <<"MUC Light">>}]}, + {<<"name">>, <<"MUC Light (legacy)">>}]}, #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_MUC}]}] ++ [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, URN}]} || {{URN, _Host}} <- RegisteredFeatures], {iq_reply, ?NS_DISCO_INFO, DiscoEls, ID}; diff --git a/src/muc_light/mod_muc_light_codec_modern.erl b/src/muc_light/mod_muc_light_codec_modern.erl index 7c44e6ad1ae..6a8560a0903 100644 --- a/src/muc_light/mod_muc_light_codec_modern.erl +++ b/src/muc_light/mod_muc_light_codec_modern.erl @@ -261,7 +261,7 @@ encode_iq({get, #disco_info{ id = ID }}, DiscoEls = [#xmlel{name = <<"identity">>, attrs = [{<<"category">>, <<"conference">>}, {<<"type">>, <<"text">>}, - {<<"name">>, <<"MUC Light">>}]}, + {<<"name">>, <<"MUC Light (modern)">>}]}, #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_MUC_LIGHT}]}] ++ [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, URN}]} || {{URN, _Host}} <- RegisteredFeatures], {reply, ?NS_DISCO_INFO, DiscoEls, ID}; From 04289117d08978de44189c7e0583e8cbfd7c1606 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:15:40 +0200 Subject: [PATCH 30/73] Use host_types to start modules in muc_SUITE --- big_tests/tests/muc_SUITE.erl | 51 +++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/big_tests/tests/muc_SUITE.erl b/big_tests/tests/muc_SUITE.erl index 92809dbe880..564f6f88e84 100644 --- a/big_tests/tests/muc_SUITE.erl +++ b/big_tests/tests/muc_SUITE.erl @@ -30,7 +30,7 @@ -import(muc_helper, [muc_host/0, - load_muc/1, + load_muc/0, unload_muc/0, muc_host/0, start_room/5, @@ -84,6 +84,9 @@ items :: [#xmlel{}] }). +host_type() -> + ct:get_config({hosts, mim, host_type}). + %%-------------------------------------------------------------------- %% Suite configuration %%-------------------------------------------------------------------- @@ -317,8 +320,9 @@ init_per_suite(Config) -> %% For mocking with unnamed functions mongoose_helper:inject_module(?MODULE), Config2 = escalus:init_per_suite(Config), - Config3 = dynamic_modules:save_modules(domain(), Config2), - load_muc(muc_host()), + Config3 = dynamic_modules:save_modules(host_type(), Config2), + dynamic_modules:start(host_type(), mod_disco, []), + load_muc(), mongoose_helper:ensure_muc_clean(), Config3. @@ -326,7 +330,7 @@ end_per_suite(Config) -> escalus_fresh:clean(), mongoose_helper:ensure_muc_clean(), unload_muc(), - dynamic_modules:restore_modules(domain(), Config), + dynamic_modules:restore_modules(host_type(), Config), escalus:end_per_suite(Config). @@ -371,8 +375,8 @@ init_per_group(G, Config) when G =:= http_auth_no_server; http_auth -> http_helper:start(8080, "/muc/auth/check_password", fun handle_http_auth/1); _ -> ok end, - ConfigWithModules = dynamic_modules:save_modules(domain(), Config), - dynamic_modules:ensure_modules(domain(), required_modules(http_auth)), + ConfigWithModules = dynamic_modules:save_modules(host_type(), Config), + dynamic_modules:ensure_modules(host_type(), required_modules(http_auth)), ConfigWithModules; init_per_group(hibernation, Config) -> case mam_helper:backend() of @@ -396,8 +400,9 @@ init_per_group(_GroupName, Config) -> escalus:create_users(Config, escalus:get_users([alice, bob, kate])). required_modules(http_auth) -> + MucHostPattern = ct:get_config({hosts, mim, muc_service_pattern}), [{mod_muc, [ - {host, subhost_pattern("muc.@HOST@")}, + {host, subhost_pattern(MucHostPattern)}, {access, muc}, {access_create, muc_create}, {http_auth_pool, muc_http_auth_test}, @@ -439,7 +444,7 @@ end_per_group(G, Config) when G =:= http_auth_no_server; _ -> ok end, ejabberd_node_utils:call_fun(mongoose_wpool, stop, [http, global, muc_http_auth_test]), - dynamic_modules:restore_modules(domain(), Config); + dynamic_modules:restore_modules(host_type(), Config); end_per_group(hibernation, Config) -> case mam_helper:backend() of rdbms -> @@ -3062,10 +3067,10 @@ create_instant_room(Config) -> %% Bob should receive (in that order): Alices presence, his presence and the topic Preds = [fun(Stanza) -> escalus_pred:is_presence(Stanza) andalso - escalus_pred:is_stanza_from(<>, Stanza) + escalus_pred:is_stanza_from(room_address(RoomName, <<"bob">>), Stanza) end, fun(Stanza) -> escalus_pred:is_presence(Stanza) andalso - escalus_pred:is_stanza_from(<>, Stanza) + escalus_pred:is_stanza_from(room_address(RoomName, <<"alice-the-owner">>), Stanza) end], escalus:assert_many(Preds, escalus:wait_for_stanzas(Bob, 2)), escalus:wait_for_stanza(Bob), %topic @@ -3191,7 +3196,7 @@ reserved_room_configuration(Config) -> {<<"muc#roomconfig_moderatedroom">>, <<"1">>, <<"boolean">>}, {<<"muc#roomconfig_persistentroom">>, <<"1">>, <<"boolean">>}]), Result = escalus:send_iq_and_wait_for_result(Alice, Form), - escalus:assert(is_stanza_from, [<>], Result), + escalus:assert(is_stanza_from, [room_address(RoomName)], Result), %% Check if it worked Stanza = escalus:send_iq_and_wait_for_result( @@ -4065,10 +4070,10 @@ create_instant_http_password_protected_room(Config) -> %% Bob should receive (in that order): Alices presence, his presence and the topic Preds = [fun(Stanza) -> escalus_pred:is_presence(Stanza) andalso - escalus_pred:is_stanza_from(<>, Stanza) + escalus_pred:is_stanza_from(room_address(RoomName, <<"bob">>), Stanza) end, fun(Stanza) -> escalus_pred:is_presence(Stanza) andalso - escalus_pred:is_stanza_from(<>, Stanza) + escalus_pred:is_stanza_from(room_address(RoomName, <<"alice-the-owner">>), Stanza) end], escalus:assert_many(Preds, escalus:wait_for_stanzas(Bob, 2)), escalus:wait_for_stanza(Bob), %topic @@ -4278,7 +4283,7 @@ can_found_in_db_when_stopped(Config) -> {ok, _, Pid} = given_fresh_room_is_hibernated( Alice, RoomName, [{persistentroom, true}]), true = wait_for_room_to_be_stopped(Pid, timer:seconds(8)), - {ok, _} = rpc(mim(), mod_muc, restore_room, [domain(), muc_host(), RoomName]) + {ok, _} = rpc(mim(), mod_muc, restore_room, [host_type(), muc_host(), RoomName]) end), destroy_room(muc_host(), RoomName), @@ -4318,8 +4323,8 @@ given_fresh_room_is_hibernated(Owner, RoomName, Opts) -> Result. given_fresh_room_for_user(Owner, RoomName, Opts) -> - RoomJID = {jid, RoomName, muc_host(), <<>>, - escalus_utils:jid_to_lower(RoomName), muc_host(), <<>>}, + RoomJID = rpc(mim(), jid, make, [RoomName, muc_host(), <<>>]), + ct:log("RoomJID ~p", [RoomJID]), Nick = escalus_utils:get_username(Owner), JoinRoom = stanza_join_room(RoomName, Nick), escalus:send(Owner, JoinRoom), @@ -4335,7 +4340,7 @@ maybe_configure(Owner, RoomName, Opts) -> Form = stanza_configuration_form(RoomName, lists:flatten(Cfg)), Result = escalus:send_iq_and_wait_for_result(Owner, Form), - escalus:assert(is_stanza_from, [<>], Result), + escalus:assert(is_stanza_from, [room_address(RoomName)], Result), maybe_set_subject(proplists:get_value(subject, Opts), Owner, RoomName). opt_to_room_config({Name, Value}) when is_atom(Value) -> @@ -4397,11 +4402,12 @@ wait_for_room_to_be_stopped(Pid, Timeout) -> end. wait_for_hibernation(Pid) -> - mongoose_helper:wait_until(fun() -> is_hibernated(Pid) end, true, #{name => is_hibernated}). + mongoose_helper:wait_until(fun() -> process_current_function(Pid) end, + {current_function, {erlang, hibernate, 3}}, + #{name => is_hibernated}). -is_hibernated(Pid) -> - CurrentFunction = rpc(mim(), erlang, process_info, [Pid, current_function]), - {current_function, {erlang, hibernate, 3}} == CurrentFunction. +process_current_function(Pid) -> + rpc(mim(), erlang, process_info, [Pid, current_function]). wait_for_mam_result(RoomName, Client, Msg) -> Props = [{mam_ns, mam_helper:mam_ns_binary_v04()}, @@ -4707,8 +4713,7 @@ stanza_change_nick(Room, NewNick) -> start_rsm_rooms(Config, User, Nick) -> From = generate_rpc_jid(User), - [muc_helper:create_instant_room( - <<"localhost">>, generate_room_name(N), From, Nick, []) + [muc_helper:create_instant_room(generate_room_name(N), From, Nick, []) || N <- lists:seq(1, 15)], Config. From 067287867f0dba74ae7f0d46a84bc834970c19be Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:17:28 +0200 Subject: [PATCH 31/73] Add options and users for muc_SUITE into big_tests/dynamic_domains.config --- big_tests/dynamic_domains.config | 38 +++++++++++++++++++++++++++++--- big_tests/test.config | 8 +++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/big_tests/dynamic_domains.config b/big_tests/dynamic_domains.config index adaa26ce5f5..bcab79b7e47 100644 --- a/big_tests/dynamic_domains.config +++ b/big_tests/dynamic_domains.config @@ -1,11 +1,16 @@ %% Options defined here are used when testing dynamic domains, see 'dynamic_domains.spec' %% They take precedence over 'test.config' +{disable_disco, true}. + {hosts, [{mim, [{node, mongooseim@localhost}, {domain, <<"domain.example.com">>}, + {host_type, <<"test type">>}, {secondary_domain, <<"domain.example.org">>}, + {secondary_host_type, <<"test type">>}, {dynamic_domains, [<<"domain.example.com">>, <<"domain.example.org">>]}, - {host_type, <<"test type">>}, + {muc_service, <<"groupchats.domain.example.com">>}, + {muc_service_pattern, <<"groupchats.@HOST@">>}, {vars, "mim1"}, {cluster, mim}, {s2s_port, 5269}, @@ -20,7 +25,15 @@ {kicking_service_port, 8666}, {hidden_service_port, 8189}, {gd_endpoint_port, 5555}, - {http_notifications_port, 8000}]} + {http_notifications_port, 8000}]}, + %% used to test s2s features + {fed, [{node, fed1@localhost}, + {domain, <<"fed1">>}, + {host_type, <<"fed1">>}, + {vars, "fed1"}, + {incoming_s2s_port, 5299}, + {c2s_port, 5242}, + {cluster, fed}]} ]}. {escalus_users, [ @@ -39,6 +52,14 @@ {server, <<"domain.example.com">>}, {host, <<"localhost">>}, {password, <<"makrolika">>}]}, + {carol, [ + {username, <<"carol">>}, + {server, <<"domain.example.com">>}, + {host, <<"localhost">>}, + {password, <<"jinglebells">>}, + {transport, escalus_bosh}, + {path, <<"/http-bind">>}, + {port, 5280}]}, {kate, [ {username, <<"kate">>}, {server, <<"domain.example.com">>}, @@ -48,5 +69,16 @@ {username, <<"mike">>}, {server, <<"domain.example.com">>}, {host, <<"localhost">>}, - {password, <<"nicniema">>}]} + {password, <<"nicniema">>}]}, + {alice3, [ %% used in dynamic_domains_SUITE + {username, <<"alice">>}, + {server, <<"example.com">>}, + {host, <<"localhost">>}, + {password, <<"makota2">>}]}, + {bob3, [ %% used in dynamic_domains_SUITE + {username, <<"bob">>}, + {server, <<"example.org">>}, + {host, <<"localhost">>}, + {password, <<"makota3">>}, + {port, 5232}]} ]}. diff --git a/big_tests/test.config b/big_tests/test.config index afe18e69308..a949a1997c8 100644 --- a/big_tests/test.config +++ b/big_tests/test.config @@ -18,10 +18,14 @@ %% so that we rein the "bag of things" approach {hosts, [{mim, [{node, mongooseim@localhost}, {domain, <<"localhost">>}, + {host_type, <<"localhost">>}, {vars, "mim1"}, {cluster, mim}, {secondary_domain, <<"localhost.bis">>}, {reloaded_domain, <<"sogndal">>}, + {secondary_host_type, <<"localhost.bis">>}, + {muc_service, <<"muc.localhost">>}, + {muc_service_pattern, <<"muc.@HOST@">>}, {s2s_port, 5269}, {incoming_s2s_port, 5269}, {metrics_rest_port, 5288}, @@ -37,6 +41,7 @@ {http_notifications_port, 8000}]}, {mim2, [{node, ejabberd2@localhost}, {domain, <<"localhost">>}, + {host_type, <<"localhost">>}, {vars, "mim2"}, {cluster, mim}, {c2s_tls_port, 5233}, @@ -45,12 +50,14 @@ {service_port, 8899}]}, {mim3, [{node, mongooseim3@localhost}, {domain, <<"localhost">>}, + {host_type, <<"localhost">>}, {vars, "mim3"}, {c2s_tls_port, 5263}, {cluster, mim}]}, %% used to test s2s features {fed, [{node, fed1@localhost}, {domain, <<"fed1">>}, + {host_type, <<"fed1">>}, {vars, "fed1"}, {incoming_s2s_port, 5299}, {c2s_port, 5242}, @@ -58,6 +65,7 @@ %% used to test global distribution features {reg, [{node, reg1@localhost}, {domain, <<"reg1">>}, + {host_type, <<"red1">>}, {vars, "reg1"}, {service_port, 9990}, {c2s_port, 5252}, From 20be1703d559cb39b4da2e80bef98f4817c4611b Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:19:50 +0200 Subject: [PATCH 32/73] Fail for any reason that is not ok in dynamic_modules:start --- big_tests/tests/dynamic_modules.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/big_tests/tests/dynamic_modules.erl b/big_tests/tests/dynamic_modules.erl index 5f51652709e..d0e53ed54ac 100644 --- a/big_tests/tests/dynamic_modules.erl +++ b/big_tests/tests/dynamic_modules.erl @@ -70,6 +70,8 @@ start(Node, HostType, Mod, Args) -> case escalus_rpc:call(Node, gen_mod, start_module, [HostType, Mod, Args], 5000, Cookie) of {badrpc, Reason} -> ct:fail("Cannot start module ~p reason ~p", [Mod, Reason]); + Reason -> + ct:fail("Cannot start module ~p reason ~p", [Mod, Reason]); R -> R end. From cd234f0c12b5ceba009f3686cb571b58b647d97f Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:20:49 +0200 Subject: [PATCH 33/73] Don't pass muc_host arg into muc_helper:load_muc/0 --- big_tests/tests/gdpr_SUITE.erl | 9 ++-- big_tests/tests/inbox_SUITE.erl | 2 +- big_tests/tests/inbox_helper.erl | 2 +- big_tests/tests/mam_SUITE.erl | 2 +- big_tests/tests/mod_aws_sns_SUITE.erl | 9 ++-- .../tests/mod_event_pusher_rabbit_SUITE.erl | 2 +- big_tests/tests/mod_global_distrib_SUITE.erl | 4 +- big_tests/tests/mongoose_helper.erl | 8 +++- big_tests/tests/muc_helper.erl | 42 +++++++++++++------ big_tests/tests/muc_http_api_SUITE.erl | 2 +- big_tests/tests/muc_light_legacy_SUITE.erl | 23 ++++++---- 11 files changed, 65 insertions(+), 40 deletions(-) diff --git a/big_tests/tests/gdpr_SUITE.erl b/big_tests/tests/gdpr_SUITE.erl index 0edf1ca431a..0cb69163ddf 100644 --- a/big_tests/tests/gdpr_SUITE.erl +++ b/big_tests/tests/gdpr_SUITE.erl @@ -189,7 +189,7 @@ all_mam_testcases() -> init_per_suite(Config) -> #{node := MimNode} = distributed_helper:mim(), Config1 = [{{ejabberd_cwd, MimNode}, get_mim_cwd()} | dynamic_modules:save_modules(domain(), Config)], - muc_helper:load_muc(muc_domain()), + muc_helper:load_muc(), escalus:init_per_suite(Config1). end_per_suite(Config) -> @@ -252,7 +252,7 @@ init_per_testcase(CN, Config) when Config1; init_per_testcase(CN, Config) when CN =:= retrieve_inbox_muc; CN =:= remove_inbox_muc -> - muc_helper:load_muc(muc_domain()), + muc_helper:load_muc(), Config0 = init_inbox(CN, Config, muc), Config0; @@ -1556,8 +1556,7 @@ domain() -> <<"localhost">>. % TODO: Make dynamic? muc_domain() -> - Domain = inbox_helper:domain(), - <<"muc.", Domain/binary>>. + muc_helper:muc_host(). assert_personal_data_via_rpc(Client, ExpectedPersonalDataEntries) -> ExpectedKeys = [ Key || {Key, _, _} <- ExpectedPersonalDataEntries ], @@ -1808,7 +1807,7 @@ given_fresh_muc_room(UserSpec, RoomOpts) -> Username = proplists:get_value(username, UserSpec), RoomName = muc_helper:fresh_room_name(Username), From = muc_helper:generate_rpc_jid({user, UserSpec}), - muc_helper:create_instant_room(<<"localhost">>, RoomName, From, Username, RoomOpts), + muc_helper:create_instant_room(RoomName, From, Username, RoomOpts), {ok, RoomName}. send_recieve_muc_private_message(Room, Domain, {User1, Nickname1}, {User2, Nickname2}, Text) -> diff --git a/big_tests/tests/inbox_SUITE.erl b/big_tests/tests/inbox_SUITE.erl index 7a0c822aac5..9d9a309e153 100644 --- a/big_tests/tests/inbox_SUITE.erl +++ b/big_tests/tests/inbox_SUITE.erl @@ -211,7 +211,7 @@ init_per_group(muclight, Config) -> muc_light_helper:create_room(?ROOM, muclight_domain(), alice, [bob, kate], Config, muc_light_helper:ver(1)); init_per_group(muc, Config) -> - muc_helper:load_muc(muc_domain()), + muc_helper:load_muc(), inbox_helper:reload_inbox_option(Config, groupchat, [muc]); init_per_group(_GroupName, Config) -> Config. diff --git a/big_tests/tests/inbox_helper.erl b/big_tests/tests/inbox_helper.erl index cc5d0b20f91..56656422920 100644 --- a/big_tests/tests/inbox_helper.erl +++ b/big_tests/tests/inbox_helper.erl @@ -81,7 +81,7 @@ -define(ROOM3, <<"testroom3">>). -define(ROOM4, <<"testroom4">>). -define(ROOM_MARKERS, <<"room_markers">>). --define(MUC_DOMAIN, <<"muc.localhost">>). +-define(MUC_DOMAIN, (ct:get_config({hosts, mim, muc_service}))). -type inbox_query_params() :: #{ order => asc | desc | undefined, % by timestamp diff --git a/big_tests/tests/mam_SUITE.erl b/big_tests/tests/mam_SUITE.erl index ec96dbcc939..38698d3e437 100644 --- a/big_tests/tests/mam_SUITE.erl +++ b/big_tests/tests/mam_SUITE.erl @@ -514,7 +514,7 @@ suite() -> require_rpc_nodes([mim]) ++ escalus:suite(). init_per_suite(Config) -> - muc_helper:load_muc(muc_host()), + muc_helper:load_muc(), disable_sessions_limit(disable_shaping( delete_users([{escalus_user_db, {module, escalus_ejabberd}} | escalus:init_per_suite(Config)]))). diff --git a/big_tests/tests/mod_aws_sns_SUITE.erl b/big_tests/tests/mod_aws_sns_SUITE.erl index f08bc4aa022..9c7136861fe 100644 --- a/big_tests/tests/mod_aws_sns_SUITE.erl +++ b/big_tests/tests/mod_aws_sns_SUITE.erl @@ -8,7 +8,6 @@ -include("assert_received_match.hrl"). --define(MUC_HOST, <<"muc.localhost">>). -define(NS_HTTP_UPLOAD, <<"urn:xmpp:http:upload">>). -define(S3_HOSTNAME, "http://bucket.s3-eu-east-25.example.com"). -define(SNS_OPTS, @@ -71,7 +70,7 @@ init_per_suite(Config) -> %% For mocking with unnamed functions mongoose_helper:inject_module(?MODULE), - muc_helper:load_muc(muc_host()), + muc_helper:load_muc(), escalus:init_per_suite(Config); {error, _} -> {skip, "erlcloud dependency is not enabled"} @@ -310,12 +309,12 @@ stanza_to_room(Stanza, Room, Nick) -> escalus_stanza:to(Stanza, room_address(Room, Nick)). room_address(Room) -> - <>. + <>. room_address(Room, Nick) -> - <>. + <>. nick(User) -> escalus_utils:get_username(User). muc_host() -> - ?MUC_HOST. + muc_helper:muc_host(). diff --git a/big_tests/tests/mod_event_pusher_rabbit_SUITE.erl b/big_tests/tests/mod_event_pusher_rabbit_SUITE.erl index 113d4c8146f..b13b5949bef 100644 --- a/big_tests/tests/mod_event_pusher_rabbit_SUITE.erl +++ b/big_tests/tests/mod_event_pusher_rabbit_SUITE.erl @@ -128,7 +128,7 @@ init_per_suite(Config) -> {ok, _} = application:ensure_all_started(amqp_client), case is_rabbitmq_available() of true -> - muc_helper:load_muc(muc_helper:muc_host()), + muc_helper:load_muc(), escalus:init_per_suite(Config); false -> {skip, "RabbitMQ server is not available on default port."} diff --git a/big_tests/tests/mod_global_distrib_SUITE.erl b/big_tests/tests/mod_global_distrib_SUITE.erl index 42b2b447ac6..e3afbf9ab04 100644 --- a/big_tests/tests/mod_global_distrib_SUITE.erl +++ b/big_tests/tests/mod_global_distrib_SUITE.erl @@ -261,10 +261,10 @@ init_per_testcase(CaseName, Config) {_, EuropeHost, _} = lists:keyfind(europe_node1, 1, get_hosts()), trigger_rebalance(asia_node, list_to_binary(EuropeHost)), %% Load muc on mim node - muc_helper:load_muc(<<"muc.localhost">>), + muc_helper:load_muc(), RegNode = ct:get_config({hosts, reg, node}), %% Wait for muc.localhost to become visible from reg node - wait_for_domain(RegNode, <<"muc.localhost">>), + wait_for_domain(RegNode, muc_helper:muc_host()), escalus:init_per_testcase(CaseName, Config); init_per_testcase(CN, Config) when CN == test_pm_with_graceful_reconnection_to_different_server; CN == test_pm_with_ungraceful_reconnection_to_different_server; diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index 77efe884aab..807e196548c 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -231,9 +231,13 @@ ensure_muc_clean() -> forget_persistent_rooms(). stop_online_rooms() -> - Host = ct:get_config({hosts, mim, domain}), - Supervisor = rpc(mim(), gen_mod, get_module_proc, [Host, ejabberd_mod_muc_sup]), + HostType = ct:get_config({hosts, mim, host_type}), + Supervisor = rpc(mim(), gen_mod, get_module_proc, [HostType, ejabberd_mod_muc_sup]), SupervisorPid = rpc(mim(), erlang, whereis, [Supervisor]), + case is_pid(SupervisorPid) of + true -> ok; + false -> ct:fail({ejabberd_mod_muc_sup_not_found, Supervisor, HostType}) + end, rpc(mim(), erlang, exit, [SupervisorPid, kill]), rpc(mim(), mnesia, clear_table, [muc_online_room]), ok. diff --git a/big_tests/tests/muc_helper.erl b/big_tests/tests/muc_helper.erl index a370a2ce1c3..761a5b6b3b5 100644 --- a/big_tests/tests/muc_helper.erl +++ b/big_tests/tests/muc_helper.erl @@ -44,36 +44,39 @@ foreach_recipient(Users, VerifyFun) -> VerifyFun(escalus:wait_for_stanza(Recipient)) end, Users). -load_muc(Host) -> +load_muc() -> %% Stop modules before trying to start them unload_muc(), Backend = muc_backend(), - %% TODO refactoring. "localhost" should be passed as a parameter - dynamic_modules:start(<<"localhost">>, mod_muc, - [{host, subhost_pattern(Host)}, + HostType = ct:get_config({hosts, mim, host_type}), + MucHostPattern = ct:get_config({hosts, mim, muc_service_pattern}), + ct:log("Starting MUC for ~p", [HostType]), + dynamic_modules:start(HostType, mod_muc, + [{host, subhost_pattern(MucHostPattern)}, {backend, Backend}, {hibernate_timeout, 2000}, {hibernated_room_check_interval, 1000}, {hibernated_room_timeout, 2000}, {access, muc}, {access_create, muc_create}]), - dynamic_modules:start(<<"localhost">>, mod_muc_log, + dynamic_modules:start(HostType, mod_muc_log, [{outdir, "/tmp/muclogs"}, {access_log, muc}]). unload_muc() -> - dynamic_modules:stop(<<"localhost">>, mod_muc), - dynamic_modules:stop(<<"localhost">>, mod_muc_log). + HostType = ct:get_config({hosts, mim, host_type}), + dynamic_modules:stop(HostType, mod_muc), + dynamic_modules:stop(HostType, mod_muc_log). muc_host() -> - <<"muc.localhost">>. + ct:get_config({hosts, mim, muc_service}). muc_backend() -> mongoose_helper:mnesia_or_rdbms_backend(). start_room(Config, User, Room, Nick, Opts) -> From = generate_rpc_jid(User), - create_instant_room(<<"localhost">>, Room, From, Nick, Opts), + create_instant_room(Room, From, Nick, Opts), RoomJID = room_address(Room), [{nick, Nick}, {room, Room}, {room_jid, RoomJID}, {muc_host, muc_host()} | Config]. @@ -138,11 +141,25 @@ generate_rpc_jid({_,User}) -> LServer = escalus_utils:jid_to_lower(Server), {jid, Username, Server, <<"rpc">>, LUsername, LServer, <<"rpc">>}. -create_instant_room(ServerHost, Room, From, Nick, Opts) -> +create_instant_room(Room, From, Nick, Opts) -> + ServerHost = ct:get_config({hosts, mim, domain}), + assert_valid_server(ServerHost), Room1 = rpc(mim(), jid, nodeprep, [Room]), - rpc(mim(), mod_muc, create_instant_room, + ok = rpc(mim(), mod_muc, create_instant_room, [ServerHost, muc_host(), Room1, From, Nick, Opts]). +assert_valid_server(ServerHost) -> + HostType = ct:get_config({hosts, mim, host_type}), + case rpc(mim(), mongoose_domain_api, get_domain_host_type, [ServerHost]) of + {ok, HostType} -> + ok; + Other -> + ct:fail(#{what => assert_valid_server_failed, + server => ServerHost, + expected_host_type => HostType, + got_host_type => Other}) + end. + destroy_room(Config) -> destroy_room(muc_host(), ?config(room, Config)). @@ -237,7 +254,8 @@ has_features(#xmlel{children = [ Query ]} = Iq, Features) -> %% %% - Loaded = rpc(mim(), gen_mod, loaded_modules_with_opts, [<<"localhost">>]), + HostType = ct:get_config({hosts, mim, host_type}), + Loaded = rpc(mim(), gen_mod, loaded_modules_with_opts, [HostType]), ct:log("Loaded modules:~n~p", [Loaded]), Identity = exml_query:subelement(Query, <<"identity">>), diff --git a/big_tests/tests/muc_http_api_SUITE.erl b/big_tests/tests/muc_http_api_SUITE.erl index f6e89b8ff64..b0518c92c85 100644 --- a/big_tests/tests/muc_http_api_SUITE.erl +++ b/big_tests/tests/muc_http_api_SUITE.erl @@ -63,7 +63,7 @@ failure_response() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - muc_helper:load_muc(muc_helper:muc_host()), + muc_helper:load_muc(), escalus:init_per_suite(Config). end_per_suite(Config) -> diff --git a/big_tests/tests/muc_light_legacy_SUITE.erl b/big_tests/tests/muc_light_legacy_SUITE.erl index 18d84021fb3..af22111be9c 100644 --- a/big_tests/tests/muc_light_legacy_SUITE.erl +++ b/big_tests/tests/muc_light_legacy_SUITE.erl @@ -65,7 +65,7 @@ -define(NS_MUC_LIGHT, <<"urn:xmpp:muclight:0">>). -define(NS_MUC_ROOMCONFIG, <<"http://jabber.org/protocol/muc#roomconfig">>). --define(MUCHOST, <<"muc.localhost">>). +-define(MUCHOST, (muc_helper:muc_host())). -define(CHECK_FUN, fun mod_muc_light_room:participant_limit_check/2). -define(BACKEND, mod_muc_light_db_backend). @@ -229,7 +229,7 @@ disco_info_with_mam(Config) -> disco_rooms(Config) -> escalus:story(Config, [{alice, 1}], fun(Alice) -> - {ok, {?ROOM2, ?MUCHOST}} = create_room(?ROOM2, ?MUCHOST, kate, [], Config), + {ok, {?ROOM2, _}} = create_room(?ROOM2, ?MUCHOST, kate, [], Config), DiscoStanza = escalus_stanza:to(escalus_stanza:iq_get(?NS_DISCO_ITEMS, []), ?MUCHOST), escalus:send(Alice, DiscoStanza), %% we should get 1 room, Alice is not in the second one @@ -265,7 +265,7 @@ disco_rooms_rsm(Config) -> ProperJID2 = exml_query:attr(Item2, <<"jid">>), BadAfter = #xmlel{ name = <<"after">>, - children = [#xmlcdata{ content = <<"oops@muc.localhost">> }] }, + children = [#xmlcdata{ content = <<"oops@", (?MUCHOST)/binary>> }] }, RSM2 = #xmlel{ name = <<"set">>, attrs = [{<<"xmlns">>, ?NS_RSM}], children = [ #xmlel{ name = <<"max">>, @@ -280,7 +280,7 @@ disco_rooms_rsm(Config) -> unauthorized_stanza(Config) -> escalus:story(Config, [{alice, 1}, {kate, 1}], fun(Alice, Kate) -> - {ok, {?ROOM2, ?MUCHOST}} = create_room(?ROOM2, ?MUCHOST, kate, [], Config), + {ok, {?ROOM2, _}} = create_room(?ROOM2, ?MUCHOST, kate, [], Config), MsgStanza = escalus_stanza:groupchat_to(room_bin_jid(?ROOM2), <<"malicious">>), escalus:send(Alice, MsgStanza), escalus:assert(is_error, [<<"cancel">>, <<"item-not-found">>], @@ -676,14 +676,16 @@ parse_blocked_item(Item) -> <<"deny">> = exml_query:attr(Item, <<"action">>), <<"jid">> = exml_query:attr(Item, <<"type">>), Value = exml_query:attr(Item, <<"value">>), + MucHost = ?MUCHOST, case binary:split(Value, <<"/">>) of - [?MUCHOST, User] -> {user, deny, User}; + [MucHost, User] -> {user, deny, User}; [Room] -> {room, deny, Room} end. -spec verify_aff_bcast(CurrentOccupants :: [escalus:client()], AffUsersChanges :: ct_aff_users(), Newcomers :: [escalus:client()], Changer :: escalus:client()) -> ok. verify_aff_bcast(CurrentOccupants, AffUsersChanges, Newcomers, Changer) -> + MucHost = ?MUCHOST, PredList = [ presence_verify_fun(AffUser) || AffUser <- AffUsersChanges ], lists:foreach( fun(Occupant) -> @@ -699,7 +701,7 @@ verify_aff_bcast(CurrentOccupants, AffUsersChanges, Newcomers, Changer) -> fun(Newcomer) -> #xmlel{ name = <<"message">> } = Incoming = escalus:wait_for_stanza(Newcomer), RoomBareJIDBin = exml_query:attr(Incoming, <<"from">>), - [_, ?MUCHOST] = binary:split(RoomBareJIDBin, <<"@">>), + [_, MucHost] = binary:split(RoomBareJIDBin, <<"@">>), X = exml_query:subelement(Incoming, <<"x">>), ?NS_MUC_USER = exml_query:attr(X, <<"xmlns">>), [Invite] = exml_query:subelements(X, <<"invite">>), @@ -753,21 +755,23 @@ verify_keytake({value, {_, Aff}, NewAffAcc}, _JID, Aff, _AffAcc) -> NewAffAcc. -spec gc_message_verify_fun(Room :: binary(), MsgText :: binary(), Id :: binary()) -> verify_fun(). gc_message_verify_fun(Room, MsgText, Id) -> + MucHost = ?MUCHOST, fun(Incoming) -> escalus:assert(is_groupchat_message, [MsgText], Incoming), [RoomBareJID, FromNick] = binary:split(exml_query:attr(Incoming, <<"from">>), <<"/">>), - [Room, ?MUCHOST] = binary:split(RoomBareJID, <<"@">>), + [Room, MucHost] = binary:split(RoomBareJID, <<"@">>), [_] = binary:split(FromNick, <<"/">>), % nick is bare JID Id = exml_query:attr(Incoming, <<"id">>) end. -spec subject_message_verify_fun(Room :: binary(), Subject :: binary()) -> verify_fun(). subject_message_verify_fun(Room, Subject) -> + MucHost = ?MUCHOST, fun(Incoming) -> escalus:assert(is_groupchat_message, Incoming), Subject = exml_query:path(Incoming, [{element, <<"subject">>}, cdata]), RoomBareJID = exml_query:attr(Incoming, <<"from">>), - [Room, ?MUCHOST] = binary:split(RoomBareJID, <<"@">>) + [Room, MucHost] = binary:split(RoomBareJID, <<"@">>) end. -spec config_msg_verify_fun() -> verify_fun(). @@ -808,9 +812,10 @@ presence_verify_fun({User, UserAff}) -> -spec presence_verify(User :: escalus:client(), UserAff :: none | member | owner, Incoming :: xmlel()) -> true. presence_verify(User, UserAff, #xmlel{ name = <<"presence">> } = Incoming) -> + MucHost = ?MUCHOST, UserJIDBin = lbin(escalus_client:short_jid(User)), [RoomBareJIDBin, UserJIDBin] = binary:split(exml_query:attr(Incoming, <<"from">>), <<"/">>), - [_, ?MUCHOST] = binary:split(RoomBareJIDBin, <<"@">>), + [_, MucHost] = binary:split(RoomBareJIDBin, <<"@">>), X = exml_query:subelement(Incoming, <<"x">>), HasDestroy = exml_query:subelement(X, <<"destroy">>) =/= undefined, {ProperAff, ProperRole} From e39dd74c4897cd1776e64b9cd2d3d04002fb1384 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:23:09 +0200 Subject: [PATCH 34/73] Add acl:match_rule_for_host_type/4 --- src/acl.erl | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/acl.erl b/src/acl.erl index d02d64fc9d9..c7510081091 100644 --- a/src/acl.erl +++ b/src/acl.erl @@ -31,7 +31,9 @@ add/3, delete/3, match_rule/3, - match_rule/4]). + match_rule/4, + match_rule_for_host_type/4, + match_rule_for_host_type/5]). -include("mongoose.hrl"). @@ -62,6 +64,7 @@ aclspec :: aclspec() }). -type acl() :: #acl{}. +-type acl_result() :: allow | deny | term(). start() -> mnesia:create_table(acl, @@ -117,27 +120,49 @@ normalize_spec(none) -> none. +%% legacy API, use match_rule_for_host_type instead -spec match_rule(Host :: host(), Rule :: rule(), - JID :: jid:jid()) -> allow | deny | term(). + JID :: jid:jid()) -> acl_result(). match_rule(Host, Rule, JID) -> match_rule(Host, Rule, JID, deny). -match_rule(_, all, _, _Default) -> +-spec match_rule_for_host_type(HostType :: mongooseim:host_type() | global, + Host :: host(), + Rule :: rule(), + JID :: jid:jid()) -> acl_result(). +match_rule_for_host_type(HostType, Host, Rule, JID) -> + match_rule_for_host_type(HostType, Host, Rule, JID, deny). + +%% legacy API, use match_rule_for_host_type instead +-spec match_rule(Host :: host(), + Rule :: rule(), + JID :: jid:jid(), + Default :: acl_result()) -> acl_result(). +match_rule(Host, Rule, JID, Default) -> + %% We don't want to cast Host to HostType here. + %% Developers should start using match_rule_for_host_type explicetly. + match_rule_for_host_type(Host, Host, Rule, JID, Default). + +-spec match_rule_for_host_type(HostType :: mongooseim:host_type() | global, + Host :: host(), + Rule :: rule(), + JID :: jid:jid(), + Default :: acl_result()) -> acl_result(). +match_rule_for_host_type(_HostType, _, all, _, _Default) -> allow; -match_rule(_, none, _, _Default) -> +match_rule_for_host_type(_HostType, _, none, _, _Default) -> deny; -match_rule(global, Rule, JID, Default) -> +match_rule_for_host_type(_HostType, global, Rule, JID, Default) -> case ejabberd_config:get_global_option({access, Rule, global}) of undefined -> Default; GACLs -> match_acls(GACLs, JID, global) end; -match_rule(Host, Rule, JID, Default) -> +match_rule_for_host_type(HostType, Host, Rule, JID, Default) -> GlobalACLs = ejabberd_config:get_global_option({access, Rule, global}), - HostACLs = ejabberd_config:get_global_option({access, Rule, Host}), - + HostACLs = ejabberd_config:get_global_option({access, Rule, HostType}), case {GlobalACLs, HostACLs} of {undefined, undefined} -> Default; From 281205a2cc2e9852eb8e87e33c0cc4670986a98e Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:24:16 +0200 Subject: [PATCH 35/73] Refactor legacy MUC to work with host types --- src/mod_muc.erl | 205 ++++++++++++++------------ src/mod_muc_log.erl | 23 +-- src/mod_muc_room.erl | 179 ++++++++++------------ src/muc_light/mod_muc_light_utils.erl | 26 +++- 4 files changed, 227 insertions(+), 206 deletions(-) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 12906cdca87..ca65f4fab13 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -162,6 +162,7 @@ start(HostType, Opts) -> gen_mod:start_backend_module(mod_muc_db, Opts, TrackedDBFuns), start_supervisor(HostType), start_server(HostType, Opts), + assert_server_running(HostType), ok. -spec stop(host_type()) -> ok. @@ -179,7 +180,10 @@ start_server(HostType, Opts) -> 1000, worker, [?MODULE]}, - ejabberd_sup:start_child(ChildSpec). + {ok, _} = ejabberd_sup:start_child(ChildSpec). + +assert_server_running(HostType) -> + true = is_pid(whereis(gen_mod:get_module_proc(HostType, ?PROCNAME))). -spec config_spec() -> mongoose_config_spec:config_section(). config_spec() -> @@ -379,13 +383,6 @@ get_nick(LServer, MucHost, From) -> %% gen_server callbacks %%==================================================================== -%%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} -%% Description: Initiates the server -%%-------------------------------------------------------------------- -spec init([host_type() | list(), ...]) -> {'ok', state()}. init([HostType, Opts]) -> mod_muc_db_backend:init(HostType, Opts), @@ -420,6 +417,13 @@ init([HostType, Opts]) -> ejabberd_hooks:add(hooks(HostType)), %% Handler PacketHandler = mongoose_packet_handler:new(?MODULE, #{state => State}), + case SubdomainPattern of + {prefix, _} -> ok; + _ -> ?LOG_WARNING(#{what => muc_host_pattern_missing, + host_type => HostType, + subdomain_pattern => SubdomainPattern, + text => <<"Only one MUC domain would work with this host type">>}) + end, mongoose_domain_api:register_subdomain(HostType, SubdomainPattern, PacketHandler), %% Loading case gen_mod:get_module_opt(HostType, mod_muc, load_permanent_rooms_at_startup, false) of @@ -440,19 +444,9 @@ set_persistent_rooms_timer(#state{hibernated_room_check_interval = infinity}) -> set_persistent_rooms_timer(#state{hibernated_room_check_interval = Timeout}) -> timer:send_after(Timeout, stop_hibernated_persistent_rooms). -%%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} -%% Description: Handling call messages -%%-------------------------------------------------------------------- handle_call(stop, _From, State) -> ejabberd_hooks:delete(hooks(State#state.host_type)), {stop, normal, ok, State}; - handle_call({create_instant, ServerHost, MucHost, Room, From, Nick, Opts}, _From, #state{host_type = HostType, @@ -466,48 +460,54 @@ handle_call({create_instant, ServerHost, MucHost, Room, From, Nick, Opts}, default -> DefOpts; _ -> Opts end, - {ok, Pid} = mod_muc_room:start( + try + {ok, Pid} = mod_muc_room:start_new(HostType, MucHost, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, From, Nick, [{instant, true}|NewOpts]), - register_room_or_stop_if_duplicate(HostType, MucHost, Room, Pid), - {reply, ok, State}. + register_room_or_stop_if_duplicate(HostType, MucHost, Room, Pid), + {reply, ok, State} + catch Class:Reason:Stacktrace -> + Err = #{what => muc_create_instant_failed, + server => ServerHost, host_type => HostType, + room => Room, from_jid => From, + class => Class, reason => Reason, + stacktrace => Stacktrace}, + ?LOG_ERROR(Err), + {reply, {error, Err}, State} + end. -%%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling cast messages -%%-------------------------------------------------------------------- handle_cast(_Msg, State) -> {noreply, State}. -%%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} -%% Description: Handling all non call/cast messages -%%-------------------------------------------------------------------- - handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> clean_table_from_bad_node(Node), {noreply, State}; handle_info(stop_hibernated_persistent_rooms, - #state{host_type = ServerHost, + #state{host_type = HostType, hibernated_room_timeout = Timeout} = State) when is_integer(Timeout) -> - ?LOG_INFO(#{what => muc_stop_hibernated_persistent_rooms, server => ServerHost, - text => <<"Closing hibernated persistent rooms">>}), - Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup), - Now = os:timestamp(), - [stop_if_hibernated(Pid, Now, Timeout * 1000) || - {undefined, Pid, worker, _} <- supervisor:which_children(Supervisor)], - + handle_stop_hibernated_persistent_rooms(HostType, Timeout), set_persistent_rooms_timer(State), {noreply, State}; handle_info(_Info, State) -> {noreply, State}. +handle_stop_hibernated_persistent_rooms(HostType, Timeout) -> + ?LOG_INFO(#{what => muc_stop_hibernated_persistent_rooms, host_type => HostType, + text => <<"Closing hibernated persistent rooms">>}), + try + Supervisor = gen_mod:get_module_proc(HostType, ejabberd_mod_muc_sup), + Now = os:timestamp(), + [stop_if_hibernated(Pid, Now, Timeout * 1000) || + {undefined, Pid, worker, _} <- supervisor:which_children(Supervisor)] + catch Error:Reason:Stacktrace -> + ?LOG_ERROR(#{what => stop_hibernated_persistent_rooms_failed, + error => Error, reason => Reason, + stacktrace => Stacktrace, + host_type => HostType}) + end. + stop_if_hibernated(Pid, Now, Timeout) -> stop_if_hibernated(Pid, Now, Timeout, erlang:process_info(Pid, current_function)). @@ -530,22 +530,11 @@ stop_if_hibernated_for_specified_time(Pid, Now, Timeout, {hibernated, LastHibern ok end. -%%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() -%% Description: This function is called by a gen_server when it is about to -%% terminate. It should be the opposite of Module:init/1 and do any necessary -%% cleaning up. When it returns, the gen_server terminates with Reason. -%% The return value is ignored. -%%-------------------------------------------------------------------- -terminate(_Reason, State = #state{host_type = HostType, - subdomain_pattern = SubdomainPattern}) -> +terminate(_Reason, #state{host_type = HostType, + subdomain_pattern = SubdomainPattern}) -> mongoose_domain_api:unregister_subdomain(HostType, SubdomainPattern), ok. -%%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} -%% Description: Convert process state when code is changed -%%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. @@ -581,8 +570,9 @@ stop_supervisor(HostType) -> #{state := state()}) -> ok | mongoose_acc:t(). process_packet(Acc, From, To, El, #{state := State}) -> {AccessRoute, _, _, _} = State#state.access, - ServerHost = State#state.host_type, - case acl:match_rule(ServerHost, AccessRoute, From) of + ServerHost = make_server_host(To, State), + HostType = State#state.host_type, + case acl:match_rule_for_host_type(HostType, ServerHost, AccessRoute, From) of allow -> {Room, MucHost, _} = jid:to_lower(To), route_to_room(MucHost, Room, {From, To, Acc, El}, State); @@ -633,14 +623,16 @@ get_registered_room_or_route_error_from_presence(MucHost, Room, From, To, Acc, P #state{host_type = HostType, access = Access} = State) -> {_, AccessCreate, _, _} = Access, - case check_user_can_create_room(HostType, AccessCreate, From, Room) of - true -> + ServerHost = make_server_host(To, State), + case check_user_can_create_room(HostType, ServerHost, AccessCreate, From, Room) of + ok -> #state{history_size = HistorySize, room_shaper = RoomShaper, http_auth_pool = HttpAuthPool, default_room_opts = DefRoomOpts} = State, {_, _, Nick} = jid:to_lower(To), - Result = start_new_room(HostType, MucHost, Access, Room, + ServerHost = make_server_host(To, State), + Result = start_new_room(HostType, ServerHost, MucHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, From, Nick, DefRoomOpts, Acc), case Result of @@ -664,9 +656,10 @@ get_registered_room_or_route_error_from_presence(MucHost, Room, From, To, Acc, P %% Do not notify user (we can send "internal server error"). erlang:error({start_new_room_failed, Room, Result}) end; - false -> + {error, Reason} -> Lang = exml_query:attr(Packet, <<"xml:lang">>, <<>>), - ErrText = <<"Room creation is denied by service policy">>, + Policy = iolist_to_binary(io_lib:format("~p", [Reason])), + ErrText = <<"Room creation is denied by service policy: ", Policy/binary>>, {Acc1, Err} = jlib:make_error_reply( Acc, Packet, mongoose_xmpp_errors:not_allowed(Lang, ErrText)), ejabberd_router:route(To, From, Acc1, Err), @@ -676,6 +669,7 @@ get_registered_room_or_route_error_from_presence(MucHost, Room, From, To, Acc, P get_registered_room_or_route_error_from_packet(MucHost, Room, From, To, Acc, Packet, #state{host_type = HostType, access = Access} = State) -> + ServerHost = make_server_host(To, State), case restore_room(HostType, MucHost, Room) of {error, room_not_found} -> Lang = exml_query:attr(Packet, <<"xml:lang">>, <<>>), @@ -699,7 +693,8 @@ get_registered_room_or_route_error_from_packet(MucHost, Room, From, To, Acc, Pac #state{history_size = HistorySize, room_shaper = RoomShaper, http_auth_pool = HttpAuthPool} = State, - {ok, Pid} = mod_muc_room:start(MucHost, HostType, Access, + {ok, Pid} = mod_muc_room:start_restored(HostType, + MucHost, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts), register_room_or_stop_if_duplicate(HostType, MucHost, Room, Pid) @@ -723,11 +718,11 @@ route_by_nick(_Nick, {From, To, Acc, Packet}, _State) -> -spec route_by_type(binary(), from_to_packet(), state()) -> 'ok' | pid(). route_by_type(<<"iq">>, {From, To, Acc, Packet}, #state{} = State) -> - ServerHost = State#state.host_type, + HostType = State#state.host_type, MucHost = To#jid.lserver, case jlib:iq_query_info(Packet) of #iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS, lang = Lang} = IQ -> - Info = mongoose_hooks:disco_info(ServerHost, ?MODULE, <<"">>, Lang), + Info = mongoose_hooks:disco_info(HostType, ?MODULE, <<"">>, Lang), Res = IQ#iq{type = result, sub_el = [#xmlel{name = <<"query">>, attrs = [{<<"xmlns">>, XMLNS}], @@ -736,7 +731,7 @@ route_by_type(<<"iq">>, {From, To, Acc, Packet}, #state{} = State) -> #iq{type = get, xmlns = ?NS_DISCO_ITEMS} = IQ -> proc_lib:spawn(fun() -> process_iq_disco_items(MucHost, From, To, IQ) end); #iq{type = get, xmlns = ?NS_REGISTER = XMLNS, lang = Lang} = IQ -> - Result = iq_get_register_info(ServerHost, MucHost, From, Lang), + Result = iq_get_register_info(HostType, MucHost, From, Lang), Res = IQ#iq{type = result, sub_el = [#xmlel{name = <<"query">>, attrs = [{<<"xmlns">>, XMLNS}], @@ -746,7 +741,7 @@ route_by_type(<<"iq">>, {From, To, Acc, Packet}, #state{} = State) -> xmlns = ?NS_REGISTER = XMLNS, lang = Lang, sub_el = SubEl} = IQ -> - case process_iq_register_set(ServerHost, MucHost, From, SubEl, Lang) of + case process_iq_register_set(HostType, MucHost, From, SubEl, Lang) of {result, IQRes} -> Res = IQ#iq{type = result, sub_el = [#xmlel{name = <<"query">>, @@ -779,15 +774,16 @@ route_by_type(<<"iq">>, {From, To, Acc, Packet}, #state{} = State) -> ok end; route_by_type(<<"message">>, {From, To, Acc, Packet}, - #state{host_type = ServerHost, - access = {_, _, AccessAdmin, _}}) -> + #state{host_type = HostType, + access = {_, _, AccessAdmin, _}} = State) -> MucHost = To#jid.lserver, + ServerHost = make_server_host(To, State), #xmlel{attrs = Attrs} = Packet, case xml:get_attr_s(<<"type">>, Attrs) of <<"error">> -> ok; _ -> - case acl:match_rule(ServerHost, AccessAdmin, From) of + case acl:match_rule_for_host_type(HostType, ServerHost, AccessAdmin, From) of allow -> Msg = xml:get_path_s(Packet, [{elem, <<"body">>}, cdata]), broadcast_service_message(MucHost, Msg); @@ -802,33 +798,44 @@ route_by_type(<<"message">>, {From, To, Acc, Packet}, route_by_type(<<"presence">>, _Routed, _State) -> ok. --spec check_user_can_create_room(global | host_type(), - allow | atom(), jid:jid(), room()) -> boolean(). -check_user_can_create_room(HostType, AccessCreate, From, RoomID) -> - case acl:match_rule(HostType, AccessCreate, From) of +-spec check_user_can_create_room(host_type(), jid:lserver(), + allow | atom(), jid:jid(), room()) -> ok | {error, term()}. +check_user_can_create_room(HostType, ServerHost, AccessCreate, From, RoomID) -> + case acl:match_rule_for_host_type(HostType, ServerHost, AccessCreate, From) of allow -> - (size(RoomID) =< gen_mod:get_module_opt(HostType, mod_muc, - max_room_id, infinity)); + MaxLen = gen_mod:get_module_opt(HostType, mod_muc, + max_room_id, infinity), + case (size(RoomID) =< MaxLen) of + true -> ok; + false -> {error, room_id_too_long} + end; _ -> - false + ?LOG_WARNING(#{what => check_user_can_create_room_failed, + host_type => HostType, + server => ServerHost, + access_create => AccessCreate, + from_jid => From, + room_id => RoomID}), + {error, no_matching_acl_rule} end. --spec start_new_room(HostType :: host_type(), MucHost :: muc_host(), - Access :: access(), room(), +-spec start_new_room(HostType :: host_type(), ServerHost :: jid:lserver(), + MucHost :: muc_host(), Access :: access(), room(), HistorySize :: 'undefined' | integer(), RoomShaper :: shaper:shaper(), HttpAuthPool :: none | mongoose_http_client:pool(), From :: jid:jid(), nick(), DefRoomOpts :: 'undefined' | [any()], Acc :: mongoose_acc:t()) -> {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}. -start_new_room(HostType, MucHost, Access, Room, +start_new_room(HostType, ServerHost, MucHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, From, Nick, DefRoomOpts, Acc) -> case mod_muc_db_backend:restore_room(HostType, MucHost, Room) of {error, room_not_found} -> ?LOG_DEBUG(#{what => muc_start_new_room, acc => Acc, room => Room, host_type => HostType, sub_host => MucHost}), - mod_muc_room:start(MucHost, HostType, Access, + mod_muc_room:start_new(HostType, + MucHost, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, From, Nick, DefRoomOpts); @@ -837,7 +844,8 @@ start_new_room(HostType, MucHost, Access, Room, {ok, Opts} -> ?LOG_DEBUG(#{what => muc_restore_room, acc => Acc, room => Room, host_type => HostType, sub_host => MucHost, room_opts => Opts}), - mod_muc_room:start(MucHost, HostType, Access, + mod_muc_room:start_restored(HostType, + MucHost, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts) end. @@ -1231,11 +1239,12 @@ can_access_identity(_, _HostType, Room, User) -> end. online_rooms_number() -> - lists:sum([online_rooms_number(MucHost) || MucHost <- ?MYHOSTS]). + lists:sum([online_rooms_number(HostType) + || HostType <- gen_mod:hosts_with_module(?MODULE)]). -online_rooms_number(MucHost) -> +online_rooms_number(HostType) -> try - Supervisor = gen_mod:get_module_proc(MucHost, ejabberd_mod_muc_sup), + Supervisor = gen_mod:get_module_proc(HostType, ejabberd_mod_muc_sup), Stats = supervisor:count_children(Supervisor), proplists:get_value(active, Stats) catch _:_ -> @@ -1243,21 +1252,22 @@ online_rooms_number(MucHost) -> end. hibernated_rooms_number() -> - lists:sum([hibernated_rooms_number(MucHost) || MucHost <- ?MYHOSTS]). + lists:sum([hibernated_rooms_number(HostType) + || HostType <- gen_mod:hosts_with_module(?MODULE)]). -hibernated_rooms_number(MucHost) -> +hibernated_rooms_number(HostType) -> try - count_hibernated_rooms(MucHost) + count_hibernated_rooms(HostType) catch _:_ -> 0 end. -count_hibernated_rooms(MucHost) -> - AllRooms = all_room_pids(MucHost), +count_hibernated_rooms(HostType) -> + AllRooms = all_room_pids(HostType), lists:foldl(fun count_hibernated_rooms/2, 0, AllRooms). -all_room_pids(MucHost) -> - Supervisor = gen_mod:get_module_proc(MucHost, ejabberd_mod_muc_sup), +all_room_pids(HostType) -> + Supervisor = gen_mod:get_module_proc(HostType, ejabberd_mod_muc_sup), [Pid || {undefined, Pid, worker, _} <- supervisor:which_children(Supervisor)]. @@ -1305,7 +1315,7 @@ server_host_to_muc_host(HostType, ServerHost) -> -> {result, [exml:element()]} | empty | {error, any()}. disco_local_items({error, _} = Acc, _From, _To, _Node, _Lang) -> Acc; -disco_local_items(Result, _From, #jid{lserver = ServerHost} = To, <<"">>, _Lang) -> +disco_local_items(Result, _From, #jid{lserver = ServerHost} = _To, <<"">>, _Lang) -> HostType = mod_muc_light_utils:server_host_to_host_type(ServerHost), MUCHost = server_host_to_muc_host(HostType, ServerHost), Item = #xmlel{name = <<"item">>, @@ -1317,3 +1327,12 @@ disco_local_items(Result, _From, #jid{lserver = ServerHost} = To, <<"">>, _Lang) empty -> {result, [Item]} end. + +make_server_host(To, State = #state{host_type = HostType, + subdomain_pattern = SubdomainPattern}) -> + case SubdomainPattern of + {prefix, _} -> + mod_muc_light_utils:room_jid_to_server_host(To); + {fqdn, _} -> + HostType + end. diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl index 892f61b3424..00b56e41008 100644 --- a/src/mod_muc_log.erl +++ b/src/mod_muc_log.erl @@ -34,7 +34,7 @@ -export([start_link/2, start/2, stop/1, - check_access_log/2, + check_access_log/3, add_to_log/5, set_room_occupants/4]). @@ -158,17 +158,17 @@ process_top_link(KVs) -> {[[{target, Target}], [{text, Text}]], []} = proplists:split(KVs, [target, text]), {Target, Text}. --spec add_to_log(jid:server(), Type :: any(), Data :: any(), mod_muc:room(), +-spec add_to_log(mongooseim:host_type(), Type :: any(), Data :: any(), mod_muc:room(), list()) -> 'ok'. -add_to_log(Host, Type, Data, Room, Opts) -> - gen_server:cast(get_proc_name(Host), +add_to_log(HostType, Type, Data, Room, Opts) -> + gen_server:cast(get_proc_name(HostType), {add_to_log, Type, Data, Room, Opts}). --spec check_access_log(jid:server(), jid:jid()) -> any(). -check_access_log(Host, From) -> - case catch gen_server:call(get_proc_name(Host), - {check_access_log, Host, From}) of +-spec check_access_log(mongooseim:host_type(), jid:lserver(), jid:jid()) -> any(). +check_access_log(HostType, ServerHost, From) -> + case catch gen_server:call(get_proc_name(HostType), + {check_access_log, HostType, ServerHost, From}) of {'EXIT', _Error} -> deny; Res -> @@ -233,11 +233,12 @@ init([Host, Opts]) -> %% Description: Handling call messages %%-------------------------------------------------------------------- -spec handle_call('stop' - | {'check_access_log', 'global' | jid:server(), jid:jid()}, + | {'check_access_log', mongooseim:host_type(), 'global' | jid:server(), jid:jid()}, From :: any(), logstate()) -> {'reply', 'allow' | 'deny', logstate()} | {'stop', 'normal', 'ok', _}. -handle_call({check_access_log, ServerHost, FromJID}, _From, State) -> - Reply = acl:match_rule(ServerHost, State#logstate.access, FromJID), +handle_call({check_access_log, HostType, ServerHost, FromJID}, _From, State) -> + Reply = acl:match_rule_for_host_type(HostType, ServerHost, + State#logstate.access, FromJID), {reply, Reply, State}; handle_call(stop, _From, State) -> {stop, normal, ok, State}. diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 066394073a7..505bb381764 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -29,10 +29,10 @@ %% External exports --export([start_link/10, - start_link/8, - start/10, - start/8, +-export([start_link/11, + start_link/9, + start_new/11, + start_restored/9, route/5, stop/1]). @@ -151,60 +151,56 @@ -define(FSMOPTS, []). -endif. -%% Module start with or without supervisor: --ifdef(NO_TRANSIENT_SUPERVISORS). --define(SUPERVISOR_START, - gen_fsm_compat:start(?MODULE, - [Host, ServerHost, Access, Room, HistorySize, - RoomShaper, HttpAuthPool, Creator, Nick, DefRoomOpts], - ?FSMOPTS)). --else. --define(SUPERVISOR_START, - Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup), - supervisor:start_child(Supervisor, - [Host, ServerHost, Access, Room, HistorySize, - RoomShaper, HttpAuthPool, Creator, Nick, DefRoomOpts])). --endif. - %%%---------------------------------------------------------------------- %%% API %%%---------------------------------------------------------------------- --spec start(Host :: jid:server(), ServerHost :: jid:server(), + +-spec start_new(HostType :: mongooseim:host_type(), Host :: jid:server(), ServerHost :: jid:server(), Access :: _, Room :: mod_muc:room(), HistorySize :: integer(), RoomShaper :: shaper:shaper(), HttpAuthPool :: none | mongoose_http_client:pool(), Creator :: jid:jid(), Nick :: mod_muc:nick(), DefRoomOpts :: list()) -> {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}. -start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, - Creator, Nick, DefRoomOpts) -> - ?SUPERVISOR_START. - - --spec start(Host :: jid:server(), ServerHost :: jid:server(), +start_new(HostType, Host, ServerHost, Access, Room, + HistorySize, RoomShaper, HttpAuthPool, Creator, Nick, DefRoomOpts) -> + Supervisor = gen_mod:get_module_proc(HostType, ejabberd_mod_muc_sup), + Args = [HostType, Host, ServerHost, Access, Room, + HistorySize, RoomShaper, HttpAuthPool, Creator, Nick, DefRoomOpts], + supervisor:start_child(Supervisor, Args). + +-spec start_restored(HostType :: mongooseim:host_type(), Host :: jid:server(), ServerHost :: jid:server(), Access :: _, Room :: mod_muc:room(), HistorySize :: integer(), RoomShaper :: shaper:shaper(), HttpAuthPool :: none | mongoose_http_client:pool(), Opts :: list()) -> {'error', _} | {'ok', 'undefined' | pid()} | {'ok', 'undefined' | pid(), _}. -start(Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts) +start_restored(HostType, Host, ServerHost, Access, Room, + HistorySize, RoomShaper, HttpAuthPool, Opts) when is_list(Opts) -> - Supervisor = gen_mod:get_module_proc(ServerHost, ejabberd_mod_muc_sup), - supervisor:start_child(Supervisor, [Host, ServerHost, Access, Room, - HistorySize, RoomShaper, HttpAuthPool, Opts]). - -start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, - Creator, Nick, DefRoomOpts) + Supervisor = gen_mod:get_module_proc(HostType, ejabberd_mod_muc_sup), + Args = [HostType, Host, ServerHost, Access, Room, + HistorySize, RoomShaper, HttpAuthPool, Opts], + supervisor:start_child(Supervisor, Args). + +%% Starts new room +start_link(HostType, Host, ServerHost, Access, Room, + HistorySize, RoomShaper, HttpAuthPool, + Creator, Nick, DefRoomOpts) when is_list(DefRoomOpts) -> gen_fsm_compat:start_link(?MODULE, - [Host, ServerHost, Access, Room, HistorySize, + [start_new, HostType, + Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Creator, Nick, DefRoomOpts], ?FSMOPTS). -start_link(Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts) +%% Starts restored room +start_link(HostType, Host, ServerHost, Access, Room, + HistorySize, RoomShaper, HttpAuthPool, Opts) when is_list(Opts) -> gen_fsm_compat:start_link(?MODULE, - [Host, ServerHost, Access, Room, HistorySize, + [start_restored, HostType, + Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts], ?FSMOPTS). @@ -270,10 +266,15 @@ can_access_identity(RoomJID, UserJID) -> %% one for groupchat). -spec init([any(), ...]) -> {ok, statename(), state()} | {ok, statename(), state(), timeout()}. -init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, - Creator, _Nick, DefRoomOpts]) when is_list(DefRoomOpts) -> +init([start_new|Args]) -> + init_new(Args); +init([start_restored|Args]) -> + init_restored(Args). + +init_new([HostType, Host, ServerHost, Access, Room, + HistorySize, RoomShaper, HttpAuthPool, + Creator, _Nick, DefRoomOpts]) when is_list(DefRoomOpts) -> process_flag(trap_exit, true), - HostType = server_host_to_host_type(ServerHost), Shaper = shaper:new(RoomShaper), State = set_affiliation(Creator, owner, #state{host = Host, host_type = HostType, @@ -285,7 +286,7 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, just_created = true, room_shaper = Shaper, http_auth_pool = HttpAuthPool, - hibernate_timeout = read_hibernate_timeout(ServerHost) + hibernate_timeout = read_hibernate_timeout(HostType) }), State1 = set_opts(DefRoomOpts, State), ?LOG_INFO(ls(#{what => muc_room_started, @@ -300,11 +301,12 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, false -> %% Locked room waiting for configuration -- MUC request {ok, initial_state, State1} - end; + end. + %% @doc A room is restored -init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts]) -> +init_restored([HostType, Host, ServerHost, Access, Room, + HistorySize, RoomShaper, HttpAuthPool, Opts]) -> process_flag(trap_exit, true), - HostType = server_host_to_host_type(ServerHost), Shaper = shaper:new(RoomShaper), State = set_opts(Opts, #state{host = Host, host_type = HostType, server_host = ServerHost, @@ -314,15 +316,12 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opt jid = jid:make(Room, Host, <<>>), room_shaper = Shaper, http_auth_pool = HttpAuthPool, - hibernate_timeout = read_hibernate_timeout(ServerHost) + hibernate_timeout = read_hibernate_timeout(HostType) }), add_to_log(room_existence, started, State), mongoose_metrics:update(global, [mod_muc, process_recreations], 1), {ok, normal_state, State, State#state.hibernate_timeout}. -read_hibernate_timeout(Host) -> - gen_mod:get_module_opt(Host, mod_muc, hibernate_timeout, timer:seconds(90)). - %%---------------------------------------------------------------------- %% Func: StateName/2 %% Returns: {next_state, NextStateName, NextStateData} | @@ -488,8 +487,7 @@ normal_state({route, From, Nick, _Acc, Activity = get_user_activity(From, StateData), Now = os:system_time(microsecond), MinPresenceInterval = - trunc(gen_mod:get_module_opt(StateData#state.server_host, - mod_muc, min_presence_interval, 0) * 1000000), + trunc(get_opt(StateData, min_presence_interval, 0) * 1000000), case (Now >= Activity#activity.presence_time + MinPresenceInterval) and (Activity#activity.presence == undefined) of true -> @@ -1398,7 +1396,8 @@ set_affiliation_and_reason(JID, Affiliation, Reason, StateData) -spec get_affiliation(jid:jid(), state()) -> mod_muc:affiliation(). get_affiliation(JID, StateData) -> AccessAdmin = access_admin(StateData), - case acl:match_rule(StateData#state.server_host, AccessAdmin, JID) of + case acl:match_rule_for_host_type(StateData#state.host_type, + StateData#state.server_host, AccessAdmin, JID) of allow -> owner; _ -> @@ -1424,7 +1423,8 @@ lookup_affiliation([], _Affiliations) -> -spec get_service_affiliation(jid:jid(), state()) -> mod_muc:affiliation(). get_service_affiliation(JID, StateData) -> AccessAdmin = access_admin(StateData), - case acl:match_rule(StateData#state.server_host, AccessAdmin, JID) of + case acl:match_rule_for_host_type(StateData#state.host_type, + StateData#state.server_host, AccessAdmin, JID) of allow -> owner; _ -> @@ -1502,15 +1502,11 @@ get_max_users(StateData) -> -spec get_service_max_users(state()) -> integer() | none. get_service_max_users(StateData) -> - gen_mod:get_module_opt(StateData#state.server_host, - mod_muc, max_users, ?MAX_USERS_DEFAULT). - + get_opt(StateData, max_users, ?MAX_USERS_DEFAULT). -spec get_max_users_admin_threshold(state()) -> integer(). get_max_users_admin_threshold(StateData) -> - gen_mod:get_module_opt(StateData#state.server_host, - mod_muc, max_users_admin_threshold, 5). - + get_opt(StateData, max_users_admin_threshold, 5). -spec get_user_activity(jid:simple_jid() | jid:jid(), state()) -> activity(). @@ -1520,13 +1516,9 @@ get_user_activity(JID, StateData) -> {ok, _P, A} -> A; error -> MessageShaper = - shaper:new(gen_mod:get_module_opt( - StateData#state.server_host, - mod_muc, user_message_shaper, none)), + shaper:new(get_opt(StateData, user_message_shaper, none)), PresenceShaper = - shaper:new(gen_mod:get_module_opt( - StateData#state.server_host, - mod_muc, user_presence_shaper, none)), + shaper:new(get_opt(StateData, user_presence_shaper, none)), #activity{message_shaper = MessageShaper, presence_shaper = PresenceShaper} end. @@ -1535,14 +1527,8 @@ get_user_activity(JID, StateData) -> -spec store_user_activity(jid:simple_jid() | jid:jid(), activity(), state()) -> state(). store_user_activity(JID, UserActivity, StateData) -> - MinMessageInterval = - gen_mod:get_module_opt( - StateData#state.server_host, - mod_muc, min_message_interval, 0), - MinPresenceInterval = - gen_mod:get_module_opt( - StateData#state.server_host, - mod_muc, min_presence_interval, 0), + MinMessageInterval = get_opt(StateData, min_message_interval, 0), + MinPresenceInterval = get_opt(StateData, min_presence_interval, 0), Key = jid:to_lower(JID), Now = os:system_time(microsecond), Activity1 = clean_treap(StateData#state.activity, {1, -Now}), @@ -1792,9 +1778,7 @@ is_user_limit_reached(From, Affiliation, StateData) -> NUsers = count_users(StateData), ServiceAffiliation = get_service_affiliation(From, StateData), NConferences = tab_count_user(From), - MaxConferences = gen_mod:get_module_opt( - StateData#state.server_host, - mod_muc, max_user_conferences, 10), + MaxConferences = get_opt(StateData, max_user_conferences, 10), (ServiceAffiliation == owner orelse MaxUsers == none orelse ((Affiliation == admin orelse Affiliation == owner) andalso @@ -1908,7 +1892,7 @@ perform_http_auth(From, Nick, Packet, Role, Password, StateData) -> handle_http_auth_result(Result, From, Nick, Packet, Role, StateData); false -> %% Perform the request in a separate process to prevent room freeze - Pid = spawn_link( + Pid = proc_lib:spawn_link( fun() -> Result = make_http_auth_request(From, RoomJid, Password, Pool), gen_fsm_compat:send_event(RoomPid, {http_auth, self(), Result, @@ -1997,6 +1981,8 @@ check_password(#state{http_auth_pool = none, config = #config{password = Password}}, Password) -> allowed; check_password(#state{http_auth_pool = none}, _Password) -> + ?LOG_WARNING(#{what => muc_check_password_failed, + text => <<"http_auth_pool not found">>}), invalid_password; check_password(#state{http_auth_pool = _Pool}, _Password) -> http_auth. @@ -3310,7 +3296,7 @@ process_authorized_submit_owner(From, XEl, StateData) -> andalso is_allowed_room_name_desc_limits(XEl, StateData) andalso is_password_settings_correct(XEl, StateData) of true -> set_config(XEl, StateData); - false -> {error, mongoose_xmpp_errors:not_acceptable()} + false -> {error, mongoose_xmpp_errors:not_acceptable(<<"en">>, <<"not allowed to configure">>)} end. -spec is_allowed_log_change(exml:element(), state(), jid:jid()) -> boolean(). @@ -3321,7 +3307,8 @@ is_allowed_log_change(XEl, StateData, From) -> true; true -> (allow == mod_muc_log:check_access_log( - StateData#state.server_host, From)) + StateData#state.host_type, + StateData#state.server_host, From)) end. @@ -3333,7 +3320,9 @@ is_allowed_persistent_change(XEl, StateData, From) -> true; true -> AccessPersistent = access_persistent(StateData), - (allow == acl:match_rule(StateData#state.server_host, AccessPersistent, From)) + (allow == acl:match_rule_for_host_type(StateData#state.host_type, + StateData#state.server_host, + AccessPersistent, From)) end. @@ -3345,9 +3334,7 @@ is_allowed_room_name_desc_limits(XEl, StateData) -> case lists:keysearch(<<"muc#roomconfig_roomname">>, 1, jlib:parse_xdata_submit(XEl)) of {value, {_, [N]}} -> - byte_size(N) =< gen_mod:get_module_opt(StateData#state.server_host, - mod_muc, max_room_name, - infinity); + byte_size(N) =< get_opt(StateData, max_room_name, infinity); _ -> true end, @@ -3355,9 +3342,7 @@ is_allowed_room_name_desc_limits(XEl, StateData) -> case lists:keysearch(<<"muc#roomconfig_roomdesc">>, 1, jlib:parse_xdata_submit(XEl)) of {value, {_, [D]}} -> - byte_size(D) =< gen_mod:get_module_opt(StateData#state.server_host, - mod_muc, max_room_desc, - infinity); + byte_size(D) =< get_opt(StateData, max_room_desc, infinity); _ -> true end, @@ -3404,8 +3389,7 @@ is_password_settings_correct(XEl, StateData) -> -spec get_default_room_maxusers(state()) -> any(). get_default_room_maxusers(RoomState) -> - DefRoomOpts = gen_mod:get_module_opt( - RoomState#state.server_host, mod_muc, default_room_options, []), + DefRoomOpts = get_opt(RoomState, default_room_options, []), RoomState2 = set_opts(DefRoomOpts, RoomState), (RoomState2#state.config)#config.max_users. @@ -3433,7 +3417,9 @@ get_config(Lang, StateData, From) -> <<"muc#roomconfig_roomdesc">>, Config#config.description, Lang) ] ++ - case acl:match_rule(StateData#state.server_host, AccessPersistent, From) of + case acl:match_rule_for_host_type(StateData#state.host_type, + StateData#state.server_host, + AccessPersistent, From) of allow -> [boolxfield(<<"Make room persistent">>, <<"muc#roomconfig_persistentroom">>, @@ -3489,7 +3475,8 @@ get_config(Lang, StateData, From) -> <<"muc#roomconfig_allowvisitornickchange">>, Config#config.allow_visitor_nickchange, Lang) ] ++ - case mod_muc_log:check_access_log(StateData#state.server_host, From) of + case mod_muc_log:check_access_log(StateData#state.host_type, + StateData#state.server_host, From) of allow -> [boolxfield( <<"Enable logging">>, @@ -4403,8 +4390,7 @@ route_message(#routed_message{allowed = true, type = <<"groupchat">>, from = From, packet = Packet, lang = Lang}, StateData) -> Activity = get_user_activity(From, StateData), Now = os:system_time(microsecond), - MinMessageInterval = trunc(gen_mod:get_module_opt(StateData#state.server_host, - mod_muc, min_message_interval, 0) * 1000000), + MinMessageInterval = trunc(get_opt(StateData, min_message_interval, 0) * 1000000), Size = element_size(Packet), {MessageShaper, MessageShaperInterval} = shaper:update(Activity#activity.message_shaper, Size), case {Activity#activity.message /= undefined, @@ -4769,11 +4755,8 @@ ls(LogMap, State) -> maps:merge(LogMap, #{room => State#state.room, sub_host => State#state.host}). --spec server_host_to_host_type(jid:lserver()) -> mongooseim:host_type(). -server_host_to_host_type(ServerHost) -> - case mongoose_domain_api:get_domain_host_type(ServerHost) of - {ok, HostType} -> - HostType; - {error, not_found} -> - ServerHost - end. +get_opt(#state{host_type = HostType}, Opt, Def) -> + gen_mod:get_module_opt(HostType, mod_muc, Opt, Def). + +read_hibernate_timeout(HostType) -> + gen_mod:get_module_opt(HostType, mod_muc, hibernate_timeout, timer:seconds(90)). diff --git a/src/muc_light/mod_muc_light_utils.erl b/src/muc_light/mod_muc_light_utils.erl index 46f8214beb5..6429ef3d7c4 100644 --- a/src/muc_light/mod_muc_light_utils.erl +++ b/src/muc_light/mod_muc_light_utils.erl @@ -31,6 +31,7 @@ -export([filter_out_prevented/4]). -export([acc_to_host_type/1]). -export([room_jid_to_host_type/1]). +-export([room_jid_to_server_host/1]). -export([muc_host_to_host_type/1]). -export([server_host_to_host_type/1]). -export([run_forget_room_hook/1]). @@ -286,13 +287,30 @@ acc_to_host_type(Acc) -> room_jid_to_host_type(#jid{lserver = MucHost}) -> muc_host_to_host_type(MucHost). +-spec room_jid_to_server_host(jid:jid()) -> jid:lserver(). +room_jid_to_server_host(#jid{lserver = MucHost}) -> + case mongoose_domain_api:get_subdomain_info(MucHost) of + {ok, #{parent_domain := ServerHost}} when is_binary(ServerHost) -> + ServerHost; + Other -> + error({room_jid_to_server_host_failed, MucHost, Other}) + end. + server_host_to_host_type(LServer) -> - {ok, HostType} = mongoose_domain_api:get_domain_host_type(LServer), - HostType. + case mongoose_domain_api:get_domain_host_type(LServer) of + {ok, HostType} -> + HostType; + Other -> + error({server_host_to_host_type_failed, LServer, Other}) + end. muc_host_to_host_type(MucHost) -> - {ok, HostType} = mongoose_domain_api:get_subdomain_host_type(MucHost), - HostType. + case mongoose_domain_api:get_subdomain_host_type(MucHost) of + {ok, HostType} -> + HostType; + Other -> + error({muc_host_to_host_type_failed, MucHost, Other}) + end. run_forget_room_hook({Room, MucHost}) -> HostType = muc_host_to_host_type(MucHost), From 59b6cd6696ed0c5e517a312cbcf73d2d5255b773 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 13:55:47 +0200 Subject: [PATCH 36/73] Route MUC IQ using host_type --- src/mod_muc_room.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 505bb381764..10f4fff1f20 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -761,7 +761,9 @@ terminate(Reason, _StateName, StateData) -> tab_remove_online_user(LJID, StateData) end, StateData#state.users), add_to_log(room_existence, stopped, StateData), - mod_muc:room_destroyed(StateData#state.host, StateData#state.room, self()), + mod_muc:room_destroyed(StateData#state.host_type, + StateData#state.host, + StateData#state.room, self()), ok. %%%---------------------------------------------------------------------- @@ -4588,9 +4590,9 @@ route_iq(Acc, #routed_iq{iq = #iq{type = Type, xmlns = ?NS_DISCO_ITEMS, lang = L Res = process_iq_disco_items(From, Type, Lang, StateData), do_route_iq(Acc, Res, Routed, StateData); route_iq(Acc, #routed_iq{iq = IQ = #iq{}, packet = Packet, from = From}, - #state{host = Host, jid = RoomJID} = StateData) -> + #state{host_type = HostType, jid = RoomJID} = StateData) -> %% Custom IQ, addressed to this room's JID. - case mod_muc_iq:process_iq(Host, From, RoomJID, Acc, IQ) of + case mod_muc_iq:process_iq(HostType, From, RoomJID, Acc, IQ) of {Acc1, error} -> {Acc2, Err} = jlib:make_error_reply(Acc1, Packet, mongoose_xmpp_errors:feature_not_implemented(<<"en">>, <<"From mod_muc_room">>)), From 070f1a525375f11e35032c73c32940351abfa181 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Fri, 21 May 2021 21:36:09 +0200 Subject: [PATCH 37/73] Use map in mod_muc_room init --- big_tests/tests/muc_SUITE.erl | 31 +++------------- src/mod_muc_room.erl | 67 ++++++++++++++++------------------- 2 files changed, 34 insertions(+), 64 deletions(-) diff --git a/big_tests/tests/muc_SUITE.erl b/big_tests/tests/muc_SUITE.erl index 564f6f88e84..b22215bb605 100644 --- a/big_tests/tests/muc_SUITE.erl +++ b/big_tests/tests/muc_SUITE.erl @@ -543,33 +543,10 @@ meck_room() -> %% Meck will register a fake room right before a 'real' room is started meck_room_start() -> - rpc(mim(), meck, expect, [mod_muc_room, start, - fun(Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts) -> - mod_muc:register_room(Host, Host, Room, ?FAKEPID), - meck:passthrough([Host, - ServerHost, - Access, - Room, - HistorySize, - RoomShaper, - HttpAuthPool, - Opts]) - end]), - - rpc(mim(), meck, expect, [mod_muc_room, start, - fun(Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Creator, Nick, DefRoomOpts) -> - - mod_muc:register_room(Host, Host, Room, ?FAKEPID), - meck:passthrough([Host, - ServerHost, - Access, - Room, - HistorySize, - RoomShaper, - HttpAuthPool, - Creator, - Nick, - DefRoomOpts]) + rpc(mim(), meck, expect, [mod_muc_room, init, + fun(#{muc_host := Host, host_type := HostType, room_name := Room} = Args) -> + mod_muc:register_room(HostType, Host, Room, ?FAKEPID), + meck:passthrough([Args]) end]). %% Meck will forward all calls to route to the test case instead diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 10f4fff1f20..1566331b2a1 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -29,8 +29,7 @@ %% External exports --export([start_link/11, - start_link/9, +-export([start_link/1, start_new/11, start_restored/9, route/5, @@ -165,9 +164,12 @@ start_new(HostType, Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Creator, Nick, DefRoomOpts) -> Supervisor = gen_mod:get_module_proc(HostType, ejabberd_mod_muc_sup), - Args = [HostType, Host, ServerHost, Access, Room, - HistorySize, RoomShaper, HttpAuthPool, Creator, Nick, DefRoomOpts], - supervisor:start_child(Supervisor, Args). + Args = #{init_type => start_new, host_type => HostType, + muc_host => Host, server_host => ServerHost, access => Access, + room_name => Room, history_size => HistorySize, + room_shaper => RoomShaper, http_auth_pool => HttpAuthPool, + creator => Creator, nick => Nick, def_opts => DefRoomOpts}, + supervisor:start_child(Supervisor, [Args]). -spec start_restored(HostType :: mongooseim:host_type(), Host :: jid:server(), ServerHost :: jid:server(), Access :: _, Room :: mod_muc:room(), HistorySize :: integer(), @@ -179,30 +181,15 @@ start_restored(HostType, Host, ServerHost, Access, Room, HistorySize, RoomShaper, HttpAuthPool, Opts) when is_list(Opts) -> Supervisor = gen_mod:get_module_proc(HostType, ejabberd_mod_muc_sup), - Args = [HostType, Host, ServerHost, Access, Room, - HistorySize, RoomShaper, HttpAuthPool, Opts], - supervisor:start_child(Supervisor, Args). - -%% Starts new room -start_link(HostType, Host, ServerHost, Access, Room, - HistorySize, RoomShaper, HttpAuthPool, - Creator, Nick, DefRoomOpts) - when is_list(DefRoomOpts) -> - gen_fsm_compat:start_link(?MODULE, - [start_new, HostType, - Host, ServerHost, Access, Room, HistorySize, - RoomShaper, HttpAuthPool, Creator, Nick, DefRoomOpts], - ?FSMOPTS). - -%% Starts restored room -start_link(HostType, Host, ServerHost, Access, Room, - HistorySize, RoomShaper, HttpAuthPool, Opts) - when is_list(Opts) -> - gen_fsm_compat:start_link(?MODULE, - [start_restored, HostType, - Host, ServerHost, Access, Room, HistorySize, - RoomShaper, HttpAuthPool, Opts], - ?FSMOPTS). + Args = #{init_type => start_restored, host_type => HostType, + muc_host => Host, server_host => ServerHost, + access => Access, room_name => Room, history_size => HistorySize, + room_shaper => RoomShaper, http_auth_pool => HttpAuthPool, + opts => Opts}, + supervisor:start_child(Supervisor, [Args]). + +start_link(Args) -> + gen_fsm_compat:start_link(?MODULE, Args, []). stop(Pid) -> gen_fsm_compat:stop(Pid). @@ -264,16 +251,18 @@ can_access_identity(RoomJID, UserJID) -> %% @doc A room is created. Depending on request type (MUC/groupchat 1.0) the %% next state is determined accordingly (a locked room for MUC or an instant %% one for groupchat). --spec init([any(), ...]) -> +-spec init(#{}) -> {ok, statename(), state()} | {ok, statename(), state(), timeout()}. -init([start_new|Args]) -> +init(#{init_type := start_new} = Args) -> init_new(Args); -init([start_restored|Args]) -> +init(#{init_type := start_restored} = Args) -> init_restored(Args). -init_new([HostType, Host, ServerHost, Access, Room, - HistorySize, RoomShaper, HttpAuthPool, - Creator, _Nick, DefRoomOpts]) when is_list(DefRoomOpts) -> +init_new(#{init_type := start_new, host_type := HostType, muc_host := Host, + server_host := ServerHost, access := Access, room_name := Room, + history_size := HistorySize, room_shaper := RoomShaper, + http_auth_pool := HttpAuthPool, creator := Creator, nick := Nick, + def_opts := DefRoomOpts}) when is_list(DefRoomOpts) -> process_flag(trap_exit, true), Shaper = shaper:new(RoomShaper), State = set_affiliation(Creator, owner, @@ -304,8 +293,12 @@ init_new([HostType, Host, ServerHost, Access, Room, end. %% @doc A room is restored -init_restored([HostType, Host, ServerHost, Access, Room, - HistorySize, RoomShaper, HttpAuthPool, Opts]) -> +init_restored(#{init_type := start_restored, + host_type := HostType, muc_host := Host, + server_host := ServerHost, access := Access, + room_name := Room, history_size := HistorySize, + room_shaper := RoomShaper, http_auth_pool := HttpAuthPool, + opts := Opts}) -> process_flag(trap_exit, true), Shaper = shaper:new(RoomShaper), State = set_opts(Opts, #state{host = Host, host_type = HostType, From ae6b8d91ffa3e4b9d4b36778bec99ed04351cb15 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:07:17 +0200 Subject: [PATCH 38/73] Add IP address mapping for domain.example.com in rel/fed1.vars-toml.config --- rel/fed1.vars-toml.config | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rel/fed1.vars-toml.config b/rel/fed1.vars-toml.config index 16edfe23b61..150670d955c 100644 --- a/rel/fed1.vars-toml.config +++ b/rel/fed1.vars-toml.config @@ -13,6 +13,7 @@ {hosts, "\"fed1\""}. {default_server_domain, "\"fed1\""}. +%% domain.example.com is for multitenancy preset, muc_SUITE:register_over_s2s {s2s_addr, "[[s2s.address]] host = \"localhost\" ip_address = \"127.0.0.1\" @@ -27,7 +28,12 @@ [[s2s.address]] host = \"localhost.bis\" - ip_address = \"127.0.0.1\""}. + ip_address = \"127.0.0.1\" + + [[s2s.address]] + host = \"domain.example.com\" + ip_address = \"127.0.0.1\" +"}. {s2s_default_policy, "\"allow\""}. {highload_vm_args, ""}. {listen_service, false}. From 4b38a64b215e13782b45e300d4121557a43897d0 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 24 May 2021 16:42:46 +0200 Subject: [PATCH 39/73] Pass mam_SUITE for multitenancy (with some tests disabled) --- big_tests/dynamic_domains.config | 2 - big_tests/dynamic_domains.spec | 3 + big_tests/tests/mam_SUITE.erl | 325 ++++++++++++++++++------------- big_tests/tests/mam_helper.erl | 35 ++-- src/mam/mod_mam.erl | 49 +++-- src/mam/mod_mam_muc.erl | 10 +- src/mam/mod_mam_rdbms_arch.erl | 4 + src/mam/mod_mam_utils.erl | 19 +- src/mongoose_hooks.erl | 20 +- src/shaper_srv.erl | 95 ++++----- 10 files changed, 308 insertions(+), 254 deletions(-) diff --git a/big_tests/dynamic_domains.config b/big_tests/dynamic_domains.config index bcab79b7e47..25558234c50 100644 --- a/big_tests/dynamic_domains.config +++ b/big_tests/dynamic_domains.config @@ -1,8 +1,6 @@ %% Options defined here are used when testing dynamic domains, see 'dynamic_domains.spec' %% They take precedence over 'test.config' -{disable_disco, true}. - {hosts, [{mim, [{node, mongooseim@localhost}, {domain, <<"domain.example.com">>}, {host_type, <<"test type">>}, diff --git a/big_tests/dynamic_domains.spec b/big_tests/dynamic_domains.spec index 6b04cd5e2c0..77c7d89cffa 100644 --- a/big_tests/dynamic_domains.spec +++ b/big_tests/dynamic_domains.spec @@ -10,6 +10,9 @@ {include, "tests"}. {suites, "tests", acc_e2e_SUITE}. +{suites, "tests", domain_isolation_SUITE}. +{suites, "tests", muc_SUITE}. +{suites, "tests", mam_SUITE}. {suites, "tests", carboncopy_SUITE}. {skip_cases, "tests", carboncopy_SUITE, [discovering_support], diff --git a/big_tests/tests/mam_SUITE.erl b/big_tests/tests/mam_SUITE.erl index 38698d3e437..8d7b5c31557 100644 --- a/big_tests/tests/mam_SUITE.erl +++ b/big_tests/tests/mam_SUITE.erl @@ -35,6 +35,7 @@ mam_service_discovery/1, muc_service_discovery/1, simple_archive_request/1, + simple_archive_request_for_the_receiver/1, text_search_query_fails_if_disabled/1, text_search_is_not_available/1, simple_text_search_request/1, @@ -195,7 +196,7 @@ parse_messages/1, run_set_and_get_prefs_case/4, muc_light_host/0, - host/0 + host_type/0 ]). -import(muc_light_helper, @@ -235,10 +236,10 @@ all_configurations() -> ++ elasticsearch_configs(true). configurations_for_running_ct() -> - cassandra_configs(is_cassandra_enabled(host())) - ++ rdbms_configs(mongoose_helper:is_rdbms_enabled(host())) - ++ riak_configs(is_riak_enabled(host())) - ++ elasticsearch_configs(is_elasticsearch_enabled(host())). + cassandra_configs(is_cassandra_enabled(host_type())) + ++ rdbms_configs(mongoose_helper:is_rdbms_enabled(host_type())) + ++ riak_configs(is_riak_enabled(host_type())) + ++ elasticsearch_configs(is_elasticsearch_enabled(host_type())). rdbms_configs(true) -> [rdbms, @@ -284,7 +285,7 @@ all() -> Reasons = case ct_helper:is_ct_running() of true -> - case is_mam_possible(host()) of + case is_mam_possible(host_type()) of false -> [require_rdbms]; true -> [] end; @@ -312,6 +313,27 @@ groups() -> is_skipped(_, _) -> false. +disco_disabled() -> + try + ct:get_config({disable_disco_tests}) + catch _:_ -> + false + end. + +carbons_disabled() -> + try + ct:get_config({disable_carbons_tests}) + catch _:_ -> + false + end. + +roster_disabled() -> + try + ct:get_config({disable_roster_tests}) + catch _:_ -> + false + end. + basic_groups() -> [ @@ -335,8 +357,6 @@ basic_groups() -> {rsm04, [parallel], rsm_cases()}, {rsm04_comp, [parallel], complete_flag_cases()}, {with_rsm04, [parallel], with_rsm_cases()}]}]}, - {chat_markers, [parallel], [archive_chat_markers, - dont_archive_chat_markers]}, {muc_all, [parallel], [{muc04, [parallel], muc_cases() ++ muc_text_search_cases()}, {muc06, [parallel], muc_cases() ++ muc_stanzaid_cases() ++ muc_retract_cases()}, @@ -347,23 +367,38 @@ basic_groups() -> {prefs_cases, [parallel], prefs_cases()}, {impl_specific, [], impl_specific()}, {disabled_text_search, [], - [ - {mam04, [], disabled_text_search_cases()} - ]}, + [{mam04, [], disabled_text_search_cases()}]}, + {chat_markers, [parallel], + [{mam04, [parallel], chat_markers_cases()}]}, {disabled_retraction, [], - [{mam06, [parallel], disabled_retract_cases() ++ [mam_service_discovery]}]}, + [{mam06, [parallel], disabled_retract_cases() ++ + case disco_disabled() of + true -> []; + false -> [mam_service_discovery] + end}]}, {muc_disabled_retraction, [], - [{muc06, [parallel], disabled_muc_retract_cases() ++ [muc_service_discovery]}]} + [{muc06, [parallel], disabled_muc_retract_cases() ++ + case disco_disabled() of + true -> []; + false -> [muc_service_discovery] + end}]} ]. +chat_markers_cases() -> + [archive_chat_markers, + dont_archive_chat_markers]. mam_metrics_cases() -> [metric_incremented_on_archive_request, metric_incremented_when_store_message]. mam_cases() -> - [mam_service_discovery, - simple_archive_request, + case disco_disabled() of + true -> []; + false -> [mam_service_discovery] + end ++ + [simple_archive_request, + simple_archive_request_for_the_receiver, range_archive_request, range_archive_request_not_empty, limit_archive_request, @@ -395,8 +430,11 @@ archived_cases() -> filter_forwarded]. stanzaid_cases() -> - [message_with_stanzaid, - stanza_id_is_appended_to_carbons]. + [message_with_stanzaid] ++ + case carbons_disabled() of + true -> []; + false -> [stanza_id_is_appended_to_carbons] + end. retract_cases() -> [retract_message, @@ -410,8 +448,11 @@ nostore_cases() -> nostore_hint]. muc_cases() -> - [muc_service_discovery, - muc_archive_request, + case disco_disabled() of + true -> []; + false -> [muc_service_discovery] + end ++ + [muc_archive_request, muc_multiple_devices, muc_protected_message, muc_deny_protected_room_access, @@ -503,12 +544,15 @@ prefs_cases() -> prefs_set_cdata_request, query_get_request, messages_filtered_when_prefs_default_policy_is_always, - messages_filtered_when_prefs_default_policy_is_never, - messages_filtered_when_prefs_default_policy_is_roster, - run_set_and_get_prefs_cases]. + messages_filtered_when_prefs_default_policy_is_never] ++ + case roster_disabled() of + true -> []; + false -> [messages_filtered_when_prefs_default_policy_is_roster] + end ++ + [run_set_and_get_prefs_cases]. impl_specific() -> - [check_user_exist]. + [check_user_exist]. suite() -> require_rpc_nodes([mim]) ++ escalus:suite(). @@ -557,7 +601,7 @@ set_shaper({Mam, Norm, Fast}) -> rpc_apply(ejabberd_config, add_global_option, [{shaper, mam_shaper, global}, Mam]), rpc_apply(ejabberd_config, add_global_option, [{shaper, normal, global}, Norm]), rpc_apply(ejabberd_config, add_global_option, [{shaper, fast, global}, Fast]), - rpc_apply(shaper_srv, reset_all_shapers, [host()]). + rpc_apply(shaper_srv, reset_all_shapers, [host_type()]). disable_sessions_limit(Config) -> OldLimit = get_sessions_limit(), @@ -634,11 +678,11 @@ init_per_group(Group, ConfigIn) -> end. backup_module_opts(Module) -> - {{params_backup, Module}, rpc_apply(gen_mod, get_module_opts, [host(), mod_mam_muc])}. + {{params_backup, Module}, rpc_apply(gen_mod, get_module_opts, [host_type(), mod_mam_muc])}. restore_module_opts(Module, Config) -> ParamsB = proplists:get_value({params_backup, Module}, Config), - rpc_apply(gen_mod, set_module_opts, [host(), Module, ParamsB]). + rpc_apply(gen_mod, set_module_opts, [host_type(), Module, ParamsB]). do_init_per_group(C, ConfigIn) -> Config0 = create_users(ConfigIn), @@ -676,94 +720,94 @@ end_per_group(Group, Config) -> init_modules(rdbms, muc_light, Config) -> Config1 = init_modules_for_muc_light(rdbms, Config), - init_module(host(), mod_mam_rdbms_user, [muc, pm]), - init_module(host(), mod_mam_muc_rdbms_arch, []), + init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), + init_module(host_type(), mod_mam_muc_rdbms_arch, []), Config1; init_modules(BT = riak_timed_yz_buckets, muc_light, Config) -> - dynamic_modules:start(host(), mod_muc_light, [{host, subhost_pattern(muc_light_host())}]), + dynamic_modules:start(host_type(), mod_muc_light, [{host_type, subhost_pattern(muc_light_host())}]), init_modules(BT, generic, [{muc_domain, "muclight.@HOST@"} | Config]); init_modules(BT = cassandra, muc_light, config) -> init_modules_for_muc_light(BT, config); init_modules(cassandra, muc_all, Config) -> - init_module(host(), mod_mam_muc_cassandra_arch, []), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}]), + init_module(host_type(), mod_mam_muc_cassandra_arch, []), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}]), Config; init_modules(BT = elasticsearch, muc_light, config) -> init_modules_for_muc_light(BT, config); init_modules(elasticsearch, muc_all, Config) -> - init_module(host(), mod_mam_muc_elasticsearch_arch, []), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}]), + init_module(host_type(), mod_mam_muc_elasticsearch_arch, []), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}]), Config; init_modules(rdbms, C, Config) when C =:= muc_all; C =:= muc_disabled_retraction -> - init_module(host(), mod_mam_muc_rdbms_arch, []), - init_module(host(), mod_mam_rdbms_prefs, [muc]), - init_module(host(), mod_mam_rdbms_user, [muc, pm]), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc_rdbms_arch, []), + init_module(host_type(), mod_mam_rdbms_prefs, [muc]), + init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_simple, C, Config) when C =:= muc_all; C =:= muc_disabled_retraction -> - init_module(host(), mod_mam_muc_rdbms_arch, []), - init_module(host(), mod_mam_rdbms_prefs, [muc]), - init_module(host(), mod_mam_rdbms_user, [muc, pm]), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc_rdbms_arch, []), + init_module(host_type(), mod_mam_rdbms_prefs, [muc]), + init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_async_pool, C, Config) when C =:= muc_all; C =:= muc_disabled_retraction -> - init_module(host(), mod_mam_muc_rdbms_arch, [no_writer]), - init_module(host(), mod_mam_muc_rdbms_async_pool_writer, [{flush_interval, 1}]), %% 1ms - init_module(host(), mod_mam_rdbms_prefs, [muc]), - init_module(host(), mod_mam_rdbms_user, [muc, pm]), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc_rdbms_arch, [no_writer]), + init_module(host_type(), mod_mam_muc_rdbms_async_pool_writer, [{flush_interval, 1}]), %% 1ms + init_module(host_type(), mod_mam_rdbms_prefs, [muc]), + init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_mnesia, C, Config) when C =:= muc_all; C =:= muc_disabled_retraction -> - init_module(host(), mod_mam_muc_rdbms_arch, []), - init_module(host(), mod_mam_mnesia_prefs, [muc]), - init_module(host(), mod_mam_rdbms_user, [muc, pm]), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc_rdbms_arch, []), + init_module(host_type(), mod_mam_mnesia_prefs, [muc]), + init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_cache, C, Config) when C =:= muc_all; C =:= muc_disabled_retraction -> - init_module(host(), mod_mam_muc_rdbms_arch, []), - init_module(host(), mod_mam_rdbms_prefs, [muc]), - init_module(host(), mod_mam_rdbms_user, [muc, pm]), - init_module(host(), mod_mam_cache_user, [muc]), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc_rdbms_arch, []), + init_module(host_type(), mod_mam_rdbms_prefs, [muc]), + init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), + init_module(host_type(), mod_mam_cache_user, [muc]), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_async_cache, C, Config) when C =:= muc_all; C =:= muc_disabled_retraction -> - init_module(host(), mod_mam_muc_rdbms_arch, [no_writer]), - init_module(host(), mod_mam_muc_rdbms_async_pool_writer, [{flush_interval, 1}]), %% 1ms - init_module(host(), mod_mam_rdbms_prefs, [muc]), - init_module(host(), mod_mam_rdbms_user, [muc, pm]), - init_module(host(), mod_mam_cache_user, [muc]), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc_rdbms_arch, [no_writer]), + init_module(host_type(), mod_mam_muc_rdbms_async_pool_writer, [{flush_interval, 1}]), %% 1ms + init_module(host_type(), mod_mam_rdbms_prefs, [muc]), + init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), + init_module(host_type(), mod_mam_cache_user, [muc]), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_mnesia_muc_cache, C, Config) when C =:= muc_all; C =:= muc_disabled_retraction -> - init_module(host(), mod_mam_muc_rdbms_arch, []), - init_module(host(), mod_mam_mnesia_prefs, [muc]), - init_module(host(), mod_mam_rdbms_user, [muc, pm]), - init_module(host(), mod_mam_muc_cache_user, [muc]), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc_rdbms_arch, []), + init_module(host_type(), mod_mam_mnesia_prefs, [muc]), + init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), + init_module(host_type(), mod_mam_muc_cache_user, [muc]), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_mnesia_muc_cache, _, _Config) -> skip; init_modules(rdbms_mnesia_cache, C, Config) when C =:= muc_all; C =:= muc_disabled_retraction -> - init_module(host(), mod_mam_muc_rdbms_arch, []), - init_module(host(), mod_mam_mnesia_prefs, [muc]), - init_module(host(), mod_mam_rdbms_user, [muc, pm]), - init_module(host(), mod_mam_cache_user, [muc]), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc_rdbms_arch, []), + init_module(host_type(), mod_mam_mnesia_prefs, [muc]), + init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), + init_module(host_type(), mod_mam_cache_user, [muc]), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(BackendType, muc_light, Config) -> @@ -771,96 +815,96 @@ init_modules(BackendType, muc_light, Config) -> case BackendType of cassandra -> ok; elasticsearch -> ok; - _ -> init_module(host(), mod_mam_rdbms_user, [muc, pm]) + _ -> init_module(host_type(), mod_mam_rdbms_user, [muc, pm]) end, Config1; init_modules(rdbms, C, Config) -> - init_module(host(), mod_mam, addin_mam_options(C, Config)), - init_module(host(), mod_mam_rdbms_arch, []), - init_module(host(), mod_mam_rdbms_prefs, [pm]), - init_module(host(), mod_mam_rdbms_user, [pm]), + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_rdbms_arch, []), + init_module(host_type(), mod_mam_rdbms_prefs, [pm]), + init_module(host_type(), mod_mam_rdbms_user, [pm]), Config; init_modules(rdbms_simple, C, Config) -> - init_module(host(), mod_mam, addin_mam_options(C, Config)), - init_module(host(), mod_mam_rdbms_arch, [rdbms_simple_opts()]), - init_module(host(), mod_mam_rdbms_prefs, [pm]), - init_module(host(), mod_mam_rdbms_user, [pm]), + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_rdbms_arch, [rdbms_simple_opts()]), + init_module(host_type(), mod_mam_rdbms_prefs, [pm]), + init_module(host_type(), mod_mam_rdbms_user, [pm]), Config; init_modules(riak_timed_yz_buckets, C, Config) -> - init_module(host(), mod_mam_riak_timed_arch_yz, [pm, muc]), - init_module(host(), mod_mam_mnesia_prefs, [pm, muc, + init_module(host_type(), mod_mam_riak_timed_arch_yz, [pm, muc]), + init_module(host_type(), mod_mam_mnesia_prefs, [pm, muc, {archive_key, mam_archive_key_server_user}]), - init_module(host(), mod_mam, addin_mam_options(C, Config)), - init_module(host(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(cassandra, C, Config) -> - init_module(host(), mod_mam_cassandra_arch, [pm]), - init_module(host(), mod_mam_cassandra_prefs, [pm]), - init_module(host(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_cassandra_arch, [pm]), + init_module(host_type(), mod_mam_cassandra_prefs, [pm]), + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), Config; init_modules(elasticsearch, C, Config) -> - init_module(host(), mod_mam_elasticsearch_arch, [pm]), - init_module(host(), mod_mam_mnesia_prefs, [pm]), - init_module(host(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_elasticsearch_arch, [pm]), + init_module(host_type(), mod_mam_mnesia_prefs, [pm]), + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), Config; init_modules(rdbms_async, C, Config) -> - init_module(host(), mod_mam, addin_mam_options(C, Config)), - init_module(host(), mod_mam_rdbms_arch, [no_writer]), - init_module(host(), mod_mam_rdbms_async_writer, [pm, {flush_interval, 1}]), % 1ms - init_module(host(), mod_mam_rdbms_prefs, [pm]), - init_module(host(), mod_mam_rdbms_user, [pm]), + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_rdbms_arch, [no_writer]), + init_module(host_type(), mod_mam_rdbms_async_writer, [pm, {flush_interval, 1}]), % 1ms + init_module(host_type(), mod_mam_rdbms_prefs, [pm]), + init_module(host_type(), mod_mam_rdbms_user, [pm]), Config; init_modules(rdbms_async_pool, C, Config) -> - init_module(host(), mod_mam, addin_mam_options(C, Config)), - init_module(host(), mod_mam_rdbms_arch, [no_writer]), - init_module(host(), mod_mam_rdbms_async_pool_writer, [pm, {flush_interval, 1}]), %% 1ms - init_module(host(), mod_mam_rdbms_prefs, [pm]), - init_module(host(), mod_mam_rdbms_user, [pm]), + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_rdbms_arch, [no_writer]), + init_module(host_type(), mod_mam_rdbms_async_pool_writer, [pm, {flush_interval, 1}]), %% 1ms + init_module(host_type(), mod_mam_rdbms_prefs, [pm]), + init_module(host_type(), mod_mam_rdbms_user, [pm]), Config; init_modules(rdbms_mnesia, C, Config) -> - init_module(host(), mod_mam, addin_mam_options(C, Config)), - init_module(host(), mod_mam_rdbms_arch, []), - init_module(host(), mod_mam_mnesia_prefs, [pm]), - init_module(host(), mod_mam_rdbms_user, [pm]), + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_rdbms_arch, []), + init_module(host_type(), mod_mam_mnesia_prefs, [pm]), + init_module(host_type(), mod_mam_rdbms_user, [pm]), Config; init_modules(rdbms_cache, C, Config) -> - init_module(host(), mod_mam, addin_mam_options(C, Config)), - init_module(host(), mod_mam_rdbms_arch, []), - init_module(host(), mod_mam_rdbms_prefs, [pm]), - init_module(host(), mod_mam_rdbms_user, [pm]), - init_module(host(), mod_mam_cache_user, [pm]), + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_rdbms_arch, []), + init_module(host_type(), mod_mam_rdbms_prefs, [pm]), + init_module(host_type(), mod_mam_rdbms_user, [pm]), + init_module(host_type(), mod_mam_cache_user, [pm]), Config; init_modules(rdbms_async_cache, C, Config) -> - init_module(host(), mod_mam, addin_mam_options(C, Config)), - init_module(host(), mod_mam_rdbms_arch, [no_writer]), - init_module(host(), mod_mam_rdbms_async_pool_writer, [pm, {flush_interval, 1}]), %% 1ms - init_module(host(), mod_mam_rdbms_prefs, [pm]), - init_module(host(), mod_mam_rdbms_user, [pm]), - init_module(host(), mod_mam_cache_user, [pm]), + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_rdbms_arch, [no_writer]), + init_module(host_type(), mod_mam_rdbms_async_pool_writer, [pm, {flush_interval, 1}]), %% 1ms + init_module(host_type(), mod_mam_rdbms_prefs, [pm]), + init_module(host_type(), mod_mam_rdbms_user, [pm]), + init_module(host_type(), mod_mam_cache_user, [pm]), Config; init_modules(rdbms_mnesia_cache, C, Config) -> - init_module(host(), mod_mam, addin_mam_options(C, Config)), - init_module(host(), mod_mam_rdbms_arch, []), - init_module(host(), mod_mam_mnesia_prefs, [pm]), - init_module(host(), mod_mam_rdbms_user, [pm]), - init_module(host(), mod_mam_cache_user, [pm]), + init_module(host_type(), mod_mam, addin_mam_options(C, Config)), + init_module(host_type(), mod_mam_rdbms_arch, []), + init_module(host_type(), mod_mam_mnesia_prefs, [pm]), + init_module(host_type(), mod_mam_rdbms_user, [pm]), + init_module(host_type(), mod_mam_cache_user, [pm]), Config. rdbms_simple_opts() -> [{db_jid_format, mam_jid_rfc}, {db_message_format, mam_message_xml}]. init_modules_for_muc_light(BackendType, Config) -> - dynamic_modules:start(host(), mod_muc_light, [{host, subhost_pattern(muc_light_host())}]), + dynamic_modules:start(host_type(), mod_muc_light, [{host_type, subhost_pattern(muc_light_host())}]), Config1 = init_modules(BackendType, muc_all, [{muc_domain, "muclight.@HOST@"} | Config]), init_modules(BackendType, pm, [{archive_groupchats, false} | Config1]). end_modules(C, muc_light, Config) -> end_modules(C, generic, Config), - dynamic_modules:stop(host(), mod_muc_light), + dynamic_modules:stop(host_type(), mod_muc_light), Config; end_modules(_, _, Config) -> - [stop_module(host(), M) || M <- mam_modules()], + [stop_module(host_type(), M) || M <- mam_modules()], Config. muc_domain(Config) -> @@ -962,19 +1006,19 @@ init_per_testcase(C=muc_archive_request, Config) -> end, escalus:init_per_testcase(C, start_alice_room(Config2)); init_per_testcase(C=muc_no_elements, Config) -> - rpc_apply(gen_mod, set_module_opts, [host(), mod_mam_muc, [no_stanzaid_element]]), + rpc_apply(gen_mod, set_module_opts, [host_type(), mod_mam_muc, [no_stanzaid_element]]), Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), escalus:init_per_testcase(C, start_alice_room(Config1)); init_per_testcase(C=muc_only_stanzaid, Config) -> - rpc_apply(gen_mod, set_module_opts, [host(), mod_mam_muc, []]), + rpc_apply(gen_mod, set_module_opts, [host_type(), mod_mam_muc, []]), Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), escalus:init_per_testcase(C, start_alice_room(Config1)); init_per_testcase(C=no_elements, Config) -> - rpc_apply(gen_mod, set_module_opts, [host(), mod_mam, [no_stanzaid_element]]), + rpc_apply(gen_mod, set_module_opts, [host_type(), mod_mam, [no_stanzaid_element]]), Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), escalus:init_per_testcase(C, start_alice_room(Config1)); init_per_testcase(C=only_stanzaid, Config) -> - rpc_apply(gen_mod, set_module_opts, [host(), mod_mam, []]), + rpc_apply(gen_mod, set_module_opts, [host_type(), mod_mam, []]), Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), escalus:init_per_testcase(C, start_alice_room(Config1)); init_per_testcase(C=muc_message_with_stanzaid, Config) -> @@ -1033,17 +1077,17 @@ init_per_testcase(C=muc_text_search_request, Config) -> skip_if_cassandra(Config, Init); init_per_testcase(C = muc_light_stored_in_pm_if_allowed_to, Config) -> - OrigVal = rpc(mim(), gen_mod, get_module_opt, [host(), mod_mam, archive_groupchats, false]), - true = rpc(mim(), gen_mod, set_module_opt, [host(), mod_mam, archive_groupchats, true]), + OrigVal = rpc(mim(), gen_mod, get_module_opt, [host_type(), mod_mam, archive_groupchats, false]), + true = rpc(mim(), gen_mod, set_module_opt, [host_type(), mod_mam, archive_groupchats, true]), clean_archives(Config), escalus:init_per_testcase(C, [{archive_groupchats_backup, OrigVal} | Config]); init_per_testcase(C = muc_light_chat_markers_are_archived_if_enabled, ConfigIn) -> Config1 = [backup_module_opts(mod_mam_muc) | ConfigIn], - rpc_apply(gen_mod, set_module_opt, [host(), mod_mam_muc, archive_chat_markers, true]), + rpc_apply(gen_mod, set_module_opt, [host_type(), mod_mam_muc, archive_chat_markers, true]), escalus:init_per_testcase(C, Config1); init_per_testcase(C = muc_light_chat_markers_are_not_archived_if_disabled, ConfigIn) -> Config1 = [backup_module_opts(mod_mam_muc) | ConfigIn], - rpc_apply(gen_mod, set_module_opt, [host(), mod_mam_muc, archive_chat_markers, false]), + rpc_apply(gen_mod, set_module_opt, [host_type(), mod_mam_muc, archive_chat_markers, false]), escalus:init_per_testcase(C, Config1); init_per_testcase(C=archive_chat_markers, Config) -> Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]), @@ -1125,7 +1169,7 @@ end_per_testcase(C=muc_only_stanzaid, Config) -> escalus:end_per_testcase(C, Config); end_per_testcase(C = muc_light_stored_in_pm_if_allowed_to, Config0) -> {value, {_, OrigVal}, Config1} = lists:keytake(archive_groupchats_backup, 1, Config0), - true = rpc(mim(), gen_mod, set_module_opt, [host(), mod_mam, archive_groupchats, OrigVal]), + true = rpc(mim(), gen_mod, set_module_opt, [host_type(), mod_mam, archive_groupchats, OrigVal]), escalus:end_per_testcase(C, Config1); end_per_testcase(C = muc_light_chat_markers_are_archived_if_enabled, Config) -> restore_module_opts(mod_mam_muc, Config), @@ -1317,6 +1361,21 @@ simple_archive_request(Config) -> end, escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). +simple_archive_request_for_the_receiver(Config) -> + P = ?config(props, Config), + F = fun(Alice, Bob) -> + escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), + BobMsg = escalus:wait_for_stanza(Bob), + escalus:assert(is_message, BobMsg), + mam_helper:wait_for_archive_size(Bob, 1), + escalus:send(Bob, stanza_archive_request(P, <<"q1">>)), + Res = wait_archive_respond(Bob), + assert_respond_size(1, Res), + assert_respond_query_id(P, <<"q1">>, parse_result_iq(Res)), + ok + end, + escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F). + text_search_is_not_available(Config) -> P = ?config(props, Config), F = fun(Alice) -> @@ -2979,7 +3038,7 @@ metric_incremented_on_archive_request(ConfigIn) -> assert_respond_query_id(P, <<"metric_q1">>, parse_result_iq(Res)), ok end, - MongooseMetrics = [{[host(), backends, mod_mam, lookup], changed}], + MongooseMetrics = [{[host_type(), backends, mod_mam, lookup], changed}], Config = [{mongoose_metrics, MongooseMetrics} | ConfigIn], escalus_fresh:story(Config, [{alice, 1}], F). diff --git a/big_tests/tests/mam_helper.erl b/big_tests/tests/mam_helper.erl index e13643882d0..c5328bd7b14 100644 --- a/big_tests/tests/mam_helper.erl +++ b/big_tests/tests/mam_helper.erl @@ -102,6 +102,7 @@ run_set_and_get_prefs_case/4, muc_light_host/0, host/0, + host_type/0, wait_for_archive_size/2, verify_archived_muc_light_aff_msg/3, wait_for_room_archive_size/3, @@ -898,26 +899,17 @@ put_msg({{MsgIdOwner, MsgIdRemote}, {_FromBin, FromJID, FromArcID}, {_ToBin, ToJID, ToArcID}, {_, Source, _}, Packet}) -> - Host = ct:get_config({hosts, mim, domain}), - archive_message([Host, #{message_id => MsgIdOwner, - archive_id => FromArcID, - local_jid => FromJID, - remote_jid => ToJID, - source_jid => Source, - origin_id => none, - direction => outgoing, - packet => Packet}]), - archive_message([Host, #{message_id => MsgIdRemote, - archive_id => ToArcID, - local_jid => ToJID, - remote_jid => FromJID, - source_jid => Source, - origin_id => none, - direction => incoming, - packet => Packet}]). - -archive_message(Args) -> - rpc_apply(mod_mam, archive_message, Args). + Map1 = #{message_id => MsgIdOwner, archive_id => FromArcID, + local_jid => FromJID, remote_jid => ToJID, source_jid => Source, + origin_id => none, direction => outgoing, packet => Packet}, + Map2 = #{message_id => MsgIdRemote, archive_id => ToArcID, + local_jid => ToJID, remote_jid => FromJID, source_jid => Source, + origin_id => none, direction => incoming, packet => Packet}, + archive_message(Map1), + archive_message(Map2). + +archive_message(#{} = Map) -> + ok = rpc_apply(mod_mam, archive_message_from_ct, [Map]). muc_bootstrap_archive(Config) -> Room = ?config(room, Config), @@ -1235,6 +1227,9 @@ muc_light_host() -> host() -> ct:get_config({hosts, mim, domain}). +host_type() -> + ct:get_config({hosts, mim, host_type}). + room_name(Config) -> AliceName = escalus_users:get_username(Config, alice), StoryPidBin = to_nodename(list_to_binary(pid_to_list(self()))), diff --git a/src/mam/mod_mam.erl b/src/mam/mod_mam.erl index 325149ef5a2..41db1be25d4 100644 --- a/src/mam/mod_mam.erl +++ b/src/mam/mod_mam.erl @@ -45,7 +45,7 @@ -export([start/2, stop/1]). %% ejabberd handlers --export([process_mam_iq/4, +-export([process_mam_iq/5, user_send_packet/4, remove_user/3, filter_packet/1, @@ -56,7 +56,7 @@ -export([get_personal_data/2]). %%private --export([archive_message/2]). +-export([archive_message_from_ct/1]). -export([lookup_messages/2]). -export([archive_id_int/2]). @@ -92,7 +92,7 @@ %% ejabberd -import(mod_mam_utils, - [is_jid_in_user_roster/2]). + [is_jid_in_user_roster/3]). -include("mongoose.hrl"). @@ -198,6 +198,7 @@ archive_id(Server, User) -spec start(HostType :: host_type(), Opts :: list()) -> any(). start(HostType, Opts) -> ?LOG_INFO(#{what => mam_starting}), + ?LOG_ERROR(#{what => mam_starting, host_type => HostType}), ensure_metrics(HostType), ejabberd_hooks:add(hooks(HostType)), add_id_handlers(HostType, Opts), @@ -207,6 +208,7 @@ start(HostType, Opts) -> -spec stop(HostType :: host_type()) -> any(). stop(HostType) -> ?LOG_INFO(#{what => mam_stopping}), + ?LOG_ERROR(#{what => mam_stopping, host_type => HostType}), unregister_features(HostType), ejabberd_hooks:delete(hooks(HostType)), remove_iq_handlers(HostType), @@ -220,15 +222,16 @@ stop(HostType) -> %% to the user on their bare JID (i.e. `From.luser'), %% while a MUC service might allow MAM queries to be sent to the room's bare JID %% (i.e `To.luser'). --spec process_mam_iq(From :: jid:jid(), To :: jid:jid(), Acc :: mongoose_acc:t(), - IQ :: jlib:iq()) -> {mongoose_acc:t(), jlib:iq() | ignore}. -process_mam_iq(From, To, Acc, IQ) -> +-spec process_mam_iq(Acc :: mongoose_acc:t(), + From :: jid:jid(), To :: jid:jid(), IQ :: jlib:iq(), + _Extra) -> {mongoose_acc:t(), jlib:iq() | ignore}. +process_mam_iq(Acc, From, To, IQ, _Extra) -> HostType = mongoose_acc:host_type(Acc), mod_mam_utils:maybe_log_deprecation(IQ), Action = mam_iq:action(IQ), case is_action_allowed(HostType, Action, From, To) of true -> - case mod_mam_utils:wait_shaper(HostType, Action, From) of + case mod_mam_utils:wait_shaper(HostType, To#jid.lserver, Action, From) of ok -> handle_error_iq(HostType, Acc, To, Action, handle_mam_iq(Action, From, To, IQ, Acc)); @@ -335,7 +338,7 @@ acc_to_host_type(Acc) -> Action :: mam_iq:action(), From :: jid:jid(), To :: jid:jid()) -> boolean(). is_action_allowed(HostType, Action, From, To) -> - case acl:match_rule(HostType, Action, From, default) of + case acl:match_rule_for_host_type(HostType, To#jid.lserver, Action, From, default) of allow -> true; deny -> false; default -> is_action_allowed_by_default(Action, From, To) @@ -527,7 +530,7 @@ is_interesting(HostType, LocJID, RemJID, ArcID) -> case get_behaviour(HostType, ArcID, LocJID, RemJID) of always -> true; never -> false; - roster -> is_jid_in_user_roster(LocJID, RemJID) + roster -> is_jid_in_user_roster(HostType, LocJID, RemJID) end. %% ---------------------------------------------------------------------- @@ -591,6 +594,10 @@ lookup_messages_without_policy_violation_check( R end. +archive_message_from_ct(Params = #{local_jid := JID}) -> + HostType = jid_to_host_type(JID), + archive_message(HostType, Params). + -spec archive_message(host_type(), mod_mam:archive_message_params()) -> ok | {error, timeout}. archive_message(HostType, Params) -> @@ -641,7 +648,8 @@ return_max_delay_reached_error_iq(IQ) -> return_error_iq(IQ, {Reason, {stacktrace, _Stacktrace}}) -> return_error_iq(IQ, Reason); return_error_iq(IQ, timeout) -> - {error, timeout, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:service_unavailable()]}}; + E = mongoose_xmpp_errors:service_unavailable(<<"en">>, <<"Timeout">>), + {error, timeout, IQ#iq{type = error, sub_el = [E]}}; return_error_iq(IQ, item_not_found) -> {error, item_not_found, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:item_not_found()]}}; return_error_iq(IQ, not_implemented) -> @@ -701,16 +709,23 @@ register_features(HostType) -> ok. add_id_handlers(HostType, Opts) -> + Component = ejabberd_sm, %% `parallel' is the only one recommended here. - IQDisc = gen_mod:get_opt(iqdisc, Opts, parallel), %% Type - gen_iq_handler:add_iq_handler(ejabberd_sm, HostType, ?NS_MAM_04, - ?MODULE, process_mam_iq, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_sm, HostType, ?NS_MAM_06, - ?MODULE, process_mam_iq, IQDisc). + ExecutionType = gen_mod:get_opt(iqdisc, Opts, parallel), + IQHandlerFn = fun ?MODULE:process_mam_iq/5, + Extra = #{}, + IQHandler = mongoose_iq_handler:new(IQHandlerFn, Extra, ExecutionType), + [gen_iq_handler:add_iq_handler_for_domain(HostType, Namespace, + Component, IQHandlerFn, + Extra, ExecutionType) + || Namespace <- [?NS_MAM_04, ?NS_MAM_06]], + ok. remove_iq_handlers(HostType) -> - gen_iq_handler:remove_iq_handler(ejabberd_sm, HostType, ?NS_MAM_04), - gen_iq_handler:remove_iq_handler(ejabberd_sm, HostType, ?NS_MAM_06). + Component = ejabberd_sm, + [gen_iq_handler:remove_iq_handler_for_domain(HostType, Namespace, Component) + || Namespace <- [?NS_MAM_04, ?NS_MAM_06]], + ok. ensure_metrics(HostType) -> mongoose_metrics:ensure_metric(HostType, [backends, ?MODULE, lookup], histogram), diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index 732b6d1086a..91fb163c23a 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -231,9 +231,9 @@ room_process_mam_iq(From, To, Acc, IQ) -> mod_mam_utils:maybe_log_deprecation(IQ), Action = mam_iq:action(IQ), MucAction = action_to_muc_action(Action), - case is_action_allowed(HostType, Action, MucAction, From, To) of + case is_action_allowed(HostType, To#jid.lserver, Action, MucAction, From, To) of true -> - case mod_mam_utils:wait_shaper(HostType, MucAction, From) of + case mod_mam_utils:wait_shaper(HostType, To#jid.lserver, MucAction, From) of ok -> handle_error_iq(Acc, HostType, To, Action, handle_mam_iq(HostType, Action, From, To, IQ)); @@ -256,10 +256,10 @@ forget_room(Acc, _HostType, MucServer, RoomName) -> %% ---------------------------------------------------------------------- %% Internal functions --spec is_action_allowed(host_type(), mam_iq:action(), muc_action(), +-spec is_action_allowed(host_type(), jid:lserver(), mam_iq:action(), muc_action(), jid:jid(), jid:jid()) -> boolean(). -is_action_allowed(HostType, Action, MucAction, From, To) -> - case acl:match_rule(HostType, MucAction, From, default) of +is_action_allowed(HostType, Domain, Action, MucAction, From, To) -> + case acl:match_rule_for_host_type(HostType, Domain, MucAction, From, default) of allow -> true; deny -> false; default -> is_room_action_allowed_by_default(HostType, Action, From, To) diff --git a/src/mam/mod_mam_rdbms_arch.erl b/src/mam/mod_mam_rdbms_arch.erl index 2565b3a32e2..637113db766 100644 --- a/src/mam/mod_mam_rdbms_arch.erl +++ b/src/mam/mod_mam_rdbms_arch.erl @@ -238,6 +238,7 @@ archive_size(Size, Host, ArcID, ArcJID) when is_integer(Size) -> -spec archive_message(_Result, jid:server(), mod_mam:archive_message_params()) -> ok. archive_message(_Result, Host, Params = #{local_jid := ArcJID}) -> try + assert_archive_id_provided(Params), Env = env_vars(Host, ArcJID), do_archive_message(Host, Params, Env), retract_message(Host, Params, Env), @@ -348,3 +349,6 @@ lookup_messages(_Result, Host, Params = #{owner_jid := ArcJID}) -> lookup_query(QueryType, Env, Filters, Order, OffsetLimit) -> mam_lookup_sql:lookup_query(QueryType, Env, Filters, Order, OffsetLimit). + +assert_archive_id_provided(#{archive_id := ArcID}) when is_integer(ArcID) -> + ok. diff --git a/src/mam/mod_mam_utils.erl b/src/mam/mod_mam_utils.erl index cb10f6956e5..6bb7938bfe4 100644 --- a/src/mam/mod_mam_utils.erl +++ b/src/mam/mod_mam_utils.erl @@ -81,13 +81,13 @@ calculate_msg_id_borders/4, maybe_encode_compact_uuid/2, is_complete_result_page/4, - wait_shaper/3, + wait_shaper/4, check_for_item_not_found/3]). %% Ejabberd -export([send_message/4, maybe_set_client_xmlns/2, - is_jid_in_user_roster/2]). + is_jid_in_user_roster/3]). %% Shared logic -export([check_result_for_policy_violation/2]). @@ -1048,12 +1048,12 @@ action_to_shaper_name(Action) -> action_to_global_shaper_name(Action) -> list_to_atom(atom_to_list(Action) ++ "_global_shaper"). --spec wait_shaper(jid:server(), mam_iq:action(), jid:jid()) -> +-spec wait_shaper(mongooseim:host_type(), jid:server(), mam_iq:action(), jid:jid()) -> 'ok' | {'error', 'max_delay_reached'}. -wait_shaper(Host, Action, From) -> - case shaper_srv:wait(Host, action_to_shaper_name(Action), From, 1) of +wait_shaper(HostType, Host, Action, From) -> + case shaper_srv:wait(HostType, Host, action_to_shaper_name(Action), From, 1) of ok -> - shaper_srv:wait(Host, action_to_global_shaper_name(Action), global, 1); + shaper_srv:wait(HostType, Host, action_to_global_shaper_name(Action), global, 1); Err -> Err end. @@ -1065,11 +1065,10 @@ wait_shaper(Host, Action, From) -> send_message(_Row, From, To, Mess) -> ejabberd_sm:route(From, To, Mess). --spec is_jid_in_user_roster(jid:jid(), jid:jid()) -> boolean(). -is_jid_in_user_roster(#jid{lserver = LServer} = ToJID, - #jid{} = RemJID) -> +-spec is_jid_in_user_roster(mongooseim:host_type(), jid:jid(), jid:jid()) -> boolean(). +is_jid_in_user_roster(HostType, #jid{} = ToJID, #jid{} = RemJID) -> RemBareJID = jid:to_bare(RemJID), - {Subscription, _G} = mongoose_hooks:roster_get_jid_info(LServer, ToJID, RemBareJID), + {Subscription, _G} = mongoose_hooks:roster_get_jid_info(HostType, ToJID, RemBareJID), Subscription == from orelse Subscription == both. %% @doc Returns a UUIDv4 canonical form binary. diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index 85ea1e15d82..1aca085f78c 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -918,24 +918,18 @@ can_access_room(HostType, Room, User) -> %% MAM related hooks -%%% @doc The `mam_archive_id' hook is called to determine the id of an archive -%%% for a particular user or entity. -%%% The hook handler is expected to accept the following arguments: -%%% * Acc with an initial value of `undefined', -%%% * Host as passed in the `HooksServer' variable, -%%% * OwnerJID, -%%% -%%% and to return an integer value corresponding to the given owner's archive. +%%% @doc The `mam_archive_id' hook is called to determine +%%% the integer id of an archive for a particular user or entity. %%% %%% If a MAM backend doesn't support or doesn't require archive IDs, %%% `undefined' may be returned. --spec mam_archive_id(HookServer, OwnerJID) -> Result when - HookServer :: jid:lserver(), +-spec mam_archive_id(HostType, OwnerJID) -> Result when + HostType :: mongooseim:host_type(), OwnerJID :: jid:jid(), Result :: undefined | mod_mam:archive_id(). -mam_archive_id(HookServer, OwnerJID) -> - ejabberd_hooks:run_for_host_type(mam_archive_id, HookServer, undefined, - [HookServer, OwnerJID]). +mam_archive_id(HostType, OwnerJID) -> + ejabberd_hooks:run_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 diff --git a/src/shaper_srv.erl b/src/shaper_srv.erl index 0b4a417023f..b9387329eb0 100644 --- a/src/shaper_srv.erl +++ b/src/shaper_srv.erl @@ -15,7 +15,7 @@ -export([start_link/1, child_specs/0, - wait/4, + wait/5, reset_shapers/1, reset_all_shapers/1]). @@ -48,7 +48,6 @@ child_specs() -> [child_spec(ProcName) || ProcName <- worker_names(<<>>)]. - -spec child_spec(atom()) -> supervisor:child_spec(). child_spec(ProcName) -> {ProcName, @@ -58,51 +57,46 @@ child_spec(ProcName) -> worker, [?MODULE]}. - -spec start_link(atom()) -> 'ignore' | {'error', _} | {'ok', pid()}. start_link(ProcName) -> gen_server:start_link({local, ProcName}, ?MODULE, [], []). - -spec worker_prefix() -> string(). worker_prefix() -> "ejabberd_shaper_". -worker_count(_Host) -> +worker_count(_HostType) -> 10. - --spec worker_names(jid:server()) -> [atom()]. -worker_names(Host) -> - [worker_name(Host, N) || N <- lists:seq(0, worker_count(Host) - 1)]. - +-spec worker_names(mongooseim:host_type()) -> [atom()]. +worker_names(HostType) -> + [worker_name(HostType, N) || N <- lists:seq(0, worker_count(HostType) - 1)]. -spec worker_name(jid:server(), integer()) -> atom(). worker_name(_Host, N) -> list_to_atom(worker_prefix() ++ integer_to_list(N)). +-spec select_worker(mongooseim:host_type(), _) -> atom(). +select_worker(HostType, Tag) -> + N = worker_number(HostType, Tag), + worker_name(HostType, N). --spec select_worker(jid:server(), _) -> atom(). -select_worker(Host, Tag) -> - N = worker_number(Host, Tag), - worker_name(Host, N). - - --spec worker_number(jid:server(), _) -> non_neg_integer(). -worker_number(Host, Tag) -> - erlang:phash2(Tag, worker_count(Host)). - +-spec worker_number(mongooseim:host_type(), _) -> non_neg_integer(). +worker_number(HostType, Tag) -> + erlang:phash2(Tag, worker_count(HostType)). %% @doc Shapes the caller from executing the action. --spec wait(_Host :: jid:server(), _Action :: atom(), - _FromJID :: jid:jid() | global, _Size :: integer() +-spec wait(HostType :: mongooseim:host_type(), + Domain :: jid:server(), Action :: atom(), + FromJID :: jid:jid() | global, Size :: integer() ) -> ok | {error, max_delay_reached}. -wait(Host, Action, FromJID, Size) -> - gen_server:call(select_worker(Host, FromJID), {wait, Host, Action, FromJID, Size}). +wait(HostType, Domain, Action, FromJID, Size) -> + gen_server:call(select_worker(HostType, FromJID), + {wait, HostType, Domain, Action, FromJID, Size}). %% @doc Ask all shaper servers to forget current shapers and read settings again -reset_all_shapers(Host) -> - [reset_shapers(ProcName) || ProcName <- worker_names(Host)]. +reset_all_shapers(HostType) -> + [reset_shapers(ProcName) || ProcName <- worker_names(HostType)]. %% @doc Ask server to forget its shapers reset_shapers(ProcName) -> @@ -122,10 +116,10 @@ init(Args) -> timer:send_interval(timer:seconds(GCInt), delete_old_shapers), {ok, State}. -handle_call({wait, Host, Action, FromJID, Size}, +handle_call({wait, HostType, Domain, Action, FromJID, Size}, From, State=#state{max_delay=MaxDelayMs}) -> - Key = new_key(Host, Action, FromJID), - Shaper = find_or_create_shaper(Key, State), + Key = new_key(Domain, Action, FromJID), + Shaper = find_or_create_shaper(HostType, Key, State), State1 = update_access_time(Key, erlang:system_time(), State), case shaper:update(Shaper, Size) of {UpdatedShaper, 0} -> @@ -159,33 +153,29 @@ code_change(_OldVsn, State, _Extra) -> -type key() :: {global | jid:server(), atom(), jid:jid()}. -spec new_key(jid:server() | global, atom(), jid:jid()) -> key(). -new_key(Host, Action, FromJID) -> - {Host, Action, FromJID}. - +new_key(Domain, Action, FromJID) -> + {Domain, Action, FromJID}. --spec find_or_create_shaper(key(), state()) -> shaper:shaper(). -find_or_create_shaper(Key, #state{shapers=Shapers}) -> +-spec find_or_create_shaper(mongooseim:host_type(), key(), state()) -> + shaper:shaper(). +find_or_create_shaper(HostType, Key, #state{shapers=Shapers}) -> case dict:find(Key, Shapers) of {ok, Shaper} -> Shaper; - error -> create_shaper(Key) + error -> create_shaper(HostType, Key) end. - -spec update_access_time(key(), _, state()) -> state(). update_access_time(Key, Now, State=#state{a_times=Times}) -> State#state{a_times=dict:store(Key, Now, Times)}. - -spec save_shaper(key(), shaper:shaper(), state()) -> state(). save_shaper(Key, Shaper, State=#state{shapers=Shapers}) -> State#state{shapers=dict:store(Key, Shaper, Shapers)}. - -spec init_dicts(state()) -> state(). init_dicts(State) -> State#state{shapers=dict:new(), a_times=dict:new()}. - -spec delete_old_shapers(state()) -> state(). delete_old_shapers(State=#state{shapers=Shapers, a_times=Times, ttl=TTL}) -> Min = subtract_seconds(TTL), @@ -197,27 +187,24 @@ delete_old_shapers(State=#state{shapers=Shapers, a_times=Times, ttl=TTL}) -> update_access_time(Key, ATime, save_shaper(Key, Shaper, Acc)) end, init_dicts(State), Times). +-spec create_shaper(mongooseim:host_type(), key()) -> + 'none' | {'maxrate', _, 0, non_neg_integer()}. +create_shaper(HostType, Key) -> + shaper:new(request_shaper_name(HostType, Key)). --spec create_shaper(key()) -> 'none' | {'maxrate', _, 0, non_neg_integer()}. -create_shaper(Key) -> - shaper:new(request_shaper_name(Key)). - - --spec request_shaper_name(key()) -> atom(). -request_shaper_name({Host, Action, FromJID}) -> - get_shaper_name(Host, Action, FromJID, default_shaper()). - +-spec request_shaper_name(mongooseim:host_type(), key()) -> atom(). +request_shaper_name(HostType, {Domain, Action, FromJID}) -> + get_shaper_name(HostType, Domain, Action, FromJID, default_shaper()). default_shaper() -> none. - --spec get_shaper_name('global' | jid:server(), - Action :: atom(), - jid:jid(), +-spec get_shaper_name(HostType :: mongooseim:host_type(), + Domain :: 'global' | jid:server(), + Action :: atom(), jid:jid(), Default :: 'none') -> 'allow' | 'none'. -get_shaper_name(Host, Action, FromJID, Default) -> - case acl:match_rule(Host, Action, FromJID) of +get_shaper_name(HostType, Domain, Action, FromJID, Default) -> + case acl:match_rule_for_host_type(HostType, Domain, Action, FromJID) of deny -> Default; Value -> Value end. From f9ae79c7cc6027ec1e92342476cfb086601cc558 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 24 May 2021 17:34:37 +0200 Subject: [PATCH 40/73] muc_light_SUITE passes --- big_tests/dynamic_domains.config | 2 ++ big_tests/test.config | 2 ++ big_tests/tests/muc_light_SUITE.erl | 41 +++++++++++++++------------- big_tests/tests/muc_light_helper.erl | 10 +++++-- 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/big_tests/dynamic_domains.config b/big_tests/dynamic_domains.config index 25558234c50..57a078934df 100644 --- a/big_tests/dynamic_domains.config +++ b/big_tests/dynamic_domains.config @@ -9,6 +9,8 @@ {dynamic_domains, [<<"domain.example.com">>, <<"domain.example.org">>]}, {muc_service, <<"groupchats.domain.example.com">>}, {muc_service_pattern, <<"groupchats.@HOST@">>}, + {muc_light_service, <<"muclight2.domain.example.com">>}, + {muc_light_service_pattern, <<"muclight2.@HOST@">>}, {vars, "mim1"}, {cluster, mim}, {s2s_port, 5269}, diff --git a/big_tests/test.config b/big_tests/test.config index a949a1997c8..7dd5ef5417b 100644 --- a/big_tests/test.config +++ b/big_tests/test.config @@ -26,6 +26,8 @@ {secondary_host_type, <<"localhost.bis">>}, {muc_service, <<"muc.localhost">>}, {muc_service_pattern, <<"muc.@HOST@">>}, + {muc_light_service, <<"muclight.localhost">>}, + {muc_light_service_pattern, <<"muclight.@HOST@">>}, {s2s_port, 5269}, {incoming_s2s_port, 5269}, {metrics_rest_port, 5288}, diff --git a/big_tests/tests/muc_light_SUITE.erl b/big_tests/tests/muc_light_SUITE.erl index 0c4f4448b29..d81a4cd6eba 100644 --- a/big_tests/tests/muc_light_SUITE.erl +++ b/big_tests/tests/muc_light_SUITE.erl @@ -98,7 +98,7 @@ -define(ROOM, <<"testroom">>). -define(ROOM2, <<"testroom2">>). --define(MUCHOST, <<"muclight.localhost">>). +-define(MUCHOST, (muc_light_helper:muc_host())). -define(CHECK_FUN, fun mod_muc_light_room:participant_limit_check/2). -define(BACKEND, mod_muc_light_db_backend). @@ -142,10 +142,10 @@ groups() -> disco_rooms_created_page_infinity, disco_rooms_empty_page_infinity, disco_rooms_empty_page_1, + unauthorized_stanza, rooms_in_rosters, rooms_in_rosters_doesnt_break_disco_info, - no_roomname_in_schema_doesnt_break_disco_and_roster, - unauthorized_stanza + no_roomname_in_schema_doesnt_break_disco_and_roster ]}, {occupant, [sequence], [ send_message, @@ -199,19 +199,19 @@ suite() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - Host = ct:get_config({hosts, mim, domain}), - {ok, _} = dynamic_modules:start(Host, mod_muc_light, - [{host, subhost_pattern(?MUCHOST)}, + HostType = host_type(), + {ok, _} = dynamic_modules:start(HostType, mod_muc_light, + [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}, {backend, mongoose_helper:mnesia_or_rdbms_backend()}, {rooms_in_rosters, true}]), Config1 = escalus:init_per_suite(Config), escalus:create_users(Config1, escalus:get_users([alice, bob, kate, mike])). end_per_suite(Config) -> - Host = ct:get_config({hosts, mim, domain}), + HostType = host_type(), muc_light_helper:clear_db(), Config1 = escalus:delete_users(Config, escalus:get_users([alice, bob, kate, mike])), - dynamic_modules:stop(Host, mod_muc_light), + dynamic_modules:stop(HostType, mod_muc_light), escalus:end_per_suite(Config1). init_per_group(_GroupName, Config) -> @@ -236,7 +236,7 @@ init_per_testcase(removing_users_from_server_triggers_room_destruction = CN, Con init_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; CaseName =:= disco_info_with_mam -> set_default_mod_config(), - dynamic_modules:start(domain(), mod_mam_muc, + dynamic_modules:start(host_type(), mod_mam_muc, [{backend, rdbms}, {host, subhost_pattern(?MUCHOST)}]), escalus:init_per_testcase(CaseName, Config); @@ -261,7 +261,7 @@ init_per_testcase(CaseName, Config) -> end_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; CaseName =:= disco_info_with_mam -> muc_light_helper:clear_db(), - dynamic_modules:stop(domain(), mod_mam_muc), + dynamic_modules:stop(host_type(), mod_mam_muc), escalus:end_per_testcase(CaseName, Config); end_per_testcase(CaseName, Config) -> case lists:member(CaseName, ?CUSTOM_CONFIG_CASES) of @@ -289,8 +289,7 @@ disco_service(Config) -> escalus:send(Alice, escalus_stanza:service_discovery(Server)), Stanza = escalus:wait_for_stanza(Alice), escalus:assert(has_service, [?MUCHOST], Stanza), - escalus:assert(is_stanza_from, - [ct:get_config({hosts, mim, domain})], Stanza) + escalus:assert(is_stanza_from, [domain()], Stanza) end). disco_features(Config) -> @@ -364,7 +363,8 @@ disco_rooms_created_page_infinity(Config) -> disco_rooms(Config) -> escalus:story(Config, [{alice, 1}], fun(Alice) -> - {ok, {?ROOM2, ?MUCHOST}} = create_room(?ROOM2, ?MUCHOST, kate, [], Config, ver(0)), + MucHost = ?MUCHOST, + {ok, {?ROOM2, MucHost}} = create_room(?ROOM2, ?MUCHOST, kate, [], Config, ver(0)), %% we should get 1 room, Alice is not in the second one [Item] = get_disco_rooms(Alice), ProperJID = room_bin_jid(?ROOM), @@ -467,7 +467,8 @@ no_roomname_in_schema_doesnt_break_disco_and_roster(Config) -> unauthorized_stanza(Config) -> escalus:story(Config, [{alice, 1}, {kate, 1}], fun(Alice, Kate) -> - {ok, {?ROOM2, ?MUCHOST}} = create_room(?ROOM2, ?MUCHOST, kate, [], Config, ver(0)), + MucHost = ?MUCHOST, + {ok, {?ROOM2, MucHost}} = create_room(?ROOM2, ?MUCHOST, kate, [], Config, ver(0)), MsgStanza = escalus_stanza:groupchat_to(room_bin_jid(?ROOM2), <<"malicious">>), escalus:send(Alice, MsgStanza), escalus:assert(is_error, [<<"cancel">>, <<"item-not-found">>], @@ -666,8 +667,9 @@ destroy_room_get_disco_items_empty(Config) -> destroy_room_get_disco_items_one_left(Config) -> escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> - {ok, {?ROOM2, ?MUCHOST}} = create_room(?ROOM2, ?MUCHOST, kate, - [bob, alice], Config, ver(0)), + MucHost = ?MUCHOST, + {ok, {?ROOM2, MucHost}} = create_room(?ROOM2, ?MUCHOST, kate, + [bob, alice], Config, ver(0)), ProperJID = room_bin_jid(?ROOM2), %% alie destroy her room escalus:send(Alice, stanza_destroy_room(?ROOM)), @@ -766,9 +768,10 @@ adding_wrongly_named_user_triggers_infinite_loop(Config)-> escalus:send(Alice, generate_buggy_aff_staza(BuggyRoomName, Username)), timer:sleep(300), AUsername = lbin(escalus_users:get_username(Config, alice)), - Host = lbin(escalus_users:get_host(Config, alice)), + Host = lbin(escalus_users:get_server(Config, alice)), Resource = <<"res1">>, JID = mongoose_helper:make_jid(AUsername, Host, Resource), + ct:log("JID ~p", [JID]), SessionRecPid = rpc(ejabberd_sm, get_session, [JID]), {session, {_,Pid}, {AUsername, Host, Resource}, _, _, _} = SessionRecPid, %% maybe throws exception @@ -823,7 +826,6 @@ manage_blocklist(Config) -> QueryEl1 = exml_query:subelement(GetResult1, <<"query">>), verify_blocklist(QueryEl1, []), Domain = domain(), - BlocklistChange1 = [{user, deny, <<"user@", Domain/binary>>}, {room, deny, room_bin_jid(?ROOM)}], escalus:send(Alice, stanza_blocking_set(BlocklistChange1)), @@ -899,7 +901,6 @@ blocking_disabled(Config) -> escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], escalus:wait_for_stanza(Alice)), Domain = domain(), - BlocklistChange1 = [{user, deny, <<"user@", Domain/binary>>}, {room, deny, room_bin_jid(?ROOM)}], escalus:send(Alice, stanza_blocking_set(BlocklistChange1)), @@ -1140,3 +1141,5 @@ set_custom_config(UserDefSchema) -> domain() -> ct:get_config({hosts, mim, domain}). +host_type() -> + ct:get_config({hosts, mim, host_type}). diff --git a/big_tests/tests/muc_light_helper.erl b/big_tests/tests/muc_light_helper.erl index deea06a3a5d..6209d5ebdfd 100644 --- a/big_tests/tests/muc_light_helper.erl +++ b/big_tests/tests/muc_light_helper.erl @@ -22,8 +22,10 @@ room_bin_jid(Room) -> <>. muc_host() -> - Host = ct:get_config({hosts, mim, domain}), - <<"muclight.", Host/binary>>. + ct:get_config({hosts, mim, muc_light_service}). + +muc_host_pattern() -> + ct:get_config({hosts, mim, muc_light_service_pattern}). create_room(RoomU, MUCHost, Owner, Members, Config, Version) -> DefaultConfig = default_config(), @@ -36,7 +38,9 @@ create_room(RoomU, MUCHost, Owner, Members, Config, Version) -> [RoomUS, DefaultConfig, AffUsersSort, Version]). -spec default_config() -> list(). -default_config() -> rpc(mim(), mod_muc_light, default_config, [muc_host()]). +default_config() -> assert_list(rpc(mim(), mod_muc_light, default_config, [muc_host()])). + +assert_list(X) when is_list(X) -> X. -spec ns_muc_light_affiliations() -> binary(). ns_muc_light_affiliations() -> From 122c615f95cc5ef756a5f96682ffa6310a78a1ad Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 11:37:46 +0200 Subject: [PATCH 41/73] Passing muc_light_SUITE and muc_light_legacy_SUITE Pass Acc into meta encoder callbacks --- big_tests/dynamic_domains.spec | 2 ++ big_tests/tests/muc_light_legacy_SUITE.erl | 37 +++++++++++++------- src/muc_light/mod_muc_light.erl | 7 ++-- src/muc_light/mod_muc_light_codec.erl | 2 ++ src/muc_light/mod_muc_light_codec_legacy.erl | 34 +++++++++--------- 5 files changed, 51 insertions(+), 31 deletions(-) diff --git a/big_tests/dynamic_domains.spec b/big_tests/dynamic_domains.spec index 77c7d89cffa..5d4dc655b7a 100644 --- a/big_tests/dynamic_domains.spec +++ b/big_tests/dynamic_domains.spec @@ -12,6 +12,8 @@ {suites, "tests", acc_e2e_SUITE}. {suites, "tests", domain_isolation_SUITE}. {suites, "tests", muc_SUITE}. +{suites, "tests", muc_light_SUITE}. +{suites, "tests", muc_light_legacy_SUITE}. {suites, "tests", mam_SUITE}. {suites, "tests", carboncopy_SUITE}. diff --git a/big_tests/tests/muc_light_legacy_SUITE.erl b/big_tests/tests/muc_light_legacy_SUITE.erl index af22111be9c..ca64a20b2ee 100644 --- a/big_tests/tests/muc_light_legacy_SUITE.erl +++ b/big_tests/tests/muc_light_legacy_SUITE.erl @@ -65,7 +65,7 @@ -define(NS_MUC_LIGHT, <<"urn:xmpp:muclight:0">>). -define(NS_MUC_ROOMCONFIG, <<"http://jabber.org/protocol/muc#roomconfig">>). --define(MUCHOST, (muc_helper:muc_host())). +-define(MUCHOST, (muc_light_helper:muc_host())). -define(CHECK_FUN, fun mod_muc_light_room:participant_limit_check/2). -define(BACKEND, mod_muc_light_db_backend). @@ -141,9 +141,9 @@ suite() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - Host = domain(), - dynamic_modules:start(Host, mod_muc_light, - [{host, subhost_pattern(?MUCHOST)}, + HostType = host_type(), + dynamic_modules:start(HostType, mod_muc_light, + [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}, {backend, mongoose_helper:mnesia_or_rdbms_backend()}, {legacy_mode, true}]), Config1 = escalus:init_per_suite(Config), @@ -152,7 +152,7 @@ init_per_suite(Config) -> end_per_suite(Config) -> clear_db(), Config1 = escalus:delete_users(Config, escalus:get_users([alice, bob, kate, mike])), - dynamic_modules:stop(domain(), mod_muc_light), + dynamic_modules:stop(host_type(), mod_muc_light), escalus:end_per_suite(Config1). init_per_group(_GroupName, Config) -> @@ -176,7 +176,7 @@ init_per_testcase(create_existing_room_deny = N, Config) -> init_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; CaseName =:= disco_info_with_mam -> set_default_mod_config(), - dynamic_modules:start(domain(), mod_mam_muc, + dynamic_modules:start(host_type(), mod_mam_muc, [{backend, rdbms}, {host, subhost_pattern(?MUCHOST)}]), escalus:init_per_testcase(CaseName, Config); @@ -188,7 +188,7 @@ init_per_testcase(CaseName, Config) -> end_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; CaseName =:= disco_info_with_mam -> clear_db(), - dynamic_modules:stop(domain(), mod_mam_muc), + dynamic_modules:stop(host_type(), mod_mam_muc), escalus:end_per_testcase(CaseName, Config); end_per_testcase(CaseName, Config) -> clear_db(), @@ -494,7 +494,6 @@ manage_blocklist(Config) -> QueryEl1 = exml_query:subelement(GetResult1, <<"query">>), verify_blocklist(QueryEl1, []), Domain = domain(), - BlocklistChange1 = [{user, deny, <<"user@", Domain/binary>>}, {room, deny, room_bin_jid(?ROOM)}], escalus:send(Alice, stanza_blocking_set(BlocklistChange1)), @@ -563,7 +562,6 @@ blocking_disabled(Config) -> escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], escalus:wait_for_stanza(Alice)), Domain = domain(), - BlocklistChange1 = [{user, deny, <<"user@", Domain/binary>>}, {room, deny, room_bin_jid(?ROOM)}], escalus:send(Alice, stanza_blocking_set(BlocklistChange1)), @@ -679,7 +677,12 @@ parse_blocked_item(Item) -> MucHost = ?MUCHOST, case binary:split(Value, <<"/">>) of [MucHost, User] -> {user, deny, User}; - [Room] -> {room, deny, Room} + [Room] -> {room, deny, Room}; + Other -> + CfgHost = rpc(gen_mod, get_module_opt, [host_type(), mod_muc_light, host, undefined]), + ct:fail(#{what => parse_blocked_item_failed, + muc_host => MucHost, other => Other, + cfg_host => CfgHost}) end. -spec verify_aff_bcast(CurrentOccupants :: [escalus:client()], AffUsersChanges :: ct_aff_users(), @@ -876,10 +879,18 @@ set_default_mod_config() -> domain() -> ct:get_config({hosts, mim, domain}). -muc_domain() -> - Domain = domain(), - <<"muc.", Domain/binary>>. +host_type() -> + ct:get_config({hosts, mim, host_type}). + +muc_domain() -> muc_light_helper:muc_host(). -spec room_bin_jid(Room :: binary()) -> binary(). room_bin_jid(Room) -> <>. + +disco_disabled() -> + try + ct:get_config({disable_disco_tests}) + catch _:_ -> + false + end. diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index 08806621dbb..cc26c2a9e61 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -57,6 +57,8 @@ -export([apply_rsm/3]). -export([config_metrics/1]). +%% for mod_muc_light_codec_legacy +-export([subdomain_pattern/1]). -type muc_server() :: jid:lserver(). -type host_type() :: mongooseim:host_type(). @@ -319,7 +321,8 @@ process_decoded_packet(HostType, From, To, Acc, El, {ok, #iq{} = IQ}) -> case mod_muc_iq:process_iq(HostType, From, To, Acc, IQ) of {Acc1, error} -> - make_err(From, To, El, Acc1, {error, feature_not_implemented}); + E = {error, {feature_not_implemented, <<"mod_muc_iq returns error">>}}, + make_err(From, To, El, Acc1, E); _ -> ok end; process_decoded_packet(_HostType, From, To, Acc, El, @@ -433,7 +436,7 @@ make_roster_item({{RoomU, RoomS}, RoomName, RoomVersion}) -> {stop, mongoose_acc:t()} | mongoose_acc:t(). process_iq_get(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ, _ActiveList) -> HostType = mod_muc_light_utils:acc_to_host_type(Acc), - MUCHost = gen_mod:get_module_opt_subhost(FromS, ?MODULE, default_host()), + MUCHost = server_host_to_muc_host(HostType, FromS), case {mod_muc_light_codec_backend:decode(From, To, IQ, Acc), gen_mod:get_module_opt(HostType, ?MODULE, blocking, ?DEFAULT_BLOCKING)} of {{ok, {get, #blocking{} = Blocking}}, true} -> diff --git a/src/muc_light/mod_muc_light_codec.erl b/src/muc_light/mod_muc_light_codec.erl index 0d2cad61f99..4ec7a49a128 100644 --- a/src/muc_light/mod_muc_light_codec.erl +++ b/src/muc_light/mod_muc_light_codec.erl @@ -66,6 +66,8 @@ make_error_elem({error, bad_request, Text}) -> mongoose_xmpp_errors:bad_request(<<"en">>, iolist_to_binary(Text)); make_error_elem({error, feature_not_implemented}) -> mongoose_xmpp_errors:feature_not_implemented(); +make_error_elem({error, {feature_not_implemented, Text}}) -> + mongoose_xmpp_errors:feature_not_implemented(<<"en">>, iolist_to_binary(Text)); make_error_elem({error, internal_server_error}) -> mongoose_xmpp_errors:internal_server_error(); make_error_elem({error, registration_required}) -> diff --git a/src/muc_light/mod_muc_light_codec_legacy.erl b/src/muc_light/mod_muc_light_codec_legacy.erl index 33a94f3dba7..af5d210c443 100644 --- a/src/muc_light/mod_muc_light_codec_legacy.erl +++ b/src/muc_light/mod_muc_light_codec_legacy.erl @@ -70,9 +70,9 @@ encode({#msg{} = Msg, AffUsers}, Sender, {RoomU, RoomS} = RoomUS, HandleFun, Acc fun({{U, S}, _}) -> send_to_aff_user(RoomJID, U, S, <<"message">>, Attrs, Children, HandleFun) end, AffUsers); -encode(OtherCase, Sender, RoomUS, HandleFun, _Acc) -> +encode(OtherCase, Sender, RoomUS, HandleFun, Acc) -> {RoomJID, RoomBin} = jids_from_room_with_resource(RoomUS, <<>>), - case encode_meta(OtherCase, RoomJID, Sender, HandleFun) of + case encode_meta(OtherCase, RoomJID, Sender, HandleFun, Acc) of {iq_reply, ID} -> IQRes = make_iq_result(RoomBin, jid:to_binary(Sender), ID, <<>>, undefined), HandleFun(RoomJID, Sender, IQRes); @@ -242,11 +242,12 @@ parse_blocking_list([Item | RItemsEls], ItemsAcc) -> -spec encode_meta(Request :: muc_light_encode_request(), RoomJID :: jid:jid(), SenderJID :: jid:jid(), - HandleFun :: mod_muc_light_codec:encoded_packet_handler()) -> + HandleFun :: mod_muc_light_codec:encoded_packet_handler(), + Acc :: mongoose_acc:t()) -> {iq_reply, ID :: binary()} | {iq_reply, XMLNS :: binary(), Els :: [jlib:xmlch()], ID :: binary()} | noreply. -encode_meta({get, #disco_info{ id = ID }}, RoomJID, SenderJID, _HandleFun) -> +encode_meta({get, #disco_info{ id = ID }}, RoomJID, SenderJID, _HandleFun, _Acc) -> {result, RegisteredFeatures} = mod_disco:get_local_features(empty, SenderJID, RoomJID, <<>>, <<>>), DiscoEls = [#xmlel{name = <<"identity">>, attrs = [{<<"category">>, <<"conference">>}, @@ -256,13 +257,13 @@ encode_meta({get, #disco_info{ id = ID }}, RoomJID, SenderJID, _HandleFun) -> [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, URN}]} || {{URN, _Host}} <- RegisteredFeatures], {iq_reply, ?NS_DISCO_INFO, DiscoEls, ID}; encode_meta({get, #disco_items{ rooms = Rooms, id = ID, rsm = RSMOut }}, - _RoomJID, _SenderJID, _HandleFun) -> + _RoomJID, _SenderJID, _HandleFun, _Acc) -> DiscoEls = [ #xmlel{ name = <<"item">>, attrs = [{<<"jid">>, <>}, {<<"name">>, RoomName}] } || {{RoomU, RoomS}, RoomName, _RoomVersion} <- Rooms ], {iq_reply, ?NS_DISCO_ITEMS, jlib:rsm_encode(RSMOut) ++ DiscoEls, ID}; -encode_meta({get, #config{} = Config}, _RoomJID, _SenderJID, _HandleFun) -> +encode_meta({get, #config{} = Config}, _RoomJID, _SenderJID, _HandleFun, _Acc) -> ConfigEls = [ jlib:form_field({K, <<"text-single">>, V, K}) || {K, V} <- Config#config.raw_config ], XEl = #xmlel{ name = <<"x">>, @@ -273,25 +274,26 @@ encode_meta({get, #config{} = Config}, _RoomJID, _SenderJID, _HandleFun) -> <<"http://jabber.org/protocol/muc#roomconfig">>}) | ConfigEls] }, {iq_reply, ?NS_MUC_OWNER, [XEl], Config#config.id}; -encode_meta({get, #affiliations{} = Affs}, _RoomJID, _SenderJID, _HandleFun) -> +encode_meta({get, #affiliations{} = Affs}, _RoomJID, _SenderJID, _HandleFun, _Acc) -> AffEls = [ aff_user_to_item(AffUser) || AffUser <- Affs#affiliations.aff_users ], {iq_reply, ?NS_MUC_ADMIN, AffEls, Affs#affiliations.id}; encode_meta({set, #affiliations{} = Affs, OldAffUsers, NewAffUsers}, - RoomJID, SenderJID, HandleFun) -> + RoomJID, SenderJID, HandleFun, _Acc) -> bcast_aff_messages(RoomJID, OldAffUsers, NewAffUsers, SenderJID, Affs#affiliations.aff_users, HandleFun), {iq_reply, Affs#affiliations.id}; -encode_meta({get, #blocking{} = Blocking}, SenderBareJID, _SenderJID, _HandleFun) -> - MUCHost = gen_mod:get_module_opt_subhost( - SenderBareJID#jid.lserver, mod_muc_light, mod_muc_light:default_host()), +encode_meta({get, #blocking{} = Blocking}, SenderBareJID, _SenderJID, _HandleFun, Acc) -> + HostType = mod_muc_light_utils:acc_to_host_type(Acc), + ServerHost = SenderBareJID#jid.lserver, + MUCHost = mongoose_subdomain_utils:get_fqdn(mod_muc_light:subdomain_pattern(HostType), ServerHost), BlockingEls = [ blocking_to_el(BlockingItem, MUCHost) || BlockingItem <- Blocking#blocking.items ], Blocklist = #xmlel{ name = <<"list">>, attrs = [{<<"name">>, ?NS_MUC_LIGHT}], children = BlockingEls }, {iq_reply, ?NS_PRIVACY, [Blocklist], Blocking#blocking.id}; -encode_meta({set, #blocking{ id = ID }}, _RoomJID, _SenderJID, _HandleFun) -> +encode_meta({set, #blocking{ id = ID }}, _RoomJID, _SenderJID, _HandleFun, _Acc) -> {iq_reply, ID}; -encode_meta({set, #create{} = Create, _UniqueRequested}, RoomJID, _SenderJID, HandleFun) -> +encode_meta({set, #create{} = Create, _UniqueRequested}, RoomJID, _SenderJID, HandleFun, _Acc) -> [{{ToU, ToS}, CreatorAff}] = Create#create.aff_users, ToBin = jid:to_binary({ToU, ToS, <<>>}), {From, FromBin} = jids_from_room_with_resource({RoomJID#jid.luser, RoomJID#jid.lserver}, ToBin), @@ -307,7 +309,7 @@ encode_meta({set, #create{} = Create, _UniqueRequested}, RoomJID, _SenderJID, Ha send_to_aff_user(From, ToU, ToS, <<"presence">>, Attrs, Children, HandleFun), noreply; -encode_meta({set, #destroy{ id = ID }, AffUsers}, RoomJID, _SenderJID, HandleFun) -> +encode_meta({set, #destroy{ id = ID }, AffUsers}, RoomJID, _SenderJID, HandleFun, _Acc) -> lists:foreach( fun({{U, S}, _}) -> FromJID = jid:replace_resource(RoomJID, jid:to_binary({U, S, <<>>})), @@ -323,7 +325,7 @@ encode_meta({set, #destroy{ id = ID }, AffUsers}, RoomJID, _SenderJID, HandleFun {iq_reply, ID}; encode_meta({set, #config{ raw_config = [{<<"subject">>, Subject}], id = ID }, AffUsers}, - RoomJID, _SenderJID, HandleFun) -> + RoomJID, _SenderJID, HandleFun, _Acc) -> Attrs = [ {<<"id">>, ID}, {<<"type">>, <<"groupchat">>}, @@ -335,7 +337,7 @@ encode_meta({set, #config{ raw_config = [{<<"subject">>, Subject}], id = ID }, A send_to_aff_user(RoomJID, U, S, <<"message">>, Attrs, [SubjectEl], HandleFun) end, AffUsers), noreply; -encode_meta({set, #config{} = Config, AffUsers}, RoomJID, _SenderJID, HandleFun) -> +encode_meta({set, #config{} = Config, AffUsers}, RoomJID, _SenderJID, HandleFun, _Acc) -> Attrs = [{<<"id">>, Config#config.id}, {<<"from">>, jid:to_binary(RoomJID)}, {<<"type">>, <<"groupchat">>}], From a058c609e164bf9fbb4482972c815dd8008ab6c3 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:03:49 +0200 Subject: [PATCH 42/73] Log muc_process_iq_failed from mod_muc_room --- src/mod_muc_room.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 1566331b2a1..2ce7a6096d1 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -4587,8 +4587,11 @@ route_iq(Acc, #routed_iq{iq = IQ = #iq{}, packet = Packet, from = From}, %% Custom IQ, addressed to this room's JID. case mod_muc_iq:process_iq(HostType, From, RoomJID, Acc, IQ) of {Acc1, error} -> - {Acc2, Err} = jlib:make_error_reply(Acc1, Packet, - mongoose_xmpp_errors:feature_not_implemented(<<"en">>, <<"From mod_muc_room">>)), + ?LOG_WARNING(#{what => muc_process_iq_failed, acc => Acc, + host_type => HostType, room_jid => RoomJID}), + E = mongoose_xmpp_errors:feature_not_implemented( + <<"en">>, <<"From mod_muc_room">>), + {Acc2, Err} = jlib:make_error_reply(Acc1, Packet, E), ejabberd_router:route(RoomJID, From, Acc2, Err); _ -> ok end, From 24c05a37a8dd4a3171446bb9335872099c5994c7 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:04:13 +0200 Subject: [PATCH 43/73] Add muc_helper:muc_host_pattern() --- big_tests/tests/muc_helper.erl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/big_tests/tests/muc_helper.erl b/big_tests/tests/muc_helper.erl index 761a5b6b3b5..39a87dabd65 100644 --- a/big_tests/tests/muc_helper.erl +++ b/big_tests/tests/muc_helper.erl @@ -71,6 +71,9 @@ unload_muc() -> muc_host() -> ct:get_config({hosts, mim, muc_service}). +muc_host_pattern() -> + ct:get_config({hosts, mim, muc_service_pattern}). + muc_backend() -> mongoose_helper:mnesia_or_rdbms_backend(). From 68eaaf5ede2aadb709b6624f459308d7fbc43a96 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:04:37 +0200 Subject: [PATCH 44/73] Start MAM for host_type in muc_SUITE --- big_tests/tests/muc_SUITE.erl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/big_tests/tests/muc_SUITE.erl b/big_tests/tests/muc_SUITE.erl index b22215bb605..7acb2b0942e 100644 --- a/big_tests/tests/muc_SUITE.erl +++ b/big_tests/tests/muc_SUITE.erl @@ -381,11 +381,11 @@ init_per_group(G, Config) when G =:= http_auth_no_server; init_per_group(hibernation, Config) -> case mam_helper:backend() of rdbms -> - dynamic_modules:start(domain(), mod_mam_muc_rdbms_arch, [muc]), - dynamic_modules:start(domain(), mod_mam_rdbms_prefs, [muc]), - dynamic_modules:start(domain(), mod_mam_rdbms_user, [pm, muc]), + dynamic_modules:start(host_type(), mod_mam_muc_rdbms_arch, [muc]), + dynamic_modules:start(host_type(), mod_mam_rdbms_prefs, [muc]), + dynamic_modules:start(host_type(), mod_mam_rdbms_user, [pm, muc]), HostPattern = subhost_pattern("muc.@HOST@"), - dynamic_modules:start(domain(), mod_mam_muc, [{host, HostPattern}]); + dynamic_modules:start(host_type(), mod_mam_muc, [{host, HostPattern}]); _ -> ok end, @@ -448,10 +448,10 @@ end_per_group(G, Config) when G =:= http_auth_no_server; end_per_group(hibernation, Config) -> case mam_helper:backend() of rdbms -> - dynamic_modules:stop(domain(), mod_mam_muc_rdbms_arch), - dynamic_modules:stop(domain(), mod_mam_rdbms_prefs), - dynamic_modules:stop(domain(), mod_mam_rdbms_user), - dynamic_modules:stop(domain(), mod_mam_muc); + dynamic_modules:stop(host_type(), mod_mam_muc_rdbms_arch), + dynamic_modules:stop(host_type(), mod_mam_rdbms_prefs), + dynamic_modules:stop(host_type(), mod_mam_rdbms_user), + dynamic_modules:stop(host_type(), mod_mam_muc); _ -> ok end, @@ -529,9 +529,9 @@ init_per_testcase(CN, Config) init_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; CaseName =:= disco_info_with_mam -> - dynamic_modules:start(domain(), mod_mam_muc, + dynamic_modules:start(host_type(), mod_mam_muc, [{backend, rdbms}, - {host, subhost_pattern(muc_host())}]), + {host, subhost_pattern(muc_helper:muc_host_pattern())}]), escalus:init_per_testcase(CaseName, Config); init_per_testcase(CaseName, Config) -> @@ -601,7 +601,7 @@ end_per_testcase(CaseName =reserved_nickname_request, Config) -> end_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; CaseName =:= disco_info_with_mam -> - dynamic_modules:stop(domain(), mod_mam_muc), + dynamic_modules:stop(host_type(), mod_mam_muc), escalus:end_per_testcase(CaseName, Config); end_per_testcase(CaseName, Config) -> From 8272d32a86460957ab840bef708142a362b843d4 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 24 May 2021 18:49:40 +0200 Subject: [PATCH 45/73] Fix dialyzer warnings --- src/gen_mod.erl | 6 +++++- src/mam/mod_mam_muc.erl | 7 ------- src/mod_muc.erl | 1 - src/mod_muc_room.erl | 8 ++++---- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/gen_mod.erl b/src/gen_mod.erl index f6b8b7a7ecd..f7e3cd4dd69 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -326,7 +326,11 @@ set_opt(Opt, Opts, Value) -> lists:keystore(Opt, 1, Opts, {Opt, Value}). --spec get_module_opt(mongooseim:host_type(), module(), atom(), term()) -> term(). +%%% TODO Make Opt an atom. Fix in mod_auth_token: +%%% 374: The call gen_mod:get_module_opt(Domain::any(), 'mod_auth_token', +%%% {'validity_period','access' | 'refresh'}, {1 | 25,'days' | 'hours'}) +%%% breaks the contract (mongooseim:host_type(), module(), atom(), term()) -> term() +-spec get_module_opt(mongooseim:host_type(), module(), term(), term()) -> term(). get_module_opt(HostType, Module, Opt, Default) -> %% Fail in dev builds. %% It protects against passing something weird as a Module argument diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index 91fb163c23a..cb1c2754adb 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -606,13 +606,6 @@ unregister_features(HostType) -> [mod_disco:unregister_feature(MUCHost, Feature) || Feature <- features(?MODULE, HostType)], ok. --spec default_host() -> mongoose_subdomain_utils:subdomain_pattern(). -default_host() -> - mod_muc:default_host(). - -subdomain_pattern(HostType) -> - gen_mod:get_module_opt(HostType, ?MODULE, host, default_host()). - add_iq_handlers(HostType, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, parallel), gen_iq_handler:add_iq_handler(mod_muc_iq, HostType, ?NS_MAM_04, diff --git a/src/mod_muc.erl b/src/mod_muc.erl index ca65f4fab13..22b7da0807f 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -94,7 +94,6 @@ -type access() :: {_AccessRoute, _AccessCreate, _AccessAdmin, _AccessPersistent}. -type host_type() :: mongooseim:host_type(). -type muc_host() :: jid:lserver(). --type server_host() :: jid:lserver(). -include("mod_muc.hrl"). diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 2ce7a6096d1..a64577c23a4 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -251,7 +251,7 @@ can_access_identity(RoomJID, UserJID) -> %% @doc A room is created. Depending on request type (MUC/groupchat 1.0) the %% next state is determined accordingly (a locked room for MUC or an instant %% one for groupchat). --spec init(#{}) -> +-spec init(map()) -> {ok, statename(), state()} | {ok, statename(), state(), timeout()}. init(#{init_type := start_new} = Args) -> init_new(Args); @@ -261,7 +261,7 @@ init(#{init_type := start_restored} = Args) -> init_new(#{init_type := start_new, host_type := HostType, muc_host := Host, server_host := ServerHost, access := Access, room_name := Room, history_size := HistorySize, room_shaper := RoomShaper, - http_auth_pool := HttpAuthPool, creator := Creator, nick := Nick, + http_auth_pool := HttpAuthPool, creator := Creator, nick := _Nick, def_opts := DefRoomOpts}) when is_list(DefRoomOpts) -> process_flag(trap_exit, true), Shaper = shaper:new(RoomShaper), @@ -723,7 +723,7 @@ stop_if_only_owner_is_online(RoomName, 1, #state{users = Users, jid = RoomJID} = stop_if_only_owner_is_online(_, _, State) -> next_normal_state(State). -do_stop_persistent_room(RoomName, State) -> +do_stop_persistent_room(_RoomName, State) -> ?LOG_INFO(ls(#{what => muc_room_stopping_persistent, text => <<"Stopping persistent room's process">>}, State)), mongoose_metrics:update(global, [mod_muc, deep_hibernations], 1), @@ -4631,7 +4631,7 @@ do_route_iq(Acc, Res1, #routed_iq{iq = #iq{xmlns = XMLNS, sub_el = SubEl} = IQ, -spec route_nick_message(routed_nick_message(), state()) -> state(). -route_nick_message(#routed_nick_message{decide = {expulse_sender, Reason}, +route_nick_message(#routed_nick_message{decide = {expulse_sender, _Reason}, packet = Packet, lang = Lang, from = From}, StateData) -> ErrorText = <<"This participant is kicked from the room because he", "sent an error message to another participant">>, From 021b51304fffc7f36fb35dfb257612e96df26d8b Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 24 May 2021 19:03:33 +0200 Subject: [PATCH 46/73] Fix muc_light tests in mam_SUITE (host opt issue) --- big_tests/tests/mam_SUITE.erl | 32 ++++++++++++++++---------------- big_tests/tests/mam_helper.erl | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/big_tests/tests/mam_SUITE.erl b/big_tests/tests/mam_SUITE.erl index 8d7b5c31557..0bfd41c68d9 100644 --- a/big_tests/tests/mam_SUITE.erl +++ b/big_tests/tests/mam_SUITE.erl @@ -724,26 +724,26 @@ init_modules(rdbms, muc_light, Config) -> init_module(host_type(), mod_mam_muc_rdbms_arch, []), Config1; init_modules(BT = riak_timed_yz_buckets, muc_light, Config) -> - dynamic_modules:start(host_type(), mod_muc_light, [{host_type, subhost_pattern(muc_light_host())}]), - init_modules(BT, generic, [{muc_domain, "muclight.@HOST@"} | Config]); + dynamic_modules:start(host_type(), mod_muc_light, [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}]), + init_modules(BT, generic, [{muc_domain, muc_light_helper:muc_host_pattern()} | Config]); init_modules(BT = cassandra, muc_light, config) -> init_modules_for_muc_light(BT, config); init_modules(cassandra, muc_all, Config) -> init_module(host_type(), mod_mam_muc_cassandra_arch, []), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}]), + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}]), Config; init_modules(BT = elasticsearch, muc_light, config) -> init_modules_for_muc_light(BT, config); init_modules(elasticsearch, muc_all, Config) -> init_module(host_type(), mod_mam_muc_elasticsearch_arch, []), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}]), + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}]), Config; init_modules(rdbms, C, Config) when C =:= muc_all; C =:= muc_disabled_retraction -> init_module(host_type(), mod_mam_muc_rdbms_arch, []), init_module(host_type(), mod_mam_rdbms_prefs, [muc]), init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_simple, C, Config) when C =:= muc_all; @@ -751,7 +751,7 @@ init_modules(rdbms_simple, C, Config) when C =:= muc_all; init_module(host_type(), mod_mam_muc_rdbms_arch, []), init_module(host_type(), mod_mam_rdbms_prefs, [muc]), init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_async_pool, C, Config) when C =:= muc_all; @@ -760,7 +760,7 @@ init_modules(rdbms_async_pool, C, Config) when C =:= muc_all; init_module(host_type(), mod_mam_muc_rdbms_async_pool_writer, [{flush_interval, 1}]), %% 1ms init_module(host_type(), mod_mam_rdbms_prefs, [muc]), init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_mnesia, C, Config) when C =:= muc_all; @@ -768,7 +768,7 @@ init_modules(rdbms_mnesia, C, Config) when C =:= muc_all; init_module(host_type(), mod_mam_muc_rdbms_arch, []), init_module(host_type(), mod_mam_mnesia_prefs, [muc]), init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_cache, C, Config) when C =:= muc_all; @@ -777,7 +777,7 @@ init_modules(rdbms_cache, C, Config) when C =:= muc_all; init_module(host_type(), mod_mam_rdbms_prefs, [muc]), init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), init_module(host_type(), mod_mam_cache_user, [muc]), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_async_cache, C, Config) when C =:= muc_all; @@ -787,7 +787,7 @@ init_modules(rdbms_async_cache, C, Config) when C =:= muc_all; init_module(host_type(), mod_mam_rdbms_prefs, [muc]), init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), init_module(host_type(), mod_mam_cache_user, [muc]), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_mnesia_muc_cache, C, Config) when C =:= muc_all; @@ -796,7 +796,7 @@ init_modules(rdbms_mnesia_muc_cache, C, Config) when C =:= muc_all; init_module(host_type(), mod_mam_mnesia_prefs, [muc]), init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), init_module(host_type(), mod_mam_muc_cache_user, [muc]), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(rdbms_mnesia_muc_cache, _, _Config) -> @@ -807,7 +807,7 @@ init_modules(rdbms_mnesia_cache, C, Config) when C =:= muc_all; init_module(host_type(), mod_mam_mnesia_prefs, [muc]), init_module(host_type(), mod_mam_rdbms_user, [muc, pm]), init_module(host_type(), mod_mam_cache_user, [muc]), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(BackendType, muc_light, Config) -> @@ -835,7 +835,7 @@ init_modules(riak_timed_yz_buckets, C, Config) -> init_module(host_type(), mod_mam_mnesia_prefs, [pm, muc, {archive_key, mam_archive_key_server_user}]), init_module(host_type(), mod_mam, addin_mam_options(C, Config)), - init_module(host_type(), mod_mam_muc, [{host_type, subhost_pattern(muc_domain(Config))}] ++ + init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}] ++ addin_mam_options(C, Config)), Config; init_modules(cassandra, C, Config) -> @@ -895,8 +895,8 @@ rdbms_simple_opts() -> [{db_jid_format, mam_jid_rfc}, {db_message_format, mam_message_xml}]. init_modules_for_muc_light(BackendType, Config) -> - dynamic_modules:start(host_type(), mod_muc_light, [{host_type, subhost_pattern(muc_light_host())}]), - Config1 = init_modules(BackendType, muc_all, [{muc_domain, "muclight.@HOST@"} | Config]), + dynamic_modules:start(host_type(), mod_muc_light, [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}]), + Config1 = init_modules(BackendType, muc_all, [{muc_domain, muc_helper:muc_host_pattern()} | Config]), init_modules(BackendType, pm, [{archive_groupchats, false} | Config1]). end_modules(C, muc_light, Config) -> @@ -908,7 +908,7 @@ end_modules(_, _, Config) -> Config. muc_domain(Config) -> - proplists:get_value(muc_domain, Config, "muc.@HOST@"). + proplists:get_value(muc_domain, Config, muc_helper:muc_host_pattern()). addin_mam_options(disabled_text_search, Config) -> [{full_text_search, false} | addin_mam_options(Config)]; diff --git a/big_tests/tests/mam_helper.erl b/big_tests/tests/mam_helper.erl index c5328bd7b14..f6e41d1e619 100644 --- a/big_tests/tests/mam_helper.erl +++ b/big_tests/tests/mam_helper.erl @@ -1222,7 +1222,7 @@ has_x_user_element(ArcMsg) -> ParsedMess#forwarded_message.has_x_user_element. muc_light_host() -> - <<"muclight.localhost">>. + muc_light_helper:muc_host(). host() -> ct:get_config({hosts, mim, domain}). From 6a7a41b6359d36821f1d9fe404c64e9829647de2 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 24 May 2021 20:03:02 +0200 Subject: [PATCH 47/73] Fix host in muc_light_legacy_SUITE --- big_tests/tests/muc_light_legacy_SUITE.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/big_tests/tests/muc_light_legacy_SUITE.erl b/big_tests/tests/muc_light_legacy_SUITE.erl index ca64a20b2ee..274b7fdc72a 100644 --- a/big_tests/tests/muc_light_legacy_SUITE.erl +++ b/big_tests/tests/muc_light_legacy_SUITE.erl @@ -65,7 +65,7 @@ -define(NS_MUC_LIGHT, <<"urn:xmpp:muclight:0">>). -define(NS_MUC_ROOMCONFIG, <<"http://jabber.org/protocol/muc#roomconfig">>). --define(MUCHOST, (muc_light_helper:muc_host())). +-define(MUCHOST, (muc_helper:muc_host())). -define(CHECK_FUN, fun mod_muc_light_room:participant_limit_check/2). -define(BACKEND, mod_muc_light_db_backend). @@ -143,7 +143,7 @@ suite() -> init_per_suite(Config) -> HostType = host_type(), dynamic_modules:start(HostType, mod_muc_light, - [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}, + [{host, subhost_pattern(muc_helper:muc_host_pattern())}, {backend, mongoose_helper:mnesia_or_rdbms_backend()}, {legacy_mode, true}]), Config1 = escalus:init_per_suite(Config), @@ -882,7 +882,7 @@ domain() -> host_type() -> ct:get_config({hosts, mim, host_type}). -muc_domain() -> muc_light_helper:muc_host(). +muc_domain() -> muc_helper:muc_host(). -spec room_bin_jid(Room :: binary()) -> binary(). room_bin_jid(Room) -> From 93dd353e59c523f5635525e46ed8cb27fa6432f7 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 09:42:49 +0200 Subject: [PATCH 48/73] Move meck_init into a separate function in muc_SUITE --- big_tests/tests/muc_SUITE.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/big_tests/tests/muc_SUITE.erl b/big_tests/tests/muc_SUITE.erl index 7acb2b0942e..babf11b7d36 100644 --- a/big_tests/tests/muc_SUITE.erl +++ b/big_tests/tests/muc_SUITE.erl @@ -543,11 +543,11 @@ meck_room() -> %% Meck will register a fake room right before a 'real' room is started meck_room_start() -> - rpc(mim(), meck, expect, [mod_muc_room, init, - fun(#{muc_host := Host, host_type := HostType, room_name := Room} = Args) -> - mod_muc:register_room(HostType, Host, Room, ?FAKEPID), - meck:passthrough([Args]) - end]). + rpc(mim(), meck, expect, [mod_muc_room, init, fun ?MODULE:meck_init/1]). + +meck_init(#{muc_host := Host, host_type := HostType, room_name := Room} = Args) -> + mod_muc:register_room(HostType, Host, Room, ?FAKEPID), + meck:passthrough([Args]). %% Meck will forward all calls to route to the test case instead meck_room_route() -> From 4f980f7e30624bf56f006d9cce8122f2a78f7096 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 09:43:31 +0200 Subject: [PATCH 49/73] Check that args is a map in mod_muc_room:start_link --- src/mod_muc_room.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index a64577c23a4..59c78fbb25d 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -188,7 +188,7 @@ start_restored(HostType, Host, ServerHost, Access, Room, opts => Opts}, supervisor:start_child(Supervisor, [Args]). -start_link(Args) -> +start_link(Args = #{}) -> gen_fsm_compat:start_link(?MODULE, Args, []). stop(Pid) -> From 1ead8dd5f3575b397f9eb6577c0ed759bcf5749e Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 25 May 2021 13:44:02 +0200 Subject: [PATCH 50/73] Fix test/muc_light_SUITE.erl --- test/muc_light_SUITE.erl | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/test/muc_light_SUITE.erl b/test/muc_light_SUITE.erl index 6b370a18f4e..510d51254cf 100644 --- a/test/muc_light_SUITE.erl +++ b/test/muc_light_SUITE.erl @@ -7,6 +7,7 @@ -include("mod_muc_light.hrl"). -include("jlib.hrl"). -include("mongoose_rsm.hrl"). +-include("mongoose.hrl"). -define(DOMAIN, <<"localhost">>). @@ -59,6 +60,7 @@ end_per_group(_, Config) -> Config. init_per_testcase(codec_calls, Config) -> + meck_mongoose_subdomain_core(), ok = mnesia:create_schema([node()]), ok = mnesia:start(), {ok, _} = application:ensure_all_started(exometer_core), @@ -82,6 +84,7 @@ end_per_testcase(codec_calls, Config) -> mongoose_subhosts:stop(), mnesia:delete_schema([node()]), application:stop(exometer_core), + meck:unload(), Config; end_per_testcase(_, Config) -> Config. @@ -124,24 +127,30 @@ codec_calls(_Config) -> % 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. + Acc = mongoose_acc:new(#{ location => ?LOCATION, + lserver => <<"localhost">>, + host_type => <<"localhost">>, + element => undefined, + from_jid => jid:make_noprep(<<"a">>, <<"localhost">>, <<>>), + to_jid => jid:make_noprep(<<>>, <<"muc.localhost">>, <<>>) }), mod_muc_light_codec_modern:encode({#msg{id = <<"ajdi">>}, AffUsers}, - Sender, RoomUS, HandleFun), + Sender, RoomUS, HandleFun, Acc), % 1 filter packet, sent 1 msg to 2 users check_count(1, 2), mod_muc_light_codec_modern:encode({set, #affiliations{}, [], []}, - Sender, RoomUS, HandleFun), + Sender, RoomUS, HandleFun, Acc), % 1 filter packet, sent 1 IQ response to Sender check_count(1, 1), mod_muc_light_codec_modern:encode({set, #create{id = <<"ajdi">>, aff_users = AffUsers}, false}, - Sender, RoomUS, HandleFun), + Sender, RoomUS, HandleFun, Acc), % 1 filter, 1 IQ response to Sender, 1 notification to 2 users check_count(1, 3), mod_muc_light_codec_modern:encode({set, #config{id = <<"ajdi">>}, AffUsers}, - Sender, RoomUS, HandleFun), + Sender, RoomUS, HandleFun, Acc), % 1 filter, 1 IQ response to Sender, 1 notification to 2 users check_count(1, 3), mod_muc_light_codec_legacy:encode({#msg{id = <<"ajdi">>}, AffUsers}, - Sender, RoomUS, HandleFun), + Sender, RoomUS, HandleFun, Acc), % 1 filter, 1 msg to 2 users check_count(1, 2), ok. @@ -273,7 +282,8 @@ validate_owner([], _, _) -> true. prop_aff_change_bad_request() -> ?FORALL({AffUsers, Changes}, bad_change_aff(), begin - {error, bad_request} = mod_muc_light_utils:change_aff_users(AffUsers, Changes), + {error, {bad_request, _}} = + mod_muc_light_utils:change_aff_users(AffUsers, Changes), true end). @@ -526,3 +536,10 @@ check_count(Hooks, Handlers) -> ?assertEqual(Handlers, Ha), ets:insert(testcalls, {hooks, 0}), ets:insert(testcalls, {handlers, 0}). + +meck_mongoose_subdomain_core() -> + meck:new(mongoose_subdomain_core), + meck:expect(mongoose_subdomain_core, register_subdomain, + fun(HostType, SubdomainPattern, PacketHandler) -> ok end), + meck:expect(mongoose_subdomain_core, unregister_subdomain, + fun(HostType, SubdomainPattern) -> ok end). From 82d48b4394565164533ea6141bdf8c4f9c38918c Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 25 May 2021 16:46:06 +0200 Subject: [PATCH 51/73] Fix offline_SUITE:room_chatmarker_is_overriden_and_only_unique_markers_are_delivered/1 --- big_tests/tests/offline_SUITE.erl | 44 ++++++++++++++----------- src/mod_smart_markers.erl | 17 +++++----- src/offline/mod_offline_chatmarkers.erl | 2 +- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/big_tests/tests/offline_SUITE.erl b/big_tests/tests/offline_SUITE.erl index 193fb36e1b1..9fc32c68485 100644 --- a/big_tests/tests/offline_SUITE.erl +++ b/big_tests/tests/offline_SUITE.erl @@ -55,29 +55,36 @@ init_per_suite(C) -> escalus:init_per_suite(C). end_per_suite(C) -> escalus_fresh:clean(), escalus:end_per_suite(C). init_per_group(with_groupchat, C) -> - OfflineBackend = mongoose_helper:get_backend_name(mod_offline_backend), - MucLightBackend = mongoose_helper:mnesia_or_rdbms_backend(), - Modules = [{mod_offline, [{store_groupchat_messages, true}, - {backend, OfflineBackend}]}, - {mod_muc_light, [{backend, MucLightBackend}]}], Config = dynamic_modules:save_modules(domain(), C), - dynamic_modules:ensure_modules(domain(), Modules), + dynamic_modules:ensure_modules(domain(), with_groupchat_modules()), Config; init_per_group(chatmarkers, C) -> - case mongoose_helper:is_rdbms_enabled(domain())of - false -> {skip, require_rdbms}; - true-> - Modules = [{mod_offline, [{store_groupchat_messages, true}, - {backend, rdbms}]}, - {mod_offline_chatmarkers,[{store_groupchat_messages, true}]}, - {mod_muc_light, [{backend, rdbms}]}], - Config = dynamic_modules:save_modules(domain(), C), - dynamic_modules:ensure_modules(domain(), Modules), - Config + case mongoose_helper:is_rdbms_enabled(domain()) of + false -> + {skip, require_rdbms}; + true -> + Config = dynamic_modules:save_modules(domain(), C), + dynamic_modules:ensure_modules(domain(), chatmarkers_modules()), + Config end; - init_per_group(_, C) -> C. +with_groupchat_modules() -> + OfflineBackend = mongoose_helper:get_backend_name(mod_offline_backend), + MucLightBackend = mongoose_helper:mnesia_or_rdbms_backend(), + MucPattern = distributed_helper:subhost_pattern(muc_light_helper:muc_host_pattern()), + [{mod_offline, [{store_groupchat_messages, true}, + {backend, OfflineBackend}]}, + {mod_muc_light, [{backend, MucLightBackend}, + {host, MucPattern}]}]. + +chatmarkers_modules() -> + MucPattern = distributed_helper:subhost_pattern(muc_light_helper:muc_host_pattern()), + [{mod_offline, [{store_groupchat_messages, true}, + {backend, rdbms}]}, + {mod_offline_chatmarkers, [{store_groupchat_messages, true}]}, + {mod_muc_light, [{backend, rdbms}, {host, MucPattern}]}]. + end_per_group(Group, C) when Group =:= chatmarkers; Group =:= with_groupchat -> dynamic_modules:restore_modules(domain(), C), @@ -98,8 +105,7 @@ offline_message_is_stored_and_delivered_at_login(Config) -> Story = fun(FreshConfig, Alice, Bob) -> logout(FreshConfig, Bob), - escalus:send(Alice, escalus_stanza:chat_to - (Bob, <<"msgtxt">>)), + escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"msgtxt">>)), NewBob = login_send_presence(FreshConfig, bob), Stanzas = escalus:wait_for_stanzas(NewBob, 2), escalus_new_assert:mix_match diff --git a/src/mod_smart_markers.erl b/src/mod_smart_markers.erl index d97410f42fb..acb2fa9c916 100644 --- a/src/mod_smart_markers.erl +++ b/src/mod_smart_markers.erl @@ -144,8 +144,7 @@ get_chat_markers(ChatType, #jid{lserver = LServer} = To, Thread, TS) -> Host = case ChatType of one2one -> LServer; groupchat -> - {ok, H} = mongoose_subhosts:get_host(LServer), - H + mod_muc_light_utils:muc_host_to_host_type(LServer) end, mod_smart_markers_backend:get_chat_markers(Host, To, Thread, TS). @@ -216,10 +215,9 @@ is_valid_markers(From, To, Packet, TS) -> -spec get_host(chat_type(), jid:lserver(), jid:jid(), jid:jid()) -> false | {true, jid:lserver()}. get_host(groupchat, SubHost, From, To) -> - case mongoose_subhosts:get_host(SubHost) of - undefined -> false; - {ok, Host} -> - can_access_room(From, To) andalso {true, Host} + case mongoose_domain_api:get_subdomain_info(SubHost) of + {ok, #{host_type := HostType}} -> + can_access_room(HostType, From, To) andalso {true, HostType} end; get_host(one2one, Host, _, _) -> Hosts = ejabberd_config:get_global_option(hosts), @@ -228,9 +226,10 @@ get_host(one2one, Host, _, _) -> _ -> {true, Host} end. --spec can_access_room(User :: jid:jid(), Room :: jid:jid()) -> boolean(). -can_access_room(User, Room) -> - mongoose_hooks:can_access_room(Room#jid.lserver, Room, User). +-spec can_access_room(HostType :: mongooseim:host_type(), + User :: jid:jid(), Room :: jid:jid()) -> boolean(). +can_access_room(HostType, User, Room) -> + mongoose_hooks:can_access_room(HostType, Room, User). add_default_backend(Opts) -> case lists:keyfind(backend, 2, Opts) of diff --git a/src/offline/mod_offline_chatmarkers.erl b/src/offline/mod_offline_chatmarkers.erl index 90d4e15654c..c236fd53c6c 100644 --- a/src/offline/mod_offline_chatmarkers.erl +++ b/src/offline/mod_offline_chatmarkers.erl @@ -111,7 +111,7 @@ inspect_packet(Acc, From, To, Packet) -> maybe_store_chat_marker(Acc, From, To, Packet) -> case mongoose_acc:get(mod_smart_markers, timestamp, undefined, Acc) of undefined -> false; - Timestamp when is_integer(Timestamp)-> + Timestamp when is_integer(Timestamp) -> Room = get_room(Acc, From), Thread = get_thread(Packet), mod_offline_chatmarkers_backend:maybe_store(To, Thread, Room, Timestamp), From cb2620546e41b8a489e0f60009a65c501b21d890 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 25 May 2021 17:15:29 +0200 Subject: [PATCH 52/73] Fix bugs in rest_client_SUITE --- big_tests/tests/mam_helper.erl | 23 +++++++++++-------- big_tests/tests/rest_client_SUITE.erl | 32 +++++++++++++-------------- big_tests/tests/rest_helper.erl | 24 +------------------- 3 files changed, 30 insertions(+), 49 deletions(-) diff --git a/big_tests/tests/mam_helper.erl b/big_tests/tests/mam_helper.erl index f6e41d1e619..19ccecfff0f 100644 --- a/big_tests/tests/mam_helper.erl +++ b/big_tests/tests/mam_helper.erl @@ -107,7 +107,8 @@ verify_archived_muc_light_aff_msg/3, wait_for_room_archive_size/3, generate_msg_for_date_user/3, - generate_msg_for_date_user/4 + generate_msg_for_date_user/4, + put_msg/1 ]). rpc_apply(M, F, Args) -> @@ -821,11 +822,11 @@ bootstrap_archive(Config) -> BobServer = escalus_users:get_server(Config, bob), CarolServer = escalus_users:get_server(Config, carol), ArcJID = {AliceJID, make_jid(AliceName, AliceServer, <<>>), - rpc_apply(mod_mam, archive_id, [AliceServer, AliceName])}, + get_archive_id(AliceServer, AliceName)}, OtherUsers = [{BobJID, make_jid(BobName, BobServer, <<>>), - rpc_apply(mod_mam, archive_id, [BobServer, BobName])}, + get_archive_id(BobServer, BobName)}, {CarolJID, make_jid(CarolName, CarolServer, <<>>), - rpc_apply(mod_mam, archive_id, [CarolServer, CarolName])}], + get_archive_id(CarolServer, CarolName)}], Msgs = generate_msgs_for_days(ArcJID, OtherUsers, 16), put_msgs(Msgs), AllUsers = [{AliceServer, AliceName}, @@ -927,8 +928,7 @@ muc_bootstrap_archive(Config) -> Domain = muc_host(), RoomJid = make_jid(Room, Domain, <<>>), - ArcJID = {R, RoomJid, - rpc_apply(mod_mam_muc, archive_id, [Domain, Room])}, + ArcJID = {R, RoomJid, get_muc_archive_id(Domain, Room)}, Msgs = generate_msgs_for_days(ArcJID, [{B, make_jid(BobName, BobServer, <<"res1">>), rpc_apply(jid, replace_resource, [RoomJid, BobNick])}, @@ -945,10 +945,9 @@ muc_bootstrap_archive(Config) -> {bob_nickname, BobNick} | Config]. put_muc_msgs(Msgs) -> - Host = host(), - [archive_muc_msg(Host, Msg) || Msg <- Msgs]. + [archive_muc_msg(Msg) || Msg <- Msgs]. -archive_muc_msg(Host, {{MsgID, _}, +archive_muc_msg({{MsgID, _}, {_RoomBin, RoomJID, RoomArcID}, {_FromBin, FromJID, SrcJID}, _, Packet}) -> rpc_apply(mod_mam_muc, archive_message_for_ct, [#{message_id => MsgID, @@ -960,6 +959,12 @@ archive_muc_msg(Host, {{MsgID, _}, direction => incoming, packet => Packet}]). +get_archive_id(Server, User) -> + rpc_apply(mod_mam, archive_id, [Server, User]). + +get_muc_archive_id(MucHost, Room) -> + rpc_apply(mod_mam_muc, archive_id, [MucHost, Room]). + %% @doc Get a binary jid of the user, that tagged with `UserName' in the config. nick_to_jid(UserName, Config) when is_atom(UserName) -> UserSpec = escalus_users:get_userspec(Config, UserName), diff --git a/big_tests/tests/rest_client_SUITE.erl b/big_tests/tests/rest_client_SUITE.erl index 685b76ac4e8..614fc1d2a41 100644 --- a/big_tests/tests/rest_client_SUITE.erl +++ b/big_tests/tests/rest_client_SUITE.erl @@ -26,7 +26,6 @@ -define(NOT_FOUND, {<<"404">>, _}). -define(NOT_IMPLEMENTED, {<<"501">>, _}). -define(UNAUTHORIZED, {<<"401">>, <<"Unauthorized">>}). --define(MUCHOST, <<"muclight.localhost">>). %% -------------------------------------------------------------------- %% Common Test stuff @@ -117,12 +116,11 @@ security_test_cases() -> init_per_suite(C) -> application:ensure_all_started(shotgun), Host = ct:get_config({hosts, mim, domain}), - MUCLightHost = <<"muclight.", Host/binary>>, C1 = rest_helper:maybe_enable_mam(mam_helper:backend(), Host, C), dynamic_modules:start(Host, mod_muc_light, - [{host, subhost_pattern(MUCLightHost)}, + [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}, {rooms_in_rosters, true}]), - [{muc_light_host, MUCLightHost} | escalus:init_per_suite(C1)]. + [{muc_light_host, muc_light_helper:muc_host()} | escalus:init_per_suite(C1)]. end_per_suite(Config) -> escalus_fresh:clean(), @@ -139,8 +137,8 @@ end_per_group(_GN, C) -> C. init_per_testcase(config_can_be_changed_by_all = CaseName, Config) -> - DefaultConfig = dynamic_modules:save_modules(domain(Config), Config), - set_mod_config(all_can_configure, true, ?MUCHOST), + DefaultConfig = dynamic_modules:save_modules(host_type(), Config), + set_mod_config(all_can_configure, true, config_to_muc_host(Config)), escalus:init_per_testcase(config_can_be_changed_by_all, DefaultConfig); init_per_testcase(TC, Config) -> @@ -161,13 +159,18 @@ init_per_testcase(TC, Config) -> rest_helper:maybe_skip_mam_test_cases(TC, MAMTestCases, Config). end_per_testcase(config_can_be_changed_by_all = CaseName, Config) -> - set_mod_config(all_can_configure, false, ?MUCHOST), - dynamic_modules:restore_modules(domain(Config), Config), + set_mod_config(all_can_configure, false, config_to_muc_host(Config)), + dynamic_modules:restore_modules(host_type(), Config), escalus:end_per_testcase(config_can_be_changed_by_all, Config); - end_per_testcase(TC, C) -> escalus:end_per_testcase(TC, C). +config_to_muc_host(Config) -> + ?config(muc_light_host, Config). + +host_type() -> + ct:get_config({hosts, mim, domain}). + %% -------------------------------------------------------------------- %% Test cases %% -------------------------------------------------------------------- @@ -219,8 +222,7 @@ aff_change_msg_is_delivered_over_sse(ConfigIn) -> Event = wait_for_event(Conn), Data = jiffy:decode(maps:get(data, Event), [return_maps]), BobJID = user_jid(Bob), - Host = ct:get_config({hosts, mim, domain}), - RoomJID = <>, + RoomJID = room_jid(RoomID, Config), assert_json_room_sse_message(#{room => RoomID, from => RoomJID, type => <<"affiliation">>, @@ -348,8 +350,7 @@ user_is_invited_to_a_room(Config) -> RoomInfo = get_room_info({alice, Alice}, RoomID), true = is_participant(Bob, <<"member">>, RoomInfo), IQ = escalus_stanza:iq_get(<<"urn:xmpp:muclight:0#affiliations">>, []), - Host = ct:get_config({hosts, mim, domain}), - RoomJID = <>, + RoomJID = room_jid(RoomID, Config), escalus:send(Alice, escalus_stanza:to(IQ, RoomJID)), escalus:assert(is_iq_result, [IQ], escalus:wait_for_stanza(Alice)) @@ -1224,12 +1225,9 @@ break_stuff(Config) -> -spec room_jid(RoomID :: binary(), Config :: list()) -> RoomJID :: binary(). room_jid(RoomID, Config) -> - MUCLightHost = ?config(muc_light_host, Config), + MUCLightHost = config_to_muc_host(Config), <>. -domain(Config) -> - ?config(muc_light_host, Config). - default_http_server_name_is_returned_if_not_changed(_Config) -> %% GIVEN MIM1 uses default name verify_server_name_in_header(distributed_helper:mim(), <<"Cowboy">>). diff --git a/big_tests/tests/rest_helper.erl b/big_tests/tests/rest_helper.erl index 140594bc368..c1e01473b9e 100644 --- a/big_tests/tests/rest_helper.erl +++ b/big_tests/tests/rest_helper.erl @@ -388,29 +388,7 @@ put_msg(Aclient, Bclient, Content, Days) -> put_msg(Msg), ok. -put_msg({{MsgIdOwner, MsgIdRemote}, - {_FromBin, FromJID, FromArcID}, - {_ToBin, ToJID, ToArcID}, - {_, Source, _}, Packet}) -> - Host = ct:get_config({hosts, mim, domain}), - OutArgs = [Host, #{message_id => MsgIdOwner, - archive_id => FromArcID, - local_jid => FromJID, - remote_jid => ToJID, - source_jid => Source, - origin_id => none, - direction => outgoing, - packet => Packet}], - ok = mam_helper:rpc_apply(mod_mam, archive_message, OutArgs), - InArgs = [Host, #{message_id => MsgIdRemote, - archive_id => ToArcID, - local_jid => ToJID, - remote_jid => FromJID, - source_jid => Source, - origin_id => none, - direction => incoming, - packet => Packet}], - ok = mam_helper:rpc_apply(mod_mam, archive_message, InArgs). +put_msg(Msg) -> mam_helper:put_msg(Msg). make_arc_id(Client) -> User = escalus_client:username(Client), From e8b3d571c7232a77b34525771e2bd2377a05f805 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 25 May 2021 18:22:35 +0200 Subject: [PATCH 53/73] Handle already started MAM modules in rest_client_SUITE --- big_tests/tests/rest_client_SUITE.erl | 8 ++++---- big_tests/tests/rest_helper.erl | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/big_tests/tests/rest_client_SUITE.erl b/big_tests/tests/rest_client_SUITE.erl index 614fc1d2a41..f78e0bd636a 100644 --- a/big_tests/tests/rest_client_SUITE.erl +++ b/big_tests/tests/rest_client_SUITE.erl @@ -115,7 +115,8 @@ security_test_cases() -> init_per_suite(C) -> application:ensure_all_started(shotgun), - Host = ct:get_config({hosts, mim, domain}), + Host = ct:get_config({hosts, mim, host_type}), + dynamic_modules:save_modules(Host, C), C1 = rest_helper:maybe_enable_mam(mam_helper:backend(), Host, C), dynamic_modules:start(Host, mod_muc_light, [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}, @@ -124,10 +125,9 @@ init_per_suite(C) -> end_per_suite(Config) -> escalus_fresh:clean(), - Host = ct:get_config({hosts, mim, domain}), - rest_helper:maybe_disable_mam(mam_helper:backend(), Host), - dynamic_modules:stop(Host, mod_muc_light), + Host = ct:get_config({hosts, mim, host_type}), application:stop(shotgun), + dynamic_modules:restore_modules(Host, Config), escalus:end_per_suite(Config). init_per_group(_GN, C) -> diff --git a/big_tests/tests/rest_helper.erl b/big_tests/tests/rest_helper.erl index c1e01473b9e..a4d2877f75e 100644 --- a/big_tests/tests/rest_helper.erl +++ b/big_tests/tests/rest_helper.erl @@ -315,6 +315,7 @@ to_list(V) when is_list(V) -> V. maybe_enable_mam(rdbms, Host, Config) -> + maybe_disable_mam(rdbms, Host), init_module(Host, mod_mam_rdbms_arch, []), init_module(Host, mod_mam_muc_rdbms_arch, []), init_module(Host, mod_mam_rdbms_prefs, [muc, pm]), @@ -324,6 +325,7 @@ maybe_enable_mam(rdbms, Host, Config) -> {archive_chat_markers, true}]), [{mam_backend, rdbms} | Config]; maybe_enable_mam(riak, Host, Config) -> + maybe_disable_mam(riak, Host), init_module(Host, mod_mam_riak_timed_arch_yz, [pm, muc]), init_module(Host, mod_mam_mnesia_prefs, [pm, muc]), init_module(Host, mod_mam, [{archive_chat_markers, true}]), @@ -337,6 +339,7 @@ init_module(Host, Mod, Opts) -> dynamic_modules:start(Host, Mod, Opts). maybe_disable_mam(rdbms, Host) -> + stop_module(Host, mod_mam_muc_rdbms_arch), stop_module(Host, mod_mam_rdbms_arch), stop_module(Host, mod_mam_rdbms_prefs), stop_module(Host, mod_mam_rdbms_user), From 5b67c4f17d5f42182de41c832dfd933b15b0b74d Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 25 May 2021 20:52:40 +0200 Subject: [PATCH 54/73] Do not overwrite config_schema option in muc_light:start function --- src/muc_light/mod_muc_light.erl | 9 +++++---- src/muc_light/mod_muc_light_room_config.erl | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index cc26c2a9e61..f780831dd32 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -89,11 +89,11 @@ config_schema(MUCServer) -> %% Internals -spec default_config_for_host_type(host_type()) -> mod_muc_light_room_config:kv(). default_config_for_host_type(HostType) -> - gen_mod:get_module_opt(HostType, ?MODULE, default_config, []). + gen_mod:get_module_opt(HostType, ?MODULE, computed_default_config, []). -spec config_schema_for_host_type(host_type()) -> mod_muc_light_room_config:schema(). config_schema_for_host_type(HostType) -> - gen_mod:get_module_opt(HostType, ?MODULE, config_schema, undefined). + gen_mod:get_module_opt(HostType, ?MODULE, computed_config_schema, undefined). %%==================================================================== %% Administration API @@ -199,10 +199,11 @@ set_dynamic_opts(HostType, Opts) -> %% Prepare config schema Def = gen_mod:get_opt(config_schema, Opts, default_schema_definition()), ConfigSchema = mod_muc_light_room_config:schema_from_definition(Def), - gen_mod:set_module_opt(HostType, ?MODULE, config_schema, ConfigSchema), + %% XXX That is a bad style + gen_mod:set_module_opt(HostType, ?MODULE, computed_config_schema, ConfigSchema), %% Prepare default config DefaultConfig = mod_muc_light_room_config:default_from_schema(ConfigSchema), - gen_mod:set_module_opt(HostType, ?MODULE, default_config, DefaultConfig). + gen_mod:set_module_opt(HostType, ?MODULE, computed_default_config, DefaultConfig). %% Config callbacks -spec config_spec() -> mongoose_config_spec:config_section(). diff --git a/src/muc_light/mod_muc_light_room_config.erl b/src/muc_light/mod_muc_light_room_config.erl index 5c13a675f2a..1bc309acb7e 100644 --- a/src/muc_light/mod_muc_light_room_config.erl +++ b/src/muc_light/mod_muc_light_room_config.erl @@ -73,7 +73,7 @@ %%==================================================================== -spec schema_from_definition(UserDefinedSchema :: user_defined_schema()) -> schema(). -schema_from_definition(UserDefinedSchema) -> +schema_from_definition(UserDefinedSchema) when is_list(UserDefinedSchema) -> lists:foldl(fun add_config_schema_field/2, #schema{}, UserDefinedSchema). -spec default_from_schema(ConfigSchema :: schema()) -> kv(). From 0e209ce5919fa8dd2c34e1ebec0e6cef2412cbc2 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 26 May 2021 09:50:49 +0200 Subject: [PATCH 55/73] Properly set computed muc_light options --- big_tests/tests/muc_light_SUITE.erl | 4 +++- big_tests/tests/muc_light_helper.erl | 2 +- src/muc_light/mod_muc_light.erl | 8 ++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/big_tests/tests/muc_light_SUITE.erl b/big_tests/tests/muc_light_SUITE.erl index d81a4cd6eba..31772b364af 100644 --- a/big_tests/tests/muc_light_SUITE.erl +++ b/big_tests/tests/muc_light_SUITE.erl @@ -4,6 +4,7 @@ -include_lib("escalus/include/escalus_xmlns.hrl"). -include_lib("common_test/include/ct.hrl"). -include_lib("exml/include/exml.hrl"). +-include("mam_helper.hrl"). -export([ % service removing_users_from_server_triggers_room_destruction/1 @@ -456,7 +457,8 @@ rooms_in_rosters_doesnt_break_disco_info(Config) -> no_roomname_in_schema_doesnt_break_disco_and_roster(Config) -> escalus:story(Config, [{alice, 1}], fun(Alice) -> [DiscoItem] = get_disco_rooms(Alice), - ?ROOM = exml_query:attr(DiscoItem, <<"name">>), + ?assert_equal_extra(?ROOM, exml_query:attr(DiscoItem, <<"name">>), + #{elem => DiscoItem}), escalus:send(Alice, escalus_stanza:roster_get()), RosterResult = escalus:wait_for_stanza(Alice), diff --git a/big_tests/tests/muc_light_helper.erl b/big_tests/tests/muc_light_helper.erl index 6209d5ebdfd..92d334a6d5f 100644 --- a/big_tests/tests/muc_light_helper.erl +++ b/big_tests/tests/muc_light_helper.erl @@ -265,7 +265,7 @@ ver(Int) -> set_mod_config(K, V, SubHost) -> {ok, HostType} = rpc(mim(), mongoose_domain_api, get_subdomain_host_type, [SubHost]), - true = rpc(mim(), gen_mod, set_module_opt, [HostType, mod_muc_light, K, V]). + true = rpc(mim(), mod_muc_light, set_module_opt_from_ct, [HostType, K, V]). assert_no_aff_duplicates(AffUsers) -> Users = [US || {US, _} <- AffUsers], diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index f780831dd32..ff19d7b10af 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -60,6 +60,9 @@ %% for mod_muc_light_codec_legacy -export([subdomain_pattern/1]). +%% For tests +-export([set_module_opt_from_ct/3]). + -type muc_server() :: jid:lserver(). -type host_type() :: mongooseim:host_type(). @@ -95,6 +98,11 @@ default_config_for_host_type(HostType) -> config_schema_for_host_type(HostType) -> gen_mod:get_module_opt(HostType, ?MODULE, computed_config_schema, undefined). +set_module_opt_from_ct(HostType, K, V) -> + gen_mod:set_module_opt(HostType, ?MODULE, K, V), + Opts = gen_mod:get_module_opts(HostType, ?MODULE), + set_dynamic_opts(HostType, Opts). + %%==================================================================== %% Administration API %%==================================================================== From 298dd1e1d14b0e25e8270125b8654fd352987135 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 09:54:18 +0200 Subject: [PATCH 56/73] Properly init muc_light_host in rest_client_SUITE --- big_tests/tests/rest_client_SUITE.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/big_tests/tests/rest_client_SUITE.erl b/big_tests/tests/rest_client_SUITE.erl index f78e0bd636a..8283eb87953 100644 --- a/big_tests/tests/rest_client_SUITE.erl +++ b/big_tests/tests/rest_client_SUITE.erl @@ -16,7 +16,6 @@ ). -import(muc_light_helper, [set_mod_config/3]). --import(distributed_helper, [subhost_pattern/1]). -define(PRT(X, Y), ct:pal("~p: ~p", [X, Y])). -define(OK, {<<"200">>, <<"OK">>}). @@ -116,12 +115,13 @@ security_test_cases() -> init_per_suite(C) -> application:ensure_all_started(shotgun), Host = ct:get_config({hosts, mim, host_type}), - dynamic_modules:save_modules(Host, C), - C1 = rest_helper:maybe_enable_mam(mam_helper:backend(), Host, C), + C1 = dynamic_modules:save_modules(Host, C), + C2 = rest_helper:maybe_enable_mam(mam_helper:backend(), Host, C1), + MucPattern = distributed_helper:subhost_pattern(muc_light_helper:muc_host_pattern()), dynamic_modules:start(Host, mod_muc_light, - [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}, + [{host, MucPattern}, {rooms_in_rosters, true}]), - [{muc_light_host, muc_light_helper:muc_host()} | escalus:init_per_suite(C1)]. + [{muc_light_host, muc_light_helper:muc_host()} | escalus:init_per_suite(C2)]. end_per_suite(Config) -> escalus_fresh:clean(), From 3bf05be87b1073c37398493c376a4f19c03d712e Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 26 May 2021 10:14:42 +0200 Subject: [PATCH 57/73] Don't call force_clear, if muc_light not loaded in push_integration_SUITE --- big_tests/tests/muc_light_helper.erl | 2 +- big_tests/tests/push_integration_SUITE.erl | 10 ++++++++-- src/muc_light/mod_muc_light.erl | 6 +++++- src/muc_light/mod_muc_light_utils.erl | 10 ++++++++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/big_tests/tests/muc_light_helper.erl b/big_tests/tests/muc_light_helper.erl index 92d334a6d5f..5a6e0900d44 100644 --- a/big_tests/tests/muc_light_helper.erl +++ b/big_tests/tests/muc_light_helper.erl @@ -255,7 +255,7 @@ stanza_aff_set(Room, AffUsers) -> clear_db() -> Node = mim(), - rpc(Node#{timeout => timer:seconds(15)}, mod_muc_light_db_backend, force_clear, []). + rpc(Node#{timeout => timer:seconds(15)}, mod_muc_light, force_clear_from_ct, []). -spec ver(Int :: integer()) -> binary(). ver(Int) -> diff --git a/big_tests/tests/push_integration_SUITE.erl b/big_tests/tests/push_integration_SUITE.erl index d3d5243b6ac..4790ca18893 100644 --- a/big_tests/tests/push_integration_SUITE.erl +++ b/big_tests/tests/push_integration_SUITE.erl @@ -157,7 +157,13 @@ init_per_group(G, Config) when G =:= pm_notifications_with_inbox; init_per_group(G, Config) -> %% Some cleaning up C = init_modules(G, Config), - muc_light_helper:clear_db(), + ReqMods = proplists:get_value(required_modules, C, []), + case lists:keymember(mod_muc_light, 1, ReqMods) of + true -> + muc_light_helper:clear_db(); + false -> + ct:log("Skip muc_light_helper:clear_db()", []) + end, C. end_per_group(_, Config) -> @@ -968,7 +974,7 @@ init_modules(G, Config) -> C = dynamic_modules:save_modules(domain(), Config), Fun = fun() -> catch dynamic_modules:ensure_modules(domain(), Modules) end, mongoose_helper:wait_until(Fun, ok), - [{api_v, MongoosePushAPI} | C]. + [{api_v, MongoosePushAPI}, {required_modules, Modules} | C]. mongoose_push_api_for_group(failure_cases_v2) -> "v2"; diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index ff19d7b10af..e6989f53de3 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -61,7 +61,8 @@ -export([subdomain_pattern/1]). %% For tests --export([set_module_opt_from_ct/3]). +-export([set_module_opt_from_ct/3, + force_clear_from_ct/0]). -type muc_server() :: jid:lserver(). -type host_type() :: mongooseim:host_type(). @@ -103,6 +104,9 @@ set_module_opt_from_ct(HostType, K, V) -> Opts = gen_mod:get_module_opts(HostType, ?MODULE), set_dynamic_opts(HostType, Opts). +force_clear_from_ct() -> + mod_muc_light_db_backend:force_clear(). + %%==================================================================== %% Administration API %%==================================================================== diff --git a/src/muc_light/mod_muc_light_utils.erl b/src/muc_light/mod_muc_light_utils.erl index 6429ef3d7c4..4ce7d0e0342 100644 --- a/src/muc_light/mod_muc_light_utils.erl +++ b/src/muc_light/mod_muc_light_utils.erl @@ -313,5 +313,11 @@ muc_host_to_host_type(MucHost) -> end. run_forget_room_hook({Room, MucHost}) -> - HostType = muc_host_to_host_type(MucHost), - mongoose_hooks:forget_room(HostType, MucHost, Room). + case mongoose_domain_api:get_subdomain_host_type(MucHost) of + {ok, HostType} -> + mongoose_hooks:forget_room(HostType, MucHost, Room); + Other -> + %% MUC light is not started probably + ?LOG_ERROR(#{what => run_forget_room_hook_skipped, + room => Room, muc_host => MucHost}) + end. From 97061cf4734b91d2aa405d133584d65f7b5c29ac Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 26 May 2021 11:42:00 +0200 Subject: [PATCH 58/73] Simplify muc_light_SUITE:set_custom_config/1 function Also, allow mod_muc_light to set computed options on its own --- big_tests/tests/muc_light_SUITE.erl | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/big_tests/tests/muc_light_SUITE.erl b/big_tests/tests/muc_light_SUITE.erl index 31772b364af..aea2e3497ce 100644 --- a/big_tests/tests/muc_light_SUITE.erl +++ b/big_tests/tests/muc_light_SUITE.erl @@ -1131,14 +1131,8 @@ set_default_mod_config() -> ]). -spec set_custom_config(UserDefSchema :: list()) -> any(). -set_custom_config(UserDefSchema) -> - ConfigSchema = rpc(mod_muc_light_room_config, schema_from_definition, [UserDefSchema]), - - % Valid default config is a proplist - [_|_] = DefaultConfig = rpc(mod_muc_light_room_config, default_from_schema, [ConfigSchema]), - - set_mod_config(config_schema, ConfigSchema, ?MUCHOST), - set_mod_config(default_config, DefaultConfig, ?MUCHOST). +set_custom_config(UserDefSchema) when is_list(UserDefSchema) -> + set_mod_config(config_schema, UserDefSchema, ?MUCHOST). domain() -> ct:get_config({hosts, mim, domain}). From 2e0b199db3dc84c0343a1d1a48762d2529398001 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Wed, 26 May 2021 13:23:18 +0200 Subject: [PATCH 59/73] Add register_for_global_distrib in mod_muc --- src/mod_muc.erl | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 22b7da0807f..ad329f33287 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -424,6 +424,7 @@ init([HostType, Opts]) -> text => <<"Only one MUC domain would work with this host type">>}) end, mongoose_domain_api:register_subdomain(HostType, SubdomainPattern, PacketHandler), + register_for_global_distrib(HostType), %% Loading case gen_mod:get_module_opt(HostType, mod_muc, load_permanent_rooms_at_startup, false) of false -> @@ -532,6 +533,7 @@ stop_if_hibernated_for_specified_time(Pid, Now, Timeout, {hibernated, LastHibern terminate(_Reason, #state{host_type = HostType, subdomain_pattern = SubdomainPattern}) -> mongoose_domain_api:unregister_subdomain(HostType, SubdomainPattern), + unregister_for_global_distrib(HostType), ok. code_change(_OldVsn, State, _Extra) -> @@ -1335,3 +1337,12 @@ make_server_host(To, State = #state{host_type = HostType, {fqdn, _} -> HostType end. + +register_for_global_distrib(HostType) -> + %% Would not work for multitenancy + SubHost = gen_mod:get_module_opt_subhost(HostType, ?MODULE, default_host()), + mongoose_hooks:register_subhost(SubHost, false). + +unregister_for_global_distrib(HostType) -> + SubHost = gen_mod:get_module_opt_subhost(HostType, ?MODULE, default_host()), + mongoose_hooks:unregister_subhost(SubHost). From 72cbb5a5dd6b114fecb6e8ef6ec4bc5dce244f81 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Thu, 27 May 2021 00:05:56 +0200 Subject: [PATCH 60/73] Update spec to skip tests --- big_tests/dynamic_domains.spec | 44 ++++++++++++++++++++++++ big_tests/tests/mam_SUITE.erl | 62 ++++++---------------------------- 2 files changed, 55 insertions(+), 51 deletions(-) diff --git a/big_tests/dynamic_domains.spec b/big_tests/dynamic_domains.spec index 5d4dc655b7a..e8f5c28451e 100644 --- a/big_tests/dynamic_domains.spec +++ b/big_tests/dynamic_domains.spec @@ -34,6 +34,50 @@ {skip_groups, "tests", inbox_extensions_SUITE, [muclight], "at the moment muclight doesn't support dynamic domains"}. +{skip_cases, "tests", mam_SUITE, + [muc_service_discovery, mam_service_discovery], + "at the moment mod_disco doesn't support dynamic domains"}. +{skip_cases, "tests", mam_SUITE, + [messages_filtered_when_prefs_default_policy_is_roster], + "at the moment mod_roster doesn't support dynamic domains"}. + +{skip_groups, "tests", muc_SUITE, + [disco, disco_non_parallel, disco_rsm, disco_rsm_with_offline], + "at the moment mod_disco doesn't support dynamic domains"}. +{skip_groups, "tests", muc_SUITE, + [register_over_s2s], + "at the moment S2S doesn't support dynamic domains " + "(requires mod_register creating CT users)"}. + +{skip_cases, "tests", muc_light_SUITE, + [disco_service, + disco_features, + disco_features_with_mam, + disco_info, + disco_info_with_mam, + disco_rooms, + disco_rooms_rsm, + disco_rooms_created_page_1, + disco_rooms_created_page_infinity, + disco_rooms_empty_page_infinity, + disco_rooms_empty_page_1], + "at the moment mod_disco doesn't support dynamic domains"}. +{skip_cases, "tests", muc_light_SUITE, + [rooms_in_rosters, + rooms_in_rosters_doesnt_break_disco_info, + no_roomname_in_schema_doesnt_break_disco_and_roster], + "at the moment mod_roster doesn't support dynamic domains"}. + +{skip_cases, "tests", muc_light_legacy_SUITE, + [disco_service, + disco_features, + disco_features_with_mam, + disco_info, + disco_info_with_mam, + disco_rooms, + disco_rooms_rsm], + "at the moment mod_disco doesn't support dynamic domains"}. + {config, ["dynamic_domains.config", "test.config"]}. {logdir, "ct_report"}. diff --git a/big_tests/tests/mam_SUITE.erl b/big_tests/tests/mam_SUITE.erl index 0bfd41c68d9..06790a54c10 100644 --- a/big_tests/tests/mam_SUITE.erl +++ b/big_tests/tests/mam_SUITE.erl @@ -313,28 +313,6 @@ groups() -> is_skipped(_, _) -> false. -disco_disabled() -> - try - ct:get_config({disable_disco_tests}) - catch _:_ -> - false - end. - -carbons_disabled() -> - try - ct:get_config({disable_carbons_tests}) - catch _:_ -> - false - end. - -roster_disabled() -> - try - ct:get_config({disable_roster_tests}) - catch _:_ -> - false - end. - - basic_groups() -> [ {mam_all, [parallel], @@ -372,16 +350,10 @@ basic_groups() -> [{mam04, [parallel], chat_markers_cases()}]}, {disabled_retraction, [], [{mam06, [parallel], disabled_retract_cases() ++ - case disco_disabled() of - true -> []; - false -> [mam_service_discovery] - end}]}, + [mam_service_discovery]}]}, {muc_disabled_retraction, [], [{muc06, [parallel], disabled_muc_retract_cases() ++ - case disco_disabled() of - true -> []; - false -> [muc_service_discovery] - end}]} + [muc_service_discovery]}]} ]. chat_markers_cases() -> @@ -393,11 +365,8 @@ mam_metrics_cases() -> metric_incremented_when_store_message]. mam_cases() -> - case disco_disabled() of - true -> []; - false -> [mam_service_discovery] - end ++ - [simple_archive_request, + [mam_service_discovery, + simple_archive_request, simple_archive_request_for_the_receiver, range_archive_request, range_archive_request_not_empty, @@ -430,11 +399,8 @@ archived_cases() -> filter_forwarded]. stanzaid_cases() -> - [message_with_stanzaid] ++ - case carbons_disabled() of - true -> []; - false -> [stanza_id_is_appended_to_carbons] - end. + [message_with_stanzaid, + stanza_id_is_appended_to_carbons]. retract_cases() -> [retract_message, @@ -448,11 +414,8 @@ nostore_cases() -> nostore_hint]. muc_cases() -> - case disco_disabled() of - true -> []; - false -> [muc_service_discovery] - end ++ - [muc_archive_request, + [muc_service_discovery, + muc_archive_request, muc_multiple_devices, muc_protected_message, muc_deny_protected_room_access, @@ -544,12 +507,9 @@ prefs_cases() -> prefs_set_cdata_request, query_get_request, messages_filtered_when_prefs_default_policy_is_always, - messages_filtered_when_prefs_default_policy_is_never] ++ - case roster_disabled() of - true -> []; - false -> [messages_filtered_when_prefs_default_policy_is_roster] - end ++ - [run_set_and_get_prefs_cases]. + messages_filtered_when_prefs_default_policy_is_never, + messages_filtered_when_prefs_default_policy_is_roster, + run_set_and_get_prefs_cases]. impl_specific() -> [check_user_exist]. From 34b7affbfd9bc0b6bd06aa9e06bb31316630ecbf Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Thu, 27 May 2021 09:56:18 +0200 Subject: [PATCH 61/73] Rename mod_muc_light_db_rdbms:main_host/1 --- src/muc_light/mod_muc_light_db_rdbms.erl | 38 +++++++++++++----------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/muc_light/mod_muc_light_db_rdbms.erl b/src/muc_light/mod_muc_light_db_rdbms.erl index ac040ad3002..d84fd2ee5a6 100644 --- a/src/muc_light/mod_muc_light_db_rdbms.erl +++ b/src/muc_light/mod_muc_light_db_rdbms.erl @@ -375,10 +375,10 @@ delete_blocking(HostType, UserU, UserS) -> AffUsers :: aff_users(), Version :: binary()) -> {ok, FinalRoomUS :: jid:simple_bare_jid()} | {error, exists}. create_room({<<>>, RoomS} = RoomUS, Config, AffUsers, Version) -> - HostType = main_host(RoomUS), + HostType = room_us_to_host_type(RoomUS), create_room_with_random_name(HostType, RoomS, Config, AffUsers, Version, 10); create_room(RoomUS, Config, AffUsers, Version) -> - HostType = main_host(RoomUS), + HostType = room_us_to_host_type(RoomUS), create_room_with_specified_name(HostType, RoomUS, Config, AffUsers, Version). create_room_with_random_name(_HostType, RoomS, _Config, _AffUsers, _Version, 0) -> @@ -418,21 +418,21 @@ create_room_with_specified_name(HostType, RoomUS, Config, AffUsers, Version) -> -spec destroy_room(RoomUS :: jid:simple_bare_jid()) -> ok | {error, not_exists | not_empty}. destroy_room(RoomUS) -> - HostType = main_host(RoomUS), + HostType = room_us_to_host_type(RoomUS), F = fun() -> destroy_room_transaction(HostType, RoomUS) end, {atomic, Res} = mongoose_rdbms:sql_transaction(HostType, F), Res. -spec room_exists(RoomUS :: jid:simple_bare_jid()) -> boolean(). room_exists({RoomU, RoomS} = RoomUS) -> - HostType = main_host(RoomUS), + HostType = room_us_to_host_type(RoomUS), {selected, Res} = select_room_id(HostType, RoomU, RoomS), Res /= []. -spec get_user_rooms(UserUS :: jid:simple_bare_jid(), MUCServer :: jid:lserver() | undefined) -> [RoomUS :: jid:simple_bare_jid()]. -get_user_rooms({LUser, LServer} = US, undefined) -> +get_user_rooms({LUser, LServer}, undefined) -> %% Only one hosttype is handled here %% It is used to be map over MYHOSTS HostType = mod_muc_light_utils:server_host_to_host_type(LServer), @@ -480,7 +480,7 @@ remove_domain(HostType, RoomS, LServer) -> -spec get_config(RoomUS :: jid:simple_bare_jid()) -> {ok, mod_muc_light_room_config:kv(), Version :: binary()} | {error, not_exists}. get_config({RoomU, RoomS} = RoomUS) -> - HostType = main_host(RoomUS), + HostType = room_us_to_host_type(RoomUS), {selected, Result} = select_config_by_us(HostType, RoomU, RoomS), case Result of [] -> @@ -498,7 +498,7 @@ get_config({RoomU, RoomS} = RoomUS) -> Version :: binary()) -> {ok, PrevVersion :: binary()} | {error, not_exists}. set_config(RoomUS, ConfigChanges, Version) -> - HostType = main_host(RoomUS), + HostType = room_us_to_host_type(RoomUS), {atomic, Res} = mongoose_rdbms:sql_transaction( HostType, fun() -> set_config_transaction(RoomUS, ConfigChanges, Version) end), @@ -515,7 +515,7 @@ set_config(RoomJID, Key, Val, Version) -> -spec get_blocking(UserUS :: jid:simple_bare_jid(), MUCServer :: jid:lserver()) -> [blocking_item()]. get_blocking({LUser, LServer}, MUCServer) -> - HostType = main_host(MUCServer), + HostType = muc_server_to_host_type(MUCServer), {selected, WhatWhos} = select_blocking(HostType, LUser, LServer), decode_blocking(WhatWhos). @@ -524,7 +524,7 @@ get_blocking({LUser, LServer}, MUCServer) -> WhatWhos :: [{blocking_what(), jid:simple_bare_jid()}]) -> blocking_action(). get_blocking({LUser, LServer}, MUCServer, WhatWhos) -> - HostType = main_host(MUCServer), + HostType = muc_server_to_host_type(MUCServer), {selected, [{Count}]} = select_blocking_cnt(HostType, LUser, LServer, WhatWhos), case mongoose_rdbms:result_to_integer(Count) of 0 -> allow; @@ -535,7 +535,7 @@ get_blocking({LUser, LServer}, MUCServer, WhatWhos) -> MUCServer :: jid:lserver(), BlockingItems :: [blocking_item()]) -> ok. set_blocking(UserUS, MUCServer, BlockingItems) -> - HostType = main_host(MUCServer), + HostType = muc_server_to_host_type(MUCServer), set_blocking_loop(HostType, UserUS, MUCServer, BlockingItems). set_blocking_loop(_HostType, _UserUS, _MUCServer, []) -> @@ -554,7 +554,7 @@ set_blocking_loop(HostType, {LUser, LServer} = UserUS, MUCServer, -spec get_aff_users(RoomUS :: jid:simple_bare_jid()) -> {ok, aff_users(), Version :: binary()} | {error, not_exists}. get_aff_users({RoomU, RoomS} = RoomUS) -> - HostType = main_host(RoomUS), + HostType = room_us_to_host_type(RoomUS), case select_affs_by_us(HostType, RoomU, RoomS) of {selected, []} -> {error, not_exists}; @@ -571,7 +571,7 @@ get_aff_users({RoomU, RoomS} = RoomUS) -> Version :: binary()) -> mod_muc_light_db:modify_aff_users_return(). modify_aff_users(RoomUS, AffUsersChanges, ExternalCheck, Version) -> - HostType = main_host(RoomUS), + HostType = room_us_to_host_type(RoomUS), F = fun() -> modify_aff_users_transaction(HostType, RoomUS, AffUsersChanges, ExternalCheck, Version) end, {atomic, Res} = mongoose_rdbms:sql_transaction(HostType, F), @@ -583,7 +583,7 @@ modify_aff_users(RoomUS, AffUsersChanges, ExternalCheck, Version) -> {ok, mod_muc_light_room_config:kv(), aff_users(), Version :: binary()} | {error, not_exists}. get_info({RoomU, RoomS} = RoomUS) -> - HostType = main_host(RoomUS), + HostType = room_us_to_host_type(RoomUS), case select_room_id_and_version(HostType, RoomU, RoomS) of {selected, [{DbRoomID, Version}]} -> RoomID = mongoose_rdbms:result_to_integer(DbRoomID), @@ -710,7 +710,7 @@ remove_user_transaction(HostType, {UserU, UserS} = UserUS, Version) -> Version :: binary()) -> {ok, PrevVersion :: binary()} | {error, not_exists}. set_config_transaction({RoomU, RoomS} = RoomUS, ConfigChanges, Version) -> - HostType = main_host(RoomUS), + HostType = room_us_to_host_type(RoomUS), case select_room_id_and_version(HostType, RoomU, RoomS) of {selected, [{DbRoomID, PrevVersion}]} -> RoomID = mongoose_rdbms:result_to_integer(DbRoomID), @@ -792,8 +792,10 @@ apply_aff_users_transaction(HostType, RoomID, AffUsersChanged, JoiningUsers) -> %% ------------------------ Common ------------------------ --spec main_host(JIDOrServer :: jid:simple_bare_jid() | binary()) -> mongooseim:host_type(). -main_host({_, RoomS}) -> - main_host(RoomS); -main_host(MUCServer) -> +-spec room_us_to_host_type(jid:simple_bare_jid()) -> mongooseim:host_type(). +room_us_to_host_type({_, RoomS}) -> + muc_server_to_host_type(RoomS). + +-spec muc_server_to_host_type(jid:lserver()) -> mongooseim:host_type(). +muc_server_to_host_type(MUCServer) -> mod_muc_light_utils:muc_host_to_host_type(MUCServer). From 8fa626f185b9e9390c9a22f32260d07dc284ad76 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Tue, 25 May 2021 15:03:58 +0200 Subject: [PATCH 62/73] Remove usage of subhosts module from mod_domain_isolation Refactor test cases in domain_isolation_SUITE Use host_types in domain_isolation_SUITE --- big_tests/tests/domain_isolation_SUITE.erl | 154 +++++++++++++-------- src/mod_domain_isolation.erl | 6 +- 2 files changed, 103 insertions(+), 57 deletions(-) diff --git a/big_tests/tests/domain_isolation_SUITE.erl b/big_tests/tests/domain_isolation_SUITE.erl index cac825a9960..ac2eb8bf960 100644 --- a/big_tests/tests/domain_isolation_SUITE.erl +++ b/big_tests/tests/domain_isolation_SUITE.erl @@ -10,91 +10,137 @@ suite() -> require_rpc_nodes([mim]). all() -> - [ - isolation_works_for_one2one, - isolation_works_for_one2one_2domains, - isolation_works_for_subdomains - ]. + [{group, two_domains}]. -domain() -> ct:get_config({hosts, mim, domain}). -domain2() -> ct:get_config({hosts, mim, secondary_domain}). -domains() -> [domain(), domain2()]. +groups() -> + [{two_domains, [parallel], cases()}]. + +cases() -> + [routing_one2one_message_inside_one_domain_works, + routing_one2one_message_to_another_domain_gets_dropped, + routing_one2one_message_to_another_domain_results_in_service_unavailable, + routing_to_yours_subdomain_gets_passed_to_muc_module, + routing_to_foreign_subdomain_results_in_service_unavailable]. + +host_type() -> ct:get_config({hosts, mim, host_type}). +host_type2() -> ct:get_config({hosts, mim, secondary_host_type}). %%-------------------------------------------------------------------- %% Init & teardown %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config2 = dynamic_modules:save_modules(domain(), Config), + Config2 = dynamic_modules:save_modules(host_type(), Config), escalus:init_per_suite(Config2). end_per_suite(Config) -> - dynamic_modules:restore_modules(domain(), Config), escalus_fresh:clean(), + dynamic_modules:restore_modules(host_type(), Config), escalus:end_per_suite(Config). +init_per_group(two_domains, Config) -> + MucHost = subhost_pattern(muc_helper:muc_host_pattern()), + dynamic_modules:restart(host_type(), mod_domain_isolation, [{extra_domains, [MucHost]}]), + dynamic_modules:restart(host_type2(), mod_domain_isolation, []), + dynamic_modules:restart(host_type(), mod_muc_light, [{host, MucHost}]), + Config. + +end_per_group(two_domains, Config) -> + dynamic_modules:stop(host_type(), mod_domain_isolation), + dynamic_modules:stop(host_type2(), mod_domain_isolation), + dynamic_modules:stop(host_type(), mod_muc_light), + Config. + +init_per_testcase(Testcase, Config) -> + escalus:init_per_testcase(Testcase, Config). -init_per_testcase(isolation_works_for_one2one_2domains = TestcaseName, Config) -> - [dynamic_modules:start(Host, mod_domain_isolation, []) || Host <- domains()], - escalus:init_per_testcase(TestcaseName, Config); -init_per_testcase(TestcaseName, Config) -> - Host = domain(), - MucHost = subhost_pattern(<<"muclight.", Host/binary>>), - dynamic_modules:start(Host, mod_domain_isolation, [{extra_domains, [MucHost]}]), - dynamic_modules:start(Host, mod_muc_light, [{host, MucHost}]), - escalus:init_per_testcase(TestcaseName, Config). - -end_per_testcase(isolation_works_for_one2one_2domains = TestcaseName, Config) -> - [dynamic_modules:stop(Host, mod_domain_isolation) || Host <- domains()], - escalus:end_per_testcase(TestcaseName, Config); -end_per_testcase(TestcaseName, Config) -> - Host = domain(), - dynamic_modules:stop(Host, mod_domain_isolation), - dynamic_modules:stop(Host, mod_muc_light), - escalus:end_per_testcase(TestcaseName, Config). +end_per_testcase(Testcase, Config) -> + escalus:end_per_testcase(Testcase, Config). %%-------------------------------------------------------------------- %% Tests %%-------------------------------------------------------------------- -isolation_works_for_one2one(Config) -> - F = fun(Alice, Bob, Bis) -> - %% Ignored from another domain - escalus_client:send(Bis, escalus_stanza:chat_to(Alice, <<"Hi!">>)), - %% Routed from the local domain +routing_one2one_message_inside_one_domain_works(Config) -> + F = fun(Alice, Bob) -> + %% WHEN Routed inside the same domain escalus_client:send(Bob, escalus_stanza:chat_to(Alice, <<"Hello">>)), + %% THEN Message gets delivered Stanza = escalus:wait_for_stanza(Alice), - escalus:assert(is_chat_message, [<<"Hello">>], Stanza), - %% Sender receives an error about the drop - Err = escalus:wait_for_stanza(Bis), - escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Err), - <<"Filtered by the domain isolation">> = get_error_text(Err) + escalus:assert(is_chat_message, [<<"Hello">>], Stanza) end, - escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {alice_bis, 1}], F). + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], F). -isolation_works_for_one2one_2domains(Config) -> - isolation_works_for_one2one(Config). +routing_one2one_message_to_another_domain_gets_dropped(Config) -> + F = fun(Alice, Bob, Bis) -> + %% GIVEN Alice and Bis are on different domains + %% WHEN A stanza is sent to another domain + escalus_client:send(Bis, escalus_stanza:chat_to(Alice, <<"Hello">>)), + %% THEN Receiver does not receive a message + verify_alice_has_no_pending_messages(Alice, Bob) + end, + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {alice_bis, 1}], F). -isolation_works_for_subdomains(Config) -> +routing_one2one_message_to_another_domain_results_in_service_unavailable(Config) -> F = fun(Alice, Bis) -> - Domain = domain(), - To = <<"muclight.", Domain/binary, "/room">>, - %% Ignored from another domain - escalus_client:send(Bis, escalus_stanza:chat_to(To, <<"Hi muc!">>)), - %% Sender receives an error about the drop - Err = escalus:wait_for_stanza(Bis), - escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Err), - <<"Filtered by the domain isolation">> = get_error_text(Err), - %% But if Alice is on the same domain, her message passes - escalus_client:send(Alice, escalus_stanza:chat_to(To, <<"Hi muc!">>)), - Ok = escalus:wait_for_stanza(Alice), - escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], Ok) + %% GIVEN Alice and Bis are on different domains + %% WHEN A stanza is sent to another domain + escalus_client:send(Bis, escalus_stanza:chat_to(Alice, <<"Hello">>)), + %% THEN Sender receives an error + receives_service_unavailable(Bis) end, escalus:fresh_story(Config, [{alice, 1}, {alice_bis, 1}], F). +routing_to_yours_subdomain_gets_passed_to_muc_module(Config) -> + F = fun(Alice) -> + %% GIVEN Alice is on the same domain + %% WHEN Alice routes a stanza + escalus_client:send(Alice, muc_stanza()), + %% THEN Alice receives an error from mod_muc, + %% like if there is no mod_domain_isolation. + receives_muc_bad_request(Alice) + end, + escalus:fresh_story(Config, [{alice, 1}], F). + +routing_to_foreign_subdomain_results_in_service_unavailable(Config) -> + F = fun(Alice) -> + %% GIVEN Alice is on another domain + %% WHEN Alice routes a stanza + escalus_client:send(Alice, muc_stanza()), + %% THEN Sender receives an error about the drop + receives_service_unavailable(Alice) + end, + escalus:fresh_story(Config, [{alice_bis, 1}], F). + %%-------------------------------------------------------------------- %% Helpers %%-------------------------------------------------------------------- get_error_text(Err) -> exml_query:path(Err, [{element, <<"error">>}, {element, <<"text">>}, cdata]). + +some_room_address() -> + MucHost = muc_helper:muc_host(), + <>. + +muc_stanza() -> + escalus_stanza:chat_to(some_room_address(), <<"Hi muc!">>). + +receives_service_unavailable(Alice) -> + Err = escalus:wait_for_stanza(Alice), + escalus:assert(is_error, [<<"cancel">>, <<"service-unavailable">>], Err), + <<"Filtered by the domain isolation">> = get_error_text(Err). + +receives_muc_bad_request(Alice) -> + Err = escalus:wait_for_stanza(Alice), + escalus:assert(is_error, [<<"modify">>, <<"bad-request">>], Err), + %% This error is generated by mod_muc: + <<"Resource expected to be empty">> = get_error_text(Err). + +%% Verify than there is no unreceived messages by Alice by routing a message from Bob. +%% Bob should be able to send messages to Alice. +%% If the Bob's message gets received - there is no pending messages. +verify_alice_has_no_pending_messages(Alice, Bob) -> + escalus_client:send(Bob, escalus_stanza:chat_to(Alice, <<"Forces to flush">>)), + Stanza = escalus:wait_for_stanza(Alice), + escalus:assert(is_chat_message, [<<"Forces to flush">>], Stanza). diff --git a/src/mod_domain_isolation.erl b/src/mod_domain_isolation.erl index 81ac9dd00fd..07e42024d4f 100644 --- a/src/mod_domain_isolation.erl +++ b/src/mod_domain_isolation.erl @@ -73,9 +73,9 @@ filter_local_packet({#jid{lserver = FromServer} = From, %% muc.localhost becomes localhost. %% localhost stays localhost. domain_to_host(Domain) -> - case mongoose_subhosts:get_host(Domain) of - {ok, Host} -> Host; - undefined -> Domain + case mongoose_domain_api:get_subdomain_info(Domain) of + {ok, #{parent_domain := Parent}} when is_binary(Parent) -> Parent; + _ -> Domain end. maybe_send_back_error(From, To, Acc, Arg) -> From 41fe3ab95a502eeb3d8ece1df3dce50c66767feb Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Thu, 27 May 2021 09:47:17 +0200 Subject: [PATCH 63/73] Use domain_helper:host_type() imports --- big_tests/tests/domain_helper.erl | 19 ++++++++++++------- big_tests/tests/domain_isolation_SUITE.erl | 8 +++----- big_tests/tests/login_SUITE.erl | 5 ++--- big_tests/tests/mam_helper.erl | 2 +- big_tests/tests/mongoose_helper.erl | 2 +- big_tests/tests/muc_SUITE.erl | 5 ++--- big_tests/tests/muc_helper.erl | 8 ++++---- big_tests/tests/muc_light_SUITE.erl | 4 +--- big_tests/tests/muc_light_legacy_SUITE.erl | 8 ++------ big_tests/tests/rest_client_SUITE.erl | 20 +++++++++----------- 10 files changed, 37 insertions(+), 44 deletions(-) diff --git a/big_tests/tests/domain_helper.erl b/big_tests/tests/domain_helper.erl index f5942bfd448..36aafcb3008 100644 --- a/big_tests/tests/domain_helper.erl +++ b/big_tests/tests/domain_helper.erl @@ -5,19 +5,24 @@ insert_domain/3, delete_domain/2, make_metrics_prefix/1, + host_type/0, host_type/1, - host_type/2]). + secondary_host_type/0, + secondary_host_type/1]). -import(distributed_helper, [get_or_fail/1, rpc/4, mim/0]). +host_type() -> + host_type(mim). + host_type(NodeKey) -> - host_type(NodeKey, domain). + get_or_fail({hosts, NodeKey, host_type}). + +secondary_host_type() -> + secondary_host_type(mim). -host_type(NodeKey, DomainKey) -> - Node = #{node => get_or_fail({hosts, NodeKey, node})}, - Domain = get_or_fail({hosts, NodeKey, DomainKey}), - {ok, HostType} = rpc(Node, mongoose_domain_core, get_host_type, [Domain]), - HostType. +secondary_host_type(NodeKey) -> + get_or_fail({hosts, NodeKey, secondary_host_type}). make_metrics_prefix(HostType) -> rpc(mim(), mongoose_metrics, make_host_type_name, [HostType]). diff --git a/big_tests/tests/domain_isolation_SUITE.erl b/big_tests/tests/domain_isolation_SUITE.erl index ac2eb8bf960..56e0d5b8128 100644 --- a/big_tests/tests/domain_isolation_SUITE.erl +++ b/big_tests/tests/domain_isolation_SUITE.erl @@ -5,6 +5,7 @@ -compile(export_all). -import(distributed_helper, [mim/0, require_rpc_nodes/1, rpc/4, subhost_pattern/1]). +-import(domain_helper, [host_type/0, secondary_host_type/0]). suite() -> require_rpc_nodes([mim]). @@ -22,9 +23,6 @@ cases() -> routing_to_yours_subdomain_gets_passed_to_muc_module, routing_to_foreign_subdomain_results_in_service_unavailable]. -host_type() -> ct:get_config({hosts, mim, host_type}). -host_type2() -> ct:get_config({hosts, mim, secondary_host_type}). - %%-------------------------------------------------------------------- %% Init & teardown %%-------------------------------------------------------------------- @@ -41,13 +39,13 @@ end_per_suite(Config) -> init_per_group(two_domains, Config) -> MucHost = subhost_pattern(muc_helper:muc_host_pattern()), dynamic_modules:restart(host_type(), mod_domain_isolation, [{extra_domains, [MucHost]}]), - dynamic_modules:restart(host_type2(), mod_domain_isolation, []), + dynamic_modules:restart(secondary_host_type(), mod_domain_isolation, []), dynamic_modules:restart(host_type(), mod_muc_light, [{host, MucHost}]), Config. end_per_group(two_domains, Config) -> dynamic_modules:stop(host_type(), mod_domain_isolation), - dynamic_modules:stop(host_type2(), mod_domain_isolation), + dynamic_modules:stop(secondary_host_type(), mod_domain_isolation), dynamic_modules:stop(host_type(), mod_muc_light), Config. diff --git a/big_tests/tests/login_SUITE.erl b/big_tests/tests/login_SUITE.erl index 39432c499a6..b4df48e9a2d 100644 --- a/big_tests/tests/login_SUITE.erl +++ b/big_tests/tests/login_SUITE.erl @@ -29,6 +29,8 @@ require_rpc_nodes/1, rpc/4]). +-import(domain_helper, [host_type/0]). + %%-------------------------------------------------------------------- %% Suite configuration %%-------------------------------------------------------------------- @@ -503,6 +505,3 @@ are_sasl_scram_modules_supported() -> restore_c2s(Config) -> {_, _, Opts} = C2SListener = proplists:get_value(c2s_listener, Config), mongoose_helper:restart_listener_with_opts(mim(), C2SListener, Opts). - -host_type() -> - ct:get_config({hosts, mim, domain}). diff --git a/big_tests/tests/mam_helper.erl b/big_tests/tests/mam_helper.erl index 19ccecfff0f..8690973126f 100644 --- a/big_tests/tests/mam_helper.erl +++ b/big_tests/tests/mam_helper.erl @@ -1233,7 +1233,7 @@ host() -> ct:get_config({hosts, mim, domain}). host_type() -> - ct:get_config({hosts, mim, host_type}). + domain_helper:host_type(). room_name(Config) -> AliceName = escalus_users:get_username(Config, alice), diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index 807e196548c..5c0d6974cc8 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -231,7 +231,7 @@ ensure_muc_clean() -> forget_persistent_rooms(). stop_online_rooms() -> - HostType = ct:get_config({hosts, mim, host_type}), + HostType = domain_helper:host_type(), Supervisor = rpc(mim(), gen_mod, get_module_proc, [HostType, ejabberd_mod_muc_sup]), SupervisorPid = rpc(mim(), erlang, whereis, [Supervisor]), case is_pid(SupervisorPid) of diff --git a/big_tests/tests/muc_SUITE.erl b/big_tests/tests/muc_SUITE.erl index babf11b7d36..329f8e9fc84 100644 --- a/big_tests/tests/muc_SUITE.erl +++ b/big_tests/tests/muc_SUITE.erl @@ -52,6 +52,8 @@ story_with_room/4 ]). +-import(domain_helper, [host_type/0]). + -define(MUC_CLIENT_HOST, <<"localhost/res1">>). -define(PASSWORD, <<"pa5sw0rd">>). -define(SUBJECT, <<"subject">>). @@ -84,9 +86,6 @@ items :: [#xmlel{}] }). -host_type() -> - ct:get_config({hosts, mim, host_type}). - %%-------------------------------------------------------------------- %% Suite configuration %%-------------------------------------------------------------------- diff --git a/big_tests/tests/muc_helper.erl b/big_tests/tests/muc_helper.erl index 39a87dabd65..afbbcddb377 100644 --- a/big_tests/tests/muc_helper.erl +++ b/big_tests/tests/muc_helper.erl @@ -48,7 +48,7 @@ load_muc() -> %% Stop modules before trying to start them unload_muc(), Backend = muc_backend(), - HostType = ct:get_config({hosts, mim, host_type}), + HostType = domain_helper:host_type(), MucHostPattern = ct:get_config({hosts, mim, muc_service_pattern}), ct:log("Starting MUC for ~p", [HostType]), dynamic_modules:start(HostType, mod_muc, @@ -64,7 +64,7 @@ load_muc() -> {access_log, muc}]). unload_muc() -> - HostType = ct:get_config({hosts, mim, host_type}), + HostType = domain_helper:host_type(), dynamic_modules:stop(HostType, mod_muc), dynamic_modules:stop(HostType, mod_muc_log). @@ -152,7 +152,7 @@ create_instant_room(Room, From, Nick, Opts) -> [ServerHost, muc_host(), Room1, From, Nick, Opts]). assert_valid_server(ServerHost) -> - HostType = ct:get_config({hosts, mim, host_type}), + HostType = domain_helper:host_type(), case rpc(mim(), mongoose_domain_api, get_domain_host_type, [ServerHost]) of {ok, HostType} -> ok; @@ -257,7 +257,7 @@ has_features(#xmlel{children = [ Query ]} = Iq, Features) -> %% %% - HostType = ct:get_config({hosts, mim, host_type}), + HostType = domain_helper:host_type(), Loaded = rpc(mim(), gen_mod, loaded_modules_with_opts, [HostType]), ct:log("Loaded modules:~n~p", [Loaded]), diff --git a/big_tests/tests/muc_light_SUITE.erl b/big_tests/tests/muc_light_SUITE.erl index aea2e3497ce..7a3a184777d 100644 --- a/big_tests/tests/muc_light_SUITE.erl +++ b/big_tests/tests/muc_light_SUITE.erl @@ -76,6 +76,7 @@ -import(escalus_ejabberd, [rpc/3]). -import(muc_helper, [foreach_occupant/3, foreach_recipient/2]). -import(distributed_helper, [subhost_pattern/1]). +-import(domain_helper, [host_type/0]). -import(muc_light_helper, [ bin_aff_users/1, gc_message_verify_fun/3, @@ -1136,6 +1137,3 @@ set_custom_config(UserDefSchema) when is_list(UserDefSchema) -> domain() -> ct:get_config({hosts, mim, domain}). - -host_type() -> - ct:get_config({hosts, mim, host_type}). diff --git a/big_tests/tests/muc_light_legacy_SUITE.erl b/big_tests/tests/muc_light_legacy_SUITE.erl index 274b7fdc72a..f8d68d34297 100644 --- a/big_tests/tests/muc_light_legacy_SUITE.erl +++ b/big_tests/tests/muc_light_legacy_SUITE.erl @@ -52,6 +52,7 @@ -import(escalus_ejabberd, [rpc/3]). -import(muc_helper, [foreach_occupant/3, foreach_recipient/2]). -import(distributed_helper, [subhost_pattern/1]). +-import(domain_helper, [host_type/0]). -import(muc_light_helper, [ bin_aff_users/1, to_lus/2, @@ -879,14 +880,9 @@ set_default_mod_config() -> domain() -> ct:get_config({hosts, mim, domain}). -host_type() -> - ct:get_config({hosts, mim, host_type}). - -muc_domain() -> muc_helper:muc_host(). - -spec room_bin_jid(Room :: binary()) -> binary(). room_bin_jid(Room) -> - <>. + <>. disco_disabled() -> try diff --git a/big_tests/tests/rest_client_SUITE.erl b/big_tests/tests/rest_client_SUITE.erl index 8283eb87953..65ae96b85cf 100644 --- a/big_tests/tests/rest_client_SUITE.erl +++ b/big_tests/tests/rest_client_SUITE.erl @@ -16,6 +16,7 @@ ). -import(muc_light_helper, [set_mod_config/3]). +-import(domain_helper, [host_type/0]). -define(PRT(X, Y), ct:pal("~p: ~p", [X, Y])). -define(OK, {<<"200">>, <<"OK">>}). @@ -112,22 +113,22 @@ security_test_cases() -> non_default_http_server_name_is_returned_if_configured ]. -init_per_suite(C) -> +init_per_suite(Config) -> application:ensure_all_started(shotgun), - Host = ct:get_config({hosts, mim, host_type}), - C1 = dynamic_modules:save_modules(Host, C), - C2 = rest_helper:maybe_enable_mam(mam_helper:backend(), Host, C1), + HostType = host_type(), + Config1 = dynamic_modules:save_modules(HostType, Config), + Config2 = rest_helper:maybe_enable_mam(mam_helper:backend(), HostType, Config1), MucPattern = distributed_helper:subhost_pattern(muc_light_helper:muc_host_pattern()), - dynamic_modules:start(Host, mod_muc_light, + dynamic_modules:start(HostType, mod_muc_light, [{host, MucPattern}, {rooms_in_rosters, true}]), - [{muc_light_host, muc_light_helper:muc_host()} | escalus:init_per_suite(C2)]. + [{muc_light_host, muc_light_helper:muc_host()} | escalus:init_per_suite(Config2)]. end_per_suite(Config) -> escalus_fresh:clean(), - Host = ct:get_config({hosts, mim, host_type}), + HostType = host_type(), application:stop(shotgun), - dynamic_modules:restore_modules(Host, Config), + dynamic_modules:restore_modules(HostType, Config), escalus:end_per_suite(Config). init_per_group(_GN, C) -> @@ -168,9 +169,6 @@ end_per_testcase(TC, C) -> config_to_muc_host(Config) -> ?config(muc_light_host, Config). -host_type() -> - ct:get_config({hosts, mim, domain}). - %% -------------------------------------------------------------------- %% Test cases %% -------------------------------------------------------------------- From 582f8898c8871718c48b1eb2ec78964fe843f719 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Thu, 27 May 2021 11:52:27 +0200 Subject: [PATCH 64/73] Refactor module starting in domain_isolation_SUITE Use HostType instead of Domain in dynamic_modules --- big_tests/tests/domain_isolation_SUITE.erl | 41 ++++++++++++---------- big_tests/tests/dynamic_modules.erl | 23 +++++++++--- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/big_tests/tests/domain_isolation_SUITE.erl b/big_tests/tests/domain_isolation_SUITE.erl index 56e0d5b8128..40131167ae2 100644 --- a/big_tests/tests/domain_isolation_SUITE.erl +++ b/big_tests/tests/domain_isolation_SUITE.erl @@ -23,30 +23,35 @@ cases() -> routing_to_yours_subdomain_gets_passed_to_muc_module, routing_to_foreign_subdomain_results_in_service_unavailable]. +host_types() -> + %% This suite tests domain isolation. + %% But two domains could be on the same host type, and still should be isolated. + %% So, we could need to init modules only once. + lists:usort([host_type(), secondary_host_type()]). + %%-------------------------------------------------------------------- %% Init & teardown %%-------------------------------------------------------------------- init_per_suite(Config) -> - Config2 = dynamic_modules:save_modules(host_type(), Config), - escalus:init_per_suite(Config2). + escalus:init_per_suite(Config). end_per_suite(Config) -> - escalus_fresh:clean(), - dynamic_modules:restore_modules(host_type(), Config), escalus:end_per_suite(Config). -init_per_group(two_domains, Config) -> +modules() -> MucHost = subhost_pattern(muc_helper:muc_host_pattern()), - dynamic_modules:restart(host_type(), mod_domain_isolation, [{extra_domains, [MucHost]}]), - dynamic_modules:restart(secondary_host_type(), mod_domain_isolation, []), - dynamic_modules:restart(host_type(), mod_muc_light, [{host, MucHost}]), - Config. + [{mod_domain_isolation, []}, + {mod_muc_light, [{host, MucHost}]}]. + +init_per_group(two_domains, Config) -> + Config2 = dynamic_modules:save_modules_for_host_types(host_types(), Config), + [dynamic_modules:ensure_modules(HostType, modules()) || HostType <- host_types()], + Config2. end_per_group(two_domains, Config) -> - dynamic_modules:stop(host_type(), mod_domain_isolation), - dynamic_modules:stop(secondary_host_type(), mod_domain_isolation), - dynamic_modules:stop(host_type(), mod_muc_light), + escalus_fresh:clean(), + dynamic_modules:restore_modules(Config), Config. init_per_testcase(Testcase, Config) -> @@ -93,7 +98,7 @@ routing_to_yours_subdomain_gets_passed_to_muc_module(Config) -> F = fun(Alice) -> %% GIVEN Alice is on the same domain %% WHEN Alice routes a stanza - escalus_client:send(Alice, muc_stanza()), + escalus_client:send(Alice, invalid_muc_stanza()), %% THEN Alice receives an error from mod_muc, %% like if there is no mod_domain_isolation. receives_muc_bad_request(Alice) @@ -104,7 +109,7 @@ routing_to_foreign_subdomain_results_in_service_unavailable(Config) -> F = fun(Alice) -> %% GIVEN Alice is on another domain %% WHEN Alice routes a stanza - escalus_client:send(Alice, muc_stanza()), + escalus_client:send(Alice, invalid_muc_stanza()), %% THEN Sender receives an error about the drop receives_service_unavailable(Alice) end, @@ -117,12 +122,12 @@ routing_to_foreign_subdomain_results_in_service_unavailable(Config) -> get_error_text(Err) -> exml_query:path(Err, [{element, <<"error">>}, {element, <<"text">>}, cdata]). -some_room_address() -> +invalid_muc_address() -> MucHost = muc_helper:muc_host(), - <>. + <>. -muc_stanza() -> - escalus_stanza:chat_to(some_room_address(), <<"Hi muc!">>). +invalid_muc_stanza() -> + escalus_stanza:chat_to(invalid_muc_address(), <<"Hi muc!">>). receives_service_unavailable(Alice) -> Err = escalus:wait_for_stanza(Alice), diff --git a/big_tests/tests/dynamic_modules.erl b/big_tests/tests/dynamic_modules.erl index d0e53ed54ac..cf204d0584a 100644 --- a/big_tests/tests/dynamic_modules.erl +++ b/big_tests/tests/dynamic_modules.erl @@ -2,14 +2,21 @@ -include_lib("common_test/include/ct.hrl"). --export([save_modules/2, ensure_modules/2, restore_modules/2]). +-export([save_modules_for_host_types/2]). +-export([save_modules/2, ensure_modules/2, restore_modules/2, restore_modules/1]). -export([stop/2, stop/3, start/3, start/4, restart/3, stop_running/2, start_running/1]). -import(distributed_helper, [mim/0, rpc/4]). +save_modules_for_host_types([HostType|HostTypes], Config) -> + Config2 = save_modules(HostType, Config), + save_modules_for_host_types(HostTypes, Config2); +save_modules_for_host_types([], Config) -> + Config. + save_modules(HostType, Config) -> - [{saved_modules, get_current_modules(HostType)} | Config]. + [{{saved_modules, HostType}, get_current_modules(HostType)} | Config]. ensure_modules(HostType, RequiredModules) -> CurrentModules = get_current_modules(HostType), @@ -31,10 +38,15 @@ to_replace([RequiredModule | Rest], CurrentModules, ReplaceAcc, ReplaceWithAcc) to_replace(Rest, CurrentModules, NewReplaceAcc, NewReplaceWithAcc). restore_modules(HostType, Config) -> - SavedModules = ?config(saved_modules, Config), + SavedModules = ?config({saved_modules, HostType}, Config), CurrentModules = get_current_modules(HostType), rpc(mim(), gen_mod_deps, replace_modules, [HostType, CurrentModules, SavedModules]). +restore_modules(Config) -> + [restore_modules(HostType, Config) + || {{saved_modules, HostType}, _SavedModules} <- Config], + Config. + get_current_modules(HostType) -> rpc(mim(), gen_mod, loaded_modules_with_opts, [HostType]). @@ -70,9 +82,10 @@ start(Node, HostType, Mod, Args) -> case escalus_rpc:call(Node, gen_mod, start_module, [HostType, Mod, Args], 5000, Cookie) of {badrpc, Reason} -> ct:fail("Cannot start module ~p reason ~p", [Mod, Reason]); + {ok, _} = R -> + R; Reason -> - ct:fail("Cannot start module ~p reason ~p", [Mod, Reason]); - R -> R + ct:fail("Cannot start module ~p reason ~p", [Mod, Reason]) end. restart(HostType, Mod, Args) -> From 4394a0e278f64acb197a97436b30da2749e9c4f4 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Fri, 28 May 2021 15:11:14 +0200 Subject: [PATCH 65/73] Add admin user to dynamic_domains.config --- big_tests/dynamic_domains.config | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/big_tests/dynamic_domains.config b/big_tests/dynamic_domains.config index 57a078934df..c9e57df686d 100644 --- a/big_tests/dynamic_domains.config +++ b/big_tests/dynamic_domains.config @@ -70,6 +70,10 @@ {server, <<"domain.example.com">>}, {host, <<"localhost">>}, {password, <<"nicniema">>}]}, + {admin, [ + {username, <<"admin">>}, + {server, <<"localhost">>}, + {password, <<"bruce_almighty">>}]}, {alice3, [ %% used in dynamic_domains_SUITE {username, <<"alice">>}, {server, <<"example.com">>}, From 075b23a8202b9ef397db6bd956fd27d43b166813 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 14:15:32 +0200 Subject: [PATCH 66/73] Skip mam_SUITE:metric_incremented_when_store_message --- big_tests/dynamic_domains.spec | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/big_tests/dynamic_domains.spec b/big_tests/dynamic_domains.spec index e8f5c28451e..6af3716dcc3 100644 --- a/big_tests/dynamic_domains.spec +++ b/big_tests/dynamic_domains.spec @@ -40,6 +40,10 @@ {skip_cases, "tests", mam_SUITE, [messages_filtered_when_prefs_default_policy_is_roster], "at the moment mod_roster doesn't support dynamic domains"}. +{skip_cases, "tests", mam_SUITE, + [metric_incremented_when_store_message, + metric_incremented_on_archive_request], + "this test is broken in PR #3120"}. {skip_groups, "tests", muc_SUITE, [disco, disco_non_parallel, disco_rsm, disco_rsm_with_offline], From ba5650bafe801592ed097a40beec260c9c64979e Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 10:21:38 +0200 Subject: [PATCH 67/73] Add gen_mod:loaded_modules_with_opts/0 and gen_mod:hosts_with_module/1 for debugging --- src/gen_mod.erl | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/gen_mod.erl b/src/gen_mod.erl index f7e3cd4dd69..7e179f2746c 100644 --- a/src/gen_mod.erl +++ b/src/gen_mod.erl @@ -67,7 +67,9 @@ loaded_modules/0, loaded_modules/1, + loaded_modules_with_opts/0, loaded_modules_with_opts/1, + hosts_with_module/1, get_module_proc/2, is_loaded/2, get_deps/3]). @@ -412,6 +414,25 @@ loaded_modules_with_opts(HostType) -> [], [{{'$1', '$2'}}]}]). +-spec loaded_modules_with_opts() -> #{host_type() => [{module(), list()}]}. +loaded_modules_with_opts() -> + Res = ets:select(ejabberd_modules, + [{#ejabberd_module{_ = '_', module_host_type = {'$1', '$2'}, + opts = '$3'}, + [], + [{{'$2', '$1', '$3'}}]}]), + Hosts = lists:usort([H || {H, _, _} <- Res]), + maps:from_list([{H, [{M, Opts} + || {HH, M, Opts} <- Res, + H =:= HH]} + || H <- Hosts]). + +-spec hosts_with_module(module()) -> [host_type()]. +hosts_with_module(Module) -> + ets:select(ejabberd_modules, + [{#ejabberd_module{_ = '_', module_host_type = {Module, '$1'}}, + [], ['$1']}]). + -spec set_module_opts_mnesia(host_type(), module(), [any()]) -> {'aborted', _} | {'atomic', _}. set_module_opts_mnesia(HostType, Module, Opts0) -> From b3951b878a50e17957952bc0005760830ccfa9f6 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 16:34:32 +0200 Subject: [PATCH 68/73] Add a reason to return_action_not_allowed_error_iq in mod_mam_muc --- src/mam/mod_mam_muc.erl | 46 ++++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index cb1c2754adb..bce44920c66 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -231,8 +231,8 @@ room_process_mam_iq(From, To, Acc, IQ) -> mod_mam_utils:maybe_log_deprecation(IQ), Action = mam_iq:action(IQ), MucAction = action_to_muc_action(Action), - case is_action_allowed(HostType, To#jid.lserver, Action, MucAction, From, To) of - true -> + case check_action_allowed(HostType, To#jid.lserver, Action, MucAction, From, To) of + ok -> case mod_mam_utils:wait_shaper(HostType, To#jid.lserver, MucAction, From) of ok -> handle_error_iq(Acc, HostType, To, Action, @@ -241,11 +241,11 @@ room_process_mam_iq(From, To, Acc, IQ) -> mongoose_metrics:update(HostType, modMucMamDroppedIQ, 1), {Acc, return_max_delay_reached_error_iq(IQ)} end; - false -> + {error, Reason} -> ?LOG_WARNING(#{what => action_not_allowed, - action => Action, acc => Acc, + action => Action, acc => Acc, reason => Reason, can_access_room => can_access_room(HostType, From, To)}), - {Acc, return_action_not_allowed_error_iq(IQ)} + {Acc, return_action_not_allowed_error_iq(Reason, IQ)} end. -spec forget_room(map(), host_type(), jid:lserver(), binary()) -> map(). @@ -256,27 +256,35 @@ forget_room(Acc, _HostType, MucServer, RoomName) -> %% ---------------------------------------------------------------------- %% Internal functions --spec is_action_allowed(host_type(), jid:lserver(), mam_iq:action(), muc_action(), - jid:jid(), jid:jid()) -> boolean(). -is_action_allowed(HostType, Domain, Action, MucAction, From, To) -> +-spec check_action_allowed(host_type(), jid:lserver(), mam_iq:action(), muc_action(), + jid:jid(), jid:jid()) -> ok | {error, binary()}. +check_action_allowed(HostType, Domain, Action, MucAction, From, To) -> case acl:match_rule_for_host_type(HostType, Domain, MucAction, From, default) of - allow -> true; - deny -> false; - default -> is_room_action_allowed_by_default(HostType, Action, From, To) + allow -> ok; + deny -> {false, <<"Blocked by service policy.">>}; + default -> check_room_action_allowed_by_default(HostType, Action, From, To) end. -spec action_to_muc_action(mam_iq:action()) -> atom(). action_to_muc_action(Action) -> list_to_atom("muc_" ++ atom_to_list(Action)). --spec is_room_action_allowed_by_default(HostType :: host_type(), +-spec check_room_action_allowed_by_default(HostType :: host_type(), Action :: mam_iq:action(), From :: jid:jid(), - To :: jid:jid()) -> boolean(). -is_room_action_allowed_by_default(HostType, Action, From, To) -> + To :: jid:jid()) -> ok | {error, binary()}. +check_room_action_allowed_by_default(HostType, Action, From, To) -> case mam_iq:action_type(Action) of - set -> is_room_owner(HostType, From, To); - get -> can_access_room(HostType, From, To) + set -> + case is_room_owner(HostType, From, To) of + true -> ok; + false -> {error, <<"Not a room owner.">>} + end; + get -> + case can_access_room(HostType, From, To) of + true -> ok; + false -> {error, <<"Not allowed to enter the room.">>} + end end. -spec is_room_owner(HostType :: host_type(), @@ -546,10 +554,10 @@ return_error_iq(IQ, missing_with_jid) -> return_error_iq(IQ, Reason) -> {error, Reason, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:internal_server_error()]}}. --spec return_action_not_allowed_error_iq(jlib:iq()) -> jlib:iq(). -return_action_not_allowed_error_iq(IQ) -> +-spec return_action_not_allowed_error_iq(Reason :: binary(), jlib:iq()) -> jlib:iq(). +return_action_not_allowed_error_iq(Reason, IQ) -> ErrorEl = jlib:stanza_errort(<<"">>, <<"cancel">>, <<"not-allowed">>, - <<"en">>, <<"The action is not allowed.">>), + <<"en">>, <<"The action is not allowed. ", Reason/binary>>), IQ#iq{type = error, sub_el = [ErrorEl]}. -spec return_max_delay_reached_error_iq(jlib:iq()) -> jlib:iq(). From b54665ad9ec2cf94a9f6978a9258b3950ff0a96d Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 16:35:34 +0200 Subject: [PATCH 69/73] Don't overwrite can_access_room from muclight, if it's already allowed by mod_muc --- src/mod_muc.erl | 6 ++++++ src/muc_light/mod_muc_light.erl | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index ad329f33287..dd654fd1b07 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1218,12 +1218,16 @@ clean_table_from_bad_node(Node, HostType) -> -spec is_muc_room_owner(Acc :: boolean(), HostType :: mongooseim:host_type(), Room :: jid:jid(), User :: jid:jid()) -> boolean(). +is_muc_room_owner(true, _HostType, _Room, _User) -> + true; is_muc_room_owner(_, _HostType, Room, User) -> mod_muc_room:is_room_owner(Room, User) =:= {ok, true}. -spec can_access_room(Acc :: boolean(), HostType :: mongooseim:host_type(), Room :: jid:jid(), User :: jid:jid()) -> boolean(). +can_access_room(true, _HostType, _Room, _User) -> + true; can_access_room(_, _HostType, Room, User) -> case mod_muc_room:can_access_room(Room, User) of {error, _} -> false; @@ -1233,6 +1237,8 @@ can_access_room(_, _HostType, Room, User) -> -spec can_access_identity(Acc :: boolean(), HostType :: mongooseim:host_type(), Room :: jid:jid(), User :: jid:jid()) -> boolean(). +can_access_identity(true, _HostType, _Room, _User) -> + true; can_access_identity(_, _HostType, Room, User) -> case mod_muc_room:can_access_identity(Room, User) of {error, _} -> false; diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index e6989f53de3..f4eb1ec1577 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -500,18 +500,24 @@ process_iq_set(Acc, #jid{ lserver = FromS } = From, To, #iq{} = IQ) -> -spec is_muc_room_owner(Acc :: boolean(), HostType :: mongooseim:host_type(), Room :: jid:jid(), User :: jid:jid()) -> boolean(). +is_muc_room_owner(true, _HostType, _Room, _User) -> + true; is_muc_room_owner(_, _HostType, Room, User) -> owner == get_affiliation(Room, User). -spec can_access_room(Acc :: boolean(), HostType :: mongooseim:host_type(), Room :: jid:jid(), User :: jid:jid()) -> boolean(). +can_access_room(true, _HostType, _Room, _User) -> + true; can_access_room(_, _HostType, Room, User) -> none =/= get_affiliation(Room, User). -spec can_access_identity(Acc :: boolean(), HostType :: mongooseim:host_type(), Room :: jid:jid(), User :: jid:jid()) -> boolean(). +can_access_identity(true, _HostType, _Room, _User) -> + true; can_access_identity(_Acc, _HostType, _Room, _User) -> %% User JIDs are explicit in MUC Light but this hook is about appending %% 0045 MUC element with user identity and we don't want it From 3f8b05beff5c547d22c21f04cdb57d2405d030cb Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 16:37:16 +0200 Subject: [PATCH 70/73] Handle already started mod_muc_light in mam_SUITE --- big_tests/tests/mam_SUITE.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/big_tests/tests/mam_SUITE.erl b/big_tests/tests/mam_SUITE.erl index 06790a54c10..8fbd9d15663 100644 --- a/big_tests/tests/mam_SUITE.erl +++ b/big_tests/tests/mam_SUITE.erl @@ -855,7 +855,7 @@ rdbms_simple_opts() -> [{db_jid_format, mam_jid_rfc}, {db_message_format, mam_message_xml}]. init_modules_for_muc_light(BackendType, Config) -> - dynamic_modules:start(host_type(), mod_muc_light, [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}]), + dynamic_modules:restart(host_type(), mod_muc_light, [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}]), Config1 = init_modules(BackendType, muc_all, [{muc_domain, muc_helper:muc_host_pattern()} | Config]), init_modules(BackendType, pm, [{archive_groupchats, false} | Config1]). From fc404de4ec2d6497d2eba177e84cc6dda79ddfb5 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 16:47:03 +0200 Subject: [PATCH 71/73] Use restart to start modules in muc_light_SUITE --- big_tests/tests/muc_light_SUITE.erl | 4 ++-- big_tests/tests/muc_light_legacy_SUITE.erl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/big_tests/tests/muc_light_SUITE.erl b/big_tests/tests/muc_light_SUITE.erl index 7a3a184777d..58b037cf284 100644 --- a/big_tests/tests/muc_light_SUITE.erl +++ b/big_tests/tests/muc_light_SUITE.erl @@ -202,7 +202,7 @@ suite() -> init_per_suite(Config) -> HostType = host_type(), - {ok, _} = dynamic_modules:start(HostType, mod_muc_light, + {ok, _} = dynamic_modules:restart(HostType, mod_muc_light, [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}, {backend, mongoose_helper:mnesia_or_rdbms_backend()}, {rooms_in_rosters, true}]), @@ -238,7 +238,7 @@ init_per_testcase(removing_users_from_server_triggers_room_destruction = CN, Con init_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; CaseName =:= disco_info_with_mam -> set_default_mod_config(), - dynamic_modules:start(host_type(), mod_mam_muc, + dynamic_modules:restart(host_type(), mod_mam_muc, [{backend, rdbms}, {host, subhost_pattern(?MUCHOST)}]), escalus:init_per_testcase(CaseName, Config); diff --git a/big_tests/tests/muc_light_legacy_SUITE.erl b/big_tests/tests/muc_light_legacy_SUITE.erl index f8d68d34297..fc046874e8d 100644 --- a/big_tests/tests/muc_light_legacy_SUITE.erl +++ b/big_tests/tests/muc_light_legacy_SUITE.erl @@ -143,7 +143,7 @@ suite() -> init_per_suite(Config) -> HostType = host_type(), - dynamic_modules:start(HostType, mod_muc_light, + dynamic_modules:restart(HostType, mod_muc_light, [{host, subhost_pattern(muc_helper:muc_host_pattern())}, {backend, mongoose_helper:mnesia_or_rdbms_backend()}, {legacy_mode, true}]), @@ -177,7 +177,7 @@ init_per_testcase(create_existing_room_deny = N, Config) -> init_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; CaseName =:= disco_info_with_mam -> set_default_mod_config(), - dynamic_modules:start(host_type(), mod_mam_muc, + dynamic_modules:restart(host_type(), mod_mam_muc, [{backend, rdbms}, {host, subhost_pattern(?MUCHOST)}]), escalus:init_per_testcase(CaseName, Config); From c212600f3ab79ee1b27745a54ddd84bbd6a29581 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 17:38:06 +0200 Subject: [PATCH 72/73] Restart modules on init in muc_SUITE --- big_tests/tests/muc_SUITE.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/big_tests/tests/muc_SUITE.erl b/big_tests/tests/muc_SUITE.erl index 329f8e9fc84..551f2b3179b 100644 --- a/big_tests/tests/muc_SUITE.erl +++ b/big_tests/tests/muc_SUITE.erl @@ -320,7 +320,7 @@ init_per_suite(Config) -> mongoose_helper:inject_module(?MODULE), Config2 = escalus:init_per_suite(Config), Config3 = dynamic_modules:save_modules(host_type(), Config2), - dynamic_modules:start(host_type(), mod_disco, []), + dynamic_modules:restart(host_type(), mod_disco, []), load_muc(), mongoose_helper:ensure_muc_clean(), Config3. @@ -380,11 +380,11 @@ init_per_group(G, Config) when G =:= http_auth_no_server; init_per_group(hibernation, Config) -> case mam_helper:backend() of rdbms -> - dynamic_modules:start(host_type(), mod_mam_muc_rdbms_arch, [muc]), - dynamic_modules:start(host_type(), mod_mam_rdbms_prefs, [muc]), - dynamic_modules:start(host_type(), mod_mam_rdbms_user, [pm, muc]), + dynamic_modules:restart(host_type(), mod_mam_muc_rdbms_arch, [muc]), + dynamic_modules:restart(host_type(), mod_mam_rdbms_prefs, [muc]), + dynamic_modules:restart(host_type(), mod_mam_rdbms_user, [pm, muc]), HostPattern = subhost_pattern("muc.@HOST@"), - dynamic_modules:start(host_type(), mod_mam_muc, [{host, HostPattern}]); + dynamic_modules:restart(host_type(), mod_mam_muc, [{host, HostPattern}]); _ -> ok end, @@ -528,7 +528,7 @@ init_per_testcase(CN, Config) init_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; CaseName =:= disco_info_with_mam -> - dynamic_modules:start(host_type(), mod_mam_muc, + dynamic_modules:restart(host_type(), mod_mam_muc, [{backend, rdbms}, {host, subhost_pattern(muc_helper:muc_host_pattern())}]), escalus:init_per_testcase(CaseName, Config); From 5b2744360227667689817c3a93c61c7a87813346 Mon Sep 17 00:00:00 2001 From: Mikhail Uvarov Date: Mon, 31 May 2021 20:24:13 +0200 Subject: [PATCH 73/73] Review leftovers --- src/mam/mod_mam.erl | 6 ++---- src/muc_light/mod_muc_light_db_rdbms.erl | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/mam/mod_mam.erl b/src/mam/mod_mam.erl index 41db1be25d4..76505416ecb 100644 --- a/src/mam/mod_mam.erl +++ b/src/mam/mod_mam.erl @@ -197,8 +197,7 @@ archive_id(Server, User) -spec start(HostType :: host_type(), Opts :: list()) -> any(). start(HostType, Opts) -> - ?LOG_INFO(#{what => mam_starting}), - ?LOG_ERROR(#{what => mam_starting, host_type => HostType}), + ?LOG_INFO(#{what => mam_starting, host_type => HostType}), ensure_metrics(HostType), ejabberd_hooks:add(hooks(HostType)), add_id_handlers(HostType, Opts), @@ -207,8 +206,7 @@ start(HostType, Opts) -> -spec stop(HostType :: host_type()) -> any(). stop(HostType) -> - ?LOG_INFO(#{what => mam_stopping}), - ?LOG_ERROR(#{what => mam_stopping, host_type => HostType}), + ?LOG_INFO(#{what => mam_stopping, host_type => HostType}), unregister_features(HostType), ejabberd_hooks:delete(hooks(HostType)), remove_iq_handlers(HostType), diff --git a/src/muc_light/mod_muc_light_db_rdbms.erl b/src/muc_light/mod_muc_light_db_rdbms.erl index d84fd2ee5a6..4492e8cf72b 100644 --- a/src/muc_light/mod_muc_light_db_rdbms.erl +++ b/src/muc_light/mod_muc_light_db_rdbms.erl @@ -71,13 +71,13 @@ %% ------------------------ Backend start/stop ------------------------ --spec start(Host :: jid:server()) -> ok. -start(_Host) -> +-spec start(HostType :: mongooseim:host_type()) -> ok. +start(_HostType) -> prepare_queries(), ok. --spec stop(Host :: jid:server()) -> ok. -stop(_Host) -> +-spec stop(HostType :: mongooseim:host_type()) -> ok. +stop(_HostType) -> ok. %% ------------------------ SQL -------------------------------------------