Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Same stanza id for peers #3376

Merged
merged 4 commits into from
Oct 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions big_tests/tests/mam_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
%% Tests
-export([no_elements/1,
only_stanzaid/1,
same_stanza_id/1,
muc_no_elements/1,
muc_only_stanzaid/1,
mam_service_discovery/1,
Expand Down Expand Up @@ -447,7 +448,8 @@ muc_configurable_archiveid_cases() ->

configurable_archiveid_cases() ->
[no_elements,
only_stanzaid
only_stanzaid,
same_stanza_id
].

muc_light_cases() ->
Expand Down Expand Up @@ -640,7 +642,7 @@ init_per_group(Group, ConfigIn) ->
end.

backup_module_opts(Module) ->
{{params_backup, Module}, rpc_apply(gen_mod, get_module_opts, [host_type(), mod_mam_muc])}.
{{params_backup, Module}, rpc_apply(gen_mod, get_module_opts, [host_type(), Module])}.

restore_module_opts(Module, Config) ->
ParamsB = proplists:get_value({params_backup, Module}, Config),
Expand Down Expand Up @@ -688,14 +690,14 @@ init_modules(rdbms, muc_light, Config) ->
init_modules(BT = riak_timed_yz_buckets, muc_light, Config) ->
dynamic_modules:start(host_type(), mod_muc_light, [{host, subhost_pattern(muc_light_helper:muc_host_pattern())}]),
init_modules(BT, generic, [{muc_domain, muc_light_helper:muc_host_pattern()} | Config]);
init_modules(BT = cassandra, muc_light, config) ->
init_modules_for_muc_light(BT, config);
init_modules(BT = cassandra, muc_light, Config) ->
init_modules_for_muc_light(BT, Config);
init_modules(cassandra, muc_all, Config) ->
init_module(host_type(), mod_mam_muc_cassandra_arch, []),
init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}]),
Config;
init_modules(BT = elasticsearch, muc_light, config) ->
init_modules_for_muc_light(BT, config);
init_modules(BT = elasticsearch, muc_light, Config) ->
init_modules_for_muc_light(BT, Config);
init_modules(elasticsearch, muc_all, Config) ->
init_module(host_type(), mod_mam_muc_elasticsearch_arch, []),
init_module(host_type(), mod_mam_muc, [{host, subhost_pattern(muc_domain(Config))}]),
Expand Down Expand Up @@ -976,6 +978,10 @@ init_per_testcase(C=only_stanzaid, Config) ->
rpc_apply(gen_mod, set_module_opts, [host_type(), mod_mam, []]),
Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]),
escalus:init_per_testcase(C, start_alice_room(Config1));
init_per_testcase(C=same_stanza_id, Config) ->
rpc_apply(gen_mod, set_module_opts, [host_type(), mod_mam, [same_mam_id_for_peers]]),
Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]),
escalus:init_per_testcase(C, start_alice_room(Config1));
init_per_testcase(C=muc_message_with_stanzaid, Config) ->
Config1 = escalus_fresh:create_users(Config, [{alice, 1}, {bob, 1}]),
escalus:init_per_testcase(C, start_alice_room(Config1));
Expand Down Expand Up @@ -1295,6 +1301,24 @@ no_elements(Config) ->
only_stanzaid(Config) ->
send_and_check_archive_elements(Config, false, true).

same_stanza_id(Config) ->
P = ?config(props, Config),
F = fun(Alice, Bob) ->
Body = <<"OH, HAI!">>,
Msg = escalus_stanza:chat_to(Bob, Body),
escalus:send(Alice, Msg),
mam_helper:wait_for_archive_size(Alice, 1),
escalus:send(Alice, stanza_archive_request(P, <<"q1">>)),
Result = wait_archive_respond(Alice),
[AliceCopyOfMessage] = respond_messages(Result),
AliceId = exml_query:path(AliceCopyOfMessage, [{element, <<"result">>}, {attr, <<"id">>}]),
%% ... and Bob receives the message
RecvMsg = escalus:wait_for_stanza(Bob),
BobId = exml_query:path(RecvMsg, [{element, <<"stanza-id">>}, {attr, <<"id">>}]),
?assert_equal(AliceId, BobId)
end,
escalus_fresh:story(Config, [{alice, 1}, {bob, 1}], F).

%% Querying the archive for messages
simple_archive_request(Config) ->
P = ?config(props, Config),
Expand Down
7 changes: 7 additions & 0 deletions doc/modules/mod_mam.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,13 @@ To disable archive for one-to-one messages please remove PM section or any PM re

When enabled, MAM will store groupchat messages in recipients' individual archives. **USE WITH CAUTION!** May increase archive size significantly. Disabling this option for existing installation will neither remove such messages from MAM storage, nor will filter out them from search results.

#### `modules.mod_mam_meta.pm.same_mam_id_for_peers`
* **Syntax:** boolean
* **Default:** `false`
* **Example:** `modules.mod_mam_meta.pm.same_mam_id_for_peers = true`

When enabled, MAM will set the same MAM ID for both sender and recipient. Note that this might not work with clients across federation, as the recipient might not implement the same retraction, nor the same IDs.

### Enable MUC message archive

Archive for MUC messages can be enabled in one of two ways:
Expand Down
15 changes: 11 additions & 4 deletions src/mam/mod_mam.erl
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@

%% UID
-import(mod_mam_utils,
[generate_message_id/1,
[get_or_generate_mam_id/1,
decode_compact_uuid/1]).

%% XML
Expand Down Expand Up @@ -515,8 +515,7 @@ handle_package(Dir, ReturnMessID,
OriginID = mod_mam_utils:get_origin_id(Packet),
case is_interesting(HostType, LocJID, RemJID, ArcID) of
true ->
TS = mongoose_acc:timestamp(Acc),
MessID = generate_message_id(TS),
MessID = get_or_generate_mam_id(Acc),
Params = #{message_id => MessID,
archive_id => ArcID,
local_jid => LocJID,
Expand All @@ -527,7 +526,7 @@ handle_package(Dir, ReturnMessID,
packet => Packet},
Result = archive_message(HostType, Params),
ExtMessId = return_external_message_id_if_ok(ReturnMessID, Result, MessID),
{ExtMessId, mongoose_acc:set(mam, mam_id, ExtMessId, Acc)};
{ExtMessId, return_acc_with_mam_id_if_configured(ExtMessId, HostType, Acc)};
false ->
{undefined, Acc}
end;
Expand All @@ -546,6 +545,14 @@ should_archive_if_groupchat(_, _) ->
return_external_message_id_if_ok(true, ok, MessID) -> mess_id_to_external_binary(MessID);
return_external_message_id_if_ok(_, _, _MessID) -> undefined.

return_acc_with_mam_id_if_configured(undefined, _, Acc) ->
Acc;
return_acc_with_mam_id_if_configured(ExtMessId, HostType, Acc) ->
case gen_mod:get_module_opt(HostType, ?MODULE, same_mam_id_for_peers, false) of
false -> mongoose_acc:set(mam, mam_id, ExtMessId, Acc);
true -> mongoose_acc:set_permanent(mam, mam_id, ExtMessId, Acc)
end.

is_interesting(LocJID, RemJID) ->
HostType = jid_to_host_type(LocJID),
ArcID = archive_id_int(HostType, LocJID),
Expand Down
5 changes: 3 additions & 2 deletions src/mam/mod_mam_meta.erl
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ config_items() ->
}.

pm_config_items() ->
#{<<"archive_groupchats">> => #option{type = boolean}}.
#{<<"archive_groupchats">> => #option{type = boolean},
<<"same_mam_id_for_peers">> => #option{type = boolean}}.

muc_config_items() ->
#{<<"host">> => #option{type = string,
Expand Down Expand Up @@ -207,7 +208,7 @@ filter_opts(Opts, ValidOpts) ->
%% the root section is enough.
-spec valid_core_mod_opts(module()) -> [atom()].
valid_core_mod_opts(mod_mam) ->
[no_stanzaid_element, archive_groupchats] ++ common_opts();
[no_stanzaid_element, archive_groupchats, same_mam_id_for_peers] ++ common_opts();
valid_core_mod_opts(mod_mam_muc) ->
[host] ++ common_opts().

Expand Down
13 changes: 12 additions & 1 deletion src/mam/mod_mam_utils.erl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
-export([maybe_microseconds/1]).

%% UID
-export([generate_message_id/1,
-export([get_or_generate_mam_id/1,
generate_message_id/1,
encode_compact_uuid/2,
decode_compact_uuid/1,
mess_id_to_external_binary/1,
Expand Down Expand Up @@ -165,6 +166,16 @@ maybe_microseconds(ISODateTime) ->
%% -----------------------------------------------------------------------
%% UID

-spec get_or_generate_mam_id(mongoose_acc:t()) -> integer().
get_or_generate_mam_id(Acc) ->
case mongoose_acc:get(mam, mam_id, undefined, Acc) of
undefined ->
CandidateStamp = mongoose_acc:timestamp(Acc),
generate_message_id(CandidateStamp);
ExtMessId ->
mod_mam_utils:external_binary_to_mess_id(ExtMessId)
end.

-spec generate_message_id(integer()) -> integer().
generate_message_id(CandidateStamp) ->
{ok, NodeId} = ejabberd_node_id:node_id(),
Expand Down
9 changes: 7 additions & 2 deletions test/config_parser_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2076,7 +2076,10 @@ mod_mam_meta_pm(_Config) ->
test_mod_mam_meta(T, M),
?eqf(M([{archive_groupchats, true}]),
T(#{<<"archive_groupchats">> => true})),
?errf(T(#{<<"archive_groupchats">> => <<"not really">>})).
?eqf(M([{same_mam_id_for_peers, true}]),
T(#{<<"same_mam_id_for_peers">> => true})),
?errf(T(#{<<"archive_groupchats">> => <<"not really">>})),
?errf(T(#{<<"same_mam_id_for_peers">> => <<"not really">>})).

mod_mam_meta_muc(_Config) ->
T = fun(Opts) -> #{<<"modules">> => #{<<"mod_mam_meta">> => #{<<"muc">> => Opts}}} end,
Expand All @@ -2088,7 +2091,9 @@ mod_mam_meta_muc(_Config) ->
T(#{<<"host">> => <<"muc.test">>})),
?errf(T(#{<<"host">> => <<"is this a host? no.">>})),
?errf(T(#{<<"host">> => [<<"invalid.sub@HOST@">>]})),
?errf(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})).
?errf(T(#{<<"host">> => [<<"invalid.sub.@HOST@.as.well">>]})),
?errf(T(#{<<"archive_groupchats">> => true})),
?errf(T(#{<<"same_mam_id_for_peers">> => true})).

test_mod_mam_meta(T, M) ->
?eqf(M([{backend, rdbms}]),
Expand Down