From 543afb5442180c862be58cc307851ea106d53104 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 26 May 2021 12:21:06 +0200 Subject: [PATCH 01/39] Add a helper for disco feature handling It is added in a separate module to avoid calling mod_disco from other extension modules (module decoupling). --- src/mongoose_disco.erl | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/mongoose_disco.erl diff --git a/src/mongoose_disco.erl b/src/mongoose_disco.erl new file mode 100644 index 00000000000..0f12a8263b7 --- /dev/null +++ b/src/mongoose_disco.erl @@ -0,0 +1,17 @@ +-module(mongoose_disco). + +-export([add_features/2]). + +-type acc() :: empty | {error, any()} | {result, [exml:element()]}. +-type feature() :: binary(). + +-spec add_features([feature()], acc()) -> acc(). +add_features(Features, Acc) -> + case Acc of + {error, _} = Error -> + Error; + empty -> + {result, Features}; + {result, InitialFeatures} -> + {result, Features ++ InitialFeatures} + end. From c1dcc5fd484ecdb264ad7e3bf03165ef3b085b9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 26 May 2021 09:21:12 +0200 Subject: [PATCH 02/39] Do not register disco features in pubsub --- big_tests/tests/pubsub_SUITE.erl | 25 +++++++++++++++++++++++++ src/pubsub/mod_pubsub.erl | 19 ++++--------------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/big_tests/tests/pubsub_SUITE.erl b/big_tests/tests/pubsub_SUITE.erl index d34a2a3cf35..5ec0d437f48 100644 --- a/big_tests/tests/pubsub_SUITE.erl +++ b/big_tests/tests/pubsub_SUITE.erl @@ -18,6 +18,8 @@ init_per_testcase/2, end_per_testcase/2]). -export([ + discover_features_test/1, + discover_service_features_test/1, discover_nodes_test/1, create_delete_node_test/1, subscribe_unsubscribe_test/1, @@ -169,6 +171,8 @@ base_groups() -> basic_tests() -> [ + discover_features_test, + discover_service_features_test, discover_nodes_test, create_delete_node_test, subscribe_unsubscribe_test, @@ -340,6 +344,27 @@ end_per_testcase(TestName, Config) -> %% Main PubSub cases %%-------------------------------------------------------------------- +discover_features_test(Config) -> + escalus:fresh_story( + Config, + [{alice, 1}], + fun(Alice) -> + Server = escalus_client:server(Alice), + escalus:send(Alice, escalus_stanza:disco_info(Server)), + Stanza = escalus:wait_for_stanza(Alice), + escalus:assert(has_feature, [?NS_PUBSUB], Stanza) + end). + +discover_service_features_test(Config) -> + escalus:fresh_story( + Config, + [{alice, 1}], + fun(Alice) -> + escalus:send(Alice, escalus_stanza:disco_info(node_addr())), + Stanza = escalus:wait_for_stanza(Alice), + escalus:assert(has_feature, [?NS_PUBSUB], Stanza) + end). + discover_nodes_test(Config) -> escalus:fresh_story( Config, diff --git a/src/pubsub/mod_pubsub.erl b/src/pubsub/mod_pubsub.erl index 80743c3de8d..7f96440cf2d 100644 --- a/src/pubsub/mod_pubsub.erl +++ b/src/pubsub/mod_pubsub.erl @@ -368,8 +368,6 @@ init([ServerHost, Opts]) -> ets:new(gen_mod:get_module_proc(ServerHost, config), [set, named_table, public]), {Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts), - mod_disco:register_feature(ServerHost, ?NS_PUBSUB), - store_config_in_ets(Host, ServerHost, Opts, Plugins, NodeTree, PepMapping), add_hooks(ServerHost, hooks()), case lists:member(?PEPNODE, Plugins) of @@ -658,20 +656,12 @@ disco_local_identity(Acc, Host, <<>>, _Lang) -> disco_local_identity(Acc, _Host, _Node, _Lang) -> Acc. --spec disco_local_features( - Acc :: {result, [exml:element()]} | empty | {error, any()}, - _From ::jid:jid(), - To ::jid:jid(), - Node :: <<>> | mod_pubsub:nodeId() | binary(), - Lang :: ejabberd:lang()) - -> {result, [exml:element()]} | empty | {error, any()}. +-spec disco_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). disco_local_features(Acc, _From, To, <<>>, _Lang) -> Host = To#jid.lserver, - Feats = case Acc of - {result, I} -> I; - _ -> [] - end, - {result, Feats ++ [feature(F) || F <- features(Host, <<>>)]}; + Features = [?NS_PUBSUB | [feature(F) || F <- features(Host, <<>>)]], + mongoose_disco:add_features(Features, Acc); disco_local_features(Acc, _From, _To, _Node, _Lang) -> Acc. @@ -1012,7 +1002,6 @@ terminate(_Reason, #state{host = Host, server_host = ServerHost, false -> ok end, delete_hooks(ServerHost, hooks()), - mod_disco:unregister_feature(ServerHost, ?NS_PUBSUB), case whereis(gen_mod:get_module_proc(ServerHost, ?LOOPNAME)) of undefined -> ?LOG_ERROR(#{what => pubsub_process_is_dead, From fdb2f78cb851243d16fb4e9df5523f6d27719588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 26 May 2021 11:41:29 +0200 Subject: [PATCH 03/39] Do not register disco features in amp Also: add missing 'drop' condition to the AMP features. --- src/mod_amp.erl | 47 +++++++++++++++++++++++------------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/mod_amp.erl b/src/mod_amp.erl index 801d2bec2e3..52fa611dc1f 100644 --- a/src/mod_amp.erl +++ b/src/mod_amp.erl @@ -26,12 +26,10 @@ -define(AMP_STRATEGY, amp_strategy). start(Host, _Opts) -> - mod_disco:register_feature(Host, ?NS_AMP), ejabberd_hooks:add(hooks(Host)). stop(Host) -> - ejabberd_hooks:delete(hooks(Host)), - mod_disco:unregister_feature(Host, ?NS_AMP). + ejabberd_hooks:delete(hooks(Host)). hooks(Host) -> [{c2s_stream_features, Host, ?MODULE, add_stream_feature, 50}, @@ -59,16 +57,13 @@ check_packet(Acc, Event) -> Rules -> process_event(Acc, Rules, Event) end. --spec add_local_features(Acc :: {result, [exml:element()]} | empty | {error, any()}, - From :: jid:jid(), - To :: jid:jid(), - NS :: binary(), - ejabberd:lang()) -> {result, [exml:element()]} | {error, any()}. -add_local_features(Acc, _From, _To, ?NS_AMP, _Lang) -> - Features = result_or(Acc, []) ++ amp_features(), - {result, Features}; -add_local_features(Acc, _From, _To, _NS, _Lang) -> - Acc. +-spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). +add_local_features(Acc, _From, _To, Node, _Lang) -> + case amp_features(Node) of + [] -> Acc; + Features -> mongoose_disco:add_features(Features, Acc) + end. add_stream_feature(Acc, _Host) -> lists:keystore(<<"amp">>, #xmlel.name, Acc, ?AMP_FEATURE). @@ -116,14 +111,22 @@ process_event(Acc, Rules, Event) when Event =/= initial_check -> NewRules = process_rules(Packet, From, Event, Rules), mongoose_acc:set_permanent(amp, rules, NewRules, Acc). +-spec amp_features(binary()) -> [binary()]. +amp_features(?NS_AMP) -> + [<> || Suffix <- amp_feature_suffixes()]; +amp_features(<<>>) -> + [?NS_AMP]; +amp_features(_) -> + []. + %% @doc This may eventually be configurable, but for now we return a constant list. -amp_features() -> - [<<"http://jabber.org/protocol/amp">> - , <<"http://jabber.org/protocol/amp?action=notify">> - , <<"http://jabber.org/protocol/amp?action=error">> - , <<"http://jabber.org/protocol/amp?condition=deliver">> - , <<"http://jabber.org/protocol/amp?condition=match-resource">> - ]. +amp_feature_suffixes() -> + [<<>>, + <<"?action=drop">>, + <<"?action=notify">>, + <<"?action=error">>, + <<"?condition=deliver">>, + <<"?condition=match-resource">>]. -spec process_rules(exml:element(), jid:jid(), amp_event(), amp_rules()) -> amp_rules() | drop. process_rules(Packet, From, Event, Rules) -> @@ -211,10 +214,6 @@ update_metric_and_drop(Packet, From) -> Packet), drop. -%% Internal -result_or({result, I}, _) -> I; -result_or(_, Or) -> Or. - -spec is_supported_rule(amp_rule_support()) -> boolean(). is_supported_rule({supported, _}) -> true; is_supported_rule(_) -> false. From aaeb66a375e7aa1ccbd23d3769ffffa0ab63c1ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 26 May 2021 12:22:35 +0200 Subject: [PATCH 04/39] Do not register disco features in mod_version --- src/mod_version.erl | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/mod_version.erl b/src/mod_version.erl index 113f8ba2599..e93aafc71f7 100644 --- a/src/mod_version.erl +++ b/src/mod_version.erl @@ -7,22 +7,25 @@ -include("mongoose.hrl"). -include("mongoose_config_spec.hrl"). --export([start/2, stop/1, config_spec/0, process_iq/4]). +-export([start/2, stop/1, config_spec/0, add_local_features/5, process_iq/4]). -xep([{xep, 92}, {version, "1.1"}]). -spec start(jid:server(), list()) -> any(). start(Host, Opts) -> - mod_disco:register_feature(Host, ?NS_VERSION), IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), gen_iq_handler:add_iq_handler(ejabberd_local, Host, - ?NS_VERSION, ?MODULE, process_iq, IQDisc). + ?NS_VERSION, ?MODULE, process_iq, IQDisc), + ejabberd_hooks:add(hooks(Host)). -spec stop(jid:server()) -> any(). stop(Host) -> - mod_disco:unregister_feature(Host, ?NS_VERSION), + ejabberd_hooks:delete(hooks(Host)), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_VERSION). +hooks(Host) -> + [{disco_local_features, Host, ?MODULE, add_local_features, 99}]. + -spec config_spec() -> mongoose_config_spec:config_section(). config_spec() -> #section{ @@ -31,6 +34,13 @@ config_spec() -> } }. +-spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). +add_local_features(Acc, _From, _To, <<>>, _Lang) -> + mongoose_disco:add_features([?NS_VERSION], Acc); +add_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + -spec process_iq(jid:jid(),jid:jid(), mongoose_acc:t(), jlib:iq()) -> {mongoose_acc:t(), jlib:iq()}. process_iq(_From, _To, Acc, #iq{type = set, sub_el = SubEl} = IQ) -> {Acc, IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:not_allowed()]}}; From de3818ea8551e9c38cb4804065b42942f4bd9fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Wed, 26 May 2021 13:59:54 +0200 Subject: [PATCH 05/39] Do not register disco features in mod_ping They are already registered by ejabberd_local with the IQ handler. --- big_tests/tests/mod_ping_SUITE.erl | 26 +++++++++++++++++++------- src/mod_ping.erl | 5 +---- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/big_tests/tests/mod_ping_SUITE.erl b/big_tests/tests/mod_ping_SUITE.erl index ac2e1db9017..72373fc572c 100644 --- a/big_tests/tests/mod_ping_SUITE.erl +++ b/big_tests/tests/mod_ping_SUITE.erl @@ -32,7 +32,7 @@ all() -> groups() -> % Don't make these parallel! Metrics tests will most probably fail % and injected hook will most probably won't work as expected. - G = [{client_ping, [], [ping]}, + G = [{client_ping, [], [disco, ping]}, {server_ping, [], all_tests()}, {server_ping_kill, [], all_tests()} ], @@ -43,7 +43,8 @@ client_ping_test_cases() -> wrong_ping]. all_tests() -> - [ping, + [disco, + ping, wrong_ping, active, active_keep_alive, @@ -128,8 +129,17 @@ clear_pong_hook(Host, Handler) -> %%-------------------------------------------------------------------- %% Ping tests %%-------------------------------------------------------------------- +disco(Config) -> + escalus:fresh_story( + Config, [{alice, 1}], + fun(Alice) -> + escalus_client:send(Alice, escalus_stanza:disco_info(domain())), + Response = escalus_client:wait_for_stanza(Alice), + escalus:assert(has_feature, [?NS_PING], Response) + end). + ping(ConfigIn) -> - Domain = ct:get_config({hosts, mim, domain}), + Domain = domain(), Metrics = [ {[Domain, mod_ping, ping_response],0}, {[Domain, mod_ping, ping_response_timeout],0} @@ -159,7 +169,7 @@ wrong_ping(Config) -> end). active(ConfigIn) -> - Domain = ct:get_config({hosts, mim, domain}), + Domain = domain(), Metrics = [ {[Domain, mod_ping, ping_response],0}, {[Domain, mod_ping, ping_response_timeout],0} @@ -177,7 +187,7 @@ active(ConfigIn) -> end). active_keep_alive(ConfigIn) -> - Domain = ct:get_config({hosts, mim, domain}), + Domain = domain(), Metrics = [ {[Domain, mod_ping, ping_response],0}, {[Domain, mod_ping, ping_response_timeout],0} @@ -193,7 +203,7 @@ active_keep_alive(ConfigIn) -> end). server_ping_pong(ConfigIn) -> - Domain = ct:get_config({hosts, mim, domain}), + Domain = domain(), Metrics = [ {[Domain, mod_ping, ping_response], 5}, {[Domain, mod_ping, ping_response_timeout], 0}, @@ -213,7 +223,7 @@ server_ping_pong(ConfigIn) -> end). server_ping_pang(ConfigIn) -> - Domain = ct:get_config({hosts, mim, domain}), + Domain = domain(), Metrics = [ {[Domain, mod_ping, ping_response], 0}, {[Domain, mod_ping, ping_response_timeout], 1} @@ -255,3 +265,5 @@ wait_for_pong_hooks(N) -> ct:fail({pong_hook_runs_missing, N}) end. +domain() -> + ct:get_config({hosts, mim, domain}). diff --git a/src/mod_ping.erl b/src/mod_ping.erl index df55a1bc1d3..b759a96c4af 100644 --- a/src/mod_ping.erl +++ b/src/mod_ping.erl @@ -76,7 +76,6 @@ hooks(Host) -> {user_ping_response, Host, ?MODULE, user_ping_response, 100}, {c2s_remote_hook, Host, ?MODULE, handle_remote_hook, 100}]. - ensure_metrics(Host) -> mongoose_metrics:ensure_metric(Host, [mod_ping, ping_response], spiral), mongoose_metrics:ensure_metric(Host, [mod_ping, ping_response_timeout], spiral), @@ -90,7 +89,6 @@ start(Host, Opts) -> ensure_metrics(Host), SendPings = gen_mod:get_opt(send_pings, Opts, ?DEFAULT_SEND_PINGS), IQDisc = gen_mod:get_opt(iqdisc, Opts, no_queue), - mod_disco:register_feature(Host, ?NS_PING), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PING, ?MODULE, iq_ping, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PING, @@ -107,8 +105,7 @@ stop(Host) -> %% won't stop currently running timers. They'll run one more time, and then stop. ejabberd_hooks:delete(hooks(Host)), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PING), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PING), - mod_disco:unregister_feature(Host, ?NS_PING). + gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PING). -spec config_spec() -> mongoose_config_spec:config_section(). config_spec() -> From 256cc1263e7f35d82bdf112e82f87d098271a38a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 08:17:53 +0200 Subject: [PATCH 06/39] Do not register disco features in mod_inbox --- src/inbox/mod_inbox.erl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/inbox/mod_inbox.erl b/src/inbox/mod_inbox.erl index 35af8827595..04b96597b00 100644 --- a/src/inbox/mod_inbox.erl +++ b/src/inbox/mod_inbox.erl @@ -27,7 +27,8 @@ filter_packet/1, inbox_unread_count/2, remove_user/3, - remove_domain/3 + remove_domain/3, + add_local_features/5 ]). -export([config_metrics/1]). @@ -147,7 +148,6 @@ start(Host, Opts) -> end, gen_mod:start_backend_module(?MODULE, FullOpts, callback_funs()), mod_inbox_backend:init(Host, FullOpts), - mod_disco:register_feature(Host, ?NS_ESL_INBOX), IQDisc = gen_mod:get_opt(iqdisc, FullOpts, no_queue), MucTypes = get_groupchat_types(Host), lists:member(muc, MucTypes) andalso mod_inbox_muc:start(Host), @@ -161,7 +161,6 @@ start(Host, Opts) -> -spec stop(Host :: jid:server()) -> ok. stop(Host) -> - mod_disco:unregister_feature(Host, ?NS_ESL_INBOX), mod_inbox_muc:stop(Host), ejabberd_hooks:delete(hooks(Host)), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_ESL_INBOX), @@ -280,6 +279,13 @@ remove_domain(Acc, HostType, Domain) -> mod_inbox_backend:remove_domain(HostType, Domain), Acc. +-spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). +add_local_features(Acc, _From, _To, <<>>, _Lang) -> + mongoose_disco:add_features([?NS_ESL_INBOX], Acc); +add_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + -spec maybe_process_message(Acc :: mongoose_acc:t(), Host :: host(), From :: jid:jid(), @@ -566,7 +572,8 @@ hooks(Host) -> {user_send_packet, Host, ?MODULE, user_send_packet, 70}, {filter_local_packet, Host, ?MODULE, filter_packet, 90}, {inbox_unread_count, Host, ?MODULE, inbox_unread_count, 80}, - {get_personal_data, Host, ?MODULE, get_personal_data, 50} + {get_personal_data, Host, ?MODULE, get_personal_data, 50}, + {disco_local_features, Host, ?MODULE, add_local_features, 99} ]. get_groupchat_types(Host) -> From a95847baccb589e4682399c7c23f5e2c79808bce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 08:31:38 +0200 Subject: [PATCH 07/39] Do not register disco features in mod_auth_token --- big_tests/tests/oauth_SUITE.erl | 10 ++++++++++ src/mod_auth_token.erl | 14 +++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/big_tests/tests/oauth_SUITE.erl b/big_tests/tests/oauth_SUITE.erl index 59480af3898..10ac102caf9 100644 --- a/big_tests/tests/oauth_SUITE.erl +++ b/big_tests/tests/oauth_SUITE.erl @@ -55,6 +55,7 @@ groups() -> token_login_tests() -> [ + disco_test, request_tokens_test, login_access_token_test, login_refresh_token_test, @@ -145,6 +146,15 @@ end_per_testcase(CaseName, Config) -> %% Tests %% +disco_test(Config) -> + escalus:story( + Config, [{alice, 1}], + fun(Alice) -> + escalus_client:send(Alice, escalus_stanza:disco_info(domain())), + Response = escalus_client:wait_for_stanza(Alice), + escalus:assert(has_feature, [?NS_ESL_TOKEN_AUTH], Response) + end). + request_tokens_test(Config) -> request_tokens_once_logged_in_impl(Config, bob). diff --git a/src/mod_auth_token.erl b/src/mod_auth_token.erl index cf89ace6320..b033655b01c 100644 --- a/src/mod_auth_token.erl +++ b/src/mod_auth_token.erl @@ -18,7 +18,8 @@ -export([process_validity_period/1]). %% Hook handlers --export([clean_tokens/3]). +-export([clean_tokens/3, + add_local_features/5]). %% gen_iq_handler handlers -export([process_iq/4]). @@ -85,7 +86,6 @@ start(Domain, Opts) -> gen_mod:start_backend_module(?MODULE, default_opts(Opts)), mod_auth_token_backend:start(Domain), - mod_disco:register_feature(Domain, ?NS_ESL_TOKEN_AUTH), IQDisc = gen_mod:get_opt(iqdisc, Opts, no_queue), [ ejabberd_hooks:add(Hook, Domain, ?MODULE, Handler, Priority) || {Hook, Handler, Priority} <- hook_handlers() ], @@ -132,7 +132,8 @@ default_opts(Opts) -> [{backend, rdbms} || not proplists:is_defined(backend, Opts)] ++ Opts. hook_handlers() -> - [{remove_user, clean_tokens, 50}]. + [{remove_user, clean_tokens, 50}, + {disco_local_features, add_local_features, 90}]. -spec commands() -> [ejabberd_commands:cmd()]. commands() -> @@ -470,3 +471,10 @@ clean_tokens(Acc, User, Server) -> config_metrics(Host) -> OptsToReport = [{backend, rdbms}], %list of tuples {option, default_value} mongoose_module_metrics:opts_for_module(Host, ?MODULE, OptsToReport). + +-spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). +add_local_features(Acc, _From, _To, <<>>, _Lang) -> + mongoose_disco:add_features([?NS_ESL_TOKEN_AUTH], Acc); +add_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. From fe81b6485d5374d3c595d8dd25825c6d0814b403 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 09:16:12 +0200 Subject: [PATCH 08/39] Do not register disco features in mod_extdisco It was already registered with the IQ handler. There was a double bug as well that caused the tests to pass: - Wrong group name caused extdisco NOT to start - Wrong IQ handler module caused extdisco NOT to stop correctly --- big_tests/tests/extdisco_SUITE.erl | 2 +- src/mod_extdisco.erl | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/big_tests/tests/extdisco_SUITE.erl b/big_tests/tests/extdisco_SUITE.erl index 7568e09641c..e450cec95dd 100644 --- a/big_tests/tests/extdisco_SUITE.erl +++ b/big_tests/tests/extdisco_SUITE.erl @@ -72,7 +72,7 @@ init_per_group(extdisco_configured, Config) -> init_per_group(multiple_extdisco_configured, Config) -> ExternalServices = [stun_service(), stun_service(), turn_service()], set_external_services(ExternalServices, Config); -init_per_group(external_service_required_elements_configured, Config) -> +init_per_group(extdisco_required_elements_configured, Config) -> ExternalServices = [[{type, ftp},{host, "3.3.3.3"}]], set_external_services(ExternalServices, Config); init_per_group(_GroupName, Config) -> diff --git a/src/mod_extdisco.erl b/src/mod_extdisco.erl index 7d49c92fd54..b33d9ab149a 100644 --- a/src/mod_extdisco.erl +++ b/src/mod_extdisco.erl @@ -31,14 +31,12 @@ -spec start(jid:server(), list()) -> ok. start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, no_queue), - mod_disco:register_feature(Host, ?NS_EXTDISCO), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_EXTDISCO, ?MODULE, process_iq, IQDisc). -spec stop(jid:server()) -> ok. stop(Host) -> - mod_disco:unregister_feature(Host, ?NS_EXTDISCO), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_EXTDISCO). + gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_EXTDISCO). -spec config_spec() -> mongoose_config_spec:config_section(). config_spec() -> From 984efd7620a232d2049769fd12d8fc6e0cd723d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 09:22:24 +0200 Subject: [PATCH 09/39] Do not register disco features in mod_time It was already registered with the IQ handler. --- src/mod_time.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mod_time.erl b/src/mod_time.erl index 1f993d05bf5..1478b0bffd8 100644 --- a/src/mod_time.erl +++ b/src/mod_time.erl @@ -20,7 +20,6 @@ -xep([{xep, 82}, {version, "1.1"}]). start(Host, Opts) -> - mod_disco:register_feature(Host, ?NS_TIME), IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), gen_iq_handler:add_iq_handler(ejabberd_local, Host, @@ -28,7 +27,6 @@ start(Host, Opts) -> stop(Host) -> - mod_disco:unregister_feature(Host, ?NS_TIME), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_TIME). From 9ea0db5ef091c1953b1dabb0b47e0cd296e6254c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 09:32:06 +0200 Subject: [PATCH 10/39] Do not register disco features in mod_blocking --- src/mod_blocking.erl | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index d4cbf2abad7..63d1e3a35f4 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -15,7 +15,8 @@ -export([start/2, process_iq_get/5, process_iq_set/4, - stop/1 + stop/1, + add_local_features/5 ]). -include("mongoose.hrl"). @@ -25,19 +26,23 @@ -type listitem() :: #listitem{}. start(Host, _Opts) -> - mod_disco:register_feature(Host, ?NS_BLOCKING), - ejabberd_hooks:add(privacy_iq_get, Host, - ?MODULE, process_iq_get, 50), - ejabberd_hooks:add(privacy_iq_set, Host, - ?MODULE, process_iq_set, 50), - ok. + ejabberd_hooks:add(hooks(Host)). stop(Host) -> - ejabberd_hooks:delete(privacy_iq_get, Host, - ?MODULE, process_iq_get, 50), - ejabberd_hooks:delete(privacy_iq_set, Host, - ?MODULE, process_iq_set, 50), - ok. + ejabberd_hooks:delete(hooks(Host)). + +hooks(Host) -> + [{disco_local_features, Host, ?MODULE, add_local_features, 99}, + {privacy_iq_get, Host, ?MODULE, process_iq_get, 50}, + {privacy_iq_set, Host, ?MODULE, process_iq_set, 50}]. + +-spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). +add_local_features(Acc, _From, _To, <<>>, _Lang) -> + mongoose_disco:add_features([?NS_BLOCKING], Acc); +add_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + process_iq_get(Acc, _From = #jid{luser = LUser, lserver = LServer}, _, #iq{xmlns = ?NS_BLOCKING}, _) -> @@ -217,4 +222,3 @@ blocking_query_response(Lst) -> attrs = [{<<"xmlns">>, ?NS_BLOCKING}], children = [#xmlel{name= <<"item">>, attrs = [{<<"jid">>, jid:to_binary(J#listitem.value)}]} || J <- Lst]}. - From 873f5a0526be5d260d94bed49d40facda5fc708d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 09:37:47 +0200 Subject: [PATCH 11/39] Do not register disco features in mod_event_pusher_push It was already registered with the IQ handler. --- src/event_pusher/mod_event_pusher_push.erl | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/event_pusher/mod_event_pusher_push.erl b/src/event_pusher/mod_event_pusher_push.erl index 97dec47302a..b119c4ac675 100644 --- a/src/event_pusher/mod_event_pusher_push.erl +++ b/src/event_pusher/mod_event_pusher_push.erl @@ -87,7 +87,6 @@ start(Host, Opts) -> mod_event_pusher_push_plugin:init(Host), IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - mod_disco:register_feature(Host, ?NS_PUSH), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_PUSH, ?MODULE, iq_handler, IQDisc), gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_PUSH, ?MODULE, @@ -102,7 +101,6 @@ stop(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_PUSH), gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_PUSH), - mod_disco:unregister_feature(Host, ?NS_PUSH), mongoose_wpool:stop(generic, Host, pusher_push), ok. From ea6fcdf98f6871e9efbf076e539e55c0a2227d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 09:46:55 +0200 Subject: [PATCH 12/39] Do not register disco features in mod_carboncopy --- src/mod_carboncopy.erl | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index 9844574fdd2..a0397ac6ff9 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -38,7 +38,8 @@ is_carbon_copy/1]). %% Hooks --export([user_send_packet/4, +-export([add_local_features/5, + user_send_packet/4, user_receive_packet/5, iq_handler2/5, iq_handler1/5, @@ -73,32 +74,35 @@ is_carbon_copy(Packet) -> start(HostType, Opts) -> %% execute disable/enable actions in the c2s process itself IQDisc = gen_mod:get_opt(iqdisc, Opts, no_queue), - %% TODO: update code related to mod_disco - mod_disco:register_feature(HostType, ?NS_CC_1), - mod_disco:register_feature(HostType, ?NS_CC_2), - mod_disco:register_feature(HostType, ?NS_CC_RULES), - ejabberd_hooks:add(unset_presence_hook, HostType, ?MODULE, remove_connection, 10), - ejabberd_hooks:add(user_send_packet, HostType, ?MODULE, user_send_packet, 89), - ejabberd_hooks:add(user_receive_packet, HostType, ?MODULE, user_receive_packet, 89), + ejabberd_hooks:add(hooks(HostType)), gen_iq_handler:add_iq_handler_for_domain(HostType, ?NS_CC_2, ejabberd_sm, fun ?MODULE:iq_handler2/5, #{}, IQDisc), gen_iq_handler:add_iq_handler_for_domain(HostType, ?NS_CC_1, ejabberd_sm, fun ?MODULE:iq_handler1/5, #{}, IQDisc). stop(HostType) -> + ejabberd_hooks:delete(hooks(HostType)), gen_iq_handler:remove_iq_handler_for_domain(HostType, ?NS_CC_1, ejabberd_sm), gen_iq_handler:remove_iq_handler_for_domain(HostType, ?NS_CC_2, ejabberd_sm), - %% TODO: update code related to mod_disco - mod_disco:unregister_feature(HostType, ?NS_CC_2), - mod_disco:unregister_feature(HostType, ?NS_CC_1), - ejabberd_hooks:delete(user_send_packet, HostType, ?MODULE, user_send_packet, 89), - ejabberd_hooks:delete(user_receive_packet, HostType, ?MODULE, user_receive_packet, 89), - ejabberd_hooks:delete(unset_presence_hook, HostType, ?MODULE, remove_connection, 10). + ok. + +hooks(HostType) -> + [{disco_local_features, HostType, ?MODULE, add_local_features, 99}, + {unset_presence_hook, HostType, ?MODULE, remove_connection, 10}, + {user_send_packet, HostType, ?MODULE, user_send_packet, 89}, + {user_receive_packet, HostType, ?MODULE, user_receive_packet, 89}]. -spec config_spec() -> mongoose_config_spec:config_section(). config_spec() -> #section{items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc()}}. +-spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). +add_local_features(Acc, _From, _To, <<>>, _Lang) -> + mongoose_disco:add_features([?NS_CC_1, ?NS_CC_2, ?NS_CC_RULES], Acc); +add_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + iq_handler2(Acc, From, _To, IQ, _Extra) -> iq_handler(Acc, From, IQ, ?NS_CC_2). iq_handler1(Acc, From, _To, IQ, _Extra) -> From 0e020274d953ceb917e640257d49f36c1e1a9ca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 10:23:26 +0200 Subject: [PATCH 13/39] Do not register disco features in mod_mam --- src/mam/mod_mam.erl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/mam/mod_mam.erl b/src/mam/mod_mam.erl index 97e3c3819c9..dc78028b816 100644 --- a/src/mam/mod_mam.erl +++ b/src/mam/mod_mam.erl @@ -45,7 +45,8 @@ -export([start/2, stop/1]). %% ejabberd handlers --export([process_mam_iq/4, +-export([add_local_features/5, + process_mam_iq/4, user_send_packet/4, remove_user/3, filter_packet/1, @@ -201,7 +202,6 @@ start(Host, Opts) -> %% `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, @@ -217,7 +217,6 @@ stop(Host) -> 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)], ok. %% ---------------------------------------------------------------------- @@ -251,6 +250,12 @@ process_mam_iq(From=#jid{lserver=Host}, To, Acc, IQ) -> {Acc, return_action_not_allowed_error_iq(IQ)} end. +-spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). +add_local_features(Acc, _From, #jid{lserver = LServer}, <<>>, _Lang) -> + mongoose_disco:add_features(features(?MODULE, LServer), Acc); +add_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. %% @doc Handle an outgoing message. %% @@ -678,7 +683,9 @@ config_metrics(Host) -> -spec hooks(jid:lserver()) -> [ejabberd_hooks:hook()]. hooks(Host) -> - [{user_send_packet, Host, ?MODULE, user_send_packet, 60}, + [ + {disco_local_features, Host, ?MODULE, add_local_features, 99}, + {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}, From 5c3438b2695a6d06b63bf69d1381e632e777723c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 14:19:44 +0200 Subject: [PATCH 14/39] Accept unordered features in MUC disco tests --- big_tests/tests/muc_helper.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/big_tests/tests/muc_helper.erl b/big_tests/tests/muc_helper.erl index a18f6544ca7..055ca28b76e 100644 --- a/big_tests/tests/muc_helper.erl +++ b/big_tests/tests/muc_helper.erl @@ -4,6 +4,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("exml/include/exml.hrl"). -include_lib("escalus/include/escalus_xmlns.hrl"). +-include_lib("eunit/include/eunit.hrl"). -import(distributed_helper, [mim/0, subhost_pattern/1, @@ -237,8 +238,10 @@ has_features(#xmlel{children = [ Query ]}, Features) -> Identity = exml_query:subelement(Query, <<"identity">>), <<"conference">> = exml_query:attr(Identity, <<"category">>), - Features = exml_query:paths(Query, [{element, <<"feature">>}, - {attr, <<"var">>}]). + ExpectedFeatures = lists:sort(Features), + ActualFeatures = lists:sort(exml_query:paths(Query, [{element, <<"feature">>}, + {attr, <<"var">>}])), + ?assertEqual(ExpectedFeatures, ActualFeatures). assert_valid_affiliation(<<"owner">>) -> ok; assert_valid_affiliation(<<"admin">>) -> ok; From fcd0264114a0e301c4bd5e091c0a6a737fae85a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 14:20:19 +0200 Subject: [PATCH 15/39] Add interface for get_local_features It will be used by modules that need to get registered features and are not returning IQ errors. Error handling can be changed in the future if needed. --- src/mongoose_disco.erl | 22 +++++++++++++++++++++- src/mongoose_hooks.erl | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/mongoose_disco.erl b/src/mongoose_disco.erl index 0f12a8263b7..ecc48b4b581 100644 --- a/src/mongoose_disco.erl +++ b/src/mongoose_disco.erl @@ -1,10 +1,30 @@ -module(mongoose_disco). --export([add_features/2]). +-export([get_local_features/5, + add_features/2]). + +-include("mongoose.hrl"). +-include("jlib.hrl"). -type acc() :: empty | {error, any()} | {result, [exml:element()]}. -type feature() :: binary(). +get_local_features(Host, From, To, Node, Lang) -> + case mongoose_hooks:disco_local_features(Host, From, To, Node, Lang) of + empty -> + []; + {error, Reason} -> + ?LOG_ERROR(#{what => get_local_features_failed, + from_jid => jid:to_binary(From), + to_jid => jid:to_binary(To), + node => Node, + lang => Lang, + reason => Reason}), + []; + {result, Features} -> + Features + end. + -spec add_features([feature()], acc()) -> acc(). add_features(Features, Acc) -> case Acc of diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index 5650df69ea9..b852de82e68 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -1271,7 +1271,7 @@ disco_info(Server, Module, Node, Lang) -> To :: jid:jid(), Node :: binary(), Lang :: ejabberd:lang(), - Result :: {error, any()} | {result, [exml:element()]}. + Result :: mongoose_disco:acc(). disco_local_features(Server, From, To, Node, Lang) -> ejabberd_hooks:run_for_host_type(disco_local_features, Server, empty, [From, To, Node, Lang]). From 1f6cb7ed51d557d2ccc7204e5163d6325c802c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 14:23:04 +0200 Subject: [PATCH 16/39] Do not call mod_disco from mod_muc and mod_mam_muc Rework feature discovery to use the hooks for disco. Additional features are added in place, as adding hooks brings no benefits in custom handlers. Also: - Use the disco hook in mod_muc (required for mod_mam_muc features) - Move 'features_to_xml' to a helper module Note: This commit breaks MUC Light tests because of the change in mod_mam_muc. --- src/mam/mod_mam_muc.erl | 15 +++++++--- src/mod_disco.erl | 18 ++---------- src/mod_muc.erl | 15 ++++------ src/mod_muc_room.erl | 62 +++++++++++++++++++---------------------- src/mongoose_disco.erl | 14 +++++++++- 5 files changed, 60 insertions(+), 64 deletions(-) diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index edf4e09ed5d..c4c792b495c 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -43,7 +43,8 @@ -export([start/2, stop/1]). %% ejabberd room handlers --export([filter_room_packet/2, +-export([add_local_features/5, + filter_room_packet/2, room_process_mam_iq/4, forget_room/2, forget_room/3]). @@ -151,7 +152,6 @@ start(Host, Opts) -> %% 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, @@ -168,12 +168,18 @@ stop(Host) -> ejabberd_hooks:delete(hooks(Host, MUCHost)), 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)], ok. %% ---------------------------------------------------------------------- %% hooks and handlers for MUC +-spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). +add_local_features(Acc, _From, #jid{lserver = LServer}, <<>>, _Lang) -> + mongoose_disco:add_features(features(?MODULE, LServer), Acc); +add_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + %% @doc Handle public MUC-message. -spec filter_room_packet(Packet :: packet(), EventData :: mod_muc:room_event_data()) -> packet(). @@ -604,7 +610,8 @@ is_archivable_message(MUCHost, Dir, Packet) -> -spec hooks(jid:lserver(), jid:lserver()) -> [ejabberd_hooks:hook()]. hooks(Host, MUCHost) -> - [{filter_room_packet, MUCHost, ?MODULE, filter_room_packet, 60}, + [{disco_local_features, MUCHost, ?MODULE, add_local_features, 99}, + {filter_room_packet, MUCHost, ?MODULE, filter_room_packet, 60}, {forget_room, MUCHost, ?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_disco.erl b/src/mod_disco.erl index cc4cdfcceaf..0dbe4a73254 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -221,7 +221,7 @@ process_local_iq_info(From, To, Acc, #iq{type = get, lang = Lang, sub_el = SubEl attrs = [{<<"xmlns">>, ?NS_DISCO_INFO} | ANode], children = Identity ++ Info ++ - features_to_xml(Features)}]}}; + mongoose_disco:features_to_xml(Features)}]}}; {error, Error} -> {Acc, IQ#iq{type = error, sub_el = [SubEl, Error]}} end. @@ -264,20 +264,6 @@ get_local_features(Acc, _From, _To, Node, _Lang) when is_binary(Node) -> end. --spec features_to_xml(FeatureList :: [{feature(), jid:server()}] - ) -> [exml:element()]. -features_to_xml(FeatureList) -> - %% Avoid duplicating features - [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, Feat}]} || - Feat <- lists:usort( - lists:map( - fun({{Feature, _Host}}) -> - Feature; - (Feature) when is_binary(Feature) -> - Feature - end, FeatureList))]. - - -spec domain_to_xml(binary() | {binary()}) -> exml:element(). domain_to_xml({Domain}) -> #xmlel{name = <<"item">>, attrs = [{<<"jid">>, Domain}]}; @@ -437,7 +423,7 @@ process_sm_iq_info(From, To, Acc, #iq{type = get, lang = Lang, sub_el = SubEl} = sub_el = [#xmlel{name = <<"query">>, attrs = [{<<"xmlns">>, ?NS_DISCO_INFO} | ANode], children = Identity ++ - features_to_xml(Features)}]}}; + mongoose_disco:features_to_xml(Features)}]}}; {error, Error} -> {Acc, IQ#iq{type = error, sub_el = [SubEl, Error]}} end; diff --git a/src/mod_muc.erl b/src/mod_muc.erl index e4b9f1ac0e9..70e2c64138f 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -919,20 +919,15 @@ default_host() -> -spec iq_disco_info(ejabberd:lang(), jid:jid(), jid:jid()) -> [exml:element(), ...]. iq_disco_info(Lang, From, To) -> - {result, RegisteredFeatures} = mod_disco:get_local_features(empty, From, To, <<>>, <<>>), + RegisteredFeatures = mongoose_disco:get_local_features(To#jid.lserver, From, To, <<>>, Lang), [#xmlel{name = <<"identity">>, attrs = [{<<"category">>, <<"conference">>}, {<<"type">>, <<"text">>}, - {<<"name">>, translate:translate(Lang, <<"Chatrooms">>)}]}, - #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_DISCO_INFO}]}, - #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_DISCO_ITEMS}]}, - #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_MUC}]}, - #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_MUC_UNIQUE}]}, - #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_REGISTER}]}, - #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_RSM}]}, - #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_VCARD}]}] ++ - [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, URN}]} || {{URN, _Host}} <- RegisteredFeatures]. + {<<"name">>, translate:translate(Lang, <<"Chatrooms">>)}]} | + mongoose_disco:features_to_xml(features() ++ RegisteredFeatures)]. +features() -> + [?NS_DISCO_INFO, ?NS_DISCO_ITEMS, ?NS_MUC, ?NS_MUC_UNIQUE, ?NS_REGISTER, ?NS_RSM, ?NS_VCARD]. -spec iq_disco_items(jid:server(), jid:jid(), ejabberd:lang(), Rsm :: none | jlib:rsm_in()) -> any(). diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index e4f3e9d8cfe..20fbc8a36f3 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -3926,18 +3926,11 @@ presence_stanza_of_type_unavailable(DestroyEl) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Disco --spec feature(binary()) -> exml:element(). -feature(Var) -> - #xmlel{name = <<"feature">>, - attrs = [{<<"var">>, Var}]}. - - --spec config_opt_to_feature(boolean(), Fiftrue :: binary(), Fiffalse :: binary()) - -> exml:element(). +-spec config_opt_to_feature(boolean(), Fiftrue :: binary(), Fiffalse :: binary()) -> binary(). config_opt_to_feature(Opt, Fiftrue, Fiffalse) -> case Opt of - true -> feature(Fiftrue); - false -> feature(Fiffalse) + true -> Fiftrue; + false -> Fiffalse end. @@ -3947,30 +3940,33 @@ config_opt_to_feature(Opt, Fiftrue, Fiffalse) -> process_iq_disco_info(_From, set, _Lang, _StateData) -> {error, mongoose_xmpp_errors:not_allowed()}; process_iq_disco_info(From, get, Lang, StateData) -> - Config = StateData#state.config, RoomJID = StateData#state.jid, - {result, RegisteredFeatures} = mod_disco:get_local_features(empty, From, RoomJID, <<>>, <<>>), - RegisteredFeaturesXML = [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, URN}]} || - {{URN, _Host}} <- RegisteredFeatures], - {result, [#xmlel{name = <<"identity">>, - attrs = [{<<"category">>, <<"conference">>}, - {<<"type">>, <<"text">>}, - {<<"name">>, get_title(StateData)}]}, - #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_MUC}]}, - config_opt_to_feature((Config#config.public), - <<"muc_public">>, <<"muc_hidden">>), - config_opt_to_feature((Config#config.persistent), - <<"muc_persistent">>, <<"muc_temporary">>), - config_opt_to_feature((Config#config.members_only), - <<"muc_membersonly">>, <<"muc_open">>), - config_opt_to_feature((Config#config.anonymous), - <<"muc_semianonymous">>, <<"muc_nonanonymous">>), - config_opt_to_feature((Config#config.moderated), - <<"muc_moderated">>, <<"muc_unmoderated">>), - config_opt_to_feature((Config#config.password_protected), - <<"muc_passwordprotected">>, <<"muc_unsecured">>) - ] ++ iq_disco_info_extras(Lang, StateData) ++ RegisteredFeaturesXML, StateData}. - + Config = StateData#state.config, + Host = StateData#state.host, + RegisteredFeatures = mongoose_disco:get_local_features(Host, From, RoomJID, <<>>, Lang), + XML = [#xmlel{name = <<"identity">>, + attrs = [{<<"category">>, <<"conference">>}, + {<<"type">>, <<"text">>}, + {<<"name">>, get_title(StateData)}]} | + mongoose_disco:features_to_xml(RegisteredFeatures ++ room_features(Config))] ++ + iq_disco_info_extras(Lang, StateData), + {result, XML, StateData}. + +-spec room_features(config()) -> [moongoose_disco:feature()]. +room_features(Config) -> + [?NS_MUC, + config_opt_to_feature((Config#config.public), + <<"muc_public">>, <<"muc_hidden">>), + config_opt_to_feature((Config#config.persistent), + <<"muc_persistent">>, <<"muc_temporary">>), + config_opt_to_feature((Config#config.members_only), + <<"muc_membersonly">>, <<"muc_open">>), + config_opt_to_feature((Config#config.anonymous), + <<"muc_semianonymous">>, <<"muc_nonanonymous">>), + config_opt_to_feature((Config#config.moderated), + <<"muc_moderated">>, <<"muc_unmoderated">>), + config_opt_to_feature((Config#config.password_protected), + <<"muc_passwordprotected">>, <<"muc_unsecured">>)]. -spec rfieldt(binary(), binary(), binary()) -> exml:element(). rfieldt(Type, Var, Val) -> diff --git a/src/mongoose_disco.erl b/src/mongoose_disco.erl index ecc48b4b581..e5186d34902 100644 --- a/src/mongoose_disco.erl +++ b/src/mongoose_disco.erl @@ -1,7 +1,8 @@ -module(mongoose_disco). -export([get_local_features/5, - add_features/2]). + add_features/2, + features_to_xml/1]). -include("mongoose.hrl"). -include("jlib.hrl"). @@ -35,3 +36,14 @@ add_features(Features, Acc) -> {result, InitialFeatures} -> {result, Features ++ InitialFeatures} end. + +features_to_xml(FeatureList) -> + %% Avoid duplicating features + [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, Feat}]} || + Feat <- lists:usort( + lists:map( + fun({{Feature, _Host}}) -> + Feature; + (Feature) when is_binary(Feature) -> + Feature + end, FeatureList))]. From a2175b18f4bb93fb8c0f31d064c48909d7e42d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 15:07:12 +0200 Subject: [PATCH 17/39] Accept unordered features in MUC Light disco tests --- big_tests/tests/muc_light_SUITE.erl | 31 ++++++++++++++++------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/big_tests/tests/muc_light_SUITE.erl b/big_tests/tests/muc_light_SUITE.erl index 0c4f4448b29..0840a5aa52c 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_lib("eunit/include/eunit.hrl"). -export([ % service removing_users_from_server_triggers_room_destruction/1 @@ -307,13 +308,7 @@ disco_features_story(Config, HasMAM) -> <<"conference">> = exml_query:path(Stanza, [{element, <<"query">>}, {element, <<"identity">>}, {attr, <<"category">>}]), - FeaturesExpected = [?NS_MUC_LIGHT] ++ case HasMAM of - true -> mam_helper:namespaces(); - false -> [] - end, - FeaturesExpected = exml_query:paths(Stanza, [{element, <<"query">>}, - {element, <<"feature">>}, - {attr, <<"var">>}]), + check_features(Stanza, HasMAM), escalus:assert(is_stanza_from, [?MUCHOST], Stanza) end). @@ -328,16 +323,24 @@ disco_info_story(Config, HasMAM) -> DiscoStanza = escalus_stanza:to(escalus_stanza:iq_get(?NS_DISCO_INFO, []), ?ROOM), escalus:send(Alice, DiscoStanza), Stanza = escalus:wait_for_stanza(Alice), - FeaturesExpected = [?NS_MUC_LIGHT] ++ case HasMAM of - true -> mam_helper:namespaces(); - false -> [] - end, - FeaturesExpected = exml_query:paths(Stanza, [{element, <<"query">>}, - {element, <<"feature">>}, - {attr, <<"var">>}]), + check_features(Stanza, HasMAM), escalus:assert(is_stanza_from, [?MUCHOST], Stanza) end). +check_features(Stanza, HasMAM) -> + ExpectedFeatures = expected_features(HasMAM), + ActualFeatures = exml_query:paths(Stanza, [{element, <<"query">>}, + {element, <<"feature">>}, + {attr, <<"var">>}]), + ?assertEqual(ExpectedFeatures, ActualFeatures). + +expected_features(HasMAM) -> + MamFeatures = case HasMAM of + true -> mam_helper:namespaces(); + false -> [] + end, + lists:sort([?NS_MUC_LIGHT | MamFeatures]). + %% The room list is empty. Rooms_per_page set to `infinity` disco_rooms_empty_page_infinity(Config) -> escalus:story(Config, [{alice, 1}], fun(Alice) -> From 25975ca70c4f28155b46ed0330760e6f2553cf7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 15:07:27 +0200 Subject: [PATCH 18/39] Do not call mod_disco from mod_muc_light This commit fixes big tests. --- src/muc_light/mod_muc_light_codec_legacy.erl | 8 ++++---- src/muc_light/mod_muc_light_codec_modern.erl | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/muc_light/mod_muc_light_codec_legacy.erl b/src/muc_light/mod_muc_light_codec_legacy.erl index b64ac380266..461ee8091e5 100644 --- a/src/muc_light/mod_muc_light_codec_legacy.erl +++ b/src/muc_light/mod_muc_light_codec_legacy.erl @@ -244,13 +244,13 @@ parse_blocking_list([Item | RItemsEls], ItemsAcc) -> {iq_reply, XMLNS :: binary(), Els :: [jlib:xmlch()], ID :: binary()} | noreply. encode_meta({get, #disco_info{ id = ID }}, RoomJID, SenderJID, _HandleFun) -> - {result, RegisteredFeatures} = mod_disco:get_local_features(empty, SenderJID, RoomJID, <<>>, <<>>), + LServer = RoomJID#jid.lserver, + RegisteredFeatures = mongoose_disco:get_local_features(LServer, SenderJID, RoomJID, <<>>, <<>>), DiscoEls = [#xmlel{name = <<"identity">>, attrs = [{<<"category">>, <<"conference">>}, {<<"type">>, <<"text">>}, - {<<"name">>, <<"MUC Light">>}]}, - #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_MUC}]}] ++ - [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, URN}]} || {{URN, _Host}} <- RegisteredFeatures], + {<<"name">>, <<"MUC Light">>}]} | + mongoose_disco:features_to_xml([?NS_MUC | RegisteredFeatures])], {iq_reply, ?NS_DISCO_INFO, DiscoEls, ID}; encode_meta({get, #disco_items{ rooms = Rooms, id = ID, rsm = RSMOut }}, _RoomJID, _SenderJID, _HandleFun) -> diff --git a/src/muc_light/mod_muc_light_codec_modern.erl b/src/muc_light/mod_muc_light_codec_modern.erl index 4d1b152aa04..14583c2b496 100644 --- a/src/muc_light/mod_muc_light_codec_modern.erl +++ b/src/muc_light/mod_muc_light_codec_modern.erl @@ -251,13 +251,13 @@ parse_blocking_list(_, _) -> %%==================================================================== encode_iq({get, #disco_info{ id = ID }}, Sender, RoomJID, _RoomBin, _HandleFun) -> - {result, RegisteredFeatures} = mod_disco:get_local_features(empty, Sender, RoomJID, <<>>, <<>>), + LServer = RoomJID#jid.lserver, + RegisteredFeatures = mongoose_disco:get_local_features(LServer, Sender, RoomJID, <<>>, <<>>), DiscoEls = [#xmlel{name = <<"identity">>, attrs = [{<<"category">>, <<"conference">>}, {<<"type">>, <<"text">>}, - {<<"name">>, <<"MUC Light">>}]}, - #xmlel{name = <<"feature">>, attrs = [{<<"var">>, ?NS_MUC_LIGHT}]}] ++ - [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, URN}]} || {{URN, _Host}} <- RegisteredFeatures], + {<<"name">>, <<"MUC Light">>}]} | + mongoose_disco:features_to_xml([?NS_MUC_LIGHT | RegisteredFeatures])], {reply, ?NS_DISCO_INFO, DiscoEls, ID}; encode_iq({get, #disco_items{ rooms = Rooms, id = ID, rsm = RSMOut }}, _Sender, _RoomJID, _RoomBin, _HandleFun) -> From 949fb68bfe4814dc405c29924a97b3a8ffe97423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Thu, 27 May 2021 15:40:53 +0200 Subject: [PATCH 19/39] Do not call mod_disco from mod_http_upload Instead: register only the necessary IQ handler directly This makes the module responsible for its IQ handlers, as it should be. --- src/http_upload/mod_http_upload.erl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/http_upload/mod_http_upload.erl b/src/http_upload/mod_http_upload.erl index 7ab621fbdf1..1c57046424a 100644 --- a/src/http_upload/mod_http_upload.erl +++ b/src/http_upload/mod_http_upload.erl @@ -53,7 +53,6 @@ start(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), SubHost = subhost(Host), - mod_disco:register_subhost(Host, SubHost), mongoose_subhosts:register(Host, SubHost), ejabberd_router:register_route(SubHost, mongoose_packet_handler:new(ejabberd_local)), ejabberd_hooks:add(disco_local_features, SubHost, ?MODULE, get_disco_features, 90), @@ -62,6 +61,8 @@ start(Host, Opts) -> ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_disco_items, 90), gen_iq_handler:add_iq_handler(ejabberd_local, SubHost, ?NS_HTTP_UPLOAD_030, ?MODULE, iq_handler, IQDisc), + gen_iq_handler:add_iq_handler(ejabberd_local, SubHost, ?NS_DISCO_INFO, + mod_disco, process_local_iq_info, IQDisc), gen_mod:start_backend_module(?MODULE, with_default_backend(Opts), [create_slot]). @@ -69,13 +70,13 @@ start(Host, Opts) -> stop(Host) -> SubHost = subhost(Host), gen_iq_handler:remove_iq_handler(ejabberd_local, SubHost, ?NS_HTTP_UPLOAD_030), - ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_disco_items, 90), + gen_iq_handler:remove_iq_handler(ejabberd_local, SubHost, ?NS_DISCO_INFO), + ejabberd_hooks:delete(disco_local_items, SubHost, ?MODULE, get_disco_items, 90), ejabberd_hooks:delete(disco_info, SubHost, ?MODULE, get_disco_info, 90), ejabberd_hooks:delete(disco_local_identity, SubHost, ?MODULE, get_disco_identity, 90), ejabberd_hooks:delete(disco_local_features, SubHost, ?MODULE, get_disco_features, 90), ejabberd_router:unregister_route(SubHost), - mongoose_subhosts:unregister(SubHost), - mod_disco:unregister_subhost(Host, SubHost). + mongoose_subhosts:unregister(SubHost). -spec iq_handler(From :: jid:jid(), To :: jid:jid(), Acc :: mongoose_acc:t(), From ee6711269f24fa4ecd8a1bbfda90269979bbd12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 08:47:52 +0200 Subject: [PATCH 20/39] Do not call mod_disco from ejabberd_local Use the hooks instead. New ETS table is used for efficient lookup of the NS list. --- src/ejabberd_local.erl | 31 +++++++++++++++++-------------- src/mod_disco.erl | 2 -- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index 23758cf3ae2..86cec0b0888 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -49,9 +49,9 @@ unregister_iq_handler/2, unregister_host/1, unregister_iq_response_handler/2, - refresh_iq_handlers/0, bounce_resource_packet/4, - sync/0 + sync/0, + add_local_features/5 ]). %% Hooks callbacks @@ -81,6 +81,7 @@ timer}). -define(IQTABLE, local_iqtable). +-define(NSTABLE, local_nstable). %% This value is used in SIP and Megaco for a transaction lifetime. -define(IQ_TIMEOUT, 32000). @@ -218,9 +219,6 @@ unregister_iq_handler(Domain, XMLNS) -> ejabberd_local ! {unregister_iq_handler, Domain, XMLNS}, ok. -refresh_iq_handlers() -> - ejabberd_local ! refresh_iq_handlers. - -spec bounce_resource_packet(Acc :: mongoose_acc:t(), From :: jid:jid(), To :: jid:jid(), @@ -238,6 +236,14 @@ register_host(Host) -> unregister_host(Host) -> gen_server:call(?MODULE, {unregister_host, Host}). +-spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). +add_local_features(Acc, _From, #jid{lserver = LServer}, <<>>, _Lang) -> + Features = ets:lookup_element(?NSTABLE, LServer, 2), + mongoose_disco:add_features(Features, Acc); +add_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. + %%==================================================================== %% API %%==================================================================== @@ -269,6 +275,7 @@ node_cleanup(Acc, Node) -> %%-------------------------------------------------------------------- init([]) -> catch ets:new(?IQTABLE, [named_table, protected]), + catch ets:new(?NSTABLE, [named_table, bag, protected]), update_table(), mnesia:create_table(iq_response, [{ram_copies, [node()]}, @@ -323,25 +330,19 @@ handle_info({route, Acc, From, To, El}, State) -> process_packet(Acc, From, To, El, #{}), {noreply, State}; handle_info({register_iq_handler, Host, XMLNS, IQHandler}, State) -> + ets:insert(?NSTABLE, {Host, XMLNS}), ets:insert(?IQTABLE, {{XMLNS, Host}, IQHandler}), - catch mod_disco:register_feature(Host, XMLNS), {noreply, State}; handle_info({unregister_iq_handler, Host, XMLNS}, State) -> case ets:lookup(?IQTABLE, {XMLNS, Host}) of [{_, IQHandler}] -> gen_iq_component:stop_iq_handler(IQHandler), - ets:delete(?IQTABLE, {XMLNS, Host}), - catch mod_disco:unregister_feature(Host, XMLNS); + ets:delete_object(?NSTABLE, {Host, XMLNS}), + ets:delete(?IQTABLE, {XMLNS, Host}); _ -> ok end, {noreply, State}; -handle_info(refresh_iq_handlers, State) -> - lists:foreach( - fun({{XMLNS, Host}, _IQHandler}) -> - catch mod_disco:register_feature(Host, XMLNS) - end, ets:tab2list(?IQTABLE)), - {noreply, State}; handle_info({timeout, _TRef, ID}, State) -> process_iq_timeout(ID), {noreply, State}; @@ -461,11 +462,13 @@ cancel_timer(TRef) -> do_register_host(Host) -> ejabberd_router:register_route(Host, mongoose_packet_handler:new(?MODULE)), + ejabberd_hooks:add(disco_local_features, Host, ?MODULE, add_local_features, 99), ejabberd_hooks:add(local_send_to_resource_hook, Host, ?MODULE, bounce_resource_packet, 100). do_unregister_host(Host) -> ejabberd_router:unregister_route(Host), + ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, add_local_features, 99), ejabberd_hooks:delete(local_send_to_resource_hook, Host, ?MODULE, bounce_resource_packet, 100). diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 0dbe4a73254..20e14236a20 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -116,8 +116,6 @@ register_subhost(Host, Subhost) -> ets:insert(disco_subhosts, {{Subhost, Host}}) end. register_host(Host, Opts) -> - ejabberd_local:refresh_iq_handlers(), - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, ?MODULE, process_local_iq_items, IQDisc), From 5959e155837908317b5315880a874a94d59287b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 09:18:12 +0200 Subject: [PATCH 21/39] Remove unnecessary disco tables for fatures and subhosts They are unused, hooks are used instead. --- src/mod_disco.erl | 75 +++++++---------------------------------------- 1 file changed, 10 insertions(+), 65 deletions(-) diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 20e14236a20..dc51f50f7d6 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -45,30 +45,21 @@ get_sm_features/5, get_sm_items/5, get_info/5, - register_feature/2, - unregister_feature/2, register_extra_domain/2, - unregister_extra_domain/2, - register_subhost/2, - unregister_subhost/2]). + unregister_extra_domain/2]). -include("mongoose.hrl"). -include("jlib.hrl"). -include("mongoose_config_spec.hrl"). --type feature() :: any(). - -type return_hidden() :: ejabberd_router:return_hidden(). -spec start(jid:server(), list()) -> 'ok'. start(Host, Opts) -> [catch ets:new(Name, [named_table, ordered_set, public]) || Name <- - [disco_features, disco_extra_domains, disco_sm_features, disco_sm_nodes, disco_subhosts]], + [disco_extra_domains]], register_host(Host, Opts), - register_feature(Host, <<"iq">>), - register_feature(Host, <<"presence">>), - register_feature(Host, <<"presence-invisible">>), ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 100), ExtraDomains = gen_mod:get_opt(extra_domains, Opts, []), lists:foreach(fun(Domain) -> register_extra_domain(Host, Domain) end, ExtraDomains). @@ -108,13 +99,6 @@ process_server_info(KVs) -> Modules = proplists:get_value(modules, KVs, all), {Modules, Name, URLs}. -register_subhost(Host, Subhost) -> - case gen_mod:is_loaded(Host, ?MODULE) of - false -> ok; - true -> - register_host(Subhost, gen_mod:get_module_opts(Host, ?MODULE)), - ets:insert(disco_subhosts, {{Subhost, Host}}) - end. register_host(Host, Opts) -> IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, @@ -134,13 +118,6 @@ register_host(Host, Opts) -> ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 100), ok. -unregister_subhost(Host, Subhost) -> - case gen_mod:is_loaded(Host, ?MODULE) of - false -> ok; - true -> - unregister_host(Subhost), - ets:delete(disco_subhosts, {Subhost, Host}) - end. unregister_host(Host) -> ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 100), ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 100), @@ -153,24 +130,9 @@ unregister_host(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO), - catch ets:match_delete(disco_features, {{'_', Host}}), catch ets:match_delete(disco_extra_domains, {{'_', Host}}), - lists:foreach(fun([{Subhost, _}]) -> unregister_subhost(Host, Subhost) end, - ets:match(disco_subhosts, {{'_', Host}})), ok. --spec register_feature(jid:server(), feature()) -> 'true'. -register_feature(Host, Feature) -> - catch ets:new(disco_features, [named_table, ordered_set, public]), - ets:insert(disco_features, {{Feature, Host}}). - - --spec unregister_feature(jid:server(), feature()) -> 'true'. -unregister_feature(Host, Feature) -> - catch ets:new(disco_features, [named_table, ordered_set, public]), - ets:delete(disco_features, {Feature, Host}). - - -spec register_extra_domain(jid:server(), binary()) -> 'true'. register_extra_domain(Host, Domain) -> catch ets:new(disco_extra_domains, [named_table, ordered_set, public]), @@ -220,7 +182,8 @@ process_local_iq_info(From, To, Acc, #iq{type = get, lang = Lang, sub_el = SubEl children = Identity ++ Info ++ mongoose_disco:features_to_xml(Features)}]}}; - {error, Error} -> + empty -> + Error = mongoose_xmpp_errors:item_not_found(), {Acc, IQ#iq{type = error, sub_el = [SubEl, Error]}} end. @@ -237,30 +200,12 @@ get_local_identity(Acc, _From, _To, <<>>, _Lang) -> get_local_identity(Acc, _From, _To, Node, _Lang) when is_binary(Node) -> Acc. - --spec get_local_features(Acc :: 'empty' | {'error', _} | {'result', _}, - From :: jid:jid(), - To :: jid:jid(), - Node :: binary(), - Lang :: ejabberd:lang()) -> {'error', _} | {'result', _}. -get_local_features({error, _Error} = Acc, _From, _To, _Node, _Lang) -> - Acc; -get_local_features(Acc, _From, To, <<>>, _Lang) -> - Feats = case Acc of - {result, Features} -> Features; - empty -> [] - end, - Host = To#jid.lserver, - {result, - ets:select(disco_features, [{{{'_', Host}}, [], ['$_']}]) ++ Feats}; -get_local_features(Acc, _From, _To, Node, _Lang) when is_binary(Node) -> - case Acc of - {result, _Features} -> - Acc; - empty -> - {error, mongoose_xmpp_errors:item_not_found()} - end. - +-spec get_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:acc(). +get_local_features(Acc, _From, _To, <<>>, _Lang) -> + mongoose_disco:add_features([<<"iq">>, <<"presence">>, <<"presence-invisible">>], Acc); +get_local_features(Acc, _From, _To, _Node, _Lang) -> + Acc. -spec domain_to_xml(binary() | {binary()}) -> exml:element(). domain_to_xml({Domain}) -> From 60829928a1b306ffb1f4214a8e78899bc617aeca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 10:02:49 +0200 Subject: [PATCH 22/39] Simplify extra_domains in mod_disco --- big_tests/tests/disco_and_caps_SUITE.erl | 19 +++++++++++-- src/mod_disco.erl | 35 +++++------------------- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/big_tests/tests/disco_and_caps_SUITE.erl b/big_tests/tests/disco_and_caps_SUITE.erl index 7a105051e12..ea9d8a1ebfc 100644 --- a/big_tests/tests/disco_and_caps_SUITE.erl +++ b/big_tests/tests/disco_and_caps_SUITE.erl @@ -10,14 +10,16 @@ groups() -> all_test_cases() -> [caps_feature_is_advertised, - user_can_query_server_caps_via_disco]. + user_can_query_server_caps_via_disco, + extra_domains_are_advertised]. domain() -> ct:get_config({hosts, mim, domain}). init_per_suite(C) -> C2 = escalus:init_per_suite(dynamic_modules:save_modules(domain(), C)), - dynamic_modules:ensure_modules(domain(), [{mod_caps, []}]), + dynamic_modules:ensure_modules(domain(), [{mod_caps, []}, + {mod_disco, [{extra_domains, [extra_domain()]}]}]), C2. end_per_suite(C) -> @@ -54,3 +56,16 @@ user_can_query_server_caps_via_disco(Config) -> {attr, <<"name">>}]), <<"MongooseIM">> = Identity, escalus_connection:stop(Alice). + +extra_domains_are_advertised(Config) -> + escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> + Server = escalus_client:server(Alice), + escalus:send(Alice, escalus_stanza:service_discovery(Server)), + Stanza = escalus:wait_for_stanza(Alice), + escalus:assert(has_service, [extra_domain()], Stanza), + escalus:assert(is_stanza_from, + [ct:get_config({hosts, mim, domain})], Stanza) + end). + +extra_domain() -> + <<"eXtra.example.com">>. diff --git a/src/mod_disco.erl b/src/mod_disco.erl index dc51f50f7d6..e7d18443e35 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -44,9 +44,7 @@ get_sm_identity/5, get_sm_features/5, get_sm_items/5, - get_info/5, - register_extra_domain/2, - unregister_extra_domain/2]). + get_info/5]). -include("mongoose.hrl"). -include("jlib.hrl"). @@ -56,13 +54,8 @@ -spec start(jid:server(), list()) -> 'ok'. start(Host, Opts) -> - [catch ets:new(Name, [named_table, ordered_set, public]) || Name <- - [disco_extra_domains]], - register_host(Host, Opts), - ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 100), - ExtraDomains = gen_mod:get_opt(extra_domains, Opts, []), - lists:foreach(fun(Domain) -> register_extra_domain(Host, Domain) end, ExtraDomains). + ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 100). -spec stop(jid:server()) -> ok. @@ -130,20 +123,8 @@ unregister_host(Host) -> gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS), gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO), - catch ets:match_delete(disco_extra_domains, {{'_', Host}}), ok. --spec register_extra_domain(jid:server(), binary()) -> 'true'. -register_extra_domain(Host, Domain) -> - catch ets:new(disco_extra_domains, [named_table, ordered_set, public]), - ets:insert(disco_extra_domains, {{Domain, Host}}). - - --spec unregister_extra_domain(jid:server(), binary()) -> 'true'. -unregister_extra_domain(Host, Domain) -> - catch ets:new(disco_extra_domains, [named_table, ordered_set, public]), - ets:delete(disco_extra_domains, {Domain, Host}). - -spec process_local_iq_items(jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) -> {mongoose_acc:t(), jlib:iq()}. @@ -228,18 +209,16 @@ get_local_services(Acc, From, To, <<>>, _Lang) -> end, Host = To#jid.lserver, ReturnHidden = should_return_hidden(Host, From), - {result, - lists:usort( - lists:map(fun domain_to_xml/1, - get_vh_services(Host, ReturnHidden) ++ - ets:select(disco_extra_domains, - [{{{'$1', Host}}, [], ['$1']}])) - ) ++ Items}; + Domains = get_vh_services(Host, ReturnHidden) ++ get_extra_domains(Host), + {result, lists:usort(lists:map(fun domain_to_xml/1, Domains)) ++ Items}; get_local_services({result, _} = Acc, _From, _To, _Node, _Lang) -> Acc; get_local_services(empty, _From, _To, _Node, _Lang) -> {error, mongoose_xmpp_errors:item_not_found()}. +get_extra_domains(Host) -> + gen_mod:get_module_opt(Host, ?MODULE, extra_domains, []). + -spec should_return_hidden(Host :: jid:lserver(), From :: jid:jid()) -> return_hidden(). should_return_hidden(_Host, #jid{ luser = <<>> } = _From) -> %% We respect "is hidden" flag only when a client performs the query From 19c45e0dda44c664661db7f2e62e9e62b232cd08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 10:16:11 +0200 Subject: [PATCH 23/39] Reorganize mod_disco start/stop --- src/mod_disco.erl | 62 ++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/src/mod_disco.erl b/src/mod_disco.erl index e7d18443e35..7686ddae9f4 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -54,14 +54,32 @@ -spec start(jid:server(), list()) -> 'ok'. start(Host, Opts) -> - register_host(Host, Opts), - ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 100). - + IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), + [gen_iq_handler:add_iq_handler(Module, Host, NS, ?MODULE, Handler, IQDisc) || + {Module, NS, Handler} <- iq_handlers()], + ejabberd_hooks:add(hooks(Host)). -spec stop(jid:server()) -> ok. stop(Host) -> - unregister_host(Host), - ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 100). + ejabberd_hooks:delete(hooks(Host)), + [gen_iq_handler:remove_iq_handler(Module, Host, NS) || + {Module, NS, _Handler} <- iq_handlers()], + ok. + +hooks(Host) -> + [{disco_local_items, Host, ?MODULE, get_local_services, 100}, + {disco_local_features, Host, ?MODULE, get_local_features, 100}, + {disco_local_identity, Host, ?MODULE, get_local_identity, 100}, + {disco_sm_items, Host, ?MODULE, get_sm_items, 100}, + {disco_sm_features, Host, ?MODULE, get_sm_features, 100}, + {disco_sm_identity, Host, ?MODULE, get_sm_identity, 100}, + {disco_info, Host, ?MODULE, get_info, 100}]. + +iq_handlers() -> + [{ejabberd_local, ?NS_DISCO_ITEMS, process_local_iq_items}, + {ejabberd_local, ?NS_DISCO_INFO, process_local_iq_info}, + {ejabberd_sm, ?NS_DISCO_ITEMS, process_sm_iq_items}, + {ejabberd_sm, ?NS_DISCO_INFO, process_sm_iq_info}]. -spec config_spec() -> mongoose_config_spec:config_section(). config_spec() -> @@ -92,40 +110,6 @@ process_server_info(KVs) -> Modules = proplists:get_value(modules, KVs, all), {Modules, Name, URLs}. -register_host(Host, Opts) -> - IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS, - ?MODULE, process_local_iq_items, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO, - ?MODULE, process_local_iq_info, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS, - ?MODULE, process_sm_iq_items, IQDisc), - gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO, - ?MODULE, process_sm_iq_info, IQDisc), - - ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_local_services, 100), - ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 100), - ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_items, 100), - ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 100), - ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 100), - ejabberd_hooks:add(disco_info, Host, ?MODULE, get_info, 100), - ok. - -unregister_host(Host) -> - ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 100), - ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 100), - ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_items, 100), - ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 100), - ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 100), - ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_services, 100), - ejabberd_hooks:delete(disco_info, Host, ?MODULE, get_info, 100), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_ITEMS), - gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_DISCO_INFO), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_ITEMS), - gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_DISCO_INFO), - ok. - - -spec process_local_iq_items(jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) -> {mongoose_acc:t(), jlib:iq()}. process_local_iq_items(_From, _To, Acc, #iq{type = set, sub_el = SubEl} = IQ) -> From f4fb40c5ad83b73d5c65596966ffa93da142e419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 11:12:12 +0200 Subject: [PATCH 24/39] Simplify mongoose_disco - Remove unnecessary error handling (no errors are returned form handlers anymore) - Remove unnecessary support for feature tuples (no tuples are returned from handlers anymore) - Add type specs --- src/mod_disco.erl | 4 ++++ src/mongoose_disco.erl | 27 +++++++-------------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 7686ddae9f4..a9b8076e05a 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -81,6 +81,8 @@ iq_handlers() -> {ejabberd_sm, ?NS_DISCO_ITEMS, process_sm_iq_items}, {ejabberd_sm, ?NS_DISCO_INFO, process_sm_iq_info}]. +%% Configuration + -spec config_spec() -> mongoose_config_spec:config_section(). config_spec() -> #section{ @@ -110,6 +112,8 @@ process_server_info(KVs) -> Modules = proplists:get_value(modules, KVs, all), {Modules, Name, URLs}. +%% IQ handlers + -spec process_local_iq_items(jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) -> {mongoose_acc:t(), jlib:iq()}. process_local_iq_items(_From, _To, Acc, #iq{type = set, sub_el = SubEl} = IQ) -> diff --git a/src/mongoose_disco.erl b/src/mongoose_disco.erl index e5186d34902..0e48874cce7 100644 --- a/src/mongoose_disco.erl +++ b/src/mongoose_disco.erl @@ -7,21 +7,15 @@ -include("mongoose.hrl"). -include("jlib.hrl"). --type acc() :: empty | {error, any()} | {result, [exml:element()]}. +-type acc() :: empty | {result, [exml:element()]}. -type feature() :: binary(). +-spec get_local_features(jid:lserver(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + [feature()]. get_local_features(Host, From, To, Node, Lang) -> case mongoose_hooks:disco_local_features(Host, From, To, Node, Lang) of empty -> []; - {error, Reason} -> - ?LOG_ERROR(#{what => get_local_features_failed, - from_jid => jid:to_binary(From), - to_jid => jid:to_binary(To), - node => Node, - lang => Lang, - reason => Reason}), - []; {result, Features} -> Features end. @@ -29,21 +23,14 @@ get_local_features(Host, From, To, Node, Lang) -> -spec add_features([feature()], acc()) -> acc(). add_features(Features, Acc) -> case Acc of - {error, _} = Error -> - Error; empty -> {result, Features}; {result, InitialFeatures} -> {result, Features ++ InitialFeatures} end. -features_to_xml(FeatureList) -> +-spec features_to_xml([feature()]) -> [exml:element()]. +features_to_xml(Features) -> %% Avoid duplicating features - [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, Feat}]} || - Feat <- lists:usort( - lists:map( - fun({{Feature, _Host}}) -> - Feature; - (Feature) when is_binary(Feature) -> - Feature - end, FeatureList))]. + [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, Feature}]} || + Feature <- lists:usort(Features)]. From 801273da7be0e69c5c1e72d52b5d111c95d51c8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 14:32:29 +0200 Subject: [PATCH 25/39] Make disco_local_items consistent with disco_local_features Update the interface and the helperes in mongoose_disco. A map is used to guarantee unique JID's in the result. This commit breaks tests as the modules need to be updated. --- src/mod_disco.erl | 35 +++++++++-------------------------- src/mongoose_disco.erl | 37 +++++++++++++++++++++++++++++++++---- src/mongoose_hooks.erl | 2 +- 3 files changed, 43 insertions(+), 31 deletions(-) diff --git a/src/mod_disco.erl b/src/mod_disco.erl index a9b8076e05a..37297ada1c9 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -121,15 +121,15 @@ process_local_iq_items(_From, _To, Acc, #iq{type = set, sub_el = SubEl} = IQ) -> 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 {result, Items} -> ANode = make_node_attr(Node), {Acc, IQ#iq{type = result, sub_el = [#xmlel{name = <<"query">>, attrs = [{<<"xmlns">>, ?NS_DISCO_ITEMS} | ANode], - children = Items}]}}; - {error, Error} -> + children = mongoose_disco:items_to_xml(Items)}]}}; + empty -> + Error = mongoose_xmpp_errors:item_not_found(), {Acc, IQ#iq{type = error, sub_el = [SubEl, Error]}} end. @@ -176,33 +176,16 @@ get_local_features(Acc, _From, _To, <<>>, _Lang) -> get_local_features(Acc, _From, _To, _Node, _Lang) -> Acc. --spec domain_to_xml(binary() | {binary()}) -> exml:element(). -domain_to_xml({Domain}) -> - #xmlel{name = <<"item">>, attrs = [{<<"jid">>, Domain}]}; -domain_to_xml(Domain) -> - #xmlel{name = <<"item">>, attrs = [{<<"jid">>, Domain}]}. - - --spec get_local_services(Acc :: 'empty' | {'error', _} | {'result', _}, - From :: jid:jid(), - To :: jid:jid(), - Node :: binary(), - Lang :: ejabberd:lang()) -> {'error', _} | {'result', _}. -get_local_services({error, _Error} = Acc, _From, _To, _Node, _Lang) -> - Acc; +-spec get_local_services(mongoose_disco:item_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:item_acc(). get_local_services(Acc, From, To, <<>>, _Lang) -> - Items = case Acc of - {result, Its} -> Its; - empty -> [] - end, Host = To#jid.lserver, ReturnHidden = should_return_hidden(Host, From), Domains = get_vh_services(Host, ReturnHidden) ++ get_extra_domains(Host), - {result, lists:usort(lists:map(fun domain_to_xml/1, Domains)) ++ Items}; -get_local_services({result, _} = Acc, _From, _To, _Node, _Lang) -> - Acc; -get_local_services(empty, _From, _To, _Node, _Lang) -> - {error, mongoose_xmpp_errors:item_not_found()}. + mongoose_disco:add_items([#{jid => Domain} || Domain <- Domains], Acc); +get_local_services(Acc, _From, _To, _Node, _Lang) -> + Acc. get_extra_domains(Host) -> gen_mod:get_module_opt(Host, ?MODULE, extra_domains, []). diff --git a/src/mongoose_disco.erl b/src/mongoose_disco.erl index 0e48874cce7..fc9bdb5e3f1 100644 --- a/src/mongoose_disco.erl +++ b/src/mongoose_disco.erl @@ -2,14 +2,22 @@ -export([get_local_features/5, add_features/2, - features_to_xml/1]). + features_to_xml/1, + add_items/2, + items_to_xml/1]). -include("mongoose.hrl"). -include("jlib.hrl"). --type acc() :: empty | {result, [exml:element()]}. +-type item_acc() :: empty | {result, map()}. +-type acc() :: empty | {result, [feature()]}. +-type item() :: #{jid := jid:lserver(), + name => binary(), + node => binary()}. -type feature() :: binary(). +-export_type([item_acc/0, acc/0, item/0, feature/0]). + -spec get_local_features(jid:lserver(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> [feature()]. get_local_features(Host, From, To, Node, Lang) -> @@ -32,5 +40,26 @@ add_features(Features, Acc) -> -spec features_to_xml([feature()]) -> [exml:element()]. features_to_xml(Features) -> %% Avoid duplicating features - [#xmlel{name = <<"feature">>, attrs = [{<<"var">>, Feature}]} || - Feature <- lists:usort(Features)]. + [feature_to_xml(Feature) || Feature <- lists:usort(Features)]. + +feature_to_xml(Feature) when is_binary(Feature) -> + #xmlel{name = <<"feature">>, attrs = [{<<"var">>, Feature}]}. + +-spec add_items([item()], item_acc()) -> item_acc(). +add_items(Items, empty) -> + {result, add_items_to_map(Items, #{})}; +add_items(Items, {result, InitialItemMap}) -> + {result, add_items_to_map(Items, InitialItemMap)}. + +add_items_to_map(Items, InitialItemMap) -> + ToAdd = maps:from_list([{JID, Item} || Item = #{jid := JID} <- Items]), + maps:merge(ToAdd, InitialItemMap). + +-spec items_to_xml(map()) -> [exml:element()]. +items_to_xml(Items) -> + [item_to_xml(Item) || Item <- maps:values(Items)]. + +item_to_xml(Item) -> + #xmlel{name = <<"item">>, + attrs = lists:map(fun({Key, Value}) -> {atom_to_binary(Key, utf8), Value} end, + maps:to_list(Item))}. diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index b852de82e68..4e90d195e6f 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -1283,7 +1283,7 @@ disco_local_features(Server, From, To, Node, Lang) -> To :: jid:jid(), Node :: binary(), Lang :: ejabberd:lang(), - Result :: {result, [exml:element()]} | {error, any()}. + Result :: mongoose_disco:item_acc(). disco_local_items(Server, From, To, Node, Lang) -> ejabberd_hooks:run_for_host_type(disco_local_items, Server, empty, [From, To, Node, Lang]). From 27cd8ccda4425b5d6aad49dbca8e355ff1d10c50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 14:35:44 +0200 Subject: [PATCH 26/39] Use mongoose_disco:add_items in mod_muc_light Also: - Fix a bug in get_module_opt - Fix the tests to detect the bug --- big_tests/tests/muc_light_SUITE.erl | 4 +++- src/muc_light/mod_muc_light.erl | 17 ++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/big_tests/tests/muc_light_SUITE.erl b/big_tests/tests/muc_light_SUITE.erl index 0840a5aa52c..0b23b36a3f2 100644 --- a/big_tests/tests/muc_light_SUITE.erl +++ b/big_tests/tests/muc_light_SUITE.erl @@ -289,7 +289,9 @@ disco_service(Config) -> Server = escalus_client:server(Alice), escalus:send(Alice, escalus_stanza:service_discovery(Server)), Stanza = escalus:wait_for_stanza(Alice), - escalus:assert(has_service, [?MUCHOST], Stanza), + Query = exml_query:subelement(Stanza, <<"query">>), + Item = exml_query:subelement_with_attr(Query, <<"jid">>, ?MUCHOST), + ?assertEqual(?NS_MUC_LIGHT, exml_query:attr(Item, <<"node">>)), escalus:assert(is_stanza_from, [ct:get_config({hosts, mim, domain})], Stanza) end). diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index 1c176e8bae6..9cf84d1837e 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -332,21 +332,16 @@ prevent_service_unavailable(Acc, _From, _To, Packet) -> _Type -> Acc end. --spec get_muc_service(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 +-spec get_muc_service(mongoose_disco:item_acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:item_acc(). +get_muc_service(Acc, _From, #jid{lserver = LServer} = _To, <<>>, _Lang) -> + XMLNS = case gen_mod:get_module_opt(LServer, ?MODULE, legacy_mode, ?DEFAULT_LEGACY_MODE) of true -> ?NS_MUC; false -> ?NS_MUC_LIGHT end, SubHost = gen_mod:get_module_opt_subhost(LServer, ?MODULE, default_host()), - Item = [#xmlel{name = <<"item">>, - attrs = [{<<"jid">>, SubHost}, - {<<"node">>, XMLNS}]}], - {result, [Item | Nodes]}; + Items = [#{jid => SubHost, node => XMLNS}], + mongoose_disco:add_items(Items, Acc); get_muc_service(Acc, _From, _To, _Node, _Lang) -> Acc. From e4c234f65da0b2c9900f805a40beb0f560c51b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 15:01:47 +0200 Subject: [PATCH 27/39] Use mongoose_disco:add_items in mod_adhoc Also: - use add_features as well - add missing disco tests - remove nested hook with only one handler %adhoc fix --- big_tests/tests/adhoc_SUITE.erl | 97 +++++++++++++++++++++++++++------ src/mod_adhoc.erl | 76 ++++++++------------------ src/mongoose_hooks.erl | 13 +---- 3 files changed, 103 insertions(+), 83 deletions(-) diff --git a/big_tests/tests/adhoc_SUITE.erl b/big_tests/tests/adhoc_SUITE.erl index e07dfecfbc8..e1ed43644e9 100644 --- a/big_tests/tests/adhoc_SUITE.erl +++ b/big_tests/tests/adhoc_SUITE.erl @@ -18,15 +18,20 @@ -include_lib("escalus/include/escalus.hrl"). -include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-define(NS_COMMANDS, <<"http://jabber.org/protocol/commands">>). %%-------------------------------------------------------------------- %% Suite configuration %%-------------------------------------------------------------------- all() -> - [{group, adhoc}]. + [{group, disco_visible}, + {group, adhoc}]. groups() -> - G = [{adhoc, [sequence], [ping]}], + G = [{adhoc, [parallel], [disco_hidden, disco_commands, ping]}, + {disco_visible, [parallel], [disco_visible, disco_commands]}], ct_helper:repeat_all_until_all_ok(G). suite() -> @@ -36,12 +41,15 @@ init_per_suite(Config) -> escalus:init_per_suite(Config). end_per_suite(Config) -> + escalus_fresh:clean(), escalus:end_per_suite(Config). -init_per_group(_GroupName, Config) -> - escalus:create_users(Config, escalus:get_users([alice, bob])). +init_per_group(GroupName, Config) -> + Config1 = init_modules(GroupName, Config), + escalus:create_users(Config1, escalus:get_users([alice, bob])). -end_per_group(_GroupName, Config) -> +end_per_group(GroupName, Config) -> + restore_modules(GroupName, Config), escalus:delete_users(Config, escalus:get_users([alice, bob])). init_per_testcase(CaseName, Config) -> @@ -50,20 +58,73 @@ init_per_testcase(CaseName, Config) -> end_per_testcase(CaseName, Config) -> escalus:end_per_testcase(CaseName, Config). +init_modules(disco_visible, Config) -> + Config1 = escalus:init_per_suite(dynamic_modules:save_modules(domain(), Config)), + dynamic_modules:ensure_modules(domain(), [{mod_adhoc, [{report_commands_node, true}]}]), + Config1; +init_modules(_, Config) -> + Config. + +restore_modules(disco_visible, Config) -> + dynamic_modules:restore_modules(domain(), Config); +restore_modules(_, Config) -> + ok. + %%-------------------------------------------------------------------- %% Adhoc tests %%-------------------------------------------------------------------- +disco_hidden(Config) -> + escalus:fresh_story(Config, [{alice, 1}], + fun(Alice) -> + Server = escalus_client:server(Alice), + escalus:send(Alice, escalus_stanza:service_discovery(Server)), + Stanza = escalus:wait_for_stanza(Alice), + Query = exml_query:subelement(Stanza, <<"query">>), + ?assertEqual(undefined, + exml_query:subelement_with_attr(Query, <<"node">>, ?NS_COMMANDS)), + escalus:assert(is_stanza_from, + [domain()], Stanza) + end). + +disco_visible(Config) -> + escalus:fresh_story(Config, [{alice, 1}], + fun(Alice) -> + Server = escalus_client:server(Alice), + escalus:send(Alice, escalus_stanza:service_discovery(Server)), + Stanza = escalus:wait_for_stanza(Alice), + Query = exml_query:subelement(Stanza, <<"query">>), + Item = exml_query:subelement_with_attr(Query, <<"node">>, ?NS_COMMANDS), + ?assertEqual(Server, exml_query:attr(Item, <<"jid">>)), + escalus:assert(is_stanza_from, + [domain()], Stanza) + end). + +disco_commands(Config) -> + escalus:fresh_story(Config, [{alice, 1}], + fun(Alice) -> + Server = escalus_client:server(Alice), + escalus:send(Alice, escalus_stanza:disco_items(Server, ?NS_COMMANDS)), + Stanza = escalus:wait_for_stanza(Alice), + Query = exml_query:subelement(Stanza, <<"query">>), + Item = exml_query:subelement_with_attr(Query, <<"node">>, <<"ping">>), + ?assertEqual(Server, exml_query:attr(Item, <<"jid">>)), + escalus:assert(is_stanza_from, + [domain()], Stanza) + end). + ping(Config) -> - escalus:story(Config, [{alice, 1}], - fun(Alice) -> - Host = ct:get_config({hosts, mim, domain}), - %% Alice pings the server using adhoc command - escalus_client:send(Alice, escalus_stanza:to( - escalus_stanza:adhoc_request(<<"ping">>), - Host)), - - %% Server replies to Alice with pong - AdHocResp = escalus_client:wait_for_stanza(Alice), - escalus:assert(is_adhoc_response, [<<"ping">>, <<"completed">>], - AdHocResp) - end). + escalus:fresh_story(Config, [{alice, 1}], + fun(Alice) -> + Host = ct:get_config({hosts, mim, domain}), + %% Alice pings the server using adhoc command + escalus_client:send(Alice, escalus_stanza:to( + escalus_stanza:adhoc_request(<<"ping">>), + Host)), + %% Server replies to Alice with pong + AdHocResp = escalus_client:wait_for_stanza(Alice), + escalus:assert(is_adhoc_response, [<<"ping">>, <<"completed">>], + AdHocResp) + end). + +domain() -> + ct:get_config({hosts, mim, domain}). diff --git a/src/mod_adhoc.erl b/src/mod_adhoc.erl index 4ab79e0f233..0f4882f0fd2 100644 --- a/src/mod_adhoc.erl +++ b/src/mod_adhoc.erl @@ -42,7 +42,6 @@ get_sm_commands/5, get_sm_identity/5, get_sm_features/5, - ping_item/4, ping_command/4]). -include("mongoose.hrl"). @@ -72,7 +71,6 @@ hooks(Host) -> {disco_sm_identity, Host, ?MODULE, get_sm_identity, 99}, {disco_sm_features, Host, ?MODULE, get_sm_features, 99}, {disco_sm_items, Host, ?MODULE, get_sm_commands, 99}, - {adhoc_local_items, Host, ?MODULE, ping_item, 100}, {adhoc_local_commands, Host, ?MODULE, ping_command, 100}]. -spec config_spec() -> mongoose_config_spec:config_section(). @@ -84,34 +82,29 @@ config_spec() -> %%------------------------------------------------------------------------- --spec get_local_commands(Acc :: {result, [exml:element()]} | {error, any()} | empty, - From :: jid:jid(), - To :: jid:jid(), - NS :: binary(), - ejabberd:lang()) -> {result, [exml:element()]} | {error, any()} | empty. -get_local_commands(Acc, _From, #jid{lserver = LServer} = _To, <<"">>, Lang) -> +-spec get_local_commands(mongoose_disco:item_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:item_acc(). +get_local_commands(Acc, _From, #jid{lserver = LServer}, <<>>, Lang) -> Display = gen_mod:get_module_opt(LServer, ?MODULE, report_commands_node, false), - case Display of - false -> - Acc; - _ -> - Items = case Acc of - {result, I} -> I; - _ -> [] - end, - Nodes = [#xmlel{name = <<"item">>, - attrs = [{<<"jid">>, LServer}, - {<<"node">>, ?NS_COMMANDS}, - {<<"name">>, translate:translate(Lang, <<"Commands">>)}]}], - {result, Items ++ Nodes} - end; -get_local_commands(_Acc, From, #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) -> - mongoose_hooks:adhoc_local_items(LServer, From, To, Lang); + Items = case Display of + false -> + []; + _ -> + [item(LServer, ?NS_COMMANDS, <<"Commands">>, Lang)] + end, + mongoose_disco:add_items(Items, Acc); +get_local_commands(Acc, _From, #jid{lserver = LServer}, ?NS_COMMANDS, Lang) -> + Items = [item(LServer, <<"ping">>, <<"Ping">>, Lang)], + mongoose_disco:add_items(Items, Acc); get_local_commands(_Acc, _From, _To, <<"ping">>, _Lang) -> - {result, []}; + {result, []}; % override the result get_local_commands(Acc, _From, _To, _Node, _Lang) -> Acc. +item(LServer, Node, Name, Lang) -> + #{jid => LServer, node => Node, name => translate:translate(Lang, Name)}. + %%------------------------------------------------------------------------- -spec get_sm_commands(Acc :: [exml:element()], @@ -181,17 +174,11 @@ get_sm_identity(Acc, _From, _To, _Node, _Lang) -> %%------------------------------------------------------------------------- --spec get_local_features(Acc :: {result, [exml:element()]} | empty | {error, any()}, - From :: jid:jid(), - To :: jid:jid(), - NS :: binary(), - ejabberd:lang()) -> {result, [exml:element()]} | {error, any()}. -get_local_features(Acc, _From, _To, <<"">>, _Lang) -> - Feats = case Acc of - {result, I} -> I; - _ -> [] - end, - {result, Feats ++ [?NS_COMMANDS]}; +-spec get_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). +get_local_features(Acc, _From, _To, <<>>, _Lang) -> + mongoose_disco:add_features([?NS_COMMANDS], Acc); get_local_features(_Acc, _From, _To, ?NS_COMMANDS, _Lang) -> %% override all lesser features... {result, []}; @@ -258,23 +245,6 @@ run_request_hook(adhoc_local_commands, Host, From, To, AdhocRequest) -> run_request_hook(adhoc_sm_commands, Host, From, To, AdhocRequest) -> mongoose_hooks:adhoc_sm_commands(Host, From, To, AdhocRequest). --spec ping_item(Acc :: {result, [exml:element()]}, - From :: jid:jid(), - To :: jid:jid(), - ejabberd:lang()) -> {result, [exml:element()]}. -ping_item(Acc, _From, #jid{lserver = Server} = _To, Lang) -> - Items = case Acc of - {result, I} -> - I; - _ -> - [] - end, - Nodes = [#xmlel{name = <<"item">>, - attrs = [{<<"jid">>, Server}, - {<<"node">>, <<"ping">>}, - {<<"name">>, translate:translate(Lang, <<"Ping">>)}]}], - {result, Items ++ Nodes}. - -spec ping_command(Acc :: command_hook_acc(), From :: jid:jid(), diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index 4e90d195e6f..1b570a7d7e8 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -8,8 +8,7 @@ -include("jlib.hrl"). -include("mod_privacy.hrl"). --export([adhoc_local_items/4, - adhoc_local_commands/4, +-export([adhoc_local_commands/4, adhoc_sm_items/4, adhoc_sm_commands/4, anonymous_purge_hook/3, @@ -173,16 +172,6 @@ c2s_remote_hook(HostType, Tag, Args, HandlerState, C2SState) -> ejabberd_hooks:run_for_host_type(c2s_remote_hook, HostType, HandlerState, [Tag, Args, C2SState]). --spec adhoc_local_items(LServer, From, To, Lang) -> Result when - LServer :: jid:lserver(), - From :: jid:jid(), - To :: jid:jid(), - Lang :: ejabberd:lang(), - Result :: {result, [exml:element()]}. -adhoc_local_items(LServer, From, To, Lang) -> - ejabberd_hooks:run_for_host_type(adhoc_local_items, LServer, {result, []}, - [From, To, Lang]). - -spec adhoc_local_commands(LServer, From, To, AdhocRequest) -> Result when LServer :: jid:lserver(), From :: jid:jid(), From e4e70b4caa5fd294e537e49f178dd00869b9ea7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 15:12:22 +0200 Subject: [PATCH 28/39] Use mongoose_disco:add_items in mod_http_upload Change the test to check that the name is included. --- big_tests/tests/mod_http_upload_SUITE.erl | 4 +++- src/http_upload/mod_http_upload.erl | 28 ++++++++--------------- 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/big_tests/tests/mod_http_upload_SUITE.erl b/big_tests/tests/mod_http_upload_SUITE.erl index 9aeff5d77cd..3d9666e11d8 100644 --- a/big_tests/tests/mod_http_upload_SUITE.erl +++ b/big_tests/tests/mod_http_upload_SUITE.erl @@ -143,7 +143,9 @@ http_upload_item_discovery(Config) -> ServJID = escalus_client:server(Bob), Result = escalus:send_and_wait(Bob, escalus_stanza:disco_items(ServJID)), escalus:assert(is_iq_result, Result), - escalus:assert(has_item, [upload_service(Bob)], Result) + Query = exml_query:subelement(Result, <<"query">>), + Item = exml_query:subelement_with_attr(Query, <<"jid">>, upload_service(Bob)), + ?assertEqual(<<"HTTP File Upload">>, exml_query:attr(Item, <<"name">>)) end). http_upload_feature_discovery(Config) -> diff --git a/src/http_upload/mod_http_upload.erl b/src/http_upload/mod_http_upload.erl index 1c57046424a..a3e4fb6d0ab 100644 --- a/src/http_upload/mod_http_upload.erl +++ b/src/http_upload/mod_http_upload.erl @@ -164,31 +164,21 @@ get_disco_identity(Acc, _From, _To, _Node = <<>>, Lang) -> get_disco_identity(Acc, _From, _To, _Node, _Lang) -> Acc. - --spec get_disco_items(Acc :: {result, [exml:element()]} | {error, any()} | empty, - From :: jid:jid(), To :: jid:jid(), - Node :: binary(), ejabberd:lang()) - -> {result, [exml:element()]} | {error, any()}. -get_disco_items({result, Nodes}, _From, #jid{lserver = Host} = _To, <<"">>, Lang) -> - Item = #xmlel{name = <<"item">>, - attrs = [{<<"jid">>, subhost(Host)}, {<<"name">>, my_disco_name(Lang)}]}, - {result, [Item | Nodes]}; -get_disco_items(empty, From, To, Node, Lang) -> - get_disco_items({result, []}, From, To, Node, Lang); +-spec get_disco_items(mongoose_disco:item_acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:item_acc(). +get_disco_items(Acc, _From, #jid{lserver = Host} = _To, <<>>, Lang) -> + mongoose_disco:add_items([#{jid => subhost(Host), name => my_disco_name(Lang)}], Acc); get_disco_items(Acc, _From, _To, _Node, _Lang) -> Acc. - --spec get_disco_features(Acc :: term(), From :: jid:jid(), To :: jid:jid(), - Node :: binary(), ejabberd:lang()) -> {result, [exml:element()]} | term(). -get_disco_features({result, Nodes}, _From, _To, _Node = <<>>, _Lang) -> - {result, [?NS_HTTP_UPLOAD_030 | Nodes]}; -get_disco_features(empty, From, To, Node, Lang) -> - get_disco_features({result, []}, From, To, Node, Lang); +-spec get_disco_features(Acc :: mongoose_disco:acc(), + From :: jid:jid(), To :: jid:jid(), + Node :: binary(), ejabberd:lang()) -> mongoose_disco:feature_acc(). +get_disco_features(Acc, _From, _To, _Node = <<>>, _Lang) -> + mongoose_disco:add_features([?NS_HTTP_UPLOAD_030], Acc); get_disco_features(Acc, _From, _To, _Node, _Lang) -> Acc. - -spec get_disco_info(Acc :: [exml:element()], jid:server(), module(), Node :: binary(), Lang :: ejabberd:lang()) -> [exml:element()]. get_disco_info(Acc, SubHost, _Mod, _Node = <<>>, _Lang) -> From 7f89fd5c83ef7fb4881691ccf394ce858f378350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 15:28:01 +0200 Subject: [PATCH 29/39] Use mongoose_disco:add_items in mod_global_distrib This commit fixes the tests as all modules are reworked now. --- .../mod_global_distrib_disco.erl | 28 ++++--------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/global_distrib/mod_global_distrib_disco.erl b/src/global_distrib/mod_global_distrib_disco.erl index 71fc7b396a8..c0ece59371e 100644 --- a/src/global_distrib/mod_global_distrib_disco.erl +++ b/src/global_distrib/mod_global_distrib_disco.erl @@ -41,29 +41,13 @@ stop(Host) -> %% Hooks implementation %%-------------------------------------------------------------------- --spec get_disco_items(Acc :: {result, [exml:element()]} | {error, any()} | empty, - From :: jid:jid(), To :: jid:jid(), - Node :: binary(), ejabberd:lang()) - -> {result, [exml:element()]} | {error, any()} | empty. -get_disco_items({result, Nodes}, From, To, <<"">>, _Lang) -> +-spec get_disco_items(mongoose_disco:item_acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> + mongoose_disco:item_acc(). +get_disco_items(Acc, From, To, <<>>, _Lang) -> Domains = domains_for_disco(To#jid.lserver, From), - ?LOG_DEBUG(#{what => gd_domains_fetched_for_disco, - domains => Domains, input_nodes => Nodes}), - NameSet = gb_sets:from_list([exml_query:attr(Node, <<"jid">>) || Node <- Nodes]), - FilteredDomains = [Domain || Domain <- Domains, not gb_sets:is_member(Domain, NameSet)], - ?LOG_DEBUG(#{what => gd_get_disco_items_result, - text => <<"Adding global domains to disco results">>, - domains => FilteredDomains}), - NewNodes = - lists:foldl( - fun(Domain, Acc) -> - [#xmlel{name = <<"item">>, attrs = [{<<"jid">>, Domain}]} | Acc] - end, - Nodes, - FilteredDomains), - {result, NewNodes}; -get_disco_items(empty, From, To, Node, Lang) -> - get_disco_items({result, []}, From, To, Node, Lang); + ?LOG_DEBUG(#{what => gd_domains_fetched_for_disco, domains => Domains}), + Items = [#{jid => Domain} || Domain <- Domains], + mongoose_disco:add_items(Items, Acc); get_disco_items(Acc, _From, _To, _Node, _Lang) -> Acc. From 8bab1f94bd1c4d6e989d991ad6d7d3f85224d0c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Fri, 28 May 2021 16:14:25 +0200 Subject: [PATCH 30/39] Rename 'acc' to 'feature_acc' --- src/ejabberd_local.erl | 5 +++-- src/http_upload/mod_http_upload.erl | 2 +- src/inbox/mod_inbox.erl | 5 +++-- src/mam/mod_mam.erl | 5 +++-- src/mam/mod_mam_muc.erl | 5 +++-- src/mod_amp.erl | 5 +++-- src/mod_auth_token.erl | 8 +++++--- src/mod_blocking.erl | 5 +++-- src/mod_carboncopy.erl | 5 +++-- src/mod_disco.erl | 5 +++-- src/mod_version.erl | 8 +++++--- src/mongoose_disco.erl | 6 +++--- src/mongoose_hooks.erl | 2 +- src/pubsub/mod_pubsub.erl | 5 +++-- 14 files changed, 42 insertions(+), 29 deletions(-) diff --git a/src/ejabberd_local.erl b/src/ejabberd_local.erl index 86cec0b0888..5273b75e83b 100644 --- a/src/ejabberd_local.erl +++ b/src/ejabberd_local.erl @@ -236,8 +236,9 @@ register_host(Host) -> unregister_host(Host) -> gen_server:call(?MODULE, {unregister_host, Host}). --spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec add_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). add_local_features(Acc, _From, #jid{lserver = LServer}, <<>>, _Lang) -> Features = ets:lookup_element(?NSTABLE, LServer, 2), mongoose_disco:add_features(Features, Acc); diff --git a/src/http_upload/mod_http_upload.erl b/src/http_upload/mod_http_upload.erl index a3e4fb6d0ab..7aab89774f9 100644 --- a/src/http_upload/mod_http_upload.erl +++ b/src/http_upload/mod_http_upload.erl @@ -171,7 +171,7 @@ get_disco_items(Acc, _From, #jid{lserver = Host} = _To, <<>>, Lang) -> get_disco_items(Acc, _From, _To, _Node, _Lang) -> Acc. --spec get_disco_features(Acc :: mongoose_disco:acc(), +-spec get_disco_features(Acc :: mongoose_disco:feature_acc(), From :: jid:jid(), To :: jid:jid(), Node :: binary(), ejabberd:lang()) -> mongoose_disco:feature_acc(). get_disco_features(Acc, _From, _To, _Node = <<>>, _Lang) -> diff --git a/src/inbox/mod_inbox.erl b/src/inbox/mod_inbox.erl index 04b96597b00..6bf808a510e 100644 --- a/src/inbox/mod_inbox.erl +++ b/src/inbox/mod_inbox.erl @@ -279,8 +279,9 @@ remove_domain(Acc, HostType, Domain) -> mod_inbox_backend:remove_domain(HostType, Domain), Acc. --spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec add_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). add_local_features(Acc, _From, _To, <<>>, _Lang) -> mongoose_disco:add_features([?NS_ESL_INBOX], Acc); add_local_features(Acc, _From, _To, _Node, _Lang) -> diff --git a/src/mam/mod_mam.erl b/src/mam/mod_mam.erl index dc78028b816..4bbe4187ead 100644 --- a/src/mam/mod_mam.erl +++ b/src/mam/mod_mam.erl @@ -250,8 +250,9 @@ process_mam_iq(From=#jid{lserver=Host}, To, Acc, IQ) -> {Acc, return_action_not_allowed_error_iq(IQ)} end. --spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec add_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). add_local_features(Acc, _From, #jid{lserver = LServer}, <<>>, _Lang) -> mongoose_disco:add_features(features(?MODULE, LServer), Acc); add_local_features(Acc, _From, _To, _Node, _Lang) -> diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index c4c792b495c..64bd8393050 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -173,8 +173,9 @@ stop(Host) -> %% ---------------------------------------------------------------------- %% hooks and handlers for MUC --spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec add_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). add_local_features(Acc, _From, #jid{lserver = LServer}, <<>>, _Lang) -> mongoose_disco:add_features(features(?MODULE, LServer), Acc); add_local_features(Acc, _From, _To, _Node, _Lang) -> diff --git a/src/mod_amp.erl b/src/mod_amp.erl index 52fa611dc1f..5864823096d 100644 --- a/src/mod_amp.erl +++ b/src/mod_amp.erl @@ -57,8 +57,9 @@ check_packet(Acc, Event) -> Rules -> process_event(Acc, Rules, Event) end. --spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec add_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). add_local_features(Acc, _From, _To, Node, _Lang) -> case amp_features(Node) of [] -> Acc; diff --git a/src/mod_auth_token.erl b/src/mod_auth_token.erl index b033655b01c..0ba30932dda 100644 --- a/src/mod_auth_token.erl +++ b/src/mod_auth_token.erl @@ -453,7 +453,8 @@ revoke_token_command(Owner) -> {error, "Internal server error"} end. --spec clean_tokens(mongoose_acc:t(), User :: jid:user(), Server :: jid:server()) -> mongoose_acc:t(). +-spec clean_tokens(mongoose_acc:t(), User :: jid:user(), Server :: jid:server()) -> + mongoose_acc:t(). clean_tokens(Acc, User, Server) -> try Owner = jid:make(User, Server, <<>>), @@ -472,8 +473,9 @@ config_metrics(Host) -> OptsToReport = [{backend, rdbms}], %list of tuples {option, default_value} mongoose_module_metrics:opts_for_module(Host, ?MODULE, OptsToReport). --spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec add_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). add_local_features(Acc, _From, _To, <<>>, _Lang) -> mongoose_disco:add_features([?NS_ESL_TOKEN_AUTH], Acc); add_local_features(Acc, _From, _To, _Node, _Lang) -> diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl index 63d1e3a35f4..d5cd0b005a1 100644 --- a/src/mod_blocking.erl +++ b/src/mod_blocking.erl @@ -36,8 +36,9 @@ hooks(Host) -> {privacy_iq_get, Host, ?MODULE, process_iq_get, 50}, {privacy_iq_set, Host, ?MODULE, process_iq_set, 50}]. --spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec add_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). add_local_features(Acc, _From, _To, <<>>, _Lang) -> mongoose_disco:add_features([?NS_BLOCKING], Acc); add_local_features(Acc, _From, _To, _Node, _Lang) -> diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl index a0397ac6ff9..a437080aaf8 100644 --- a/src/mod_carboncopy.erl +++ b/src/mod_carboncopy.erl @@ -96,8 +96,9 @@ hooks(HostType) -> config_spec() -> #section{items = #{<<"iqdisc">> => mongoose_config_spec:iqdisc()}}. --spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec add_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). add_local_features(Acc, _From, _To, <<>>, _Lang) -> mongoose_disco:add_features([?NS_CC_1, ?NS_CC_2, ?NS_CC_RULES], Acc); add_local_features(Acc, _From, _To, _Node, _Lang) -> diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 37297ada1c9..98ec5feac48 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -169,8 +169,9 @@ get_local_identity(Acc, _From, _To, <<>>, _Lang) -> get_local_identity(Acc, _From, _To, Node, _Lang) when is_binary(Node) -> Acc. --spec get_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec get_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). get_local_features(Acc, _From, _To, <<>>, _Lang) -> mongoose_disco:add_features([<<"iq">>, <<"presence">>, <<"presence-invisible">>], Acc); get_local_features(Acc, _From, _To, _Node, _Lang) -> diff --git a/src/mod_version.erl b/src/mod_version.erl index e93aafc71f7..26e4d191516 100644 --- a/src/mod_version.erl +++ b/src/mod_version.erl @@ -34,14 +34,16 @@ config_spec() -> } }. --spec add_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec add_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). add_local_features(Acc, _From, _To, <<>>, _Lang) -> mongoose_disco:add_features([?NS_VERSION], Acc); add_local_features(Acc, _From, _To, _Node, _Lang) -> Acc. --spec process_iq(jid:jid(),jid:jid(), mongoose_acc:t(), jlib:iq()) -> {mongoose_acc:t(), jlib:iq()}. +-spec process_iq(jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) -> + {mongoose_acc:t(), jlib:iq()}. process_iq(_From, _To, Acc, #iq{type = set, sub_el = SubEl} = IQ) -> {Acc, IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:not_allowed()]}}; process_iq(From, _To, Acc, #iq{type = get} = IQ) -> diff --git a/src/mongoose_disco.erl b/src/mongoose_disco.erl index fc9bdb5e3f1..5464d46f916 100644 --- a/src/mongoose_disco.erl +++ b/src/mongoose_disco.erl @@ -10,13 +10,13 @@ -include("jlib.hrl"). -type item_acc() :: empty | {result, map()}. --type acc() :: empty | {result, [feature()]}. +-type feature_acc() :: empty | {result, [feature()]}. -type item() :: #{jid := jid:lserver(), name => binary(), node => binary()}. -type feature() :: binary(). --export_type([item_acc/0, acc/0, item/0, feature/0]). +-export_type([item_acc/0, feature_acc/0, item/0, feature/0]). -spec get_local_features(jid:lserver(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> [feature()]. @@ -28,7 +28,7 @@ get_local_features(Host, From, To, Node, Lang) -> Features end. --spec add_features([feature()], acc()) -> acc(). +-spec add_features([feature()], feature_acc()) -> feature_acc(). add_features(Features, Acc) -> case Acc of empty -> diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index 1b570a7d7e8..0f8a83b30d1 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -1260,7 +1260,7 @@ disco_info(Server, Module, Node, Lang) -> To :: jid:jid(), Node :: binary(), Lang :: ejabberd:lang(), - Result :: mongoose_disco:acc(). + Result :: mongoose_disco:feature_acc(). disco_local_features(Server, From, To, Node, Lang) -> ejabberd_hooks:run_for_host_type(disco_local_features, Server, empty, [From, To, Node, Lang]). diff --git a/src/pubsub/mod_pubsub.erl b/src/pubsub/mod_pubsub.erl index 7f96440cf2d..577a9ab4b9d 100644 --- a/src/pubsub/mod_pubsub.erl +++ b/src/pubsub/mod_pubsub.erl @@ -656,8 +656,9 @@ disco_local_identity(Acc, Host, <<>>, _Lang) -> disco_local_identity(Acc, _Host, _Node, _Lang) -> Acc. --spec disco_local_features(mongoose_disco:acc(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> - mongoose_disco:acc(). +-spec disco_local_features(mongoose_disco:feature_acc(), jid:jid(), jid:jid(), binary(), + ejabberd:lang()) -> + mongoose_disco:feature_acc(). disco_local_features(Acc, _From, To, <<>>, _Lang) -> Host = To#jid.lserver, Features = [?NS_PUBSUB | [feature(F) || F <- features(Host, <<>>)]], From 612fa3d803e4558d5adedc3673d6a2067820a3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Mon, 31 May 2021 08:11:51 +0200 Subject: [PATCH 31/39] Make item_acc a list to be more consistent with feature_acc --- src/mongoose_disco.erl | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/src/mongoose_disco.erl b/src/mongoose_disco.erl index 5464d46f916..d40a3ec4730 100644 --- a/src/mongoose_disco.erl +++ b/src/mongoose_disco.erl @@ -9,15 +9,17 @@ -include("mongoose.hrl"). -include("jlib.hrl"). --type item_acc() :: empty | {result, map()}. -type feature_acc() :: empty | {result, [feature()]}. --type item() :: #{jid := jid:lserver(), - name => binary(), - node => binary()}. -type feature() :: binary(). +-type item_acc() :: empty | {result, [item()]}. +-type item() :: #{jid := jid:lserver(), name => binary(), node => binary()}. + -export_type([item_acc/0, feature_acc/0, item/0, feature/0]). +%% @doc Run the 'disco_local_features' hook and unpack the results. +%% Used by extension modules which support their own subdomains +%% and add their own features to the end result. -spec get_local_features(jid:lserver(), jid:jid(), jid:jid(), binary(), ejabberd:lang()) -> [feature()]. get_local_features(Host, From, To, Node, Lang) -> @@ -29,13 +31,10 @@ get_local_features(Host, From, To, Node, Lang) -> end. -spec add_features([feature()], feature_acc()) -> feature_acc(). -add_features(Features, Acc) -> - case Acc of - empty -> - {result, Features}; - {result, InitialFeatures} -> - {result, Features ++ InitialFeatures} - end. +add_features(Features, empty) -> + {result, Features}; +add_features(Features, {result, InitialFeatures}) -> + {result, Features ++ InitialFeatures}. -spec features_to_xml([feature()]) -> [exml:element()]. features_to_xml(Features) -> @@ -47,17 +46,16 @@ feature_to_xml(Feature) when is_binary(Feature) -> -spec add_items([item()], item_acc()) -> item_acc(). add_items(Items, empty) -> - {result, add_items_to_map(Items, #{})}; -add_items(Items, {result, InitialItemMap}) -> - {result, add_items_to_map(Items, InitialItemMap)}. - -add_items_to_map(Items, InitialItemMap) -> - ToAdd = maps:from_list([{JID, Item} || Item = #{jid := JID} <- Items]), - maps:merge(ToAdd, InitialItemMap). + {result, Items}; +add_items(Items, {result, InitialItems}) -> + {result, Items ++ InitialItems}. --spec items_to_xml(map()) -> [exml:element()]. +-spec items_to_xml([item()]) -> [exml:element()]. items_to_xml(Items) -> - [item_to_xml(Item) || Item <- maps:values(Items)]. + %% For each JID, leave only the rightmost item with that JID (the one which was added first). + %% This is needed as extension modules might add more detailed information about an item + %% than the default which is obtained from the registered routes and contains only the JID. + maps:values(maps:from_list([{JID, item_to_xml(Item)} || #{jid := JID} = Item <- Items])). item_to_xml(Item) -> #xmlel{name = <<"item">>, From 1ee6053f7a1f8256efaa1600a64713b672ce4e6b Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Mon, 31 May 2021 22:41:11 +0200 Subject: [PATCH 32/39] GDPR get data takes host type as a parameter --- doc/migrations/4.2.0_4.3.0.md | 4 ++++ src/inbox/mod_inbox.erl | 7 ++++--- src/mam/mod_mam.erl | 8 ++++---- src/mam/mod_mam_muc.erl | 9 +++++---- src/mod_muc.erl | 8 ++++---- src/mod_muc_room.erl | 12 +++++++----- src/mod_private.erl | 6 +++--- src/mod_roster.erl | 6 +++--- src/mod_vcard.erl | 6 +++--- src/mongoose_hooks.erl | 26 +++++++++++++------------- src/offline/mod_offline.erl | 4 ++-- src/pubsub/mod_pubsub.erl | 6 +++--- 12 files changed, 55 insertions(+), 47 deletions(-) diff --git a/doc/migrations/4.2.0_4.3.0.md b/doc/migrations/4.2.0_4.3.0.md index aef6474bf1a..f5b291e5f29 100644 --- a/doc/migrations/4.2.0_4.3.0.md +++ b/doc/migrations/4.2.0_4.3.0.md @@ -94,6 +94,10 @@ GO - `muc_room_pid` hook removed. - `load_permanent_rooms_at_startup` option is ignored now. - `gen_mod:get_module_opt_by_subhost` API removed. +- `update_inbox_for_muc` is called for HostType. +- `get_mam_muc_gdpr_data` is called for HostType. +- `get_mam_pm_gdpr_data` is called for HostType. +- `get_personal_data` handlers take an extra argument: `HostType` as the second parameter. ## Metrics REST API (obsolete) diff --git a/src/inbox/mod_inbox.erl b/src/inbox/mod_inbox.erl index 95973fcfdd5..7b26000ed86 100644 --- a/src/inbox/mod_inbox.erl +++ b/src/inbox/mod_inbox.erl @@ -19,7 +19,7 @@ -include("mongoose_logger.hrl"). -include("mongoose_ns.hrl"). --export([get_personal_data/2]). +-export([get_personal_data/3]). %% gen_mod -export([start/2]). @@ -118,8 +118,9 @@ %%-------------------------------------------------------------------- %% gdpr callbacks %%-------------------------------------------------------------------- --spec get_personal_data(gdpr:personal_data(), jid:jid()) -> gdpr:personal_data(). -get_personal_data(Acc, #jid{ luser = LUser, lserver = LServer }) -> +-spec get_personal_data(gdpr:personal_data(), mongooseim:host_type(), jid:jid()) -> + gdpr:personal_data(). +get_personal_data(Acc, _HostType, #jid{luser = LUser, lserver = LServer}) -> Schema = ["jid", "content", "unread_count", "timestamp"], InboxParams = #{ start => 0, diff --git a/src/mam/mod_mam.erl b/src/mam/mod_mam.erl index 76505416ecb..b3e485e3493 100644 --- a/src/mam/mod_mam.erl +++ b/src/mam/mod_mam.erl @@ -53,7 +53,7 @@ sm_filter_offline_message/4]). %% gdpr callbacks --export([get_personal_data/2]). +-export([get_personal_data/3]). %%private -export([archive_message_from_ct/1]). @@ -160,9 +160,9 @@ %% ---------------------------------------------------------------------- %% API --spec get_personal_data(gdpr:personal_data(), jid:jid()) -> gdpr:personal_data(). -get_personal_data(Acc, #jid{} = ArcJID) -> - HostType = jid_to_host_type(ArcJID), +-spec get_personal_data(gdpr:personal_data(), mongooseim:host_type(), jid:jid()) -> + gdpr:personal_data(). +get_personal_data(Acc, HostType, ArcJID) -> Schema = ["id", "from", "message"], Entries = mongoose_hooks:get_mam_pm_gdpr_data(HostType, ArcJID), [{mam_pm, Schema, Entries} | Acc]. diff --git a/src/mam/mod_mam_muc.erl b/src/mam/mod_mam_muc.erl index bce44920c66..0995cb996ac 100644 --- a/src/mam/mod_mam_muc.erl +++ b/src/mam/mod_mam_muc.erl @@ -48,7 +48,7 @@ forget_room/4]). %% gdpr callback --export([get_personal_data/2]). +-export([get_personal_data/3]). %% private -export([archive_message_for_ct/1]). @@ -109,10 +109,11 @@ %% ---------------------------------------------------------------------- %% API --spec get_personal_data(gdpr:personal_data(), jid:jid()) -> gdpr:personal_data(). -get_personal_data(Acc, #jid{ lserver = LServer } = JID) -> +-spec get_personal_data(gdpr:personal_data(), mongooseim:host_type(), jid:jid()) -> + gdpr:personal_data(). +get_personal_data(Acc, HostType, ArcJID) -> Schema = ["id", "message"], - Entries = mongoose_hooks:get_mam_muc_gdpr_data(LServer, JID), + Entries = mongoose_hooks:get_mam_muc_gdpr_data(HostType, ArcJID), [{mam_muc, Schema, Entries} | Acc]. -spec delete_archive(jid:server(), jid:user()) -> ok. diff --git a/src/mod_muc.erl b/src/mod_muc.erl index dd654fd1b07..b46a6a3b425 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -462,10 +462,10 @@ handle_call({create_instant, ServerHost, MucHost, Room, From, Nick, Opts}, end, try {ok, Pid} = mod_muc_room:start_new(HostType, - MucHost, ServerHost, Access, - Room, HistorySize, - RoomShaper, HttpAuthPool, From, - Nick, [{instant, true}|NewOpts]), + 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} catch Class:Reason:Stacktrace -> diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 59c78fbb25d..065a0695d34 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -131,6 +131,7 @@ -type update_inbox_for_muc_payload() :: #{ + host_type := mongooseim:host_type(), room_jid := jid:jid(), from_jid := jid:jid(), from_room_jid := jid:jid(), @@ -880,12 +881,13 @@ broadcast_room_packet(From, FromNick, Role, Packet, StateData) -> RouteFrom = jid:replace_resource(StateData#state.jid, FromNick), RoomJid = StateData#state.jid, - HookInfo = #{room_jid => RoomJid, + HookInfo = #{host_type => StateData#state.host_type, + room_jid => RoomJid, from_jid => From, from_room_jid => RouteFrom, packet => FilteredPacket, affiliations_map => StateData#state.affiliations}, - run_update_inbox_for_muc_hook(StateData#state.server_host, HookInfo), + run_update_inbox_for_muc_hook(StateData#state.host_type, HookInfo), maps_foreach(fun(_LJID, Info) -> ejabberd_router:route(RouteFrom, Info#user.jid, @@ -897,10 +899,10 @@ broadcast_room_packet(From, FromNick, Role, Packet, StateData) -> StateData), next_normal_state(NewStateData2). --spec run_update_inbox_for_muc_hook(jid:server(), +-spec run_update_inbox_for_muc_hook(mongooseim:host_type(), update_inbox_for_muc_payload()) -> ok. -run_update_inbox_for_muc_hook(ServerHost, HookInfo) -> - mongoose_hooks:update_inbox_for_muc(ServerHost, HookInfo), +run_update_inbox_for_muc_hook(HostType, HookInfo) -> + mongoose_hooks:update_inbox_for_muc(HostType, HookInfo), ok. change_subject_error(From, FromNick, Packet, Lang, StateData) -> diff --git a/src/mod_private.erl b/src/mod_private.erl index ef7774a10e2..7cf004bd375 100644 --- a/src/mod_private.erl +++ b/src/mod_private.erl @@ -35,7 +35,7 @@ process_sm_iq/4, remove_user/3]). --export([get_personal_data/2]). +-export([get_personal_data/3]). -export([config_metrics/1]). @@ -81,8 +81,8 @@ %% gdpr callback %%-------------------------------------------------------------------- --spec get_personal_data(gdpr:personal_data(), jid:jid()) -> gdpr:personal_data(). -get_personal_data(Acc, #jid{ luser = LUser, lserver = LServer }) -> +-spec get_personal_data(gdpr:personal_data(), mongooseim:host_type(), jid:jid()) -> gdpr:personal_data(). +get_personal_data(Acc, _HostType, #jid{ luser = LUser, lserver = LServer }) -> Schema = ["ns", "xml"], NSs = mod_private_backend:get_all_nss(LUser, LServer), Entries = lists:map( diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 184d0b2a986..5ef4601c595 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -65,7 +65,7 @@ remove_user/2, % for tests remove_user/3, get_versioning_feature/2, - get_personal_data/2 + get_personal_data/3 ]). % Deprecated Hooks @@ -213,8 +213,8 @@ %% gdpr callback %%-------------------------------------------------------------------- --spec get_personal_data(gdpr:personal_data(), jid:jid()) -> gdpr:personal_data(). -get_personal_data(Acc, #jid{ luser = LUser, lserver = LServer }) -> +-spec get_personal_data(gdpr:personal_data(), mongooseim:host_type(), jid:jid()) -> gdpr:personal_data(). +get_personal_data(Acc, _HostType, #jid{ luser = LUser, lserver = LServer }) -> Schema = ["jid", "name", "subscription", "ask", "groups", "askmessage", "xs"], Records = mod_roster_backend:get_roster(LUser, LServer), SerializedRecords = lists:map(fun roster_record_to_gdpr_entry/1, Records), diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index b805eda3744..5f3dd0c0106 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -80,7 +80,7 @@ -export([config_change/4]). %% GDPR related --export([get_personal_data/2]). +-export([get_personal_data/3]). -export([config_metrics/1]). @@ -135,8 +135,8 @@ %% gdpr callback %%-------------------------------------------------------------------- --spec get_personal_data(gdpr:personal_data(), jid:jid()) -> gdpr:personal_data(). -get_personal_data(Acc, #jid{luser = LUser, lserver = LServer}) -> +-spec get_personal_data(gdpr:personal_data(), mongooseim:host_type(), jid:jid()) -> gdpr:personal_data(). +get_personal_data(Acc, _HostType, #jid{luser = LUser, lserver = LServer}) -> Jid = jid:to_binary({LUser, LServer}), Schema = ["jid", "vcard"], Entries = case mod_vcard_backend:get_vcard(LUser, LServer) of diff --git a/src/mongoose_hooks.erl b/src/mongoose_hooks.erl index 1aca085f78c..c37760a378e 100644 --- a/src/mongoose_hooks.erl +++ b/src/mongoose_hooks.erl @@ -1140,21 +1140,21 @@ mam_muc_flush_messages(HookServer, MessageCount) -> %%% @doc `get_mam_pm_gdpr_data' hook is called to provide %%% a user's archive for GDPR purposes. --spec get_mam_pm_gdpr_data(HookServer, JID) -> Result when - HookServer :: jid:lserver(), +-spec get_mam_pm_gdpr_data(HostType, JID) -> Result when + HostType :: mongooseim:host_type(), JID :: jid:jid(), Result :: ejabberd_gen_mam_archive:mam_pm_gdpr_data(). -get_mam_pm_gdpr_data(HookServer, JID) -> - ejabberd_hooks:run_for_host_type(get_mam_pm_gdpr_data, HookServer, [], [JID]). +get_mam_pm_gdpr_data(HostType, JID) -> + ejabberd_hooks:run_for_host_type(get_mam_pm_gdpr_data, HostType, [], [JID]). %%% @doc `get_mam_muc_gdpr_data' hook is called to provide %%% a user's archive for GDPR purposes. --spec get_mam_muc_gdpr_data(HookServer, JID) -> Result when - HookServer :: jid:lserver(), +-spec get_mam_muc_gdpr_data(HostType, JID) -> Result when + HostType :: mongooseim:host_type(), JID :: jid:jid(), Result :: ejabberd_gen_mam_archive:mam_muc_gdpr_data(). -get_mam_muc_gdpr_data(HookServer, JID) -> - ejabberd_hooks:run_for_host_type(get_mam_muc_gdpr_data, HookServer, [], [JID]). +get_mam_muc_gdpr_data(HostType, JID) -> + ejabberd_hooks:run_for_host_type(get_mam_muc_gdpr_data, HostType, [], [JID]). %%% @doc `get_personal_data' hook is called to retrieve %%% a user's personal data for GDPR purposes. @@ -1163,7 +1163,7 @@ get_mam_muc_gdpr_data(HookServer, JID) -> JID :: jid:jid(), Result :: gdpr:personal_data(). get_personal_data(HostType, JID) -> - ejabberd_hooks:run_for_host_type(get_personal_data, HostType, [], [JID]). + ejabberd_hooks:run_for_host_type(get_personal_data, HostType, [], [HostType, JID]). %% S2S related hooks @@ -1430,12 +1430,12 @@ room_packet(Server, FromNick, FromJID, JID, Packet) -> ejabberd_hooks:run_for_host_type(room_packet, Server, ok, [FromNick, FromJID, JID, Packet]). --spec update_inbox_for_muc(Server, Info) -> Result when - Server :: jid:server(), +-spec update_inbox_for_muc(HostType, Info) -> Result when + HostType :: mongooseim:host_type(), Info :: mod_muc_room:update_inbox_for_muc_payload(), Result :: mod_muc_room:update_inbox_for_muc_payload(). -update_inbox_for_muc(Server, Info) -> - ejabberd_hooks:run_for_host_type(update_inbox_for_muc, Server, Info, []). +update_inbox_for_muc(HostType, Info) -> + ejabberd_hooks:run_for_host_type(update_inbox_for_muc, HostType, Info, []). %% Caps related hooks diff --git a/src/offline/mod_offline.erl b/src/offline/mod_offline.erl index 7e3d14c32c9..8268221c24f 100644 --- a/src/offline/mod_offline.erl +++ b/src/offline/mod_offline.erl @@ -58,7 +58,7 @@ -export([is_expired_message/2]). %% GDPR related --export([get_personal_data/2]). +-export([get_personal_data/3]). -export([config_metrics/1]). @@ -520,7 +520,7 @@ pop_messages(#jid{lserver = LServer} = JID) -> Other end. -get_personal_data(Acc, #jid{} = JID) -> +get_personal_data(Acc, _HostType, #jid{} = JID) -> {ok, Messages} = mod_offline_backend:fetch_messages(JID), [ {offline, ["timestamp", "from", "to", "packet"], offline_messages_to_gdpr_format(Messages)} | Acc]. diff --git a/src/pubsub/mod_pubsub.erl b/src/pubsub/mod_pubsub.erl index 80743c3de8d..7ccb71f5937 100644 --- a/src/pubsub/mod_pubsub.erl +++ b/src/pubsub/mod_pubsub.erl @@ -101,7 +101,7 @@ -export([default_host/0]). --export([get_personal_data/2]). +-export([get_personal_data/3]). %% packet handler export -export([process_packet/5]). @@ -334,8 +334,8 @@ process_packet(_Acc, From, To, El, #{state := State}) -> %% GDPR callback %%==================================================================== --spec get_personal_data(gdpr:personal_data(), jid:jid()) -> gdpr:personal_data(). -get_personal_data(Acc, #jid{ luser = LUser, lserver = LServer }) -> +-spec get_personal_data(gdpr:personal_data(), mongooseim:host_type(), jid:jid()) -> gdpr:personal_data(). +get_personal_data(Acc, _HostType, #jid{ luser = LUser, lserver = LServer }) -> Payloads = mod_pubsub_db_backend:get_user_payloads(LUser, LServer), Nodes = mod_pubsub_db_backend:get_user_nodes(LUser, LServer), Subscriptions = mod_pubsub_db_backend:get_user_subscriptions(LUser, LServer), From 7458c1b3de7c7773a3c7a9085c17728cf048895f Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Mon, 31 May 2021 22:56:17 +0200 Subject: [PATCH 33/39] Fix domain being dynamic in gdpr_SUITE --- big_tests/tests/gdpr_SUITE.erl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/big_tests/tests/gdpr_SUITE.erl b/big_tests/tests/gdpr_SUITE.erl index 0cb69163ddf..11fe1990ec7 100644 --- a/big_tests/tests/gdpr_SUITE.erl +++ b/big_tests/tests/gdpr_SUITE.erl @@ -1553,10 +1553,7 @@ data_is_not_retrieved_for_missing_user(Config) -> %% ------------------------------------------------------------- domain() -> - <<"localhost">>. % TODO: Make dynamic? - -muc_domain() -> - muc_helper:muc_host(). + ct:get_config({hosts, mim, domain}). assert_personal_data_via_rpc(Client, ExpectedPersonalDataEntries) -> ExpectedKeys = [ Key || {Key, _, _} <- ExpectedPersonalDataEntries ], From 1160df6195bb29af4c0de88f092a358128e54fc9 Mon Sep 17 00:00:00 2001 From: Gustaw Lippa Date: Tue, 1 Jun 2021 10:34:04 +0200 Subject: [PATCH 34/39] Fix metrics in mam suite --- big_tests/dynamic_domains.spec | 4 ---- big_tests/tests/mam_SUITE.erl | 4 +++- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/big_tests/dynamic_domains.spec b/big_tests/dynamic_domains.spec index 6af3716dcc3..e8f5c28451e 100644 --- a/big_tests/dynamic_domains.spec +++ b/big_tests/dynamic_domains.spec @@ -40,10 +40,6 @@ {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], diff --git a/big_tests/tests/mam_SUITE.erl b/big_tests/tests/mam_SUITE.erl index 8fbd9d15663..d371c1df1e2 100644 --- a/big_tests/tests/mam_SUITE.erl +++ b/big_tests/tests/mam_SUITE.erl @@ -2998,7 +2998,9 @@ metric_incremented_on_archive_request(ConfigIn) -> assert_respond_query_id(P, <<"metric_q1">>, parse_result_iq(Res)), ok end, - MongooseMetrics = [{[host_type(), backends, mod_mam, lookup], changed}], + HostType = domain_helper:host_type(mim), + HostTypePrefix = domain_helper:make_metrics_prefix(HostType), + MongooseMetrics = [{[HostTypePrefix, backends, mod_mam, lookup], changed}], Config = [{mongoose_metrics, MongooseMetrics} | ConfigIn], escalus_fresh:story(Config, [{alice, 1}], F). From 60a1d3a1a8c70c2de7e6f358994307ae932f0c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chrz=C4=85szcz?= Date: Tue, 1 Jun 2021 11:23:11 +0200 Subject: [PATCH 35/39] Minor test case cleanup --- big_tests/tests/adhoc_SUITE.erl | 12 ++++-------- big_tests/tests/disco_and_caps_SUITE.erl | 3 +-- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/big_tests/tests/adhoc_SUITE.erl b/big_tests/tests/adhoc_SUITE.erl index e1ed43644e9..e78fe273929 100644 --- a/big_tests/tests/adhoc_SUITE.erl +++ b/big_tests/tests/adhoc_SUITE.erl @@ -82,8 +82,7 @@ disco_hidden(Config) -> Query = exml_query:subelement(Stanza, <<"query">>), ?assertEqual(undefined, exml_query:subelement_with_attr(Query, <<"node">>, ?NS_COMMANDS)), - escalus:assert(is_stanza_from, - [domain()], Stanza) + escalus:assert(is_stanza_from, [domain()], Stanza) end). disco_visible(Config) -> @@ -95,8 +94,7 @@ disco_visible(Config) -> Query = exml_query:subelement(Stanza, <<"query">>), Item = exml_query:subelement_with_attr(Query, <<"node">>, ?NS_COMMANDS), ?assertEqual(Server, exml_query:attr(Item, <<"jid">>)), - escalus:assert(is_stanza_from, - [domain()], Stanza) + escalus:assert(is_stanza_from, [domain()], Stanza) end). disco_commands(Config) -> @@ -108,18 +106,16 @@ disco_commands(Config) -> Query = exml_query:subelement(Stanza, <<"query">>), Item = exml_query:subelement_with_attr(Query, <<"node">>, <<"ping">>), ?assertEqual(Server, exml_query:attr(Item, <<"jid">>)), - escalus:assert(is_stanza_from, - [domain()], Stanza) + escalus:assert(is_stanza_from, [domain()], Stanza) end). ping(Config) -> escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> - Host = ct:get_config({hosts, mim, domain}), %% Alice pings the server using adhoc command escalus_client:send(Alice, escalus_stanza:to( escalus_stanza:adhoc_request(<<"ping">>), - Host)), + domain())), %% Server replies to Alice with pong AdHocResp = escalus_client:wait_for_stanza(Alice), escalus:assert(is_adhoc_response, [<<"ping">>, <<"completed">>], diff --git a/big_tests/tests/disco_and_caps_SUITE.erl b/big_tests/tests/disco_and_caps_SUITE.erl index ea9d8a1ebfc..7baf54f871e 100644 --- a/big_tests/tests/disco_and_caps_SUITE.erl +++ b/big_tests/tests/disco_and_caps_SUITE.erl @@ -63,8 +63,7 @@ extra_domains_are_advertised(Config) -> escalus:send(Alice, escalus_stanza:service_discovery(Server)), Stanza = escalus:wait_for_stanza(Alice), escalus:assert(has_service, [extra_domain()], Stanza), - escalus:assert(is_stanza_from, - [ct:get_config({hosts, mim, domain})], Stanza) + escalus:assert(is_stanza_from, [domain()], Stanza) end). extra_domain() -> From e1081d9919892e41c87b24db573d0f29b1c31fca Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Mon, 31 May 2021 23:17:06 +0200 Subject: [PATCH 36/39] Migrate inbox backend to host_type --- big_tests/tests/inbox_SUITE.erl | 18 +- big_tests/tests/inbox_extensions_SUITE.erl | 3 +- big_tests/tests/inbox_helper.erl | 3 +- big_tests/tests/muc_helper.erl | 2 +- include/mod_inbox.hrl | 1 - src/inbox/mod_inbox.erl | 217 ++++++++++----------- src/inbox/mod_inbox_entries.erl | 30 +-- src/inbox/mod_inbox_muc.erl | 57 +++--- src/inbox/mod_inbox_muclight.erl | 75 ++++--- src/inbox/mod_inbox_one2one.erl | 30 +-- src/inbox/mod_inbox_rdbms.erl | 180 +++++++++-------- src/inbox/mod_inbox_utils.erl | 98 +++++----- 12 files changed, 351 insertions(+), 363 deletions(-) diff --git a/big_tests/tests/inbox_SUITE.erl b/big_tests/tests/inbox_SUITE.erl index 9d9a309e153..1633a212f2a 100644 --- a/big_tests/tests/inbox_SUITE.erl +++ b/big_tests/tests/inbox_SUITE.erl @@ -74,7 +74,6 @@ -import(muc_light_helper, [room_bin_jid/1]). -import(inbox_helper, [ - muclight_domain/0, inbox_modules/0, muclight_modules/0, inbox_opts/0, @@ -208,7 +207,7 @@ init_per_group(one_to_one, Config) -> init_per_group(muclight, Config) -> ok = dynamic_modules:ensure_modules(domain_helper:host_type(mim), muclight_modules()), inbox_helper:reload_inbox_option(Config, groupchat, [muclight]), - muc_light_helper:create_room(?ROOM, muclight_domain(), alice, + muc_light_helper:create_room(?ROOM, muc_light_helper:muc_host(), alice, [bob, kate], Config, muc_light_helper:ver(1)); init_per_group(muc, Config) -> muc_helper:load_muc(), @@ -231,22 +230,22 @@ init_per_testcase(create_groupchat_no_affiliation_stored, Config) -> escalus:init_per_testcase(create_groupchat_no_affiliation_stored, Config); init_per_testcase(groupchat_markers_one_reset, Config) -> clear_inbox_all(), - muc_light_helper:create_room(?ROOM_MARKERS, muclight_domain(), alice, [bob, kate], + muc_light_helper:create_room(?ROOM_MARKERS, muc_light_helper:muc_host(), alice, [bob, kate], Config, muc_light_helper:ver(1)), escalus:init_per_testcase(groupchat_markers_one_reset, Config); init_per_testcase(non_reset_marker_should_not_affect_muclight_inbox, Config) -> clear_inbox_all(), - muc_light_helper:create_room(?ROOM_MARKERS2, muclight_domain(), alice, [bob, kate], + muc_light_helper:create_room(?ROOM_MARKERS2, muc_light_helper:muc_host(), alice, [bob, kate], Config, muc_light_helper:ver(1)), escalus:init_per_testcase(non_reset_marker_should_not_affect_muclight_inbox, Config); init_per_testcase(groupchat_reset_stanza_resets_inbox, Config) -> clear_inbox_all(), - muc_light_helper:create_room(?ROOM_MARKERS_RESET, muclight_domain(), alice, [bob, kate], + muc_light_helper:create_room(?ROOM_MARKERS_RESET, muc_light_helper:muc_host(), alice, [bob, kate], Config, muc_light_helper:ver(1)), escalus:init_per_testcase(groupchat_reset_stanza_resets_inbox, Config); init_per_testcase(leave_and_remove_conversation, Config) -> clear_inbox_all(), - muc_light_helper:create_room(?ROOM2, muclight_domain(), alice, [bob, kate], + muc_light_helper:create_room(?ROOM2, muc_light_helper:muc_host(), alice, [bob, kate], Config, muc_light_helper:ver(1)), escalus:init_per_testcase(leave_and_remove_conversation, Config); init_per_testcase(leave_and_store_conversation, Config) -> @@ -255,13 +254,13 @@ init_per_testcase(leave_and_store_conversation, Config) -> escalus:init_per_testcase(leave_and_store_conversation, Config); init_per_testcase(no_aff_stored_and_remove_on_kicked, Config) -> clear_inbox_all(), - muc_light_helper:create_room(?ROOM3, muclight_domain(), alice, [bob, kate], + muc_light_helper:create_room(?ROOM3, muc_light_helper:muc_host(), alice, [bob, kate], Config, muc_light_helper:ver(1)), inbox_helper:reload_inbox_option(Config, [{remove_on_kicked, true}, {aff_changes, false}]), escalus:init_per_testcase(no_aff_stored_and_remove_on_kicked, Config); init_per_testcase(no_stored_and_remain_after_kicked, Config) -> clear_inbox_all(), - muc_light_helper:create_room(?ROOM4, muclight_domain(), alice, [bob, kate], + muc_light_helper:create_room(?ROOM4, muc_light_helper:muc_host(), alice, [bob, kate], Config, muc_light_helper:ver(1)), inbox_helper:reload_inbox_option(Config, [{remove_on_kicked, false}, {aff_changes, true}]), escalus:init_per_testcase(no_stored_and_remain_after_kicked, Config); @@ -306,7 +305,8 @@ end_per_testcase(no_stored_and_remain_after_kicked, Config) -> inbox_helper:restore_inbox_option(Config), escalus:end_per_testcase(no_stored_and_remain_after_kicked, Config); end_per_testcase(msg_sent_to_not_existing_user, Config) -> - escalus_ejabberd:rpc(mod_inbox_utils, clear_inbox, [<<"not_existing_user">>,<<"localhost">>]), + HostType = domain_helper:host_type(mim), + escalus_ejabberd:rpc(mod_inbox_utils, clear_inbox, [HostType, <<"not_existing_user">>,<<"localhost">>]), escalus:end_per_testcase(msg_sent_to_not_existing_user, Config); end_per_testcase(CaseName, Config) -> escalus:end_per_testcase(CaseName, Config). diff --git a/big_tests/tests/inbox_extensions_SUITE.erl b/big_tests/tests/inbox_extensions_SUITE.erl index 937cf29f966..7490fe60e11 100644 --- a/big_tests/tests/inbox_extensions_SUITE.erl +++ b/big_tests/tests/inbox_extensions_SUITE.erl @@ -76,7 +76,6 @@ -import(inbox_helper, [ - muclight_domain/0, inbox_modules/0, muclight_modules/0, inbox_opts/0 @@ -175,7 +174,7 @@ end_per_group(_GroupName, Config) -> init_per_testcase(groupchat_setunread_stanza_sets_inbox, Config) -> inbox_helper:clear_inbox_all(), - muc_light_helper:create_room(?ROOM_MARKERS_RESET, muclight_domain(), alice, [bob, kate], + muc_light_helper:create_room(?ROOM_MARKERS_RESET, muc_light_helper:muc_host(), alice, [bob, kate], Config, muc_light_helper:ver(1)), escalus:init_per_testcase(groupchat_setunread_stanza_sets_inbox, Config); init_per_testcase(TestCase, Config) -> diff --git a/big_tests/tests/inbox_helper.erl b/big_tests/tests/inbox_helper.erl index 56656422920..57975b2e3e0 100644 --- a/big_tests/tests/inbox_helper.erl +++ b/big_tests/tests/inbox_helper.erl @@ -249,10 +249,11 @@ clear_inbox_all() -> clear_inboxes([alice, bob, kate, mike]). clear_inboxes(UserList) -> + HostType = domain_helper:host_type(mim), Usernames = [{escalus_users:get_username(escalus_users:get_users(UserList),U), escalus_users:get_server(escalus_users:get_users(UserList),U)} || U <- UserList], - [escalus_ejabberd:rpc(mod_inbox_utils, clear_inbox, [User, Server]) || {User, Server} <- Usernames]. + [escalus_ejabberd:rpc(mod_inbox_utils, clear_inbox, [HostType, User, Server]) || {User, Server} <- Usernames]. reload_inbox_option(Config, KeyValueList) -> HostType = domain_helper:host_type(mim), diff --git a/big_tests/tests/muc_helper.erl b/big_tests/tests/muc_helper.erl index 4bd7127b18e..cb10bcb9579 100644 --- a/big_tests/tests/muc_helper.erl +++ b/big_tests/tests/muc_helper.erl @@ -148,7 +148,7 @@ generate_rpc_jid({_,User}) -> create_instant_room(Room, From, Nick, Opts) -> ServerHost = ct:get_config({hosts, mim, domain}), assert_valid_server(ServerHost), - Room1 = rpc(mim(), jid, nodeprep, [Room]), + Room1 = jid:nodeprep(Room), ok = rpc(mim(), mod_muc, create_instant_room, [ServerHost, muc_host(), Room1, From, Nick, Opts]). diff --git a/include/mod_inbox.hrl b/include/mod_inbox.hrl index ec07a728377..601e7f43f34 100644 --- a/include/mod_inbox.hrl +++ b/include/mod_inbox.hrl @@ -28,4 +28,3 @@ -type inbox_write_res() :: ok | {error, any()}. -type marker() :: binary(). - diff --git a/src/inbox/mod_inbox.erl b/src/inbox/mod_inbox.erl index f02329a2909..80e7254e3f1 100644 --- a/src/inbox/mod_inbox.erl +++ b/src/inbox/mod_inbox.erl @@ -39,72 +39,9 @@ -export([config_metrics/1]). --callback init(Host, Opts) -> ok when - Host :: jid:lserver(), - Opts :: list(). - --callback get_inbox(LUsername, LServer, Params) -> get_inbox_res() when - LUsername :: jid:luser(), - LServer :: jid:lserver(), - Params :: get_inbox_params(). - --callback set_inbox(LUsername, LServer, ToBareJid, - Content, Count, MsgId, Timestamp) -> inbox_write_res() when - LUsername :: jid:luser(), - LServer :: jid:lserver(), - ToBareJid :: binary(), - Content :: binary(), - Count :: integer(), - MsgId :: binary(), - Timestamp :: integer(). - --callback remove_inbox_row(LUsername, LServer, ToBareJid) -> inbox_write_res() when - LUsername :: jid:luser(), - LServer :: jid:lserver(), - ToBareJid :: binary(). - --callback set_inbox_incr_unread(LUsername, LServer, ToBareJid, - Content, MsgId, Timestamp) -> {ok, integer()} | ok when - LUsername :: jid:luser(), - LServer :: jid:lserver(), - ToBareJid :: binary(), - Content :: binary(), - MsgId :: binary(), - Timestamp :: integer(). - --callback reset_unread(LUsername, LServer, BareJid, MsgId) -> inbox_write_res() when - LUsername :: jid:luser(), - LServer :: jid:lserver(), - BareJid :: binary(), - MsgId :: binary(). - --callback clear_inbox(LServer) -> inbox_write_res() when - LServer :: jid:lserver(). - --callback clear_inbox(LUsername, LServer) -> inbox_write_res() when - LUsername :: jid:luser(), - LServer :: jid:lserver(). - --callback get_inbox_unread(LUsername, LServer, InterlocutorJID) -> {ok, integer()} when - LUsername :: jid:luser(), - LServer :: jid:lserver(), - InterlocutorJID :: jid:literal_jid(). - --callback get_entry_properties(LUsername, LServer, EntryJID) -> Ret when - LUsername :: jid:luser(), +-type entry_key() :: {LUser :: jid:luser(), LServer :: jid:lserver(), - EntryJID :: jid:literal_jid(), - Ret :: entry_properties(). - --callback set_entry_properties(LUsername, LServer, EntryJID, Params) -> Ret when - LUsername :: jid:luser(), - LServer :: jid:lserver(), - EntryJID :: jid:literal_jid(), - Params :: entry_properties(), - Ret :: entry_properties() | {error, binary()}. - --callback remove_domain(HostType :: mongooseim:host_type(), - LServer :: jid:lserver()) -> ok. + ToBareJid :: jid:literal_jid()}. -type get_inbox_params() :: #{ start => integer(), @@ -114,14 +51,74 @@ archive => boolean() }. --export_type([get_inbox_params/0]). +-export_type([entry_key/0, get_inbox_params/0]). + +-callback init(Host, Opts) -> ok when + Host :: mongooseim:host_type(), + Opts :: list(). + +-callback get_inbox(HostType, LUser, LServer, Params) -> get_inbox_res() when + HostType :: mongooseim:host_type(), + LUser :: jid:luser(), + LServer :: jid:lserver(), + Params :: get_inbox_params(). + +-callback clear_inbox(HostType, LUser, LServer) -> inbox_write_res() when + HostType :: mongooseim:host_type(), + LUser :: jid:luser(), + LServer :: jid:lserver(). + +-callback remove_domain(HostType, LServer) -> ok when + HostType :: mongooseim:host_type(), + LServer :: jid:lserver(). + +-callback set_inbox(HostType, InboxEntryKey, Content, Count, MsgId, Timestamp) -> + inbox_write_res() when + HostType :: mongooseim:host_type(), + InboxEntryKey :: entry_key(), + Content :: binary(), + Count :: integer(), + MsgId :: binary(), + Timestamp :: integer(). + +-callback remove_inbox_row(HostType, InboxEntryKey) -> inbox_write_res() when + HostType :: mongooseim:host_type(), + InboxEntryKey :: entry_key(). + +-callback set_inbox_incr_unread(HostType, InboxEntryKey, Content, MsgId, Timestamp) -> + {ok, integer()} | ok when + HostType :: mongooseim:host_type(), + InboxEntryKey :: entry_key(), + Content :: binary(), + MsgId :: binary(), + Timestamp :: integer(). + +-callback reset_unread(HostType, InboxEntryKey, MsgId) -> inbox_write_res() when + HostType :: mongooseim:host_type(), + InboxEntryKey :: entry_key(), + MsgId :: binary(). + +-callback get_inbox_unread(HostType, InboxEntryKey) -> {ok, integer()} when + HostType :: mongooseim:host_type(), + InboxEntryKey :: entry_key(). + +-callback get_entry_properties(HostType, InboxEntryKey) -> Ret when + HostType :: mongooseim:host_type(), + InboxEntryKey :: entry_key(), + Ret :: entry_properties(). + +-callback set_entry_properties(HostType, InboxEntryKey, Params) -> Ret when + HostType :: mongooseim:host_type(), + InboxEntryKey :: entry_key(), + Params :: entry_properties(), + Ret :: entry_properties() | {error, binary()}. %%-------------------------------------------------------------------- %% gdpr callbacks %%-------------------------------------------------------------------- -spec get_personal_data(gdpr:personal_data(), mongooseim:host_type(), jid:jid()) -> gdpr:personal_data(). -get_personal_data(Acc, _HostType, #jid{luser = LUser, lserver = LServer}) -> +get_personal_data(Acc, HostType, #jid{luser = LUser, lserver = LServer}) -> Schema = ["jid", "content", "unread_count", "timestamp"], InboxParams = #{ start => 0, @@ -129,7 +126,7 @@ get_personal_data(Acc, _HostType, #jid{luser = LUser, lserver = LServer}) -> order => asc, hidden_read => false }, - Entries = mod_inbox_backend:get_inbox(LUser, LServer, InboxParams), + Entries = mod_inbox_backend:get_inbox(HostType, LUser, LServer, InboxParams), ProcessedEntries = lists:map(fun process_entry/1, Entries), [{inbox, Schema, ProcessedEntries} | Acc]. @@ -203,13 +200,14 @@ process_iq(Acc, _From, _To, #iq{type = get, sub_el = SubEl} = IQ, _Extra) -> SubElWithForm = SubEl#xmlel{ children = [Form] }, {Acc, IQ#iq{type = result, sub_el = SubElWithForm}}; process_iq(Acc, From, _To, #iq{type = set, id = QueryId, sub_el = QueryEl} = IQ, _Extra) -> - Username = From#jid.luser, - Host = From#jid.lserver, + HostType = mongoose_acc:host_type(Acc), + LUser = From#jid.luser, + LServer = From#jid.lserver, case query_to_params(QueryEl) of {error, bad_request, Msg} -> {Acc, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:bad_request(<<"en">>, Msg)]}}; Params -> - List = mod_inbox_backend:get_inbox(Username, Host, Params), + List = mod_inbox_backend:get_inbox(HostType, LUser, LServer, Params), forward_messages(Acc, List, QueryId, From), Res = IQ#iq{type = result, sub_el = [build_result_iq(List)]}, {Acc, Res} @@ -244,8 +242,7 @@ send_message(Acc, To = #jid{lserver = LServer}, Msg) -> To :: jid:jid(), Packet :: exml:element()) -> map(). user_send_packet(Acc, From, To, #xmlel{name = <<"message">>} = Msg) -> - Host = From#jid.lserver, - maybe_process_message(Acc, Host, From, To, Msg, outgoing), + maybe_process_message(Acc, From, To, Msg, outgoing), Acc; user_send_packet(Acc, _From, _To, _Packet) -> Acc. @@ -263,12 +260,11 @@ inbox_unread_count(Acc, To) -> filter_packet(drop) -> drop; filter_packet({From, To, Acc, Msg = #xmlel{name = <<"message">>}}) -> - Host = To#jid.lserver, %% In case of PgSQL we can we can update inbox and obtain unread_count in one query, %% so we put it in accumulator here. %% In case of MySQL/MsSQL it costs an extra query, so we fetch it only if necessary %% (when push notification is created) - Acc0 = case maybe_process_message(Acc, Host, From, To, Msg, incoming) of + Acc0 = case maybe_process_message(Acc, From, To, Msg, incoming) of {ok, UnreadCount} -> mongoose_acc:set(inbox, unread_count, UnreadCount, Acc); _ -> @@ -280,7 +276,8 @@ filter_packet({From, To, Acc, Packet}) -> {From, To, Acc, Packet}. remove_user(Acc, User, Server) -> - mod_inbox_utils:clear_inbox(User, Server), + HostType = mongoose_acc:host_type(Acc), + mod_inbox_utils:clear_inbox(HostType, User, Server), Acc. -spec remove_domain(mongoose_hooks:simple_acc(), @@ -299,16 +296,16 @@ add_local_features(Acc, _From, _To, _Node, _Lang) -> Acc. -spec maybe_process_message(Acc :: mongoose_acc:t(), - Host :: host(), From :: jid:jid(), To :: jid:jid(), Msg :: exml:element(), Dir :: outgoing | incoming) -> ok | {ok, integer()}. -maybe_process_message(Acc, Host, From, To, Msg, Dir) -> +maybe_process_message(Acc, From, To, Msg, Dir) -> + HostType = mongoose_acc:host_type(Acc), case should_be_stored_in_inbox(Msg) andalso inbox_owner_exists(Acc, From, To, Dir) of true -> Type = get_message_type(Msg), - maybe_process_acceptable_message(Host, From, To, Msg, Acc, Dir, Type); + maybe_process_acceptable_message(HostType, From, To, Msg, Acc, Dir, Type); false -> ok end. @@ -324,33 +321,33 @@ inbox_owner_exists(Acc, _From, To, incoming) -> HostType = mongoose_acc:host_type(Acc), mongoose_users:does_user_exist(HostType, To). -maybe_process_acceptable_message(Host, From, To, Msg, Acc, Dir, one2one) -> - process_message(Host, From, To, Msg, Acc, Dir, one2one); -maybe_process_acceptable_message(Host, From, To, Msg, Acc, Dir, groupchat) -> - muclight_enabled(Host) andalso - process_message(Host, From, To, Msg, Acc, Dir, groupchat). +maybe_process_acceptable_message(HostType, From, To, Msg, Acc, Dir, one2one) -> + process_message(HostType, From, To, Msg, Acc, Dir, one2one); +maybe_process_acceptable_message(HostType, From, To, Msg, Acc, Dir, groupchat) -> + muclight_enabled(HostType) andalso + process_message(HostType, From, To, Msg, Acc, Dir, groupchat). --spec process_message(Host :: host(), +-spec process_message(HostType :: host(), From :: jid:jid(), To :: jid:jid(), Message :: exml:element(), Acc :: mongoose_acc:t(), Dir :: outgoing | incoming, Type :: one2one | groupchat) -> ok | {ok, integer()}. -process_message(Host, From, To, Message, Acc, outgoing, one2one) -> - mod_inbox_one2one:handle_outgoing_message(Host, From, To, Message, Acc); -process_message(Host, From, To, Message, Acc, incoming, one2one) -> - mod_inbox_one2one:handle_incoming_message(Host, From, To, Message, Acc); -process_message(Host, From, To, Message, Acc, outgoing, groupchat) -> - mod_inbox_muclight:handle_outgoing_message(Host, From, To, Message, Acc); -process_message(Host, From, To, Message, Acc, incoming, groupchat) -> - mod_inbox_muclight:handle_incoming_message(Host, From, To, Message, Acc); -process_message(Host, From, To, Message, _TS, Dir, Type) -> +process_message(HostType, From, To, Message, Acc, outgoing, one2one) -> + mod_inbox_one2one:handle_outgoing_message(HostType, From, To, Message, Acc); +process_message(HostType, From, To, Message, Acc, incoming, one2one) -> + mod_inbox_one2one:handle_incoming_message(HostType, From, To, Message, Acc); +process_message(HostType, From, To, Message, Acc, outgoing, groupchat) -> + mod_inbox_muclight:handle_outgoing_message(HostType, From, To, Message, Acc); +process_message(HostType, From, To, Message, Acc, incoming, groupchat) -> + mod_inbox_muclight:handle_incoming_message(HostType, From, To, Message, Acc); +process_message(HostType, From, To, Message, _TS, Dir, Type) -> ?LOG_WARNING(#{what => inbox_unknown_message, text => <<"Unknown message was not written into inbox">>, exml_packet => Message, from_jid => jid:to_binary(From), to_jid => jid:to_binary(To), - server => Host, dir => Dir, inbox_message_type => Type}), + host_type => HostType, dir => Dir, inbox_message_type => Type}), ok. @@ -570,11 +567,11 @@ invalid_field_value(Field, Value) -> get_inbox_unread(Value, Acc, _) when is_integer(Value) -> Acc; get_inbox_unread(undefined, Acc, To) -> -%% TODO this value should be bound to a stanza reference inside Acc - {User, Host} = jid:to_lus(To), + %% TODO this value should be bound to a stanza reference inside Acc InterlocutorJID = mongoose_acc:from_jid(Acc), - RemBareJIDBin = jid:to_binary(jid:to_lus(InterlocutorJID)), - {ok, Count} = mod_inbox_backend:get_inbox_unread(User, Host, RemBareJIDBin), + InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(To, InterlocutorJID), + HostType = mongoose_acc:host_type(Acc), + {ok, Count} = mod_inbox_backend:get_inbox_unread(HostType, InboxEntryKey), mongoose_acc:set(inbox, unread_count, Count, Acc). hooks(HostType) -> @@ -594,18 +591,18 @@ add_default_backend(Opts) -> _ -> Opts end. -get_groupchat_types(Host) -> - gen_mod:get_module_opt(Host, ?MODULE, groupchat, [muclight]). +get_groupchat_types(HostType) -> + gen_mod:get_module_opt(HostType, ?MODULE, groupchat, [muclight]). -config_metrics(Host) -> +config_metrics(HostType) -> OptsToReport = [{backend, rdbms}], %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 store_bin_reset_markers(Host :: host(), Opts :: list()) -> boolean(). -store_bin_reset_markers(Host, Opts) -> +-spec store_bin_reset_markers(HostType :: mongooseim:host_type(), Opts :: list()) -> boolean(). +store_bin_reset_markers(HostType, Opts) -> ResetMarkers = gen_mod:get_opt(reset_markers, Opts, [displayed]), ResetMarkersBin = [mod_inbox_utils:reset_marker_to_bin(Marker) || Marker <- ResetMarkers ], - gen_mod:set_module_opt(Host, ?MODULE, reset_markers, ResetMarkersBin). + gen_mod:set_module_opt(HostType, ?MODULE, reset_markers, ResetMarkersBin). groupchat_deps(Opts) -> case lists:keyfind(groupchat, 1, Opts) of @@ -630,11 +627,11 @@ muc_dep(List) -> callback_funs() -> [get_inbox, set_inbox, set_inbox_incr_unread, reset_unread, remove_inbox_row, clear_inbox, get_inbox_unread, - get_entry_properties, set_entry_properties]. + get_entry_properties, set_entry_properties, remove_domain]. --spec muclight_enabled(Host :: binary()) -> boolean(). -muclight_enabled(Host) -> - Groupchats = get_groupchat_types(Host), +-spec muclight_enabled(HostType :: mongooseim:host_type()) -> boolean(). +muclight_enabled(HostType) -> + Groupchats = get_groupchat_types(HostType), lists:member(muclight, Groupchats). -spec get_message_type(Msg :: exml:element()) -> groupchat | one2one. diff --git a/src/inbox/mod_inbox_entries.erl b/src/inbox/mod_inbox_entries.erl index c91fc617fe1..a51293d6f38 100644 --- a/src/inbox/mod_inbox_entries.erl +++ b/src/inbox/mod_inbox_entries.erl @@ -1,5 +1,6 @@ -module(mod_inbox_entries). +-include("mongoose_logger.hrl"). -include("mongoose_ns.hrl"). -include("jlib.hrl"). -include("mod_inbox.hrl"). @@ -20,7 +21,7 @@ process_iq_conversation(Acc, From, _To, #iq{type = get, sub_el = SubEl} = IQ, _E process_iq_conversation(Acc, From, _To, #iq{type = set, sub_el = #xmlel{name = <<"reset">>} = ResetStanza} = IQ, _Extra) -> - maybe_process_reset_stanza(From, Acc, IQ, ResetStanza); + maybe_process_reset_stanza(Acc, From, IQ, ResetStanza); process_iq_conversation(Acc, From, _To, #iq{type = set, sub_el = Query} = IQ, _Extra) -> process_iq_conversation_set(Acc, IQ, From, Query). @@ -49,9 +50,9 @@ build_inbox_entry_form() -> -spec get_properties_for_jid(mongoose_acc:t(), jlib:iq(), jid:jid(), jid:jid()) -> {mongoose_acc:t(), jlib:iq()}. get_properties_for_jid(Acc, IQ, From, EntryJID) -> - {LUser, LServer} = jid:to_lus(From), - BinEntryJID = jid:to_binary(jid:to_lus(EntryJID)), - case mod_inbox_backend:get_entry_properties(LUser, LServer, BinEntryJID) of + HostType = mongoose_acc:host_type(Acc), + {_, _, BinEntryJID} = InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(From, EntryJID), + case mod_inbox_backend:get_entry_properties(HostType, InboxEntryKey) of [] -> return_error(Acc, IQ, <<"Entry not found">>); Result -> CurrentTS = mongoose_acc:timestamp(Acc), @@ -88,18 +89,18 @@ extract_requests(Acc, IQ, From, EntryJID, Requests) -> -spec process_requests(mongoose_acc:t(), jlib:iq(), jid:jid(), jid:jid(), integer(), map()) -> {mongoose_acc:t(), jlib:iq()}. process_requests(Acc, IQ, From, EntryJID, CurrentTS, Params) -> - {LUser, LServer} = jid:to_lus(From), - BinEntryJID = jid:to_binary(jid:to_lus(EntryJID)), - case mod_inbox_backend:set_entry_properties(LUser, LServer, BinEntryJID, Params) of + HostType = mongoose_acc:host_type(Acc), + InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(From, EntryJID), + case mod_inbox_backend:set_entry_properties(HostType, InboxEntryKey, Params) of {error, Msg} -> return_error(Acc, IQ, Msg); Result -> - forward_result(Acc, IQ, From, BinEntryJID, Result, CurrentTS) + forward_result(Acc, IQ, From, InboxEntryKey, Result, CurrentTS) end. --spec forward_result(mongoose_acc:t(), jlib:iq(), jid:jid(), binary(), entry_properties(), integer()) -> +-spec forward_result(mongoose_acc:t(), jlib:iq(), jid:jid(), mod_inbox:entry_key(), entry_properties(), integer()) -> {mongoose_acc:t(), jlib:iq()}. -forward_result(Acc, IQ, From, ToBareJidBin, Result, CurrentTS) -> +forward_result(Acc, IQ, From, {_, _, ToBareJidBin}, Result, CurrentTS) -> Properties = build_result(Result, CurrentTS), X = [#xmlel{name = <<"x">>, attrs = [{<<"xmlns">>, ?NS_ESL_INBOX_CONVERSATION}, @@ -111,16 +112,17 @@ forward_result(Acc, IQ, From, ToBareJidBin, Result, CurrentTS) -> Acc1 = ejabberd_router:route(From, jid:to_bare(From), Acc, Msg), {Acc1, IQ#iq{type = result, sub_el = []}}. -maybe_process_reset_stanza(From, Acc, IQ, ResetStanza) -> +maybe_process_reset_stanza(Acc, From, IQ, ResetStanza) -> case mod_inbox_utils:extract_attr_jid(ResetStanza) of {error, Msg} -> {Acc, IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:bad_request(<<"en">>, Msg)]}}; InterlocutorJID -> - process_reset_stanza(From, Acc, IQ, ResetStanza, InterlocutorJID) + process_reset_stanza(Acc, From, IQ, ResetStanza, InterlocutorJID) end. -process_reset_stanza(From, Acc, IQ, _ResetStanza, InterlocutorJID) -> - ok = mod_inbox_utils:reset_unread_count_to_zero(From, InterlocutorJID), +process_reset_stanza(Acc, From, IQ, _ResetStanza, InterlocutorJID) -> + HostType = mongoose_acc:host_type(Acc), + ok = mod_inbox_utils:reset_unread_count_to_zero(HostType, From, InterlocutorJID), {Acc, IQ#iq{type = result, sub_el = [#xmlel{name = <<"reset">>, attrs = [{<<"xmlns">>, ?NS_ESL_INBOX_CONVERSATION}], diff --git a/src/inbox/mod_inbox_muc.erl b/src/inbox/mod_inbox_muc.erl index 58c882d6303..49ff9fd069c 100644 --- a/src/inbox/mod_inbox_muc.erl +++ b/src/inbox/mod_inbox_muc.erl @@ -15,27 +15,26 @@ %% User jid example is "alice@localhost" -type user_jid() :: jid:jid(). -%% Receiver's host in lowercase --type receiver_host() :: jid:lserver(). -type receiver_bare_user_jid() :: user_jid(). -type room_bare_jid() :: jid:jid(). -type packet() :: exml:element(). -start(Host) -> - ejabberd_hooks:add(update_inbox_for_muc, Host, ?MODULE, update_inbox_for_muc, 90), +start(HostType) -> + ejabberd_hooks:add(update_inbox_for_muc, HostType, ?MODULE, update_inbox_for_muc, 90), % TODO check ooptions: if system messages stored -> % add hook handler for system messages on hook ie. invitation_sent ok. -stop(Host) -> - ejabberd_hooks:delete(update_inbox_for_muc, Host, ?MODULE, update_inbox_for_muc, 90), +stop(HostType) -> + ejabberd_hooks:delete(update_inbox_for_muc, HostType, ?MODULE, update_inbox_for_muc, 90), ok. -spec update_inbox_for_muc(Acc) -> Acc when Acc :: mod_muc_room:update_inbox_for_muc_payload(). update_inbox_for_muc( - #{room_jid := Room, + #{host_type := HostType, + room_jid := Room, from_jid := From, from_room_jid := FromRoomJid, packet := Packet, @@ -46,9 +45,8 @@ update_inbox_for_muc( To = jid:to_bare(jid:make(AffLJID)), %% Guess direction based on user JIDs Direction = direction(From, To), - Host = To#jid.lserver, Packet2 = jlib:replace_from_to(FromRoomJid, To, Packet), - update_inbox_for_user(Direction, Host, Room, To, Packet2); + update_inbox_for_user(HostType, Direction, Room, To, Packet2); false -> ok end @@ -60,18 +58,19 @@ update_inbox_for_muc( is_allowed_affiliation(outcast) -> false; is_allowed_affiliation(_) -> true. --spec update_inbox_for_user(Direction, Host, Room, To, Packet) -> term() when +-spec update_inbox_for_user(HostType, Direction, Room, To, Packet) -> term() when + HostType :: mongooseim:host_type(), Direction :: incoming | outgoing, - Host :: receiver_host(), Room :: room_bare_jid(), To :: receiver_bare_user_jid(), Packet :: packet(). -update_inbox_for_user(Direction, Host, Room, To, Packet) -> +update_inbox_for_user(HostType, Direction, Room, To, Packet) -> + Host = To#jid.lserver, case {is_local_xmpp_host(Host), Direction} of {true, outgoing} -> - handle_outgoing_message(Host, Room, To, Packet); + handle_outgoing_message(HostType, Room, To, Packet); {true, incoming} -> - handle_incoming_message(Host, Room, To, Packet); + handle_incoming_message(HostType, Room, To, Packet); _ -> %% We ignore inbox for users on the remote (s2s) hosts %% We ignore inbox for components (also known as services or bots) @@ -86,30 +85,30 @@ direction(From, To) -> end. %% Sender and receiver is the same user --spec handle_outgoing_message(Host, Room, To, Packet) -> term() when - Host :: receiver_host(), +-spec handle_outgoing_message(HostType, Room, To, Packet) -> term() when + HostType :: mongooseim:host_type(), Room :: room_bare_jid(), To :: receiver_bare_user_jid(), Packet :: packet(). -handle_outgoing_message(Host, Room, To, Packet) -> - maybe_reset_unread_count(Host, To, Room, Packet), - Acc = mongoose_acc:new(#{location => ?LOCATION, lserver => Host}), - maybe_write_to_inbox(Host, To, Room, Packet, Acc, fun write_to_sender_inbox/5). +handle_outgoing_message(HostType, Room, To, Packet) -> + maybe_reset_unread_count(HostType, To, Room, Packet), + Acc = mongoose_acc:new(#{location => ?LOCATION, lserver => To#jid.lserver, host_type => HostType}), + maybe_write_to_inbox(HostType, To, Room, Packet, Acc, fun write_to_sender_inbox/5). --spec handle_incoming_message(Host, Room, To, Packet) -> term() when - Host :: receiver_host(), +-spec handle_incoming_message(HostType, Room, To, Packet) -> term() when + HostType :: mongooseim:host_type(), Room :: room_bare_jid(), To :: receiver_bare_user_jid(), Packet :: packet(). -handle_incoming_message(Host, Room, To, Packet) -> - Acc = mongoose_acc:new(#{location => ?LOCATION, lserver => Host}), - maybe_write_to_inbox(Host, Room, To, Packet, Acc, fun write_to_receiver_inbox/5). +handle_incoming_message(HostType, Room, To, Packet) -> + Acc = mongoose_acc:new(#{location => ?LOCATION, lserver => To#jid.lserver, host_type => HostType}), + maybe_write_to_inbox(HostType, Room, To, Packet, Acc, fun write_to_receiver_inbox/5). -maybe_reset_unread_count(Host, User, Room, Packet) -> - mod_inbox_utils:maybe_reset_unread_count(Host, User, Room, Packet). +maybe_reset_unread_count(HostType, User, Room, Packet) -> + mod_inbox_utils:maybe_reset_unread_count(HostType, User, Room, Packet). -maybe_write_to_inbox(Host, User, Remote, Packet, Acc, WriteF) -> - mod_inbox_utils:maybe_write_to_inbox(Host, User, Remote, Packet, Acc, WriteF). +maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, WriteF) -> + mod_inbox_utils:maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, WriteF). write_to_sender_inbox(Server, User, Remote, Packet, Acc) -> mod_inbox_utils:write_to_sender_inbox(Server, User, Remote, Packet, Acc). diff --git a/src/inbox/mod_inbox_muclight.erl b/src/inbox/mod_inbox_muclight.erl index 64dc443e768..ef44d602078 100644 --- a/src/inbox/mod_inbox_muclight.erl +++ b/src/inbox/mod_inbox_muclight.erl @@ -22,115 +22,114 @@ -type r_owner() :: binary(). -type r_none() :: binary(). --spec handle_outgoing_message(Host :: jid:server(), +-spec handle_outgoing_message(HostType :: mongooseim:host_type(), User :: jid:jid(), Room :: jid:jid(), Packet :: packet(), Acc :: mongoose_acc:t()) -> any(). -handle_outgoing_message(Host, User, Room, Packet, _TS) -> - maybe_reset_unread_count(Host, User, Room, Packet). +handle_outgoing_message(HostType, User, Room, Packet, _TS) -> + maybe_reset_unread_count(HostType, User, Room, Packet). --spec handle_incoming_message(Host :: jid:server(), +-spec handle_incoming_message(HostType :: mongooseim:host_type(), RoomUser :: jid:jid(), Remote :: jid:jid(), Packet :: packet(), Acc :: mongoose_acc:t()) -> any(). -handle_incoming_message(Host, RoomUser, Remote, Packet, Acc) -> +handle_incoming_message(HostType, RoomUser, Remote, Packet, Acc) -> case mod_inbox_utils:has_chat_marker(Packet) of true -> %% don't store chat markers in inbox ok; false -> - maybe_handle_system_message(Host, RoomUser, Remote, Packet, Acc) + maybe_handle_system_message(HostType, RoomUser, Remote, Packet, Acc) end. -maybe_reset_unread_count(Host, User, Room, Packet) -> - mod_inbox_utils:maybe_reset_unread_count(Host, User, Room, Packet). +maybe_reset_unread_count(HostType, User, Room, Packet) -> + mod_inbox_utils:maybe_reset_unread_count(HostType, User, Room, Packet). --spec maybe_handle_system_message(Host :: host(), +-spec maybe_handle_system_message(HostType :: mongooseim:host_type(), RoomOrUser :: jid:jid(), Receiver :: jid:jid(), Packet :: exml:element(), Acc :: mongoose_acc:t()) -> ok. -maybe_handle_system_message(Host, RoomOrUser, Receiver, Packet, Acc) -> +maybe_handle_system_message(HostType, RoomOrUser, Receiver, Packet, Acc) -> case is_system_message(RoomOrUser, Receiver, Packet) of true -> - handle_system_message(Host, RoomOrUser, Receiver, Packet, Acc); + handle_system_message(HostType, RoomOrUser, Receiver, Packet, Acc); _ -> Sender = jid:from_binary(RoomOrUser#jid.lresource), - write_to_inbox(Host, RoomOrUser, Receiver, Sender, Packet, Acc) + write_to_inbox(HostType, RoomOrUser, Receiver, Sender, Packet, Acc) end. --spec handle_system_message(Host :: host(), +-spec handle_system_message(HostType :: mongooseim:host_type(), Room :: jid:jid(), Remote :: jid:jid(), Packet :: exml:element(), Acc :: mongoose_acc:t()) -> ok. -handle_system_message(Host, Room, Remote, Packet, Acc) -> +handle_system_message(HostType, Room, Remote, Packet, Acc) -> case system_message_type(Remote, Packet) of kick -> - handle_kicked_message(Host, Room, Remote, Packet, Acc); + handle_kicked_message(HostType, Room, Remote, Packet, Acc); invite -> - handle_invitation_message(Host, Room, Remote, Packet, Acc); + handle_invitation_message(HostType, Room, Remote, Packet, Acc); other -> ?LOG_DEBUG(#{what => irrelevant_system_message_for_mod_inbox_muclight, room => Room, exml_packet => Packet}), ok end. --spec handle_invitation_message(Host :: host(), +-spec handle_invitation_message(HostType :: mongooseim:host_type(), Room :: jid:jid(), Remote :: jid:jid(), Packet :: exml:element(), Acc :: mongoose_acc:t()) -> ok. -handle_invitation_message(Host, Room, Remote, Packet, Acc) -> - maybe_store_system_message(Host, Room, Remote, Packet, Acc). +handle_invitation_message(HostType, Room, Remote, Packet, Acc) -> + maybe_store_system_message(HostType, Room, Remote, Packet, Acc). --spec handle_kicked_message(Host :: host(), +-spec handle_kicked_message(HostType :: mongooseim:host_type(), Room :: jid:jid(), Remote :: jid:jid(), Packet :: exml:element(), Acc :: mongoose_acc:t()) -> ok. -handle_kicked_message(Host, Room, Remote, Packet, Acc) -> - CheckRemove = mod_inbox_utils:get_option_remove_on_kicked(Host), - maybe_store_system_message(Host, Room, Remote, Packet, Acc), - maybe_remove_inbox_row(Host, Room, Remote, CheckRemove). +handle_kicked_message(HostType, Room, Remote, Packet, Acc) -> + CheckRemove = mod_inbox_utils:get_option_remove_on_kicked(HostType), + maybe_store_system_message(HostType, Room, Remote, Packet, Acc), + maybe_remove_inbox_row(HostType, Room, Remote, CheckRemove). --spec maybe_store_system_message(Host :: host(), +-spec maybe_store_system_message(HostType :: mongooseim:host_type(), Room :: jid:jid(), Remote :: jid:jid(), Packet :: exml:element(), Acc :: mongoose_acc:t()) -> ok. -maybe_store_system_message(Host, Room, Remote, Packet, Acc) -> - WriteAffChanges = mod_inbox_utils:get_option_write_aff_changes(Host), +maybe_store_system_message(HostType, Room, Remote, Packet, Acc) -> + WriteAffChanges = mod_inbox_utils:get_option_write_aff_changes(HostType), case WriteAffChanges of true -> - write_to_inbox(Host, Room, Remote, Room, Packet, Acc); + write_to_inbox(HostType, Room, Remote, Room, Packet, Acc); false -> ok end. --spec maybe_remove_inbox_row(Host :: host(), +-spec maybe_remove_inbox_row(HostType :: mongooseim:host_type(), Room :: jid:jid(), Remote :: jid:jid(), WriteAffChanges :: boolean()) -> ok. maybe_remove_inbox_row(_, _, _, false) -> ok; -maybe_remove_inbox_row(Host, Room, Remote, true) -> - UserBin = Remote#jid.luser, - RoomBin = jid:to_binary(Room), - ok = mod_inbox_backend:remove_inbox_row(UserBin, Host, RoomBin). +maybe_remove_inbox_row(HostType, Room, Remote, true) -> + InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(Remote, Room), + ok = mod_inbox_backend:remove_inbox_row(HostType, InboxEntryKey). --spec write_to_inbox(Server :: host(), +-spec write_to_inbox(HostType :: mongooseim:host_type(), RoomUser :: jid:jid(), Remote :: jid:jid(), Sender :: jid:jid(), Packet :: exml:element(), Acc :: mongoose_acc:t()) -> ok. -write_to_inbox(Server, RoomUser, Remote, Remote, Packet, Acc) -> - mod_inbox_utils:write_to_sender_inbox(Server, Remote, RoomUser, Packet, Acc); -write_to_inbox(Server, RoomUser, Remote, _Sender, Packet, Acc) -> - mod_inbox_utils:write_to_receiver_inbox(Server, RoomUser, Remote, Packet, Acc). +write_to_inbox(HostType, RoomUser, Remote, Remote, Packet, Acc) -> + mod_inbox_utils:write_to_sender_inbox(HostType, Remote, RoomUser, Packet, Acc); +write_to_inbox(HostType, RoomUser, Remote, _Sender, Packet, Acc) -> + mod_inbox_utils:write_to_receiver_inbox(HostType, RoomUser, Remote, Packet, Acc). %%%%%%% %% Predicate funs diff --git a/src/inbox/mod_inbox_one2one.erl b/src/inbox/mod_inbox_one2one.erl index 657887a911c..70b6acd43ec 100644 --- a/src/inbox/mod_inbox_one2one.erl +++ b/src/inbox/mod_inbox_one2one.erl @@ -16,31 +16,31 @@ -type packet() :: exml:element(). --spec handle_outgoing_message(Host :: jid:server(), +-spec handle_outgoing_message(HostType :: mongooseim:host_type(), User :: jid:jid(), Remote :: jid:jid(), Packet :: packet(), Acc :: mongoose_acc:t()) -> ok. -handle_outgoing_message(Host, User, Remote, Packet, Acc) -> - maybe_reset_unread_count(Host, User, Remote, Packet), - maybe_write_to_inbox(Host, User, Remote, Packet, Acc, fun write_to_sender_inbox/5). +handle_outgoing_message(HostType, User, Remote, Packet, Acc) -> + maybe_reset_unread_count(HostType, User, Remote, Packet), + maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, fun write_to_sender_inbox/5). --spec handle_incoming_message(Host :: jid:server(), +-spec handle_incoming_message(HostType :: mongooseim:host_type(), User :: jid:jid(), Remote :: jid:jid(), Packet :: packet(), Acc :: mongoose_acc:t()) -> ok | {ok, integer()}. -handle_incoming_message(Host, User, Remote, Packet, Acc) -> - maybe_write_to_inbox(Host, User, Remote, Packet, Acc, fun write_to_receiver_inbox/5). +handle_incoming_message(HostType, User, Remote, Packet, Acc) -> + maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, fun write_to_receiver_inbox/5). -maybe_reset_unread_count(Host, User, Remote, Packet) -> - mod_inbox_utils:maybe_reset_unread_count(Host, User, Remote, Packet). +maybe_reset_unread_count(HostType, User, Remote, Packet) -> + mod_inbox_utils:maybe_reset_unread_count(HostType, User, Remote, Packet). -maybe_write_to_inbox(Host, User, Remote, Packet, Acc, WriteF) -> - mod_inbox_utils:maybe_write_to_inbox(Host, User, Remote, Packet, Acc, WriteF). +maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, WriteF) -> + mod_inbox_utils:maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, WriteF). -write_to_sender_inbox(Server, User, Remote, Packet, Acc) -> - mod_inbox_utils:write_to_sender_inbox(Server, User, Remote, Packet, Acc). +write_to_sender_inbox(HostType, User, Remote, Packet, Acc) -> + mod_inbox_utils:write_to_sender_inbox(HostType, User, Remote, Packet, Acc). -write_to_receiver_inbox(Server, User, Remote, Packet, Acc) -> - mod_inbox_utils:write_to_receiver_inbox(Server, User, Remote, Packet, Acc). +write_to_receiver_inbox(HostType, User, Remote, Packet, Acc) -> + mod_inbox_utils:write_to_receiver_inbox(HostType, User, Remote, Packet, Acc). diff --git a/src/inbox/mod_inbox_rdbms.erl b/src/inbox/mod_inbox_rdbms.erl index 20c62cbcca6..128b87cf36d 100644 --- a/src/inbox/mod_inbox_rdbms.erl +++ b/src/inbox/mod_inbox_rdbms.erl @@ -16,18 +16,17 @@ -behaviour(mod_inbox). %% API --export([get_inbox/3, +-export([get_inbox/4, init/2, - set_inbox/7, - set_inbox_incr_unread/6, - reset_unread/4, - remove_inbox_row/3, + set_inbox/6, + set_inbox_incr_unread/5, + reset_unread/3, + remove_inbox_row/2, remove_domain/2, - clear_inbox/1, - clear_inbox/2, - get_inbox_unread/3, - get_entry_properties/3, - set_entry_properties/4]). + clear_inbox/3, + get_inbox_unread/2, + get_entry_properties/2, + set_entry_properties/3]). %% For specific backends -export([esc_string/1, esc_int/1]). @@ -71,9 +70,7 @@ init(HostType, _Options) -> [luser, lserver], <<"DELETE FROM inbox WHERE luser = ? AND lserver = ?">>), mongoose_rdbms:prepare(inbox_delete_domain, inbox, - [lserver], - <<"DELETE FROM inbox WHERE lserver = ?">>), - mongoose_rdbms:prepare(inbox_delete_all, inbox, [], <<"DELETE FROM inbox">>), + [lserver], <<"DELETE FROM inbox WHERE lserver = ?">>), UniqueKeyFields = [<<"luser">>, <<"lserver">>, <<"remote_bare_jid">>], InsertFields = UniqueKeyFields ++ [<<"content">>, <<"unread_count">>, <<"msg_id">>, <<"timestamp">>], @@ -89,52 +86,52 @@ init(HostType, _Options) -> UniqueKeyFields), ok. --spec get_inbox(LUsername :: jid:luser(), +-spec get_inbox(HostType :: mongooseim:host_type(), + LUser :: jid:luser(), LServer :: jid:lserver(), Params :: mod_inbox:get_inbox_params()) -> get_inbox_res(). -get_inbox(LUsername, LServer, Params) -> - case get_inbox_rdbms(LUsername, LServer, Params) of +get_inbox(HostType, LUser, LServer, Params) -> + case get_inbox_rdbms(HostType, LUser, LServer, Params) of {selected, []} -> []; {selected, Res} -> - [decode_row(LServer, R) || R <- Res] + [decode_row(HostType, R) || R <- Res] end. --spec get_inbox_unread(jid:luser(), jid:lserver(), jid:literal_jid()) -> {ok, integer()}. -get_inbox_unread(LUser, LServer, RemBareJID) -> - Res = execute_select_unread_count(LUser, LServer, RemBareJID), +-spec get_inbox_unread(mongooseim:host_type(), mod_inbox:entry_key()) -> + {ok, integer()}. +get_inbox_unread(HostType, {LUser, LServer, RemBareJID}) -> + Res = execute_select_unread_count(HostType, LUser, LServer, RemBareJID), {ok, Val} = check_result(Res), %% We read unread_count value when the message is sent and is not yet in receiver inbox %% so we have to add +1 {ok, Val + 1}. --spec set_inbox(LUsername, LServer, ToBareJid, Content, Count, MsgId, Timestamp) -> +-spec set_inbox(HostType, InboxEntryKey, Content, Count, MsgId, Timestamp) -> inbox_write_res() when - LUsername :: jid:luser(), - LServer :: jid:lserver(), - ToBareJid :: jid:literal_jid(), + HostType :: mongooseim:host_type(), + InboxEntryKey :: mod_inbox:entry_key(), Content :: binary(), Count :: integer(), MsgId :: binary(), Timestamp :: integer(). -set_inbox(LUsername, LServer, ToBareJid, Content, Count, MsgId, Timestamp) -> +set_inbox(HostType, {LUser, LServer, ToBareJid}, Content, Count, MsgId, Timestamp) -> LToBareJid = jid:nameprep(ToBareJid), - InsertParams = [LUsername, LServer, LToBareJid, + InsertParams = [LUser, LServer, LToBareJid, Content, Count, MsgId, Timestamp], UpdateParams = [Content, Count, MsgId, Timestamp, false], - UniqueKeyValues = [LUsername, LServer, LToBareJid], - Res = rdbms_queries:execute_upsert(LServer, inbox_upsert, + UniqueKeyValues = [LUser, LServer, LToBareJid], + Res = rdbms_queries:execute_upsert(HostType, inbox_upsert, InsertParams, UpdateParams, UniqueKeyValues), %% MySQL returns 1 when an upsert is an insert %% and 2, when an upsert acts as update ok = check_result(Res, [1, 2]). --spec remove_inbox_row(LUsername :: jid:luser(), - LServer :: jid:lserver(), - ToBareJid :: jid:literal_jid()) -> ok. -remove_inbox_row(LUsername, LServer, ToBareJid) -> +-spec remove_inbox_row(HostType :: mongooseim:host_type(), + InboxEntryKey :: mod_inbox:entry_key()) -> ok. +remove_inbox_row(HostType, {LUser, LServer, ToBareJid}) -> LToBareJid = jid:nameprep(ToBareJid), - Res = execute_delete(LUsername, LServer, LToBareJid), + Res = execute_delete(HostType, LUser, LServer, LToBareJid), check_result(Res). -spec remove_domain(HostType :: mongooseim:host_type(), @@ -145,55 +142,52 @@ remove_domain(HostType, LServer) -> %% This function was not refatorected to use the generic upsert helper %% becase this helper doesn't support parametrized queries for incremental change --spec set_inbox_incr_unread(LUsername :: jid:luser(), - LServer :: jid:lserver(), - ToBareJid :: jid:literal_jid(), +-spec set_inbox_incr_unread(HostType :: mongooseim:host_type(), + InboxEntryKey :: mod_inbox:entry_key(), Content :: binary(), MsgId :: binary(), Timestamp :: integer()) -> ok | {ok, integer()}. -set_inbox_incr_unread(LUsername, LServer, ToBareJid, Content, MsgId, Timestamp) -> +set_inbox_incr_unread(HostType, {LUser, LServer, ToBareJid}, Content, MsgId, Timestamp) -> LToBareJid = jid:nameprep(ToBareJid), - InsertParams = [LUsername, LServer, LToBareJid, Content, 1, MsgId, Timestamp], + InsertParams = [LUser, LServer, LToBareJid, Content, 1, MsgId, Timestamp], UpdateParams = [Content, MsgId, Timestamp, false], - UniqueKeyValues = [LUsername, LServer, LToBareJid], - Res = rdbms_queries:execute_upsert(LServer, inbox_upsert_incr_unread, + UniqueKeyValues = [LUser, LServer, LToBareJid], + Res = rdbms_queries:execute_upsert(HostType, inbox_upsert_incr_unread, InsertParams, UpdateParams, UniqueKeyValues), check_result(Res). --spec reset_unread(LUsername :: jid:luser(), - LServer :: jid:lserver(), - BareJid :: jid:literal_jid(), +-spec reset_unread(HosType :: mongooseim:host_type(), + InboxEntryKey :: mod_inbox:entry_key(), MsgId :: binary() | undefined) -> ok. -reset_unread(LUsername, LServer, ToBareJid, MsgId) -> +reset_unread(HostType, {LUser, LServer, ToBareJid}, MsgId) -> LToBareJid = jid:nameprep(ToBareJid), - Res = execute_reset_unread(LUsername, LServer, LToBareJid, MsgId), + Res = execute_reset_unread(HostType, LUser, LServer, LToBareJid, MsgId), check_result(Res). --spec clear_inbox(LUsername :: jid:luser(), LServer :: jid:lserver()) -> inbox_write_res(). -clear_inbox(LUsername, LServer) -> - Res = execute_delete(LUsername, LServer), +-spec clear_inbox(HostType :: mongooseim:host_type(), + LUser :: jid:luser(), + LServer :: jid:lserver()) -> inbox_write_res(). +clear_inbox(HostType, LUser, LServer) -> + Res = execute_delete(HostType, LUser, LServer), check_result(Res). --spec clear_inbox(LServer :: jid:lserver()) -> inbox_write_res(). -clear_inbox(LServer) -> - Res = execute_delete(LServer), - check_result(Res). - - --spec get_entry_properties(jid:luser(), jid:lserver(), jid:literal_jid()) -> +-spec get_entry_properties(HosType :: mongooseim:host_type(), + InboxEntryKey :: mod_inbox:entry_key()) -> entry_properties(). -get_entry_properties(LUser, LServer, RemBareJID) -> - case execute_select_properties(LUser, LServer, RemBareJID) of +get_entry_properties(HostType, {LUser, LServer, RemBareJID}) -> + case execute_select_properties(HostType, LUser, LServer, RemBareJID) of {selected, []} -> []; {selected, [Selected]} -> decode_entries(Selected) end. --spec set_entry_properties(jid:luser(), jid:lserver(), jid:literal_jid(), entry_properties()) -> +-spec set_entry_properties(HostType :: mongooseim:host_type(), + InboxEntryKey :: mod_inbox:entry_key(), + entry_properties()) -> entry_properties() | {error, binary()}. -set_entry_properties(LUser, LServer, RemBareJID, Properties) -> - case set_entry_properties_rdbms(LUser, LServer, RemBareJID, Properties) of +set_entry_properties(HostType, {LUser, LServer, RemBareJID}, Properties) -> + case set_entry_properties_rdbms(HostType, LUser, LServer, RemBareJID, Properties) of {error, Msg} when is_list(Msg) -> {error, list_to_binary(Msg)}; {error, Msg} -> @@ -225,9 +219,12 @@ esc_int(Integer) -> %% Internal functions %% ---------------------------------------------------------------------- --spec get_inbox_rdbms(jid:luser(), jid:lserver(), mod_inbox:get_inbox_params()) -> +-spec get_inbox_rdbms(HostType :: mongooseim:host_type(), + LUser :: jid:luser(), + LServer :: jid:lserver(), + Params :: mod_inbox:get_inbox_params()) -> mongoose_rdbms:query_result(). -get_inbox_rdbms(LUser, LServer, Params) -> +get_inbox_rdbms(HostType, LUser, LServer, Params) -> QueryName = lookup_query_name(Params), case mongoose_rdbms:prepared(QueryName) of false -> @@ -238,9 +235,9 @@ get_inbox_rdbms(LUser, LServer, Params) -> ok end, Args = lookup_query_args(LServer, LUser, Params), - mongoose_rdbms:execute_successfully(LServer, QueryName, Args). + mongoose_rdbms:execute_successfully(HostType, QueryName, Args). -set_entry_properties_rdbms(LUser, LServer, RemBareJID, Properties) -> +set_entry_properties_rdbms(HostType, LUser, LServer, RemBareJID, Properties) -> QueryName = update_query_name(Properties), case mongoose_rdbms:prepared(QueryName) of false -> @@ -253,17 +250,17 @@ set_entry_properties_rdbms(LUser, LServer, RemBareJID, Properties) -> {atomic, TransactionResult} = mongoose_rdbms:sql_transaction( LServer, - fun() -> set_entry_properties_t(QueryName, LUser, LServer, RemBareJID, Properties) end), + fun() -> set_entry_properties_t(HostType, QueryName, LUser, LServer, RemBareJID, Properties) end), TransactionResult. --spec set_entry_properties_t(atom(), jid:luser(), jid:lserver(), jid:literal_jid(), +-spec set_entry_properties_t(mongooseim:host_type(), atom(), jid:luser(), jid:lserver(), jid:literal_jid(), entry_properties()) -> mongoose_rdbms:query_result(). -set_entry_properties_t(QueryName, LUser, LServer, RemBareJID, Properties) -> +set_entry_properties_t(HostType, QueryName, LUser, LServer, RemBareJID, Properties) -> Args = update_query_args(LUser, LServer, RemBareJID, Properties), - case mongoose_rdbms:execute_successfully(LServer, QueryName, Args) of + case mongoose_rdbms:execute_successfully(HostType, QueryName, Args) of {updated, 1} -> - execute_select_properties(LUser, LServer, RemBareJID); + execute_select_properties(HostType, LUser, LServer, RemBareJID); Other -> Other end. @@ -396,39 +393,36 @@ update_sql_part(muted_until, Val) when is_integer(Val) -> %% Query execution --spec execute_select_unread_count(jid:luser(), jid:lserver(), jid:literal_jid()) -> +-spec execute_select_unread_count(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid()) -> mongoose_rdbms:query_result(). -execute_select_unread_count(LUser, LServer, RemBareJID) -> - mongoose_rdbms:execute_successfully(LServer, inbox_select_unread_count, +execute_select_unread_count(HostType, LUser, LServer, RemBareJID) -> + mongoose_rdbms:execute_successfully(HostType, inbox_select_unread_count, [LUser, LServer, RemBareJID]). --spec execute_select_properties(jid:luser(), jid:lserver(), jid:literal_jid()) -> +-spec execute_select_properties(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid()) -> mongoose_rdbms:query_result(). -execute_select_properties(LUser, LServer, RemBareJID) -> - mongoose_rdbms:execute_successfully(LServer, inbox_select_properties, +execute_select_properties(HostType, LUser, LServer, RemBareJID) -> + mongoose_rdbms:execute_successfully(HostType, inbox_select_properties, [LUser, LServer, RemBareJID]). --spec execute_reset_unread(jid:luser(), jid:lserver(), jid:literal_jid(), binary() | undefined) -> +-spec execute_reset_unread(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid(), binary() | undefined) -> mongoose_rdbms:query_result(). -execute_reset_unread(LUser, LServer, RemBareJID, undefined) -> - mongoose_rdbms:execute_successfully(LServer, inbox_reset_unread, +execute_reset_unread(HostType, LUser, LServer, RemBareJID, undefined) -> + mongoose_rdbms:execute_successfully(HostType, inbox_reset_unread, [LUser, LServer, RemBareJID]); -execute_reset_unread(LUser, LServer, RemBareJID, MsgId) -> - mongoose_rdbms:execute_successfully(LServer, inbox_reset_unread_msg, +execute_reset_unread(HostType, LUser, LServer, RemBareJID, MsgId) -> + mongoose_rdbms:execute_successfully(HostType, inbox_reset_unread_msg, [LUser, LServer, RemBareJID, MsgId]). --spec execute_delete(jid:luser(), jid:lserver(), jid:literal_jid()) -> +-spec execute_delete(mongooseim:host_type(), + jid:luser(), jid:lserver(), jid:literal_jid()) -> mongoose_rdbms:query_result(). -execute_delete(LUser, LServer, RemBareJID) -> - mongoose_rdbms:execute_successfully(LServer, inbox_delete_row, [LUser, LServer, RemBareJID]). - --spec execute_delete(jid:luser(), jid:lserver()) -> mongoose_rdbms:query_result(). -execute_delete(LUser, LServer) -> - mongoose_rdbms:execute_successfully(LServer, inbox_delete, [LUser, LServer]). +execute_delete(HostType, LUser, LServer, RemBareJID) -> + mongoose_rdbms:execute_successfully(HostType, inbox_delete_row, [LUser, LServer, RemBareJID]). --spec execute_delete(jid:lserver()) -> mongoose_rdbms:query_result(). -execute_delete(LServer) -> - mongoose_rdbms:execute_successfully(LServer, inbox_delete_all, [LServer]). +-spec execute_delete(mongooseim:host_type(), jid:lserver(), jid:luser()) -> mongoose_rdbms:query_result(). +execute_delete(HostType, LUser, LServer) -> + mongoose_rdbms:execute_successfully(HostType, inbox_delete, [LUser, LServer]). -spec execute_delete_domain(HostType :: mongooseim:host_type(), LServer :: jid:lserver()) -> @@ -438,9 +432,9 @@ execute_delete_domain(HostType, LServer) -> %% Result processing --spec decode_row(host(), db_return()) -> inbox_res(). -decode_row(LServer, {Username, Content, Count, Timestamp, Archive, MutedUntil}) -> - Data = mongoose_rdbms:unescape_binary(LServer, Content), +-spec decode_row(mongooseim:host_type(), db_return()) -> inbox_res(). +decode_row(HostType, {Username, Content, Count, Timestamp, Archive, MutedUntil}) -> + Data = mongoose_rdbms:unescape_binary(HostType, Content), BCount = mongoose_rdbms:result_to_integer(Count), NumericTimestamp = mongoose_rdbms:result_to_integer(Timestamp), BoolArchive = mongoose_rdbms:to_bool(Archive), diff --git a/src/inbox/mod_inbox_utils.erl b/src/inbox/mod_inbox_utils.erl index bdbc1468c50..cdbff7280a6 100644 --- a/src/inbox/mod_inbox_utils.erl +++ b/src/inbox/mod_inbox_utils.erl @@ -17,12 +17,11 @@ %%%%%%%%%%%%%%%%%%% %% DB Operations shared by mod_inbox_one2one and mod_inbox_muclight -export([maybe_reset_unread_count/4, - reset_unread_count_to_zero/2, + reset_unread_count_to_zero/3, maybe_write_to_inbox/6, write_to_sender_inbox/5, write_to_receiver_inbox/5, - clear_inbox/1, - clear_inbox/2, + clear_inbox/3, get_reset_markers/1, if_chat_marker_get_id/2, has_chat_marker/1, @@ -35,83 +34,77 @@ maybe_binary_to_positive_integer/1, maybe_muted_until/2, binary_to_bool/1, - bool_to_binary/1 + bool_to_binary/1, + build_inbox_entry_key/2 ]). --spec maybe_reset_unread_count(Server :: host(), +-spec maybe_reset_unread_count(HostType :: mongooseim:host_type(), User :: jid:jid(), Remote :: jid:jid(), Packet :: exml:element()) -> ok. -maybe_reset_unread_count(Server, User, Remote, Packet) -> - ResetMarkers = get_reset_markers(Server), +maybe_reset_unread_count(HostType, User, Remote, Packet) -> + ResetMarkers = get_reset_markers(HostType), case if_chat_marker_get_id(Packet, ResetMarkers) of undefined -> ok; Id -> - reset_unread_count(User, Remote, Id) + reset_unread_count(HostType, User, Remote, Id) end. --spec reset_unread_count_to_zero(jid:jid(), jid:jid()) -> ok. -reset_unread_count_to_zero(#jid{luser = FromUsername, lserver = Server}, Remote) -> - ToBareJid = jid:to_binary(jid:to_bare(Remote)), - ok = mod_inbox_backend:reset_unread(FromUsername, Server, ToBareJid, undefined). +-spec reset_unread_count_to_zero(mongooseim:host_type(), jid:jid(), jid:jid()) -> ok. +reset_unread_count_to_zero(HostType, From, Remote) -> + InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(From, Remote), + ok = mod_inbox_backend:reset_unread(HostType, InboxEntryKey, undefined). --spec reset_unread_count(User :: jid:jid(), +-spec reset_unread_count(HostType ::mongooseim:host_type(), + From :: jid:jid(), Remote :: jid:jid(), MsgId :: id()) -> ok. -reset_unread_count(User, Remote, MsgId) -> - FromUsername = User#jid.luser, - Server = User#jid.lserver, - ToBareJid = jid:to_binary(jid:to_bare(Remote)), - ok = mod_inbox_backend:reset_unread(FromUsername, Server, ToBareJid, MsgId). +reset_unread_count(HostType, From, Remote, MsgId) -> + InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(From, Remote), + ok = mod_inbox_backend:reset_unread(HostType, InboxEntryKey, MsgId). --spec write_to_sender_inbox(Server :: host(), +-spec write_to_sender_inbox(HostType :: mongooseim:host_type(), Sender :: jid:jid(), Receiver :: jid:jid(), Packet :: exml:element(), Acc :: mongoose_acc:t()) -> ok. -write_to_sender_inbox(Server, Sender, Receiver, Packet, Acc) -> +write_to_sender_inbox(HostType, Sender, Receiver, Packet, Acc) -> MsgId = get_msg_id(Packet), Content = exml:to_binary(Packet), - Username = Sender#jid.luser, - RemoteBareJid = jid:to_binary(jid:to_bare(Receiver)), Timestamp = mongoose_acc:timestamp(Acc), %% no unread for a user because he writes new messages which assumes he read all previous messages. Count = 0, - ok = mod_inbox_backend:set_inbox(Username, Server, RemoteBareJid, - Content, Count, MsgId, Timestamp). + InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(Sender, Receiver), + ok = mod_inbox_backend:set_inbox(HostType, InboxEntryKey, Content, Count, MsgId, Timestamp). --spec write_to_receiver_inbox(Server :: host(), +-spec write_to_receiver_inbox(HostType :: mongooseim:host_type(), Sender :: jid:jid(), Receiver :: jid:jid(), Packet :: exml:element(), Acc :: mongoose_acc:t()) -> ok | {ok, integer()}. -write_to_receiver_inbox(Server, Sender, Receiver, Packet, Acc) -> +write_to_receiver_inbox(HostType, Sender, Receiver, Packet, Acc) -> MsgId = get_msg_id(Packet), Content = exml:to_binary(Packet), - Username = Receiver#jid.luser, - RemoteBareJid = jid:to_binary(jid:to_bare(Sender)), Timestamp = mongoose_acc:timestamp(Acc), - mod_inbox_backend:set_inbox_incr_unread(Username, Server, RemoteBareJid, + InboxEntryKey = mod_inbox_utils:build_inbox_entry_key(Receiver, Sender), + mod_inbox_backend:set_inbox_incr_unread(HostType, InboxEntryKey, Content, MsgId, Timestamp). --spec clear_inbox(User :: jid:luser(), Server :: host()) -> inbox_write_res(). -clear_inbox(User, Server) when is_binary(User) -> +-spec clear_inbox(HostType :: mongooseim:host_type(), + User :: jid:user(), + Server :: jid:server()) -> inbox_write_res(). +clear_inbox(HostType, User, Server) when is_binary(User) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), - ok = mod_inbox_backend:clear_inbox(LUser, LServer). - --spec clear_inbox(Server :: host()) -> inbox_write_res(). -clear_inbox(Server) -> - ok = mod_inbox_backend:clear_inbox(Server). - + ok = mod_inbox_backend:clear_inbox(HostType, LUser, LServer). %%%%%%%%%%%%%%%%%%% %% Helpers --spec get_reset_markers(Host :: host()) -> list(marker()). -get_reset_markers(Host) -> - gen_mod:get_module_opt(Host, mod_inbox, reset_markers, [<<"displayed">>]). +-spec get_reset_markers(HostType :: mongooseim:host_type()) -> list(marker()). +get_reset_markers(HostType) -> + gen_mod:get_module_opt(HostType, mod_inbox, reset_markers, [<<"displayed">>]). -spec if_chat_marker_get_id(Packet :: exml:element(), Markers :: list(marker())) -> undefined | id(). @@ -137,21 +130,21 @@ if_chat_marker_get_id(Packet, Marker) -> has_chat_marker(Packet) -> mongoose_chat_markers:has_chat_markers(Packet). --spec maybe_write_to_inbox(Host, User, Remote, Packet, Acc, WriteF) -> ok | {ok, integer()} when - Host :: host(), +-spec maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, WriteF) -> ok | {ok, integer()} when + HostType ::mongooseim:host_type(), User :: jid:jid(), Remote :: jid:jid(), Packet :: exml:element(), Acc :: mongoose_acc:t(), %% WriteF is write_to_receiver_inbox/5 or write_to_sender_inbox/5 WriteF :: fun(). -maybe_write_to_inbox(Host, User, Remote, Packet, Acc, WriteF) -> +maybe_write_to_inbox(HostType, User, Remote, Packet, Acc, WriteF) -> case has_chat_marker(Packet) of true -> ok; false -> Packet2 = fill_from_attr(Packet, User), - WriteF(Host, User, Remote, Packet2, Acc) + WriteF(HostType, User, Remote, Packet2, Acc) end. -spec get_msg_id(Msg :: exml:element()) -> binary(). @@ -172,13 +165,13 @@ fill_from_attr(Msg = #xmlel{attrs = Attrs}, From) -> wrapper_id() -> uuid:uuid_to_string(uuid:get_v4(), binary_standard). --spec get_option_write_aff_changes(Host :: host()) -> boolean(). -get_option_write_aff_changes(Host) -> - gen_mod:get_module_opt(Host, mod_inbox, aff_changes, true). +-spec get_option_write_aff_changes(HostType :: mongooseim:host_type()) -> boolean(). +get_option_write_aff_changes(HostType) -> + gen_mod:get_module_opt(HostType, mod_inbox, aff_changes, true). --spec get_option_remove_on_kicked(Host :: host()) -> boolean(). -get_option_remove_on_kicked(Host) -> - gen_mod:get_module_opt(Host, mod_inbox, remove_on_kicked, true). +-spec get_option_remove_on_kicked(HostType :: mongooseim:host_type()) -> boolean(). +get_option_remove_on_kicked(HostType) -> + gen_mod:get_module_opt(HostType, mod_inbox, remove_on_kicked, true). -spec reset_marker_to_bin(atom()) -> marker(). reset_marker_to_bin(displayed) -> <<"displayed">>; @@ -223,3 +216,8 @@ binary_to_bool(_) -> error. bool_to_binary(true) -> <<"true">>; bool_to_binary(false) -> <<"false">>; bool_to_binary(_) -> error. + +build_inbox_entry_key(FromJid, ToJid) -> + {LUser, LServer} = jid:to_lus(FromJid), + ToBareJid = jid:to_binary(jid:to_lus(ToJid)), + {LUser, LServer, ToBareJid}. From c9938dcdfdbe9ef323f3e5708a801ff89bc68e5e Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Tue, 1 Jun 2021 00:25:03 +0200 Subject: [PATCH 37/39] Fix inbox for muc and dynamic domains --- big_tests/tests/inbox_helper.erl | 2 +- src/inbox/mod_inbox_muc.erl | 4 +++- src/inbox/mod_inbox_muclight.erl | 10 +++++----- src/muc_light/mod_muc_light.erl | 1 + 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/big_tests/tests/inbox_helper.erl b/big_tests/tests/inbox_helper.erl index 57975b2e3e0..5cc26cd3323 100644 --- a/big_tests/tests/inbox_helper.erl +++ b/big_tests/tests/inbox_helper.erl @@ -139,7 +139,7 @@ inbox_modules() -> muclight_modules() -> [ - {mod_muc_light, [{host, subhost_pattern(muclight_config_domain())}, + {mod_muc_light, [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}, {backend, rdbms}]} ]. diff --git a/src/inbox/mod_inbox_muc.erl b/src/inbox/mod_inbox_muc.erl index 49ff9fd069c..d9aaf0d7512 100644 --- a/src/inbox/mod_inbox_muc.erl +++ b/src/inbox/mod_inbox_muc.erl @@ -120,4 +120,6 @@ write_to_receiver_inbox(Server, User, Remote, Packet, Acc) -> %% A local host can be used to fire hooks or write into database on this node. -spec is_local_xmpp_host(jid:lserver()) -> boolean(). is_local_xmpp_host(LServer) -> - lists:member(LServer, ?MYHOSTS). + F = fun mongoose_domain_api:get_domains_by_host_type/1, + ServedDomains = lists:flatmap(F, ?ALL_HOST_TYPES), + lists:member(LServer, ServedDomains). diff --git a/src/inbox/mod_inbox_muclight.erl b/src/inbox/mod_inbox_muclight.erl index ef44d602078..87a6c3a05cc 100644 --- a/src/inbox/mod_inbox_muclight.erl +++ b/src/inbox/mod_inbox_muclight.erl @@ -53,7 +53,7 @@ maybe_reset_unread_count(HostType, User, Room, Packet) -> Packet :: exml:element(), Acc :: mongoose_acc:t()) -> ok. maybe_handle_system_message(HostType, RoomOrUser, Receiver, Packet, Acc) -> - case is_system_message(RoomOrUser, Receiver, Packet) of + case is_system_message(HostType, RoomOrUser, Receiver, Packet) of true -> handle_system_message(HostType, RoomOrUser, Receiver, Packet, Acc); _ -> @@ -136,13 +136,13 @@ write_to_inbox(HostType, RoomUser, Remote, _Sender, Packet, Acc) -> %% @doc Check if sender is just 'roomname@muclight.domain' with no resource %% TODO: Replace sender domain check with namespace check - current logic won't handle all cases! --spec is_system_message(Sender :: jid:jid(), +-spec is_system_message(HostType :: mongooseim:host_type(), + Sender :: jid:jid(), Receiver :: jid:jid(), Packet :: exml:element()) -> boolean(). -is_system_message(Sender, Receiver, Packet) -> +is_system_message(HostType, Sender, Receiver, Packet) -> ReceiverDomain = Receiver#jid.lserver, - MUCLightDomain = gen_mod:get_module_opt_subhost(ReceiverDomain, mod_muc_light, - mod_muc_light:default_host()), + MUCLightDomain = mod_muc_light:server_host_to_muc_host(HostType, ReceiverDomain), case {Sender#jid.lserver, Sender#jid.lresource} of {MUCLightDomain, <<>>} -> true; diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index 9b626d9e8a3..07b80333aff 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -25,6 +25,7 @@ %% API -export([default_schema_definition/0, default_host/0]). +-export([server_host_to_muc_host/2]). -export([config_schema/1, default_config/1]). %% For Administration API From b3ab7d9f76c1d5c75a9a2cdd7767de6d9e8c5954 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Tue, 1 Jun 2021 00:26:14 +0200 Subject: [PATCH 38/39] Rearrange suites and enable muc/muclight for inbox --- big_tests/dynamic_domains.spec | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/big_tests/dynamic_domains.spec b/big_tests/dynamic_domains.spec index f4daf4e488d..e4304faee66 100644 --- a/big_tests/dynamic_domains.spec +++ b/big_tests/dynamic_domains.spec @@ -10,32 +10,22 @@ {include, "tests"}. {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}. {skip_cases, "tests", carboncopy_SUITE, [discovering_support], "at the moment mod_disco doesn't support dynamic domains"}. -{suites, "tests", mod_ping_SUITE}. -{skip_cases, "tests", mod_ping_SUITE, [disco], - "at the moment mod_disco doesn't support dynamic domains"}. +{suites, "tests", domain_isolation_SUITE}. {suites, "tests", inbox_SUITE}. {skip_cases, "tests", inbox_SUITE, [disco_service], "at the moment mod_disco doesn't support dynamic domains"}. {skip_cases, "tests", inbox_SUITE, [msg_sent_to_offline_user], "at the moment mod_offline doesn't support dynamic domains"}. -{skip_groups, "tests", inbox_SUITE, [muclight, muc], - "at the moment muc/muclight doesn't support dynamic domains"}. {suites, "tests", inbox_extensions_SUITE}. -{skip_groups, "tests", inbox_extensions_SUITE, [muclight], - "at the moment muclight doesn't support dynamic domains"}. +{suites, "tests", mam_SUITE}. {skip_cases, "tests", mam_SUITE, [muc_service_discovery, mam_service_discovery], "at the moment mod_disco doesn't support dynamic domains"}. @@ -43,6 +33,11 @@ [messages_filtered_when_prefs_default_policy_is_roster], "at the moment mod_roster doesn't support dynamic domains"}. +{suites, "tests", mod_ping_SUITE}. +{skip_cases, "tests", mod_ping_SUITE, [disco], + "at the moment mod_disco doesn't support dynamic domains"}. + +{suites, "tests", muc_SUITE}. {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"}. @@ -51,6 +46,7 @@ "at the moment S2S doesn't support dynamic domains " "(requires mod_register creating CT users)"}. +{suites, "tests", muc_light_SUITE}. {skip_cases, "tests", muc_light_SUITE, [disco_service, disco_features, @@ -70,6 +66,7 @@ no_roomname_in_schema_doesnt_break_disco_and_roster], "at the moment mod_roster doesn't support dynamic domains"}. +{suites, "tests", muc_light_legacy_SUITE}. {skip_cases, "tests", muc_light_legacy_SUITE, [disco_service, disco_features, From 1f63c7b5b1c42c5f6838204ee983a8c8ff7f4967 Mon Sep 17 00:00:00 2001 From: Nelson Vides Date: Tue, 1 Jun 2021 13:01:32 +0200 Subject: [PATCH 39/39] Inbox for muc as in for muclight --- src/inbox/mod_inbox_muc.erl | 24 ++++++++++-------------- src/mod_muc.erl | 1 + 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/inbox/mod_inbox_muc.erl b/src/inbox/mod_inbox_muc.erl index d9aaf0d7512..ab1300c282c 100644 --- a/src/inbox/mod_inbox_muc.erl +++ b/src/inbox/mod_inbox_muc.erl @@ -65,18 +65,22 @@ is_allowed_affiliation(_) -> true. To :: receiver_bare_user_jid(), Packet :: packet(). update_inbox_for_user(HostType, Direction, Room, To, Packet) -> - Host = To#jid.lserver, - case {is_local_xmpp_host(Host), Direction} of - {true, outgoing} -> - handle_outgoing_message(HostType, Room, To, Packet); - {true, incoming} -> - handle_incoming_message(HostType, Room, To, Packet); + ReceiverDomain = To#jid.lserver, + MucDomain = mod_muc:server_host_to_muc_host(HostType, ReceiverDomain), + case Room#jid.lserver of + MucDomain -> + handle_message(HostType, Room, To, Packet, Direction); _ -> %% We ignore inbox for users on the remote (s2s) hosts %% We ignore inbox for components (also known as services or bots) ok end. +handle_message(HostType, Room, To, Packet, outgoing) -> + handle_outgoing_message(HostType, Room, To, Packet); +handle_message(HostType, Room, To, Packet, incoming) -> + handle_incoming_message(HostType, Room, To, Packet). + -spec direction(From :: user_jid(), To :: user_jid()) -> incoming | outgoing. direction(From, To) -> case jid:are_bare_equal(From, To) of @@ -115,11 +119,3 @@ write_to_sender_inbox(Server, User, Remote, Packet, Acc) -> write_to_receiver_inbox(Server, User, Remote, Packet, Acc) -> mod_inbox_utils:write_to_receiver_inbox(Server, User, Remote, Packet, Acc). - -%% @doc Check, that the host is served by MongooseIM. -%% A local host can be used to fire hooks or write into database on this node. --spec is_local_xmpp_host(jid:lserver()) -> boolean(). -is_local_xmpp_host(LServer) -> - F = fun mongoose_domain_api:get_domains_by_host_type/1, - ServedDomains = lists:flatmap(F, ?ALL_HOST_TYPES), - lists:member(LServer, ServedDomains). diff --git a/src/mod_muc.erl b/src/mod_muc.erl index e2cd637e8c8..2dc1efc4a51 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -46,6 +46,7 @@ can_use_nick/4, room_jid_to_pid/1, default_host/0]). +-export([server_host_to_muc_host/2]). %% For testing purposes only -export([register_room/4]).