From fce7fe8558b36478381f3c95a7cae16ce6dd4be9 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 28 Jul 2021 18:29:19 +0200 Subject: [PATCH 001/106] PubSub: Bump default value for 'max_items' limit Bump the default value for mod_pubsub's 'max_items_node' option, which hard-limits the 'max_items' value requested by clients. These days, use cases such as microblogging or XEP-0402 may need a large number of items per node. Bumping the limit makes sure such functionality is properly supported with the default configuration. --- include/pubsub.hrl | 2 +- src/mod_pubsub.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/pubsub.hrl b/include/pubsub.hrl index 8496b95ac..da919e9e2 100644 --- a/include/pubsub.hrl +++ b/include/pubsub.hrl @@ -23,7 +23,7 @@ -define(ERR_EXTENDED(E, C), mod_pubsub:extended_error(E, C)). %% The actual limit can be configured with mod_pubsub's option max_items_node --define(MAXITEMS, 10). +-define(MAXITEMS, 1000). %% this is currently a hard limit. %% Would be nice to have it configurable. diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index fecb35341..e64b73083 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -4276,7 +4276,7 @@ mod_doc() -> #{value => "MaxItems", desc => ?T("Define the maximum number of items that can be " - "stored in a node. Default value is: '10'.")}}, + "stored in a node. Default value is: '1000'.")}}, {max_nodes_discoitems, #{value => "pos_integer() | infinity", desc => From 0c403c0f0eddc429d1929bb4117fed4f12a92695 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Sun, 19 Sep 2021 06:02:06 +0300 Subject: [PATCH 002/106] Fix SQL_UPSERT in mod_push_sql:store_session --- src/mod_push_sql.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_push_sql.erl b/src/mod_push_sql.erl index f89b904c2..c024a12d1 100644 --- a/src/mod_push_sql.erl +++ b/src/mod_push_sql.erl @@ -52,7 +52,7 @@ store_session(LUser, LServer, NowTS, PushJID, Node, XData) -> case ?SQL_UPSERT(LServer, "push_session", ["!username=%(LUser)s", "!server_host=%(LServer)s", - "!timestamp=%(TS)d", + "timestamp=%(TS)d", "!service=%(Service)s", "!node=%(Node)s", "xml=%(XML)s"]) of From 32cf44827d5bd47c1918250c86104f6331a4f15e Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Sun, 19 Sep 2021 05:59:12 +0300 Subject: [PATCH 003/106] Use INSERT ... ON CONFLICT in SQL_UPSERT for PostgreSQL >= 9.5 --- src/ejabberd_sql_pt.erl | 77 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl index 130228fe1..d131570f7 100644 --- a/src/ejabberd_sql_pt.erl +++ b/src/ejabberd_sql_pt.erl @@ -564,15 +564,23 @@ make_sql_upsert(Table, ParseRes, Pos) -> [] end, erl_syntax:fun_expr( - [erl_syntax:clause( - [erl_syntax:atom(pgsql), erl_syntax:variable("__Version")], - [erl_syntax:infix_expr( - erl_syntax:variable("__Version"), - erl_syntax:operator('>='), - erl_syntax:integer(90100))], - [make_sql_upsert_pgsql901(Table, ParseRes), - erl_syntax:atom(ok)])] ++ - MySqlReplace ++ + [erl_syntax:clause( + [erl_syntax:atom(pgsql), erl_syntax:variable("__Version")], + [erl_syntax:infix_expr( + erl_syntax:variable("__Version"), + erl_syntax:operator('>='), + erl_syntax:integer(90500))], + [make_sql_upsert_pgsql905(Table, ParseRes), + erl_syntax:atom(ok)]), + erl_syntax:clause( + [erl_syntax:atom(pgsql), erl_syntax:variable("__Version")], + [erl_syntax:infix_expr( + erl_syntax:variable("__Version"), + erl_syntax:operator('>='), + erl_syntax:integer(90100))], + [make_sql_upsert_pgsql901(Table, ParseRes), + erl_syntax:atom(ok)])] ++ + MySqlReplace ++ [erl_syntax:clause( [erl_syntax:underscore(), erl_syntax:underscore()], none, @@ -713,6 +721,57 @@ make_sql_upsert_pgsql901(Table, ParseRes0) -> erl_syntax:atom(sql_query_t), [Upsert]). +make_sql_upsert_pgsql905(Table, ParseRes0) -> + ParseRes = lists:map( + fun({"family", A2, A3}) -> {"\"family\"", A2, A3}; + (Other) -> Other + end, ParseRes0), + Vals = + lists:map( + fun({_Field, _, ST}) -> + ST + end, ParseRes), + Fields = + lists:map( + fun({Field, _, _ST}) -> + #state{'query' = [{str, Field}]} + end, ParseRes), + SPairs = + lists:flatmap( + fun({_Field, key, _ST}) -> + []; + ({_Field, {false}, _ST}) -> + []; + ({Field, {true}, ST}) -> + [ST#state{ + 'query' = [{str, Field}, {str, "="}] ++ ST#state.'query' + }] + end, ParseRes), + Set = join_states(SPairs, ", "), + KeyFields = + lists:flatmap( + fun({Field, key, _ST}) -> + [#state{'query' = [{str, Field}]}]; + ({_Field, _, _ST}) -> + [] + end, ParseRes), + State = + concat_states( + [#state{'query' = [{str, "INSERT INTO "}, {str, Table}, {str, "("}]}, + join_states(Fields, ", "), + #state{'query' = [{str, ") VALUES ("}]}, + join_states(Vals, ", "), + #state{'query' = [{str, ") ON CONFLICT ("}]}, + join_states(KeyFields, ", "), + #state{'query' = [{str, ") DO UPDATE SET "}]}, + Set + ]), + Upsert = make_sql_query(State), + erl_syntax:application( + erl_syntax:atom(ejabberd_sql), + erl_syntax:atom(sql_query_t), + [Upsert]). + check_upsert(ParseRes, Pos) -> Set = From bf068f56591bc84f360a8d600c157520e821425f Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Tue, 21 Sep 2021 12:10:00 +0300 Subject: [PATCH 004/106] Small optimization in mod_roster_sql:get_roster --- src/mod_roster_sql.erl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/mod_roster_sql.erl b/src/mod_roster_sql.erl index 76ddb29dd..ebfcde463 100644 --- a/src/mod_roster_sql.erl +++ b/src/mod_roster_sql.erl @@ -80,9 +80,10 @@ get_roster(LUser, LServer) -> [] end, GroupsDict = lists:foldl(fun({J, G}, Acc) -> - dict:append(J, G, Acc) + Gs = maps:get(J, Acc, []), + maps:put(J, [G | Gs], Acc) end, - dict:new(), JIDGroups), + maps:new(), JIDGroups), {ok, lists:flatmap( fun(I) -> case raw_to_record(LServer, I) of @@ -90,10 +91,7 @@ get_roster(LUser, LServer) -> error -> []; R -> SJID = jid:encode(R#roster.jid), - Groups = case dict:find(SJID, GroupsDict) of - {ok, Gs} -> Gs; - error -> [] - end, + Groups = maps:get(SJID, GroupsDict, []), [R#roster{groups = Groups}] end end, Items)}; From c9c5839da49d93007d7764aeb131d85df9a90472 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Tue, 21 Sep 2021 13:30:52 +0300 Subject: [PATCH 005/106] Fix roster_tests:get_items --- test/roster_tests.erl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/roster_tests.erl b/test/roster_tests.erl index a3b6009c9..3092b8cd8 100644 --- a/test/roster_tests.erl +++ b/test/roster_tests.erl @@ -224,13 +224,21 @@ get_items(Config, Version) -> sub_els = [#roster_query{ver = Version}]}) of #iq{type = result, sub_els = [#roster_query{ver = NewVersion, items = Items}]} -> - {NewVersion, Items}; + {NewVersion, normalize_items(Items)}; #iq{type = result, sub_els = []} -> {empty, []}; #iq{type = error} = Err -> xmpp:get_error(Err) end. +normalize_items(Items) -> + Items2 = + lists:map( + fun(I) -> + I#roster_item{groups = lists:sort(I#roster_item.groups)} + end, Items), + lists:sort(Items2). + get_item(Config, JID) -> case get_items(Config) of {_Ver, Items} when is_list(Items) -> From cfc393a12e3507c9ecb541b8f784c14ff3a9786e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 21 Sep 2021 12:16:30 +0200 Subject: [PATCH 006/106] When exporting mod_mam, MUC entries are assigned to the MUC service (#3680) --- src/ejd2sql.erl | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/ejd2sql.erl b/src/ejd2sql.erl index ad0cc5e88..427e13087 100644 --- a/src/ejd2sql.erl +++ b/src/ejd2sql.erl @@ -73,11 +73,16 @@ export(Server, Output) -> end, Modules), close_output(Output, IO). -export(Server, Output, Module1) -> - Module = case Module1 of - mod_pubsub -> pubsub_db; - _ -> Module1 - end, +export(Server, Output, mod_mam = M1) -> + MucServices = gen_mod:get_module_opt_hosts(Server, mod_muc), + [export2(MucService, Output, M1, M1) || MucService <- MucServices], + export2(Server, Output, M1, M1); +export(Server, Output, mod_pubsub = M1) -> + export2(Server, Output, M1, pubsub_db); +export(Server, Output, M1) -> + export2(Server, Output, M1, M1). + +export2(Server, Output, Module1, Module) -> SQLMod = gen_mod:db_mod(sql, Module), LServer = jid:nameprep(iolist_to_binary(Server)), IO = prepare_output(Output), From ceeba3eea12e4057b517efa0658d6805dad702ba Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 22 Sep 2021 12:11:25 +0200 Subject: [PATCH 007/106] Don't crash when exporting a module that is not enabled --- src/ejabberd_piefxis.erl | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl index 8dff06837..8d5c9101c 100644 --- a/src/ejabberd_piefxis.erl +++ b/src/ejabberd_piefxis.erl @@ -214,26 +214,30 @@ parse_scram_password(PassData) -> get_vcard(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), - case mod_vcard:get_vcard(LUser, LServer) of + try mod_vcard:get_vcard(LUser, LServer) of error -> []; Els -> Els + catch + error:{module_not_loaded, _, _} -> [] end. -spec get_offline(binary(), binary()) -> [xmlel()]. get_offline(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), - case mod_offline:get_offline_els(LUser, LServer) of + try mod_offline:get_offline_els(LUser, LServer) of [] -> []; Els -> NewEls = lists:map(fun xmpp:encode/1, Els), [#xmlel{name = <<"offline-messages">>, children = NewEls}] + catch + error:{module_not_loaded, _, _} -> [] end. -spec get_privacy(binary(), binary()) -> [xmlel()]. get_privacy(User, Server) -> - case mod_privacy:get_user_lists(User, Server) of + try mod_privacy:get_user_lists(User, Server) of {ok, #privacy{default = Default, lists = [_|_] = Lists}} -> XLists = lists:map( @@ -246,12 +250,14 @@ get_privacy(User, Server) -> [xmpp:encode(#privacy_query{default = Default, lists = XLists})]; _ -> [] + catch + error:{module_not_loaded, _, _} -> [] end. -spec get_roster(binary(), binary()) -> [xmlel()]. get_roster(User, Server) -> JID = jid:make(User, Server), - case mod_roster:get_roster(User, Server) of + try mod_roster:get_roster(User, Server) of [_|_] = Items -> Subs = lists:flatmap( @@ -278,15 +284,19 @@ get_roster(User, Server) -> [xmpp:encode(#roster_query{items = Rs}) | Subs]; _ -> [] + catch + error:{module_not_loaded, _, _} -> [] end. -spec get_private(binary(), binary()) -> [xmlel()]. get_private(User, Server) -> - case mod_private:get_data(User, Server) of + try mod_private:get_data(User, Server) of [_|_] = Els -> [xmpp:encode(#private{sub_els = Els})]; _ -> [] + catch + error:{module_not_loaded, _, _} -> [] end. process(#state{xml_stream_state = XMLStreamState, fd = Fd} = State) -> From af4b49f720211b241c57ee1365449a13dee74076 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 22 Sep 2021 12:53:18 +0200 Subject: [PATCH 008/106] Update export/import of scram password to XEP-0227 1.1 (#3676) --- src/ejabberd_piefxis.erl | 106 ++++++++++++++++++++++++++------------- 1 file changed, 71 insertions(+), 35 deletions(-) diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl index 8d5c9101c..d62efb300 100644 --- a/src/ejabberd_piefxis.erl +++ b/src/ejabberd_piefxis.erl @@ -24,17 +24,15 @@ %%%---------------------------------------------------------------------- %%% Not implemented: +%%% - PEP nodes export/import +%%% - message archives export/import %%% - write mod_piefxis with ejabberdctl commands -%%% - Export from mod_offline_sql.erl -%%% - Export from mod_private_sql.erl -%%% - XEP-227: 6. Security Considerations %%% - Other schemas of XInclude are not tested, and may not be imported correctly. %%% - If a host has many users, split that host in XML files with 50 users each. -%%%% Headers -module(ejabberd_piefxis). --protocol({xep, 227, '1.0'}). +-protocol({xep, 227, '1.1'}). -export([import_file/1, export_server/1, export_host/2]). @@ -166,33 +164,66 @@ export_users([], _Server, _Fd) -> export_user(User, Server, Fd) -> Password = ejabberd_auth:get_password_s(User, Server), LServer = jid:nameprep(Server), - Pass = case ejabberd_auth:password_format(LServer) of - scram -> format_scram_password(Password); - _ -> Password + {PassPlain, PassScram} = case ejabberd_auth:password_format(LServer) of + scram -> {[], [format_scram_password(Password)]}; + _ -> {[{<<"password">>, Password}], []} end, - Els = get_offline(User, Server) ++ + Els = + PassScram ++ + get_offline(User, Server) ++ get_vcard(User, Server) ++ get_privacy(User, Server) ++ get_roster(User, Server) ++ get_private(User, Server), print(Fd, fxml:element_to_binary( #xmlel{name = <<"user">>, - attrs = [{<<"name">>, User}, - {<<"password">>, Pass}], + attrs = [{<<"name">>, User} | PassPlain], children = Els})). format_scram_password(#scram{hash = Hash, storedkey = StoredKey, serverkey = ServerKey, salt = Salt, iterationcount = IterationCount}) -> - StoredKeyB64 = base64:encode(StoredKey), - ServerKeyB64 = base64:encode(ServerKey), - SaltB64 = base64:encode(Salt), - IterationCountBin = (integer_to_binary(IterationCount)), - Hash2 = case Hash of - sha -> <<>>; - sha256 -> <<"sha256,">>; - sha512 -> <<"sha512,">> - end, - <<"scram:", Hash2/binary, StoredKeyB64/binary, ",", ServerKeyB64/binary, ",", SaltB64/binary, ",", IterationCountBin/binary>>. + StoredKeyB64 = base64:encode(StoredKey), + ServerKeyB64 = base64:encode(ServerKey), + SaltB64 = base64:encode(Salt), + IterationCountBin = (integer_to_binary(IterationCount)), + MechanismB = case Hash of + sha -> <<"SCRAM-SHA-1">>; + sha256 -> <<"SCRAM-SHA-256">>; + sha512 -> <<"SCRAM-SHA-512">> + end, + Children = + [ + #xmlel{name = <<"iter-count">>, + children = [{xmlcdata, IterationCountBin}]}, + #xmlel{name = <<"salt">>, + children = [{xmlcdata, SaltB64}]}, + #xmlel{name = <<"server-key">>, + children = [{xmlcdata, ServerKeyB64}]}, + #xmlel{name = <<"stored-key">>, + children = [{xmlcdata, StoredKeyB64}]} + ], + #xmlel{name = <<"scram-credentials">>, + attrs = [{<<"xmlns">>, <>}, + {<<"mechanism">>, MechanismB}], + children = Children}. + +parse_scram_password(#xmlel{attrs = Attrs} = El) -> + Hash = case fxml:get_attr_s(<<"mechanism">>, Attrs) of + <<"SCRAM-SHA-1">> -> sha; + <<"SCRAM-SHA-256">> -> sha256; + <<"SCRAM-SHA-512">> -> sha512 + end, + StoredKeyB64 = fxml:get_path_s(El, [{elem, <<"stored-key">>}, cdata]), + ServerKeyB64 = fxml:get_path_s(El, [{elem, <<"server-key">>}, cdata]), + IterationCountBin = fxml:get_path_s(El, [{elem, <<"iter-count">>}, cdata]), + SaltB64 = fxml:get_path_s(El, [{elem, <<"salt">>}, cdata]), + #scram{ + storedkey = base64:decode(StoredKeyB64), + serverkey = base64:decode(ServerKeyB64), + salt = base64:decode(SaltB64), + hash = Hash, + iterationcount = (binary_to_integer(IterationCountBin)) + }; parse_scram_password(PassData) -> Split = binary:split(PassData, <<",">>, [global]), @@ -408,21 +439,10 @@ process_users([_|Els], State) -> process_users([], State) -> {ok, State}. -process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els}, +process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els} = El, #state{server = LServer} = State) -> Name = fxml:get_attr_s(<<"name">>, Attrs), - Password = fxml:get_attr_s(<<"password">>, Attrs), - PasswordFormat = ejabberd_auth:password_format(LServer), - Pass = case PasswordFormat of - scram -> - case Password of - <<"scram:", PassData/binary>> -> - parse_scram_password(PassData); - P -> P - end; - _ -> Password - end, - + Pass = process_password(El, LServer), case jid:nodeprep(Name) of error -> stop("Invalid 'user': ~ts", [Name]); @@ -430,13 +450,29 @@ process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els}, case ejabberd_auth:try_register(LUser, LServer, Pass) of ok -> process_user_els(Els, State#state{user = LUser}); - {error, invalid_password} when (Password == <<>>) -> + {error, invalid_password} when (Pass == <<>>) -> process_user_els(Els, State#state{user = LUser}); {error, Err} -> stop("Failed to create user '~ts': ~p", [Name, Err]) end end. +process_password(#xmlel{name = <<"user">>, attrs = Attrs} = El, LServer) -> + {PassPlain, PassOldScram} = case fxml:get_attr_s(<<"password">>, Attrs) of + <<"scram:", PassData/binary>> -> {<<"">>, PassData}; + P -> {P, false} + end, + ScramCred = fxml:get_subtag(El, <<"scram-credentials">>), + PasswordFormat = ejabberd_auth:password_format(LServer), + case {PassPlain, PassOldScram, ScramCred, PasswordFormat} of + {PassPlain, false, false, plain} -> PassPlain; + {<<"">>, false, ScramCred, plain} -> parse_scram_password(ScramCred); + {<<"">>, PassOldScram, false, plain} -> parse_scram_password(PassOldScram); + {PassPlain, false, false, scram} -> PassPlain; + {<<"">>, false, ScramCred, scram} -> parse_scram_password(ScramCred); + {<<"">>, PassOldScram, false, scram} -> parse_scram_password(PassOldScram) + end. + process_user_els([#xmlel{} = El|Els], State) -> case process_user_el(El, State) of {ok, NewState} -> From f74a715713e90d34d07a4070f839a3d828430b36 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 27 Sep 2021 12:05:49 +0200 Subject: [PATCH 009/106] Add indexes from 95fa43aa to the old-to-new schema update function --- src/mod_admin_update_sql.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_admin_update_sql.erl b/src/mod_admin_update_sql.erl index 4e932fe83..02beb4bf8 100644 --- a/src/mod_admin_update_sql.erl +++ b/src/mod_admin_update_sql.erl @@ -140,6 +140,7 @@ update_tables(State) -> add_sh_column(State, "sr_group"), add_pkey(State, "sr_group", ["server_host", "name"]), + create_unique_index(State, "sr_group", "i_sr_group_sh_name", ["server_host", "name"]), drop_sh_default(State, "sr_group"), add_sh_column(State, "sr_user"), @@ -147,6 +148,7 @@ update_tables(State) -> drop_index(State, "i_sr_user_jid"), drop_index(State, "i_sr_user_grp"), add_pkey(State, "sr_user", ["server_host", "jid", "grp"]), + create_unique_index(State, "sr_user", "i_sr_user_sh_jid_grp", ["server_host", "jid", "grp"]), create_index(State, "sr_user", "i_sr_user_sh_jid", ["server_host", "jid"]), create_index(State, "sr_user", "i_sr_user_sh_grp", ["server_host", "grp"]), drop_sh_default(State, "sr_user"), From dd359a7328be09f5244341afcbf408d895977016 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 27 Sep 2021 15:49:44 +0200 Subject: [PATCH 010/106] Add indexes from 95fa43aa to the old-to-new MySQL schema update script --- sql/mysql.old-to-new.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sql/mysql.old-to-new.sql b/sql/mysql.old-to-new.sql index 59c9befe2..9614d55a8 100644 --- a/sql/mysql.old-to-new.sql +++ b/sql/mysql.old-to-new.sql @@ -77,6 +77,7 @@ BEGIN ALTER TABLE `last` ADD PRIMARY KEY (`server_host`, `username`); ALTER TABLE `sr_group` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `name`; ALTER TABLE `sr_group` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `sr_group` ADD UNIQUE INDEX `i_sr_group_sh_name` (`server_host`, `name`); ALTER TABLE `sr_group` ADD PRIMARY KEY (`server_host`, `name`); ALTER TABLE `muc_registered` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `host`; ALTER TABLE `muc_registered` ALTER COLUMN `server_host` DROP DEFAULT; @@ -99,6 +100,7 @@ BEGIN ALTER TABLE `sr_user` DROP INDEX `i_sr_user_jid_group`; ALTER TABLE `sr_user` ADD COLUMN `server_host` VARCHAR (191) COLLATE `utf8mb4_unicode_ci` NOT NULL DEFAULT @DEFAULT_HOST AFTER `jid`; ALTER TABLE `sr_user` ALTER COLUMN `server_host` DROP DEFAULT; + ALTER TABLE `sr_user` ADD UNIQUE INDEX `i_sr_user_sh_jid_group` (`server_host`, `jid`, `grp`); ALTER TABLE `sr_user` ADD INDEX `i_sr_user_sh_jid` (`server_host`, `jid`); ALTER TABLE `sr_user` ADD INDEX `i_sr_user_sh_grp` (`server_host`, `grp`); ALTER TABLE `sr_user` ADD PRIMARY KEY (`server_host`, `jid`, `grp`); From d205e6ff1ff32c075afea8b3189edc5f3c0c1ab0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 27 Sep 2021 17:05:42 +0200 Subject: [PATCH 011/106] Support old scram records before xmpp's 651050f9 and ejabberd's e5cad9be6 (#3680) --- src/ejabberd_auth_sql.erl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index 1f7106c59..e51bf276c 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -299,6 +299,20 @@ export(_Server) -> ["username=%(LUser)s", "server_host=%(LServer)s", "password=%(Password)s"])]; + (Host, #passwd{us = {LUser, LServer}, + password = {scram, StoredKey1, ServerKey, Salt, IterationCount}}) + when LServer == Host -> + Hash = sha, + StoredKey = scram_hash_encode(Hash, StoredKey1), + [?SQL("delete from users where username=%(LUser)s and %(LServer)H;"), + ?SQL_INSERT( + "users", + ["username=%(LUser)s", + "server_host=%(LServer)s", + "password=%(StoredKey)s", + "serverkey=%(ServerKey)s", + "salt=%(Salt)s", + "iterationcount=%(IterationCount)d"])]; (Host, #passwd{us = {LUser, LServer}, password = #scram{} = Scram}) when LServer == Host -> StoredKey = scram_hash_encode(Scram#scram.hash, Scram#scram.storedkey), From 85408662ffb26bc92a65ae027e773618cf24e313 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 4 Oct 2021 11:14:56 +0200 Subject: [PATCH 012/106] Use mod_register to format some common error messages --- src/mod_register_web.erl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index 0e216c81c..afb8f9f24 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -579,12 +579,8 @@ get_error_text({error, exists}) -> ?T("The account already exists"); get_error_text({error, password_incorrect}) -> ?T("Incorrect password"); -get_error_text({error, invalid_jid}) -> - ?T("The username is not valid"); get_error_text({error, host_unknown}) -> ?T("Host unknown"); -get_error_text({error, not_allowed}) -> - ?T("Not allowed"); get_error_text({error, account_doesnt_exist}) -> ?T("Account doesn't exist"); get_error_text({error, account_exists}) -> @@ -594,7 +590,9 @@ get_error_text({error, password_not_changed}) -> get_error_text({error, passwords_not_identical}) -> ?T("The passwords are different"); get_error_text({error, wrong_parameters}) -> - ?T("Wrong parameters in the web formulary"). + ?T("Wrong parameters in the web formulary"); +get_error_text({error, Why}) -> + mod_register:format_error(Why). mod_options(_) -> []. From 595b01601939074d28c1bb36383a7427cc459a7e Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 4 Oct 2021 11:24:24 +0200 Subject: [PATCH 013/106] Use mod_register in web register form, so its restrictions are used (#3688) --- src/mod_register_web.erl | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index afb8f9f24..1ac3b58dc 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -85,7 +85,7 @@ process([Section], process([<<"new">>], #request{method = 'POST', q = Q, ip = {Ip, _Port}, lang = Lang, host = _HTTPHost}) -> - case form_new_post(Q) of + case form_new_post(Q, Ip) of {success, ok, {Username, Host, _Password}} -> Jid = jid:make(Username, Host), mod_register:send_registration_notifications(?MODULE, Jid, Ip), @@ -290,10 +290,10 @@ form_new_get2(Host, Lang, CaptchaEls) -> %%% Formulary new POST %%%---------------------------------------------------------------------- -form_new_post(Q) -> +form_new_post(Q, Ip) -> case catch get_register_parameters(Q) of [Username, Host, Password, Password, Id, Key] -> - form_new_post(Username, Host, Password, {Id, Key}); + form_new_post(Username, Host, Password, {Id, Key}, Ip); [_Username, _Host, _Password, _Password2, false, false] -> {error, passwords_not_identical}; [_Username, _Host, _Password, _Password2, Id, Key] -> @@ -312,13 +312,12 @@ get_register_parameters(Q) -> [<<"username">>, <<"host">>, <<"password">>, <<"password2">>, <<"id">>, <<"key">>]). -form_new_post(Username, Host, Password, - {false, false}) -> - register_account(Username, Host, Password); -form_new_post(Username, Host, Password, {Id, Key}) -> +form_new_post(Username, Host, Password, {false, false}, Ip) -> + register_account(Username, Host, Password, Ip); +form_new_post(Username, Host, Password, {Id, Key}, Ip) -> case ejabberd_captcha:check_captcha(Id, Key) of captcha_valid -> - register_account(Username, Host, Password); + register_account(Username, Host, Password, Ip); captcha_non_valid -> {error, captcha_non_valid}; captcha_not_found -> {error, captcha_non_valid} end. @@ -502,11 +501,11 @@ form_del_get(Host, Lang) -> {<<"Content-Type">>, <<"text/html">>}], ejabberd_web:make_xhtml(HeadEls, Els)}. -%% @spec(Username, Host, Password) -> {success, ok, {Username, Host, Password} | +%% @spec(Username, Host, Password, Ip) -> {success, ok, {Username, Host, Password} | %% {success, exists, {Username, Host, Password}} | %% {error, not_allowed} | %% {error, invalid_jid} -register_account(Username, Host, Password) -> +register_account(Username, Host, Password, Ip) -> try mod_register_opt:access(Host) of Access -> case jid:make(Username, Host) of @@ -514,16 +513,15 @@ register_account(Username, Host, Password) -> JID -> case acl:match_rule(Host, Access, JID) of deny -> {error, not_allowed}; - allow -> register_account2(Username, Host, Password) + allow -> register_account2(Username, Host, Password, Ip) end end catch _:{module_not_loaded, mod_register, _Host} -> {error, host_unknown} end. -register_account2(Username, Host, Password) -> - case ejabberd_auth:try_register(Username, Host, - Password) +register_account2(Username, Host, Password, Ip) -> + case mod_register:try_register(Username, Host, Password, Ip) of ok -> {success, ok, {Username, Host, Password}}; From 1377dcf6d2bf7e51856b9f221fb7279d6c4689ef Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Wed, 6 Oct 2021 01:13:11 +0200 Subject: [PATCH 014/106] mod_mam: Declare XEP-0441 support --- src/mod_mam.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index abb2333cc..9bf154f58 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -28,6 +28,7 @@ -protocol({xep, 313, '0.6.1'}). -protocol({xep, 334, '0.2'}). -protocol({xep, 359, '0.5.0'}). +-protocol({xep, 441, '0.2.0'}). -behaviour(gen_mod). From 964cb3aacab94fcf88f4a445fcf9bc6b414b35fc Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 7 Oct 2021 16:23:11 +0200 Subject: [PATCH 015/106] Github Actions: use MD5 pass encryption to support PostgreSQL 14 (#3691) --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d498760be..7cafdfe67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,6 +52,8 @@ jobs: - name: Prepare databases run: | + sudo sed -i 's|#password_encryption.*|password_encryption = md5|g' /etc/postgresql/14/main/postgresql.conf + sudo sed -i 's|scram-sha-256|md5|g' /etc/postgresql/14/main/pg_hba.conf sudo systemctl start mysql.service sudo systemctl start postgresql.service mysql -u root -proot -e "CREATE USER 'ejabberd_test'@'localhost' From 6b0fa44386709151bb48ad9284a060888b7c5c54 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 9 Oct 2021 10:56:30 +0200 Subject: [PATCH 016/106] Update 'xmpp' dependency Fixes #3529. --- mix.exs | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 0b5d40b09..021de5e2b 100644 --- a/mix.exs +++ b/mix.exs @@ -124,7 +124,7 @@ defmodule Ejabberd.MixProject do {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, {:stun, "~> 1.0"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "e943c0285aa85e3cbd4bfb9259f6b7de32b00395", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "3f18c9e6b239c3a8a4d82dc478b26565037b7247", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index a70ccfba3..1f1710bf9 100644 --- a/rebar.config +++ b/rebar.config @@ -59,7 +59,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.27"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.44"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "e943c0285aa85e3cbd4bfb9259f6b7de32b00395"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "3f18c9e6b239c3a8a4d82dc478b26565037b7247"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.12"}}} ]}. From db920b7d7b958a0c57fcde23102954a831981760 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 13 Oct 2021 19:38:31 +0200 Subject: [PATCH 017/106] Only install some files when option enabled in configure (#3633) --- Makefile.in | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 6549665f7..45e223ca2 100644 --- a/Makefile.in +++ b/Makefile.in @@ -249,7 +249,15 @@ $(call TO_DEST,priv/bin/captcha.sh): tools/captcha.sh $(call TO_DEST,priv/bin) $(call TO_DEST,priv/lua/redis_sm.lua): priv/lua/redis_sm.lua $(call TO_DEST,priv/lua) $(INSTALL) -m 644 $< $@ -copy-files-sub2: $(call TO_DEST,$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh priv/sql/lite.sql priv/sql/lite.new.sql priv/lua/redis_sm.lua) +ifeq (@sqlite@,true) +SQLITE_FILES = priv/sql/lite.sql priv/sql/lite.new.sql +endif + +ifeq (@redis@,true) +REDIS_FILES = priv/lua/redis_sm.lua +endif + +copy-files-sub2: $(call TO_DEST,$(DEPS_FILES) $(MAIN_FILES) priv/bin/captcha.sh $(SQLITE_FILES) $(REDIS_FILES)) .PHONY: $(call TO_DEST,$(DEPS_FILES) $(MAIN_DIRS) $(DEPS_DIRS)) From d3aa329769c09bb0614948358db2d5f98066c32a Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 14 Oct 2021 13:31:46 +0200 Subject: [PATCH 018/106] Fix vcard_search definition in pgsql new schema (thanks to Stu Tomlinson)(#3695) How to update an existing database: ALTER TABLE vcard_search DROP CONSTRAINT vcard_search_pkey; ALTER TABLE vcard_search ADD PRIMARY KEY (server_host, lusername); --- sql/pg.new.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/pg.new.sql b/sql/pg.new.sql index b3473a1a0..6a0743f23 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -311,7 +311,7 @@ CREATE TABLE vcard_search ( lorgname text NOT NULL, orgunit text NOT NULL, lorgunit text NOT NULL, - PRIMARY KEY (server_host, username) + PRIMARY KEY (server_host, lusername) ); CREATE INDEX i_vcard_search_sh_lfn ON vcard_search(server_host, lfn); From c2db0034311286035ce8ad6ee5f32101a02dae5a Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 15 Oct 2021 14:49:30 +0200 Subject: [PATCH 019/106] When tests fail, show also error.log as it may have meaningful content --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7cafdfe67..634bd2a10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -160,6 +160,10 @@ jobs: if: failure() run: find logs/ -name ejabberd.log -exec cat '{}' ';' + - name: View error.log + if: failure() + run: find logs/ -name error.log -exec cat '{}' ';' + - name: View exunit.log if: failure() run: find logs/ -name exunit.log -exec cat '{}' ';' From 4d384b6bf58f8bf68531cee35c3cb394d63e73a3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 15 Oct 2021 14:50:51 +0200 Subject: [PATCH 020/106] If tests succeed, test also new SQL schema --- .github/workflows/ci.yml | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 634bd2a10..7baa5790f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -181,6 +181,53 @@ jobs: "payload":{"build_num":$GITHUB_RUN_ID, "status":"done"}}' + - name: Prepare new schema + run: | + [[ -d logs ]] && rm -rf logs + [[ -d _build/test/logs ]] && rm -rf _build/test/logs + mysql -u root -proot -e "DROP DATABASE ejabberd_test;" + sudo -u postgres psql -c "DROP DATABASE ejabberd_test;" + mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" + mysql -u root -proot -e "GRANT ALL ON ejabberd_test.* + TO 'ejabberd_test'@'localhost';" + mysql -u root -proot ejabberd_test < sql/mysql.new.sql + sudo -u postgres psql -c "CREATE DATABASE ejabberd_test;" + sudo -u postgres psql ejabberd_test -f sql/pg.new.sql + sudo -u postgres psql -c "GRANT ALL PRIVILEGES + ON DATABASE ejabberd_test TO ejabberd_test;" + sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL + TABLES IN SCHEMA public + TO ejabberd_test;" + sudo -u postgres psql ejabberd_test -c "GRANT ALL PRIVILEGES ON ALL + SEQUENCES IN SCHEMA public + TO ejabberd_test;" + sudo sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl + - run: CT_BACKENDS=mysql,pgsql make test + - name: Check results + if: always() + run: | + [[ -d _build ]] && ln -s _build/test/logs/ logs \ + || ln dialyzer/error.log logs/dialyzer.log + ln `find logs/ -name suite.log` logs/suite.log + grep 'TEST COMPLETE' logs/suite.log + grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log + test $(find logs/ -empty -name error.log) + - name: View full suite.log + run: cat logs/suite.log + - name: View suite.log failures + if: failure() + run: cat logs/suite.log | awk + 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' + - name: View full ejabberd.log + if: failure() + run: find logs/ -name ejabberd.log -exec cat '{}' ';' + - name: View error.log + if: failure() + run: find logs/ -name error.log -exec cat '{}' ';' + - name: View exunit.log + if: failure() + run: find logs/ -name exunit.log -exec cat '{}' ';' + binaries: name: Binaries needs: [tests] From 54c23a65db7491a9cc51eac3f89734c161603481 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 21 Oct 2021 12:44:51 +0200 Subject: [PATCH 021/106] Fix create_room_with_opts when using SQL storage (#3700) --- src/mod_muc_admin.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 2abeee45c..ce4665d7e 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -710,7 +710,7 @@ create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) -> maybe_store_room(ServerHost, Host, Name, RoomOpts) -> case proplists:get_bool(persistent, RoomOpts) of true -> - {atomic, ok} = mod_muc:store_room(ServerHost, Host, Name, RoomOpts), + {atomic, _} = mod_muc:store_room(ServerHost, Host, Name, RoomOpts), ok; false -> ok From 5f3c8dcca429893e85d92b47d9f84054e2b12fed Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 6 Oct 2021 12:18:08 +0200 Subject: [PATCH 022/106] Use the configured user in systemd's ejabberd.service --- Makefile.in | 3 ++- ejabberd.service.template | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Makefile.in b/Makefile.in index 45e223ca2..ecee982e3 100644 --- a/Makefile.in +++ b/Makefile.in @@ -306,7 +306,8 @@ install: copy-files chmod 755 ejabberd.init # # Service script - $(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*g" ejabberd.service.template \ + $(SED) -e "s*@ctlscriptpath@*$(SBINDIR)*g" \ + -e "s*@installuser@*$(INIT_USER)*g" ejabberd.service.template \ > ejabberd.service chmod 644 ejabberd.service # diff --git a/ejabberd.service.template b/ejabberd.service.template index c779ea031..685a104d0 100644 --- a/ejabberd.service.template +++ b/ejabberd.service.template @@ -4,8 +4,8 @@ After=network.target [Service] Type=notify -User=ejabberd -Group=ejabberd +User=@installuser@ +Group=@installuser@ LimitNOFILE=65536 Restart=on-failure RestartSec=5 From 5462a26a0a337217f4875b5471985e2587b3e7f3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 6 Oct 2021 12:28:10 +0200 Subject: [PATCH 023/106] Remove obsolete cookie preparation in spool dir, it's now stored in HOME --- Makefile.in | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Makefile.in b/Makefile.in index ecee982e3..fc9718006 100644 --- a/Makefile.in +++ b/Makefile.in @@ -71,9 +71,6 @@ SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd # /var/lock/ejabberdctl CTLLOCKDIR = $(DESTDIR)@localstatedir@/lock/ejabberdctl -# /var/lib/ejabberd/.erlang.cookie -COOKIEFILE = $(SPOOLDIR)/.erlang.cookie - # /var/log/ejabberd/ LOGDIR = $(DESTDIR)@localstatedir@/log/ejabberd @@ -315,7 +312,6 @@ install: copy-files $(INSTALL) -d -m 750 $(O_USER) $(SPOOLDIR) $(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT) chmod -R 750 $(SPOOLDIR) - [ ! -f $(COOKIEFILE) ] || { $(CHOWN_COMMAND) @INSTALLUSER@ $(COOKIEFILE) >$(CHOWN_OUTPUT) ; chmod 400 $(COOKIEFILE) ; } # # ejabberdctl lock directory $(INSTALL) -d -m 750 $(O_USER) $(CTLLOCKDIR) From 5d0e599f1784d7529dcd365ad8c3dd46c1ac85ad Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 29 Oct 2021 05:12:26 +0300 Subject: [PATCH 024/106] Support MUC hats (XEP-0317, conversejs/prosody compatible) --- include/mod_muc_room.hrl | 2 + src/mod_muc_room.erl | 420 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 416 insertions(+), 6 deletions(-) diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index bbe656575..a98833662 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -65,6 +65,7 @@ captcha_whitelist = (?SETS):empty() :: gb_sets:set(), mam = false :: boolean(), pubsub = <<"">> :: binary(), + enable_hats = false :: boolean(), lang = ejabberd_option:language() :: binary() }). @@ -124,6 +125,7 @@ history = #lqueue{} :: lqueue(), subject = [] :: [text()], subject_author = <<"">> :: binary(), + hats_users = #{} :: #{ljid() => #{binary() => binary()}}, just_created = erlang:system_time(microsecond) :: true | integer(), activity = treap:empty() :: treap:treap(), room_shaper = none :: ejabberd_shaper:shaper(), diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 035e851fd..3f2c655df 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -76,6 +76,12 @@ -define(DEFAULT_MAX_USERS_PRESENCE,1000). +-define(MUC_HAT_ADD_CMD, <<"http://prosody.im/protocol/hats#add">>). +-define(MUC_HAT_REMOVE_CMD, <<"http://prosody.im/protocol/hats#remove">>). +-define(MUC_HAT_LIST_CMD, <<"p1:hats#list">>). +-define(MAX_HATS_USERS, 100). +-define(MAX_HATS_PER_USER, 10). + %-define(DBGFSM, true). -ifdef(DBGFSM). @@ -446,6 +452,8 @@ normal_state({route, <<"">>, process_iq_mucsub(From, IQ, StateData); #xcaptcha{} -> process_iq_captcha(From, IQ, StateData); + #adhoc_command{} -> + process_iq_adhoc(From, IQ, StateData); _ -> Txt = ?T("The feature requested is not " "supported by the conference"), @@ -1405,6 +1413,12 @@ is_occupant_or_admin(JID, StateData) -> _ -> false end. +%% Check if the user is an admin or owner. +-spec is_admin(jid(), state()) -> boolean(). +is_admin(JID, StateData) -> + FAffiliation = get_affiliation(JID, StateData), + FAffiliation == admin orelse FAffiliation == owner. + %% Decide the fate of the message and its sender %% Returns: continue_delivery | forget_message | {expulse_sender, Reason} -spec decide_fate_message(message(), jid(), state()) -> @@ -1935,7 +1949,7 @@ filter_presence(Presence) -> XMLNS = xmpp:get_ns(El), case catch binary:part(XMLNS, 0, size(?NS_MUC)) of ?NS_MUC -> false; - _ -> true + _ -> XMLNS /= ?NS_HATS end end, xmpp:get_els(Presence)), xmpp:set_els(Presence, Els). @@ -2485,9 +2499,10 @@ send_new_presence(NJID, Reason, IsInitialPresence, StateData, OldStateData) -> Pres = if Presence == undefined -> #presence{}; true -> Presence end, - Packet = xmpp:set_subtag( - Pres, #muc_user{items = [Item], - status_codes = StatusCodes}), + Packet = xmpp:set_subtag( + add_presence_hats(NJID, Pres, StateData), + #muc_user{items = [Item], + status_codes = StatusCodes}), send_wrapped(jid:replace_resource(StateData#state.jid, Nick), Info#user.jid, Packet, Node1, StateData), Type = xmpp:get_type(Packet), @@ -2536,7 +2551,9 @@ send_existing_presences1(ToJID, StateData) -> false -> Item0 end, Packet = xmpp:set_subtag( - Presence, #muc_user{items = [Item]}), + add_presence_hats( + FromJID, Presence, StateData), + #muc_user{items = [Item]}), send_wrapped(jid:replace_resource(StateData#state.jid, FromNick), RealToJID, Packet, ?NS_MUCSUB_NODES_PRESENCE, StateData) end @@ -3579,7 +3596,8 @@ get_config(Lang, StateData, From) -> {allow_voice_requests, Config#config.allow_voice_requests}, {allow_subscription, Config#config.allow_subscription}, {voice_request_min_interval, Config#config.voice_request_min_interval}, - {pubsub, Config#config.pubsub}] + {pubsub, Config#config.pubsub}, + {enable_hats, Config#config.enable_hats}] ++ case ejabberd_captcha:is_feature_available() of true -> @@ -3667,6 +3685,7 @@ set_config(Opts, Config, ServerHost, Lang) -> ({maxusers, V}, C) -> C#config{max_users = V}; ({enablelogging, V}, C) -> C#config{logging = V}; ({pubsub, V}, C) -> C#config{pubsub = V}; + ({enable_hats, V}, C) -> C#config{enable_hats = V}; ({lang, L}, C) -> C#config{lang = L}; ({captcha_whitelist, Js}, C) -> LJIDs = [jid:tolower(J) || J <- Js], @@ -3897,6 +3916,9 @@ set_opts([{Opt, Val} | Opts], StateData) -> allow_subscription -> StateData#state{config = (StateData#state.config)#config{allow_subscription = Val}}; + enable_hats -> + StateData#state{config = + (StateData#state.config)#config{enable_hats = Val}}; lang -> StateData#state{config = (StateData#state.config)#config{lang = Val}}; @@ -3927,6 +3949,11 @@ set_opts([{Opt, Val} | Opts], StateData) -> end, StateData#state{subject = Subj}; subject_author -> StateData#state{subject_author = Val}; + hats_users -> + Hats = maps:from_list( + lists:map(fun({U, H}) -> {U, maps:from_list(H)} end, + Val)), + StateData#state{hats_users = Hats}; _ -> StateData end, set_opts(Opts, NSD). @@ -3983,6 +4010,7 @@ make_opts(StateData) -> ?MAKE_CONFIG_OPT(#config.vcard), ?MAKE_CONFIG_OPT(#config.vcard_xupdate), ?MAKE_CONFIG_OPT(#config.pubsub), + ?MAKE_CONFIG_OPT(#config.enable_hats), ?MAKE_CONFIG_OPT(#config.lang), {captcha_whitelist, (?SETS):to_list((StateData#state.config)#config.captcha_whitelist)}, @@ -3990,6 +4018,9 @@ make_opts(StateData) -> maps:to_list(StateData#state.affiliations)}, {subject, StateData#state.subject}, {subject_author, StateData#state.subject_author}, + {hats_users, + lists:map(fun({U, H}) -> {U, maps:to_list(H)} end, + maps:to_list(StateData#state.hats_users))}, {hibernation_time, erlang:system_time(microsecond)}, {subscribers, Subscribers}]. @@ -4080,6 +4111,7 @@ maybe_forget_room(StateData) -> make_disco_info(_From, StateData) -> Config = StateData#state.config, Feats = [?NS_VCARD, ?NS_MUC, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS, + ?NS_COMMANDS, ?CONFIG_OPT_TO_FEATURE((Config#config.public), <<"muc_public">>, <<"muc_hidden">>), ?CONFIG_OPT_TO_FEATURE((Config#config.persistent), @@ -4119,6 +4151,77 @@ process_iq_disco_info(From, #iq{type = get, lang = Lang, DiscoInfo = make_disco_info(From, StateData), Extras = iq_disco_info_extras(Lang, StateData, false), {result, DiscoInfo#disco_info{xdata = [Extras]}}; +process_iq_disco_info(From, #iq{type = get, lang = Lang, + sub_els = [#disco_info{node = ?NS_COMMANDS}]}, + StateData) -> + case (StateData#state.config)#config.enable_hats andalso + is_admin(From, StateData) + of + true -> + {result, + #disco_info{ + identities = [#identity{category = <<"automation">>, + type = <<"command-list">>, + name = translate:translate( + Lang, ?T("Commands"))}]}}; + false -> + Txt = ?T("Node not found"), + {error, xmpp:err_item_not_found(Txt, Lang)} + end; +process_iq_disco_info(From, #iq{type = get, lang = Lang, + sub_els = [#disco_info{node = ?MUC_HAT_ADD_CMD}]}, + StateData) -> + case (StateData#state.config)#config.enable_hats andalso + is_admin(From, StateData) + of + true -> + {result, + #disco_info{ + identities = [#identity{category = <<"automation">>, + type = <<"command-node">>, + name = translate:translate( + Lang, ?T("Add a hat to a user"))}], + features = [?NS_COMMANDS]}}; + false -> + Txt = ?T("Node not found"), + {error, xmpp:err_item_not_found(Txt, Lang)} + end; +process_iq_disco_info(From, #iq{type = get, lang = Lang, + sub_els = [#disco_info{node = ?MUC_HAT_REMOVE_CMD}]}, + StateData) -> + case (StateData#state.config)#config.enable_hats andalso + is_admin(From, StateData) + of + true -> + {result, + #disco_info{ + identities = [#identity{category = <<"automation">>, + type = <<"command-node">>, + name = translate:translate( + Lang, ?T("Remove a hat from a user"))}], + features = [?NS_COMMANDS]}}; + false -> + Txt = ?T("Node not found"), + {error, xmpp:err_item_not_found(Txt, Lang)} + end; +process_iq_disco_info(From, #iq{type = get, lang = Lang, + sub_els = [#disco_info{node = ?MUC_HAT_LIST_CMD}]}, + StateData) -> + case (StateData#state.config)#config.enable_hats andalso + is_admin(From, StateData) + of + true -> + {result, + #disco_info{ + identities = [#identity{category = <<"automation">>, + type = <<"command-node">>, + name = translate:translate( + Lang, ?T("List users with hats"))}], + features = [?NS_COMMANDS]}}; + false -> + Txt = ?T("Node not found"), + {error, xmpp:err_item_not_found(Txt, Lang)} + end; process_iq_disco_info(From, #iq{type = get, lang = Lang, sub_els = [#disco_info{node = Node}]}, StateData) -> @@ -4199,6 +4302,46 @@ process_iq_disco_items(From, #iq{type = get, sub_els = [#disco_items{node = <<>> {result, #disco_items{}} end end; +process_iq_disco_items(From, #iq{type = get, lang = Lang, + sub_els = [#disco_items{node = ?NS_COMMANDS}]}, + StateData) -> + case (StateData#state.config)#config.enable_hats andalso + is_admin(From, StateData) + of + true -> + {result, + #disco_items{ + items = [#disco_item{jid = StateData#state.jid, + node = ?MUC_HAT_ADD_CMD, + name = translate:translate( + Lang, ?T("Add a hat to a user"))}, + #disco_item{jid = StateData#state.jid, + node = ?MUC_HAT_REMOVE_CMD, + name = translate:translate( + Lang, ?T("Remove a hat from a user"))}, + #disco_item{jid = StateData#state.jid, + node = ?MUC_HAT_LIST_CMD, + name = translate:translate( + Lang, ?T("List users with hats"))}]}}; + false -> + Txt = ?T("Node not found"), + {error, xmpp:err_item_not_found(Txt, Lang)} + end; +process_iq_disco_items(From, #iq{type = get, lang = Lang, + sub_els = [#disco_items{node = Node}]}, + StateData) + when Node == ?MUC_HAT_ADD_CMD; + Node == ?MUC_HAT_REMOVE_CMD; + Node == ?MUC_HAT_LIST_CMD -> + case (StateData#state.config)#config.enable_hats andalso + is_admin(From, StateData) + of + true -> + {result, #disco_items{}}; + false -> + Txt = ?T("Node not found"), + {error, xmpp:err_item_not_found(Txt, Lang)} + end; process_iq_disco_items(_From, #iq{lang = Lang}, _StateData) -> Txt = ?T("Node not found"), {error, xmpp:err_item_not_found(Txt, Lang)}. @@ -4441,6 +4584,271 @@ get_mucroom_disco_items(StateData) -> end, [], StateData#state.nicks), #disco_items{items = Items}. +-spec process_iq_adhoc(jid(), iq(), state()) -> + {result, adhoc_command()} | + {result, adhoc_command(), state()} | + {error, stanza_error()}. +process_iq_adhoc(_From, #iq{type = get}, _StateData) -> + {error, xmpp:err_bad_request()}; +process_iq_adhoc(From, #iq{type = set, lang = Lang1, + sub_els = [#adhoc_command{} = Request]}, + StateData) -> + % Ad-Hoc Commands are used only for Hats here + case (StateData#state.config)#config.enable_hats andalso + is_admin(From, StateData) + of + true -> + #adhoc_command{lang = Lang2, node = Node, + action = Action, xdata = XData} = Request, + Lang = case Lang2 of + <<"">> -> Lang1; + _ -> Lang2 + end, + case {Node, Action} of + {_, cancel} -> + {result, + xmpp_util:make_adhoc_response( + Request, + #adhoc_command{status = canceled, lang = Lang, + node = Node})}; + {?MUC_HAT_ADD_CMD, execute} -> + Form = + #xdata{ + title = translate:translate( + Lang, ?T("Add a hat to a user")), + type = form, + fields = + [#xdata_field{ + type = 'jid-single', + label = translate:translate(Lang, ?T("Jabber ID")), + required = true, + var = <<"jid">>}, + #xdata_field{ + type = 'text-single', + label = translate:translate(Lang, ?T("Hat title")), + var = <<"hat_title">>}, + #xdata_field{ + type = 'text-single', + label = translate:translate(Lang, ?T("Hat URI")), + required = true, + var = <<"hat_uri">>} + ]}, + {result, + xmpp_util:make_adhoc_response( + Request, + #adhoc_command{ + status = executing, + xdata = Form})}; + {?MUC_HAT_ADD_CMD, complete} when XData /= undefined -> + JID = try + jid:decode(hd(xmpp_util:get_xdata_values( + <<"jid">>, XData))) + catch _:_ -> error + end, + URI = try + hd(xmpp_util:get_xdata_values( + <<"hat_uri">>, XData)) + catch _:_ -> error + end, + Title = case xmpp_util:get_xdata_values( + <<"hat_title">>, XData) of + [] -> <<"">>; + [T] -> T + end, + if + (JID /= error) and (URI /= error) -> + case add_hat(JID, URI, Title, StateData) of + {ok, NewStateData} -> + store_room(NewStateData), + send_update_presence( + JID, NewStateData, StateData), + {result, + xmpp_util:make_adhoc_response( + Request, + #adhoc_command{status = completed}), + NewStateData}; + {error, size_limit} -> + Txt = ?T("Hats limit exceeded"), + {error, xmpp:err_not_allowed(Txt, Lang)} + end; + true -> + {error, xmpp:err_bad_request()} + end; + {?MUC_HAT_ADD_CMD, complete} -> + {error, xmpp:err_bad_request()}; + {?MUC_HAT_ADD_CMD, _} -> + Txt = ?T("Incorrect value of 'action' attribute"), + {error, xmpp:err_bad_request(Txt, Lang)}; + {?MUC_HAT_REMOVE_CMD, execute} -> + Form = + #xdata{ + title = translate:translate( + Lang, ?T("Remove a hat from a user")), + type = form, + fields = + [#xdata_field{ + type = 'jid-single', + label = translate:translate(Lang, ?T("Jabber ID")), + required = true, + var = <<"jid">>}, + #xdata_field{ + type = 'text-single', + label = translate:translate(Lang, ?T("Hat URI")), + required = true, + var = <<"hat_uri">>} + ]}, + {result, + xmpp_util:make_adhoc_response( + Request, + #adhoc_command{ + status = executing, + xdata = Form})}; + {?MUC_HAT_REMOVE_CMD, complete} when XData /= undefined -> + JID = try + jid:decode(hd(xmpp_util:get_xdata_values( + <<"jid">>, XData))) + catch _:_ -> error + end, + URI = try + hd(xmpp_util:get_xdata_values( + <<"hat_uri">>, XData)) + catch _:_ -> error + end, + if + (JID /= error) and (URI /= error) -> + NewStateData = del_hat(JID, URI, StateData), + store_room(NewStateData), + send_update_presence( + JID, NewStateData, StateData), + {result, + xmpp_util:make_adhoc_response( + Request, + #adhoc_command{status = completed}), + NewStateData}; + true -> + {error, xmpp:err_bad_request()} + end; + {?MUC_HAT_REMOVE_CMD, complete} -> + {error, xmpp:err_bad_request()}; + {?MUC_HAT_REMOVE_CMD, _} -> + Txt = ?T("Incorrect value of 'action' attribute"), + {error, xmpp:err_bad_request(Txt, Lang)}; + {?MUC_HAT_LIST_CMD, execute} -> + Hats = get_all_hats(StateData), + Items = + lists:map( + fun({JID, URI, Title}) -> + [#xdata_field{ + var = <<"jid">>, + values = [jid:encode(JID)]}, + #xdata_field{ + var = <<"hat_title">>, + values = [URI]}, + #xdata_field{ + var = <<"hat_uri">>, + values = [Title]}] + end, Hats), + Form = + #xdata{ + title = translate:translate( + Lang, ?T("List of users with hats")), + type = result, + reported = + [#xdata_field{ + label = translate:translate(Lang, ?T("Jabber ID")), + var = <<"jid">>}, + #xdata_field{ + label = translate:translate(Lang, ?T("Hat title")), + var = <<"hat_title">>}, + #xdata_field{ + label = translate:translate(Lang, ?T("Hat URI")), + var = <<"hat_uri">>}], + items = Items}, + {result, + xmpp_util:make_adhoc_response( + Request, + #adhoc_command{ + status = completed, + xdata = Form})}; + {?MUC_HAT_LIST_CMD, _} -> + Txt = ?T("Incorrect value of 'action' attribute"), + {error, xmpp:err_bad_request(Txt, Lang)}; + _ -> + {error, xmpp:err_item_not_found()} + end; + _ -> + {error, xmpp:err_forbidden()} + end. + +-spec add_hat(jid(), binary(), binary(), state()) -> + {ok, state()} | {error, size_limit}. +add_hat(JID, URI, Title, StateData) -> + Hats = StateData#state.hats_users, + LJID = jid:remove_resource(jid:tolower(JID)), + UserHats = maps:get(LJID, Hats, #{}), + UserHats2 = maps:put(URI, Title, UserHats), + USize = maps:size(UserHats2), + if + USize =< ?MAX_HATS_PER_USER -> + Hats2 = maps:put(LJID, UserHats2, Hats), + Size = maps:size(Hats2), + if + Size =< ?MAX_HATS_USERS -> + {ok, StateData#state{hats_users = Hats2}}; + true -> + {error, size_limit} + end; + true -> + {error, size_limit} + end. + +-spec del_hat(jid(), binary(), state()) -> state(). +del_hat(JID, URI, StateData) -> + Hats = StateData#state.hats_users, + LJID = jid:remove_resource(jid:tolower(JID)), + UserHats = maps:get(LJID, Hats, #{}), + UserHats2 = maps:remove(URI, UserHats), + Hats2 = + case maps:size(UserHats2) of + 0 -> + maps:remove(LJID, Hats); + _ -> + maps:put(LJID, UserHats2, Hats) + end, + StateData#state{hats_users = Hats2}. + +-spec get_all_hats(state()) -> list({jid(), binary(), binary()}). +get_all_hats(StateData) -> + lists:flatmap( + fun({LJID, H}) -> + JID = jid:make(LJID), + lists:map(fun({URI, Title}) -> {JID, URI, Title} end, + maps:to_list(H)) + end, + maps:to_list(StateData#state.hats_users)). + +-spec add_presence_hats(jid(), #presence{}, state()) -> #presence{}. +add_presence_hats(JID, Pres, StateData) -> + case (StateData#state.config)#config.enable_hats of + true -> + Hats = StateData#state.hats_users, + LJID = jid:remove_resource(jid:tolower(JID)), + UserHats = maps:get(LJID, Hats, #{}), + case maps:size(UserHats) of + 0 -> Pres; + _ -> + Items = + lists:map(fun({URI, Title}) -> + #muc_hat{uri = URI, title = Title} + end, + maps:to_list(UserHats)), + xmpp:set_subtag(Pres, + #muc_hats{hats = Items}) + end; + false -> + Pres + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Voice request support From 5d48329a3ff46d2e29f9f09921d99ac10f655589 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Fri, 29 Oct 2021 05:48:03 +0300 Subject: [PATCH 025/106] Update 'xmpp' dependency --- mix.exs | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 021de5e2b..8debcd6a1 100644 --- a/mix.exs +++ b/mix.exs @@ -124,7 +124,7 @@ defmodule Ejabberd.MixProject do {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, {:stun, "~> 1.0"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "3f18c9e6b239c3a8a4d82dc478b26565037b7247", override: true}, + {:xmpp, git: "https://github.com/processone/xmpp", ref: "a85dc699ff02471ecad08d85e3bed87c57c48aba", override: true}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/rebar.config b/rebar.config index 1f1710bf9..77d017c99 100644 --- a/rebar.config +++ b/rebar.config @@ -59,7 +59,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.27"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.44"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "3f18c9e6b239c3a8a4d82dc478b26565037b7247"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", "a85dc699ff02471ecad08d85e3bed87c57c48aba"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.12"}}} ]}. From 29dcc9b94ccfd514cf388979e7210d01cb97d5f4 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 30 Oct 2021 13:19:30 +0200 Subject: [PATCH 026/106] PubSub: Add delete_expired_pubsub_items command Support XEP-0060's pubsub#item_expire feature by adding a command for deleting expired PubSub items. Thanks to Ammonit Measurement GmbH for sponsoring this work. --- src/gen_pubsub_node.erl | 4 +++ src/mod_pubsub.erl | 67 ++++++++++++++++++++++++++++++++++++++++- src/node_flat.erl | 18 ++++++++++- src/node_flat_sql.erl | 19 +++++++++++- src/node_pep.erl | 5 ++- src/node_pep_sql.erl | 5 ++- 6 files changed, 113 insertions(+), 5 deletions(-) diff --git a/src/gen_pubsub_node.erl b/src/gen_pubsub_node.erl index 625e490fc..3f83fe48f 100644 --- a/src/gen_pubsub_node.erl +++ b/src/gen_pubsub_node.erl @@ -133,6 +133,10 @@ {result, {[itemId()], [itemId()]} }. +-callback remove_expired_items(NodeIdx :: nodeIdx(), + Seconds :: infinity | non_neg_integer()) -> + {result, [itemId()]}. + -callback get_node_affiliations(NodeIdx :: nodeIdx()) -> {result, [{ljid(), affiliation()}]}. diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 2e40d8f0e..a36c6e645 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -95,7 +95,7 @@ terminate/2, code_change/3, depends/2, mod_opt_type/1, mod_options/1]). %% ejabberd commands --export([get_commands_spec/0, delete_old_items/1]). +-export([get_commands_spec/0, delete_old_items/1, delete_expired_items/0]). -export([route/1]). @@ -3431,6 +3431,14 @@ max_items(Host, Options) -> end end. +-spec item_expire(host(), [{atom(), any()}]) -> non_neg_integer() | infinity. +item_expire(Host, Options) -> + case get_option(Options, item_expire) of + I when is_integer(I), I < 0 -> 0; + I when is_integer(I) -> I; + _ -> get_max_item_expire_node(Host) + end. + -spec get_configure_xfields(_, pubsub_node_config:result(), binary(), [binary()]) -> [xdata_field()]. get_configure_xfields(_Type, Options, Lang, Groups) -> @@ -3575,6 +3583,10 @@ check_opt_range(Opt, Opts, Max) -> get_max_items_node(Host) -> config(Host, max_items_node, undefined). +-spec get_max_item_expire_node(host()) -> infinity | non_neg_integer(). +get_max_item_expire_node(Host) -> + config(Host, max_item_expire_node, infinity). + -spec get_max_subscriptions_node(host()) -> undefined | non_neg_integer(). get_max_subscriptions_node(Host) -> config(Host, max_subscriptions_node, undefined). @@ -4181,6 +4193,44 @@ delete_old_items(N) -> ok end. +-spec delete_expired_items() -> ok | error. +delete_expired_items() -> + Results = lists:flatmap( + fun(Host) -> + case tree_action(Host, get_all_nodes, [Host]) of + Nodes when is_list(Nodes) -> + lists:map( + fun(#pubsub_node{id = Nidx, type = Type, + options = Options}) -> + case item_expire(Host, Options) of + infinity -> + ok; + Seconds -> + case node_action( + Host, Type, + remove_expired_items, + [Nidx, Seconds]) of + {result, []} -> + ok; + {result, [_|_]} -> + unset_cached_item( + Host, Nidx); + {error, _} -> + error + end + end + end, Nodes); + _ -> + error + end + end, ejabberd_option:hosts()), + case lists:member(error, Results) of + true -> + error; + false -> + ok + end. + -spec get_commands_spec() -> [ejabberd_commands()]. get_commands_spec() -> [#ejabberd_commands{name = delete_old_pubsub_items, tags = [purge], @@ -4191,6 +4241,13 @@ get_commands_spec() -> result = {res, rescode}, result_desc = "0 if command failed, 1 when succeeded", args_example = [1000], + result_example = ok}, + #ejabberd_commands{name = delete_expired_pubsub_items, tags = [purge], + desc = "Delete expired PubSub items", + module = ?MODULE, function = delete_expired_items, + args = [], + result = {res, rescode}, + result_desc = "0 if command failed, 1 when succeeded", result_example = ok}]. -spec mod_opt_type(atom()) -> econf:validator(). @@ -4204,6 +4261,8 @@ mod_opt_type(last_item_cache) -> econf:bool(); mod_opt_type(max_items_node) -> econf:non_neg_int(unlimited); +mod_opt_type(max_item_expire_node) -> + econf:timeout(second, infinity); mod_opt_type(max_nodes_discoitems) -> econf:non_neg_int(infinity); mod_opt_type(max_subscriptions_node) -> @@ -4251,6 +4310,7 @@ mod_options(Host) -> {ignore_pep_from_offline, true}, {last_item_cache, false}, {max_items_node, ?MAXITEMS}, + {max_item_expire_node, infinity}, {max_nodes_discoitems, 100}, {nodetree, ?STDTREE}, {pep_mapping, []}, @@ -4329,6 +4389,11 @@ mod_doc() -> " so many nodes, caching last items speeds up pubsub " "and allows to raise user connection rate. The cost " "is memory usage, as every item is stored in memory.")}}, + {max_item_expire_node, + #{value => "timeout() | infinity", + desc => + ?T("Specify the maximum item epiry time. Default value " + "is: 'infinity'.")}}, {max_items_node, #{value => "non_neg_integer() | infinity", desc => diff --git a/src/node_flat.erl b/src/node_flat.erl index c597b9ce9..55dea0d8d 100644 --- a/src/node_flat.erl +++ b/src/node_flat.erl @@ -40,7 +40,7 @@ create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, - remove_extra_items/2, remove_extra_items/3, + remove_extra_items/2, remove_extra_items/3, remove_expired_items/2, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, @@ -432,6 +432,22 @@ remove_extra_items(Nidx, MaxItems, ItemIds) -> del_items(Nidx, OldItems), {result, {NewItems, OldItems}}. +remove_expired_items(_Nidx, infinity) -> + {result, []}; +remove_expired_items(Nidx, Seconds) -> + Items = mnesia:index_read(pubsub_item, Nidx, #pubsub_item.nodeidx), + ExpT = misc:usec_to_now( + erlang:system_time(microsecond) - (Seconds * 1000000)), + ExpItems = lists:filtermap( + fun(#pubsub_item{itemid = {ItemId, _}, + modification = {ModT, _}}) when ModT < ExpT -> + {true, ItemId}; + (#pubsub_item{}) -> + false + end, Items), + del_items(Nidx, ExpItems), + {result, ExpItems}. + %% @doc

Triggers item deletion.

%%

Default plugin: The user performing the deletion must be the node owner %% or a publisher, or PublishModel being open.

diff --git a/src/node_flat_sql.erl b/src/node_flat_sql.erl index 240dc3760..f9c8a209d 100644 --- a/src/node_flat_sql.erl +++ b/src/node_flat_sql.erl @@ -43,7 +43,7 @@ create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, - remove_extra_items/2, remove_extra_items/3, + remove_extra_items/2, remove_extra_items/3, remove_expired_items/2, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, @@ -285,6 +285,23 @@ remove_extra_items(Nidx, MaxItems, ItemIds) -> del_items(Nidx, OldItems), {result, {NewItems, OldItems}}. +remove_expired_items(_Nidx, infinity) -> + {result, []}; +remove_expired_items(Nidx, Seconds) -> + ExpT = encode_now( + misc:usec_to_now( + erlang:system_time(microsecond) - (Seconds * 1000000))), + case ejabberd_sql:sql_query_t( + ?SQL("select @(itemid)s from pubsub_item where nodeid=%(Nidx)d " + "and creation < %(ExpT)s")) of + {selected, RItems} -> + ItemIds = [ItemId || {ItemId} <- RItems], + del_items(Nidx, ItemIds), + {result, ItemIds}; + _ -> + {result, []} + end. + delete_item(Nidx, Publisher, PublishModel, ItemId) -> SubKey = jid:tolower(Publisher), GenKey = jid:remove_resource(SubKey), diff --git a/src/node_pep.erl b/src/node_pep.erl index 44388ca31..a7132a691 100644 --- a/src/node_pep.erl +++ b/src/node_pep.erl @@ -36,7 +36,7 @@ create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, - remove_extra_items/2, remove_extra_items/3, + remove_extra_items/2, remove_extra_items/3, remove_expired_items/2, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, @@ -142,6 +142,9 @@ remove_extra_items(Nidx, MaxItems) -> remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat:remove_extra_items(Nidx, MaxItems, ItemIds). +remove_expired_items(Nidx, ItemIds) -> + node_flat:remove_expired_items(Nidx, ItemIds). + delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId). diff --git a/src/node_pep_sql.erl b/src/node_pep_sql.erl index c0cf2b166..7a9d92bcc 100644 --- a/src/node_pep_sql.erl +++ b/src/node_pep_sql.erl @@ -38,7 +38,7 @@ create_node_permission/6, create_node/2, delete_node/1, purge_node/2, subscribe_node/8, unsubscribe_node/4, publish_item/7, delete_item/4, - remove_extra_items/2, remove_extra_items/3, + remove_extra_items/2, remove_extra_items/3, remove_expired_items/2, get_entity_affiliations/2, get_node_affiliations/1, get_affiliation/2, set_affiliation/3, get_entity_subscriptions/2, get_node_subscriptions/1, @@ -99,6 +99,9 @@ remove_extra_items(Nidx, MaxItems) -> remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat_sql:remove_extra_items(Nidx, MaxItems, ItemIds). +remove_expired_items(Nidx, ItemIds) -> + node_flat_sql:remove_expired_items(Nidx, ItemIds). + delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat_sql:delete_item(Nidx, Publisher, PublishModel, ItemId). From 2f1611f9185d3d4ce55fee3cef91b6ded5573976 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 30 Oct 2021 13:45:10 +0200 Subject: [PATCH 027/106] mod_pubsub: Fix get_max_items_node/1 specification Make it explicit that the get_max_items_node/1 function returns ?MAXITEMS if the 'max_items_node' option isn't specified. The function didn't actually fall back to 'undefined' (but to the 'max_items_node' default; i.e., ?MAXITEMS) anyway. This change just clarifies the behavior and adjusts the function specification accordingly. --- src/mod_pubsub.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index a36c6e645..34a95da85 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3579,9 +3579,9 @@ check_opt_range(Opt, Opts, Max) -> Val -> Val =< Max end. --spec get_max_items_node(host()) -> undefined | unlimited | non_neg_integer(). +-spec get_max_items_node(host()) -> unlimited | non_neg_integer(). get_max_items_node(Host) -> - config(Host, max_items_node, undefined). + config(Host, max_items_node, ?MAXITEMS). -spec get_max_item_expire_node(host()) -> infinity | non_neg_integer(). get_max_item_expire_node(Host) -> From 65a900668cc7da973081c718fb9170eeb7f22a06 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 31 Oct 2021 21:32:45 +0100 Subject: [PATCH 028/106] node_pep: Fix remove_expired_items/2 argument name --- src/node_pep.erl | 4 ++-- src/node_pep_sql.erl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/node_pep.erl b/src/node_pep.erl index a7132a691..a30f8cb91 100644 --- a/src/node_pep.erl +++ b/src/node_pep.erl @@ -142,8 +142,8 @@ remove_extra_items(Nidx, MaxItems) -> remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat:remove_extra_items(Nidx, MaxItems, ItemIds). -remove_expired_items(Nidx, ItemIds) -> - node_flat:remove_expired_items(Nidx, ItemIds). +remove_expired_items(Nidx, Seconds) -> + node_flat:remove_expired_items(Nidx, Seconds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat:delete_item(Nidx, Publisher, PublishModel, ItemId). diff --git a/src/node_pep_sql.erl b/src/node_pep_sql.erl index 7a9d92bcc..3bb66bc4c 100644 --- a/src/node_pep_sql.erl +++ b/src/node_pep_sql.erl @@ -99,8 +99,8 @@ remove_extra_items(Nidx, MaxItems) -> remove_extra_items(Nidx, MaxItems, ItemIds) -> node_flat_sql:remove_extra_items(Nidx, MaxItems, ItemIds). -remove_expired_items(Nidx, ItemIds) -> - node_flat_sql:remove_expired_items(Nidx, ItemIds). +remove_expired_items(Nidx, Seconds) -> + node_flat_sql:remove_expired_items(Nidx, Seconds). delete_item(Nidx, Publisher, PublishModel, ItemId) -> node_flat_sql:delete_item(Nidx, Publisher, PublishModel, ItemId). From 13cbd7c35daaefd33c5e5efb47a015df6d20f945 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 31 Oct 2021 21:38:49 +0100 Subject: [PATCH 029/106] mod_pubsub: Remove unused check_opt_range/3 clause --- src/mod_pubsub.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 34a95da85..13b44b3ec 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3568,9 +3568,7 @@ decode_get_pending(#xdata{fields = Fs}, Lang) -> end. -spec check_opt_range(atom(), [proplists:property()], - non_neg_integer() | unlimited | undefined) -> boolean(). -check_opt_range(_Opt, _Opts, undefined) -> - true; + non_neg_integer() | unlimited) -> boolean(). check_opt_range(_Opt, _Opts, unlimited) -> true; check_opt_range(Opt, Opts, Max) -> From c4f6c9dfe765e017c5f2d83c8ab6311c97304250 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sun, 31 Oct 2021 21:59:00 +0100 Subject: [PATCH 030/106] mod_muc_room.hrl: Work around old Dialyzer bug On Erlang/OTP versions older than 21, Dialyzer stumbles over non-empty map type specifications for record fields (OTP-15098). --- include/mod_muc_room.hrl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mod_muc_room.hrl b/include/mod_muc_room.hrl index a98833662..cc00f73c8 100644 --- a/include/mod_muc_room.hrl +++ b/include/mod_muc_room.hrl @@ -125,7 +125,7 @@ history = #lqueue{} :: lqueue(), subject = [] :: [text()], subject_author = <<"">> :: binary(), - hats_users = #{} :: #{ljid() => #{binary() => binary()}}, + hats_users = #{} :: map(), % FIXME on OTP 21+: #{ljid() => #{binary() => binary()}}, just_created = erlang:system_time(microsecond) :: true | integer(), activity = treap:empty() :: treap:treap(), room_shaper = none :: ejabberd_shaper:shaper(), From 6e3df8e80bffda97a24c76f75377f2ed5eea648e Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 2 Nov 2021 12:34:19 +0100 Subject: [PATCH 031/106] Update 'stun' dependency The new 'stun' release should improve UDP performance quite a bit. --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 77d017c99..f0d916e81 100644 --- a/rebar.config +++ b/rebar.config @@ -58,7 +58,7 @@ {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.13"}}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.27"}}}, {if_var_true, stun, - {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.44"}}}}, + {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.45"}}}}, {xmpp, ".*", {git, "https://github.com/processone/xmpp", "a85dc699ff02471ecad08d85e3bed87c57c48aba"}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.12"}}} ]}. From 684ef60ec3e7bf590c67a15b8c9d8fd7833c4fc9 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 5 Nov 2021 11:11:29 +0100 Subject: [PATCH 032/106] Annotate support for XEP-0317: Hats, since commit 5d0e599f1 --- src/mod_muc_room.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 3f2c655df..a94b7be07 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -27,6 +27,8 @@ -author('alexey@process-one.net'). +-protocol({xep, 317, '0.1'}). + -behaviour(p1_fsm). %% External exports From b6a2eeebebdfb7b91e98d6062f96b87b5d6cce9a Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 5 Nov 2021 13:45:20 +0100 Subject: [PATCH 033/106] Mention "help" as an available ejabberdctl command --- src/ejabberd_ctl.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 04e383d53..354e5ba2f 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -525,6 +525,7 @@ print_usage(Version) -> print_usage(HelpMode, MaxC, ShCode, Version) -> AllCommands = [ + {"help", ["[arguments]"], "Get help"}, {"status", [], "Get ejabberd status"}, {"stop", [], "Stop ejabberd"}, {"restart", [], "Restart ejabberd"}, From 4e014d23bd174707a5a89e244cef9a9f4c9bac91 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 5 Nov 2021 13:24:36 +0100 Subject: [PATCH 034/106] Improve documentation of some commands --- src/ejabberd_admin.erl | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 0174cd7ff..14305e84b 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -120,7 +120,10 @@ get_commands_spec() -> module = init, function = restart, args = [], result = {res, rescode}}, #ejabberd_commands{name = reopen_log, tags = [logs], - desc = "Reopen the log files", + desc = "Reopen the log files after being renamed", + longdesc = "This can be useful when an external tool is " + "used for log rotation. See " + "https://docs.ejabberd.im/admin/guide/troubleshooting/#log-files", policy = admin, module = ?MODULE, function = reopen_log, args = [], result = {res, rescode}}, @@ -345,31 +348,41 @@ get_commands_spec() -> {oldbackup, string}, {newbackup, string}], result = {res, restuple}}, #ejabberd_commands{name = backup, tags = [mnesia], - desc = "Store the database to backup file", + desc = "Backup the Mnesia database to a binary file", module = ?MODULE, function = backup_mnesia, args_desc = ["Full path for the destination backup file"], args_example = ["/var/lib/ejabberd/database.backup"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = restore, tags = [mnesia], - desc = "Restore the database from backup file", + desc = "Restore the Mnesia database from a binary backup file", + longdesc = "This restores immediately from a " + "binary backup file the internal Mnesia " + "database. This will consume a lot of memory if " + "you have a large database, you may prefer " + "'install_fallback'.", module = ?MODULE, function = restore_mnesia, args_desc = ["Full path to the backup file"], args_example = ["/var/lib/ejabberd/database.backup"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = dump, tags = [mnesia], - desc = "Dump the database to a text file", + desc = "Dump the Mnesia database to a text file", module = ?MODULE, function = dump_mnesia, args_desc = ["Full path for the text file"], args_example = ["/var/lib/ejabberd/database.txt"], args = [{file, string}], result = {res, restuple}}, #ejabberd_commands{name = dump_table, tags = [mnesia], - desc = "Dump a table to a text file", + desc = "Dump a Mnesia table to a text file", module = ?MODULE, function = dump_table, args_desc = ["Full path for the text file", "Table name"], args_example = ["/var/lib/ejabberd/table-muc-registered.txt", "muc_registered"], args = [{file, string}, {table, string}], result = {res, restuple}}, #ejabberd_commands{name = load, tags = [mnesia], - desc = "Restore the database from a text file", + desc = "Restore Mnesia database from a text dump file", + longdesc = "Restore immediately. This is not " + "recommended for big databases, as it will " + "consume much time, memory and processor. In " + "that case it's preferable to use 'backup' and " + "'install_fallback'.", module = ?MODULE, function = load_mnesia, args_desc = ["Full path to the text file"], args_example = ["/var/lib/ejabberd/database.txt"], @@ -385,7 +398,14 @@ get_commands_spec() -> args_example = ["roster"], args = [{table, string}], result = {res, string}}, #ejabberd_commands{name = install_fallback, tags = [mnesia], - desc = "Install the database from a fallback file", + desc = "Install Mnesia database from a binary backup file", + longdesc = "The binary backup file is " + "installed as fallback: it will be used to " + "restore the database at the next ejabberd " + "start. This means that, after running this " + "command, you have to restart ejabberd. This " + "command requires less memory than + 'restore'.", module = ?MODULE, function = install_fallback_mnesia, args_desc = ["Full path to the fallback file"], args_example = ["/var/lib/ejabberd/database.fallback"], From 2cdda4cf496a893aca03d1e0b55f63b2a396ddb8 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 6 Nov 2021 23:48:49 +0100 Subject: [PATCH 035/106] mod_caps: Don't forget caps on XEP-0198 resumption Many thanks to Thilo Molitor for spotting the issue and testing the fix. --- src/mod_caps.erl | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/mod_caps.erl b/src/mod_caps.erl index c8f548169..bc48dac6f 100644 --- a/src/mod_caps.erl +++ b/src/mod_caps.erl @@ -49,7 +49,8 @@ handle_cast/2, terminate/2, code_change/3]). -export([user_send_packet/1, user_receive_packet/1, - c2s_presence_in/2, mod_opt_type/1, mod_options/1, mod_doc/0]). + c2s_presence_in/2, c2s_copy_session/2, + mod_opt_type/1, mod_options/1, mod_doc/0]). -include("logger.hrl"). @@ -274,6 +275,13 @@ c2s_presence_in(C2SState, C2SState#{caps_resources => NewRs} end. +-spec c2s_copy_session(ejabberd_c2s:state(), ejabberd_c2s:state()) + -> ejabberd_c2s:state(). +c2s_copy_session(C2SState, #{caps_resources := Rs}) -> + C2SState#{caps_resources => Rs}; +c2s_copy_session(C2SState, _) -> + C2SState. + -spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. depends(_Host, _Opts) -> []. @@ -304,6 +312,8 @@ init([Host|_]) -> caps_stream_features, 75), ejabberd_hooks:add(s2s_in_post_auth_features, Host, ?MODULE, caps_stream_features, 75), + ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE, + c2s_copy_session, 75), ejabberd_hooks:add(disco_local_features, Host, ?MODULE, disco_features, 75), ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, @@ -341,6 +351,8 @@ terminate(_Reason, State) -> ?MODULE, caps_stream_features, 75), ejabberd_hooks:delete(s2s_in_post_auth_features, Host, ?MODULE, caps_stream_features, 75), + ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE, + c2s_copy_session, 75), ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, disco_features, 75), ejabberd_hooks:delete(disco_local_identity, Host, From 132ebb8f2dbe222db0c4d025c131bb3e525939e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 10 Nov 2021 17:00:42 +0100 Subject: [PATCH 036/106] Fix exception in mucsub {un}subscription events multicast handler While those event are wrapped in mucsub envelope they doesn't contain regular messages that require updating 'to' attribute, so don't process in that same way as events with wrapped message in them. --- src/mod_muc_room.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index a94b7be07..e8b0d1bce 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -5097,7 +5097,7 @@ send_subscriptions_change_notifications(From, Nick, Type, State) -> id = p1_rand:get_string(), sub_els = [Payload1]}]}}]}, ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host, - WJ, Packet1, true); + WJ, Packet1, false); true -> ok end, if WN /= [] -> @@ -5113,7 +5113,7 @@ send_subscriptions_change_notifications(From, Nick, Type, State) -> id = p1_rand:get_string(), sub_els = [Payload2]}]}}]}, ejabberd_router_multicast:route_multicast(State#state.jid, State#state.server_host, - WN, Packet2, true); + WN, Packet2, false); true -> ok end. From 03817de8273592466af7a71ac351dd1f162cc330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 16 Nov 2021 10:25:03 +0100 Subject: [PATCH 037/106] Make s2s connection table cleanup more robust Using monitors instead of doint that from terminate() makes us immune to s2s handler processes being forcefully killed. --- src/ejabberd_s2s.erl | 77 +++++++++++++++++----------------------- src/ejabberd_s2s_out.erl | 22 ++++-------- 2 files changed, 38 insertions(+), 61 deletions(-) diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 8057c9a35..f3259b75c 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -33,8 +33,8 @@ %% API -export([start_link/0, stop/0, route/1, have_connection/1, - get_connections_pids/1, try_register/1, - remove_connection/2, start_connection/2, start_connection/3, + get_connections_pids/1, + start_connection/2, start_connection/3, dirty_get_connections/0, allow_host/2, incoming_s2s_number/0, outgoing_s2s_number/0, stop_s2s_connections/0, @@ -112,24 +112,6 @@ is_temporarly_blocked(Host) -> end end. --spec remove_connection({binary(), binary()}, pid()) -> ok. -remove_connection({From, To} = FromTo, Pid) -> - case mnesia:dirty_match_object(s2s, #s2s{fromto = FromTo, pid = Pid}) of - [#s2s{pid = Pid}] -> - F = fun() -> - mnesia:delete_object(#s2s{fromto = FromTo, pid = Pid}) - end, - case mnesia:transaction(F) of - {atomic, _} -> ok; - {aborted, Reason} -> - ?ERROR_MSG("Failed to unregister s2s connection ~ts -> ~ts: " - "Mnesia failure: ~p", - [From, To, Reason]) - end; - _ -> - ok - end. - -spec have_connection({binary(), binary()}) -> boolean(). have_connection(FromTo) -> case catch mnesia:dirty_read(s2s, FromTo) of @@ -148,31 +130,6 @@ get_connections_pids(FromTo) -> [] end. --spec try_register({binary(), binary()}) -> boolean(). -try_register({From, To} = FromTo) -> - MaxS2SConnectionsNumber = max_s2s_connections_number(FromTo), - MaxS2SConnectionsNumberPerNode = - max_s2s_connections_number_per_node(FromTo), - F = fun () -> - L = mnesia:read({s2s, FromTo}), - NeededConnections = needed_connections_number(L, - MaxS2SConnectionsNumber, - MaxS2SConnectionsNumberPerNode), - if NeededConnections > 0 -> - mnesia:write(#s2s{fromto = FromTo, pid = self()}), - true; - true -> false - end - end, - case mnesia:transaction(F) of - {atomic, Res} -> Res; - {aborted, Reason} -> - ?ERROR_MSG("Failed to register s2s connection ~ts -> ~ts: " - "Mnesia failure: ~p", - [From, To, Reason]), - false - end. - -spec dirty_get_connections() -> [{binary(), binary()}]. dirty_get_connections() -> mnesia:dirty_all_keys(s2s). @@ -269,6 +226,8 @@ init([]) -> {stop, Reason} end. +handle_call({new_connection, Args}, _From, State) -> + {reply, erlang:apply(fun new_connection_int/7, Args), State}; handle_call(Request, From, State) -> ?WARNING_MSG("Unexpected call from ~p: ~p", [From, Request]), {noreply, State}. @@ -289,6 +248,21 @@ handle_info({route, Packet}, State) -> misc:format_exception(2, Class, Reason, StackTrace)]) end, {noreply, State}; +handle_info({'DOWN', _Ref, process, Pid, _Reason}, State) -> + case mnesia:dirty_match_object(s2s, #s2s{pid = Pid, fromto = '_'}) of + [#s2s{pid = Pid, fromto = {From, To}} = Obj] -> + F = fun() -> mnesia:delete_object(Obj) end, + case mnesia:transaction(F) of + {atomic, _} -> ok; + {aborted, Reason} -> + ?ERROR_MSG("Failed to unregister s2s connection for pid ~p (~ts -> ~ts):" + "Mnesia failure: ~p", + [Pid, From, To, Reason]) + end, + {noreply, State}; + _ -> + {noreply, State} + end; handle_info(Info, State) -> ?WARNING_MSG("Unexpected info: ~p", [Info]), {noreply, State}. @@ -458,6 +432,18 @@ open_several_connections(N, MyServer, Server, From, integer(), integer(), [proplists:property()]) -> [pid()]. new_connection(MyServer, Server, From, FromTo, MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts) -> + case whereis(ejabberd_s2s) == self() of + true -> + new_connection_int(MyServer, Server, From, FromTo, + MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts); + false -> + gen_server:call(ejabberd_s2s, {new_connection, [MyServer, Server, From, FromTo, + MaxS2SConnectionsNumber, + MaxS2SConnectionsNumberPerNode, Opts]}) + end. + +new_connection_int(MyServer, Server, From, FromTo, + MaxS2SConnectionsNumber, MaxS2SConnectionsNumberPerNode, Opts) -> {ok, Pid} = ejabberd_s2s_out:start(MyServer, Server, Opts), F = fun() -> L = mnesia:read({s2s, FromTo}), @@ -474,6 +460,7 @@ new_connection(MyServer, Server, From, FromTo, case TRes of {atomic, Pid1} -> if Pid1 == Pid -> + erlang:monitor(process, Pid), ejabberd_s2s_out:connect(Pid); true -> ejabberd_s2s_out:stop_async(Pid) diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index d58396533..f057705ed 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -318,7 +318,6 @@ handle_info(Info, #{server_host := ServerHost} = State) -> terminate(Reason, #{server := LServer, remote_server := RServer} = State) -> - ejabberd_s2s:remove_connection({LServer, RServer}, self()), State1 = case Reason of normal -> State; _ -> State#{stop_reason => internal_failure} @@ -351,21 +350,12 @@ bounce_queue(State) -> end, State). -spec bounce_message_queue({binary(), binary()}, state()) -> state(). -bounce_message_queue({LServer, RServer} = FromTo, State) -> - Pids = ejabberd_s2s:get_connections_pids(FromTo), - case lists:member(self(), Pids) of - true -> - ?WARNING_MSG("Outgoing s2s connection ~ts -> ~ts is supposed " - "to be unregistered, but pid ~p still presents " - "in 's2s' table", [LServer, RServer, self()]), - State; - false -> - receive {route, Pkt} -> - State1 = bounce_packet(Pkt, State), - bounce_message_queue(FromTo, State1) - after 0 -> - State - end +bounce_message_queue(FromTo, State) -> + receive {route, Pkt} -> + State1 = bounce_packet(Pkt, State), + bounce_message_queue(FromTo, State1) + after 0 -> + State end. -spec bounce_packet(xmpp_element(), state()) -> state(). From bdd4e52699507cdf80a0ff5959439d256f9f9d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 16 Nov 2021 10:57:15 +0100 Subject: [PATCH 038/106] Make dialyzer happy --- src/ejabberd_s2s.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index f3259b75c..71b3c8e17 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -249,7 +249,7 @@ handle_info({route, Packet}, State) -> end, {noreply, State}; handle_info({'DOWN', _Ref, process, Pid, _Reason}, State) -> - case mnesia:dirty_match_object(s2s, #s2s{pid = Pid, fromto = '_'}) of + case mnesia:dirty_match_object(s2s, {s2s, '_', Pid}) of [#s2s{pid = Pid, fromto = {From, To}} = Obj] -> F = fun() -> mnesia:delete_object(Obj) end, case mnesia:transaction(F) of From 97b8373fd28d821b04eacb8da17586fffabee2f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 16 Nov 2021 10:59:53 +0100 Subject: [PATCH 039/106] Better version of dialyzer fix --- src/ejabberd_s2s.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 71b3c8e17..04490071c 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -64,7 +64,7 @@ %% once a server is temporary blocked, it stay blocked for 60 seconds --record(s2s, {fromto :: {binary(), binary()}, +-record(s2s, {fromto :: {binary(), binary()} | '_', pid :: pid()}). -record(state, {}). @@ -249,7 +249,7 @@ handle_info({route, Packet}, State) -> end, {noreply, State}; handle_info({'DOWN', _Ref, process, Pid, _Reason}, State) -> - case mnesia:dirty_match_object(s2s, {s2s, '_', Pid}) of + case mnesia:dirty_match_object(s2s, #s2s{fromto = '_', pid = Pid}) of [#s2s{pid = Pid, fromto = {From, To}} = Obj] -> F = fun() -> mnesia:delete_object(Obj) end, case mnesia:transaction(F) of From 405a5172d5bda5fd40b6a580b87f3fab1ecdd47c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Tue, 16 Nov 2021 19:39:59 +0100 Subject: [PATCH 040/106] Improve mod_multicast --- src/mod_multicast.erl | 422 ++++++++++++++---------------------------- 1 file changed, 134 insertions(+), 288 deletions(-) diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl index 161d3a4c4..fa076da70 100644 --- a/src/mod_multicast.erl +++ b/src/mod_multicast.erl @@ -35,7 +35,7 @@ %% API -export([start/2, stop/1, reload/3, - user_send_packet/1]). + user_send_packet/1]). %% gen_server callbacks -export([init/1, handle_info/2, handle_call/3, @@ -51,11 +51,6 @@ response, ts :: integer()}). --record(dest, {jid_string :: binary() | none, - jid_jid :: jid() | undefined, - type :: bcc | cc | noreply | ofrom | replyroom | replyto | to, - address :: address()}). - -type limit_value() :: {default | custom, integer()}. -record(limits, {message :: limit_value(), presence :: limit_value()}). @@ -63,14 +58,6 @@ -record(service_limits, {local :: #limits{}, remote :: #limits{}}). --type routing() :: route_single | {route_multicast, binary(), #service_limits{}}. - --record(group, {server :: binary(), - dests :: [#dest{}], - multicast :: routing() | undefined, - others :: [address()], - addresses :: [address()]}). - -record(state, {lserver :: binary(), lservice :: binary(), access :: atom(), @@ -117,7 +104,7 @@ reload(LServerS, NewOpts, OldOpts) -> user_send_packet({#presence{} = Packet, C2SState} = Acc) -> case xmpp:get_subtag(Packet, #addresses{}) of #addresses{list = Addresses} -> - {ToDeliver, _Delivereds} = split_addresses_todeliver(Addresses), + {CC, BCC, _Invalid, _Delivered} = partition_addresses(Addresses), NewState = lists:foldl( fun(Address, St) -> @@ -138,7 +125,7 @@ user_send_packet({#presence{} = Packet, C2SState} = Acc) -> undefined -> St end - end, C2SState, ToDeliver), + end, C2SState, CC ++ BCC), {Packet, NewState}; false -> Acc @@ -308,19 +295,10 @@ iq_vcard(Lang, State) -> %%%------------------------- -spec route_trusted(binary(), binary(), jid(), [jid()], stanza()) -> 'ok'. -route_trusted(LServiceS, LServerS, FromJID, - Destinations, Packet) -> - Packet_stripped = Packet, - Delivereds = [], - Dests2 = lists:map( - fun(D) -> - #dest{jid_string = jid:encode(D), - jid_jid = D, type = bcc, - address = #address{type = bcc, jid = D}} - end, Destinations), - Groups = group_dests(Dests2), - route_common(LServerS, LServiceS, FromJID, Groups, - Delivereds, Packet_stripped). +route_trusted(LServiceS, LServerS, FromJID, Destinations, Packet) -> + Addresses = [#address{type = bcc, jid = D} || D <- Destinations], + Groups = group_by_destinations(Addresses, #{}), + route_grouped(LServerS, LServiceS, FromJID, Groups, [], Packet). -spec route_untrusted(binary(), binary(), atom(), #service_limits{}, stanza()) -> 'ok'. route_untrusted(LServiceS, LServerS, Access, SLimits, Packet) -> @@ -356,50 +334,88 @@ route_untrusted(LServiceS, LServerS, Access, SLimits, Packet) -> route_untrusted2(LServiceS, LServerS, Access, SLimits, Packet) -> FromJID = xmpp:get_from(Packet), ok = check_access(LServerS, Access, FromJID), - {ok, Packet_stripped, Addresses} = strip_addresses_element(Packet), - {To_deliver, Delivereds} = split_addresses_todeliver(Addresses), - Dests = convert_dest_record(To_deliver), - {Dests2, Not_jids} = split_dests_jid(Dests), - report_not_jid(FromJID, Packet, Not_jids), - ok = check_limit_dests(SLimits, FromJID, Packet, Dests2), - Groups = group_dests(Dests2), + {ok, PacketStripped, Addresses} = strip_addresses_element(Packet), + {CC, BCC, NotJids, Rest} = partition_addresses(Addresses), + report_not_jid(FromJID, Packet, NotJids), + ok = check_limit_dests(SLimits, FromJID, Packet, length(CC) + length(BCC)), + Groups0 = group_by_destinations(CC, #{}), + Groups = group_by_destinations(BCC, Groups0), ok = check_relay(FromJID#jid.server, LServerS, Groups), - route_common(LServerS, LServiceS, FromJID, Groups, - Delivereds, Packet_stripped). + route_grouped(LServerS, LServiceS, FromJID, Groups, Rest, PacketStripped). --spec route_common(binary(), binary(), jid(), [#group{}], - [address()], stanza()) -> 'ok'. -route_common(LServerS, LServiceS, FromJID, Groups, - Delivereds, Packet_stripped) -> - Groups2 = look_cached_servers(LServerS, LServiceS, Groups), - Groups3 = build_others_xml(Groups2), - Groups4 = add_addresses(Delivereds, Groups3), - AGroups = decide_action_groups(Groups4), - act_groups(FromJID, Packet_stripped, LServiceS, - AGroups). +-spec mark_as_delivered([address()]) -> [address()]. +mark_as_delivered(Addresses) -> + [A#address{delivered = true} || A <- Addresses]. --spec act_groups(jid(), stanza(), binary(), [{routing(), #group{}}]) -> 'ok'. -act_groups(FromJID, Packet_stripped, LServiceS, AGroups) -> +-spec route_individual(jid(), [address()], [address()], [address()], stanza()) -> ok. +route_individual(From, CC, BCC, Other, Packet) -> + CCDelivered = mark_as_delivered(CC), + Addresses = CCDelivered ++ Other, + PacketWithAddresses = xmpp:append_subtags(Packet, [#addresses{list = Addresses}]), lists:foreach( - fun(AGroup) -> - perform(FromJID, Packet_stripped, LServiceS, - AGroup) - end, AGroups). - --spec perform(jid(), stanza(), binary(), - {routing(), #group{}}) -> 'ok'. -perform(From, Packet, _, - {route_single, Group}) -> + fun(#address{jid = To}) -> + ejabberd_router:route(xmpp:set_from_to(PacketWithAddresses, From, To)) + end, CC), lists:foreach( - fun(ToUser) -> - Group_others = strip_other_bcc(ToUser, Group#group.others), - route_packet(From, ToUser, Packet, - Group_others, Group#group.addresses) - end, Group#group.dests); -perform(From, Packet, _, - {{route_multicast, JID, RLimits}, Group}) -> - route_packet_multicast(From, JID, Packet, - Group#group.dests, Group#group.addresses, RLimits). + fun(#address{jid = To} = Address) -> + Packet2 = case Addresses of + [] -> + Packet; + _ -> + xmpp:append_subtags(Packet, [#addresses{list = [Address | Addresses]}]) + end, + ejabberd_router:route(xmpp:set_from_to(Packet2, From, To)) + end, BCC). + +-spec route_chunk(jid(), jid(), stanza(), [address()]) -> ok. +route_chunk(From, To, Packet, Addresses) -> + PacketWithAddresses = xmpp:append_subtags(Packet, [#addresses{list = Addresses}]), + ejabberd_router:route(xmpp:set_from_to(PacketWithAddresses, From, To)). + +-spec route_in_chunks(jid(), jid(), stanza(), integer(), [address()], [address()], [address()]) -> ok. +route_in_chunks(_From, _To, _Packet, _Limit, [], [], _) -> + ok; +route_in_chunks(From, To, Packet, Limit, CC, BCC, RestOfAddresses) when length(CC) > Limit -> + {Chunk, Rest} = lists:split(Limit, CC), + route_chunk(From, To, Packet, Chunk ++ RestOfAddresses), + route_in_chunks(From, To, Packet, Limit, Rest, BCC, RestOfAddresses); +route_in_chunks(From, To, Packet, Limit, [], BCC, RestOfAddresses) when length(BCC) > Limit -> + {Chunk, Rest} = lists:split(Limit, BCC), + route_chunk(From, To, Packet, Chunk ++ RestOfAddresses), + route_in_chunks(From, To, Packet, Limit, [], Rest, RestOfAddresses); +route_in_chunks(From, To, Packet, Limit, CC, BCC, RestOfAddresses) when length(BCC) + length(CC) > Limit -> + {Chunk, Rest} = lists:split(Limit - length(CC), BCC), + route_chunk(From, To, Packet, CC ++ Chunk ++ RestOfAddresses), + route_in_chunks(From, To, Packet, Limit, [], Rest, RestOfAddresses); +route_in_chunks(From, To, Packet, _Limit, CC, BCC, RestOfAddresses) -> + route_chunk(From, To, Packet, CC ++ BCC ++ RestOfAddresses). + +-spec route_multicast(jid(), jid(), [address()], [address()], [address()], stanza(), #limits{}) -> ok. +route_multicast(From, To, CC, BCC, RestOfAddresses, Packet, Limits) -> + {_Type, Limit} = get_limit_number(element(1, Packet), + Limits), + route_in_chunks(From, To, Packet, Limit, CC, BCC, RestOfAddresses). + +-spec route_grouped(binary(), binary(), jid(), #{}, [address()], stanza()) -> ok. +route_grouped(LServer, LService, From, Groups, RestOfAddresses, Packet) -> + maps:fold( + fun(Server, {CC, BCC}, _) -> + OtherCC = maps:fold( + fun(Server2, _, Res) when Server2 == Server -> + Res; + (_, {CC2, _}, Res) -> + mark_as_delivered(CC2) ++ Res + end, [], Groups), + case search_server_on_cache(Server, + LServer, LService, + {?MAXTIME_CACHE_POSITIVE, + ?MAXTIME_CACHE_NEGATIVE}) of + route_single -> + route_individual(From, CC, BCC, OtherCC ++ RestOfAddresses, Packet); + {route_multicast, Service, Limits} -> + route_multicast(From, Service, CC, BCC, OtherCC ++ RestOfAddresses, Packet, Limits) + end + end, ok, Groups). %%%------------------------- %%% Check access permission @@ -425,245 +441,89 @@ strip_addresses_element(Packet) -> throw(eadsele) end. -%%%------------------------- -%%% Strip third-party bcc 'addresses' -%%%------------------------- - -strip_other_bcc(#dest{jid_jid = ToUserJid}, Group_others) -> - lists:filter( - fun(#address{jid = JID, type = Type}) -> - case {JID, Type} of - {ToUserJid, bcc} -> true; - {_, bcc} -> false; - _ -> true - end - end, - Group_others). - %%%------------------------- %%% Split Addresses %%%------------------------- --spec split_addresses_todeliver([address()]) -> {[address()], [address()]}. -split_addresses_todeliver(Addresses) -> - lists:partition( - fun(#address{delivered = true}) -> - false; - (#address{type = Type}) -> - case Type of - to -> true; - cc -> true; - bcc -> true; - _ -> false - end - end, Addresses). +partition_addresses(Addresses) -> + lists:foldl( + fun(#address{delivered = true} = A, {C, B, I, D}) -> + {C, B, I, [A | D]}; + (#address{type = T, jid = undefined} = A, {C, B, I, D}) + when T == to; T == cc; T == bcc -> + {C, B, [A | I], D}; + (#address{type = T} = A, {C, B, I, D}) + when T == to; T == cc -> + {[A | C], B, I, D}; + (#address{type = bcc} = A, {C, B, I, D}) -> + {C, [A | B], I, D}; + (A, {C, B, I, D}) -> + {C, B, I, [A | D]} + end, {[], [], [], []}, Addresses). %%%------------------------- %%% Check does not exceed limit of destinations %%%------------------------- --spec check_limit_dests(#service_limits{}, jid(), stanza(), [address()]) -> ok. -check_limit_dests(SLimits, FromJID, Packet, - Addresses) -> +-spec check_limit_dests(#service_limits{}, jid(), stanza(), integer()) -> ok. +check_limit_dests(SLimits, FromJID, Packet, NumOfAddresses) -> SenderT = sender_type(FromJID), Limits = get_slimit_group(SenderT, SLimits), - Type_of_stanza = type_of_stanza(Packet), - {_Type, Limit_number} = get_limit_number(Type_of_stanza, - Limits), - case length(Addresses) > Limit_number of + StanzaType = type_of_stanza(Packet), + {_Type, Limit} = get_limit_number(StanzaType, + Limits), + case NumOfAddresses > Limit of false -> ok; true -> throw(etoorec) end. -%%%------------------------- -%%% Convert Destination XML to record -%%%------------------------- --spec convert_dest_record([address()]) -> [#dest{}]. -convert_dest_record(Addrs) -> - lists:map( - fun(#address{jid = undefined, type = Type} = Addr) -> - #dest{jid_string = none, - type = Type, address = Addr}; - (#address{jid = JID, type = Type} = Addr) -> - #dest{jid_string = jid:encode(JID), jid_jid = JID, - type = Type, address = Addr} - end, Addrs). - -%%%------------------------- -%%% Split destinations by existence of JID -%%% and send error messages for other dests -%%%------------------------- - --spec split_dests_jid([#dest{}]) -> {[#dest{}], [#dest{}]}. -split_dests_jid(Dests) -> - lists:partition(fun (Dest) -> - case Dest#dest.jid_string of - none -> false; - _ -> true - end - end, - Dests). - --spec report_not_jid(jid(), stanza(), [#dest{}]) -> any(). -report_not_jid(From, Packet, Dests) -> - Dests2 = [fxml:element_to_binary(xmpp:encode(Dest#dest.address)) - || Dest <- Dests], - [route_error( - xmpp:set_from_to(Packet, From, From), jid_malformed, - str:format(?T("This service can not process the address: ~s"), [D])) - || D <- Dests2]. +-spec report_not_jid(jid(), stanza(), [address()]) -> any(). +report_not_jid(From, Packet, Addresses) -> + lists:foreach( + fun(Address) -> + route_error( + xmpp:set_from_to(Packet, From, From), jid_malformed, + str:format(?T("This service can not process the address: ~s"), + [fxml:element_to_binary(xmpp:encode(Address))])) + end, Addresses). %%%------------------------- %%% Group destinations by their servers %%%------------------------- --spec group_dests([#dest{}]) -> [#group{}]. -group_dests(Dests) -> - D = lists:foldl(fun (Dest, Dict) -> - ServerS = (Dest#dest.jid_jid)#jid.server, - dict:append(ServerS, Dest, Dict) - end, - dict:new(), Dests), - Keys = dict:fetch_keys(D), - [#group{server = Key, dests = dict:fetch(Key, D), - addresses = [], others = []} - || Key <- Keys]. - -%%%------------------------- -%%% Look for cached responses -%%%------------------------- - -look_cached_servers(LServerS, LServiceS, Groups) -> - [look_cached(LServerS, LServiceS, Group) || Group <- Groups]. - -look_cached(LServerS, LServiceS, G) -> - Maxtime_positive = (?MAXTIME_CACHE_POSITIVE), - Maxtime_negative = (?MAXTIME_CACHE_NEGATIVE), - Cached_response = search_server_on_cache(G#group.server, - LServerS, LServiceS, - {Maxtime_positive, - Maxtime_negative}), - G#group{multicast = Cached_response}. - -%%%------------------------- -%%% Build delivered XML element -%%%------------------------- - -build_others_xml(Groups) -> - [Group#group{others = - build_other_xml(Group#group.dests)} - || Group <- Groups]. - -build_other_xml(Dests) -> - lists:foldl(fun (Dest, R) -> - XML = Dest#dest.address, - case Dest#dest.type of - to -> [add_delivered(XML) | R]; - cc -> [add_delivered(XML) | R]; - _ -> [XML | R] - end - end, - [], Dests). - --spec add_delivered(address()) -> address(). -add_delivered(Addr) -> - Addr#address{delivered = true}. - -%%%------------------------- -%%% Add preliminary packets -%%%------------------------- - -add_addresses(Delivereds, Groups) -> - Ps = [Group#group.others || Group <- Groups], - add_addresses2(Delivereds, Groups, [], [], Ps). - -add_addresses2(_, [], Res, _, []) -> Res; -add_addresses2(Delivereds, [Group | Groups], Res, Pa, - [Pi | Pz]) -> - Addresses = lists:append([Delivereds] ++ Pa ++ Pz), - Group2 = Group#group{addresses = Addresses}, - add_addresses2(Delivereds, Groups, [Group2 | Res], - [Pi | Pa], Pz). - -%%%------------------------- -%%% Decide action groups -%%%------------------------- - --spec decide_action_groups([#group{}]) -> [{routing(), #group{}}]. -decide_action_groups(Groups) -> - [{Group#group.multicast, Group} - || Group <- Groups]. +group_by_destinations(Addrs, Map) -> + lists:foldl( + fun + (#address{type = Type, jid = #jid{lserver = Server}} = Addr, Map2) when Type == to; Type == cc -> + maps:update_with(Server, + fun({CC, BCC}) -> + {[Addr | CC], BCC} + end, {[Addr], []}, Map2); + (#address{type = bcc, jid = #jid{lserver = Server}} = Addr, Map2) -> + maps:update_with(Server, + fun({CC, BCC}) -> + {CC, [Addr | BCC]} + end, {[], [Addr]}, Map2) + end, Map, Addrs). %%%------------------------- %%% Route packet %%%------------------------- --spec route_packet(jid(), #dest{}, stanza(), [addresses()], [addresses()]) -> 'ok'. -route_packet(From, ToDest, Packet, Others, Addresses) -> - Dests = case ToDest#dest.type of - bcc -> []; - _ -> [ToDest] - end, - route_packet2(From, ToDest#dest.jid_string, Dests, - Packet, {Others, Addresses}). - --spec route_packet_multicast(jid(), binary(), stanza(), [#dest{}], [address()], #limits{}) -> 'ok'. -route_packet_multicast(From, ToS, Packet, Dests, - Addresses, Limits) -> - Type_of_stanza = type_of_stanza(Packet), - {_Type, Limit_number} = get_limit_number(Type_of_stanza, - Limits), - Fragmented_dests = fragment_dests(Dests, Limit_number), - lists:foreach(fun(DFragment) -> - route_packet2(From, ToS, DFragment, Packet, - Addresses) - end, Fragmented_dests). - --spec route_packet2(jid(), binary(), [#dest{}], stanza(), {[address()], [address()]} | [address()]) -> 'ok'. -route_packet2(From, ToS, Dests, Packet, Addresses) -> - Els = case append_dests(Dests, Addresses) of - [] -> - xmpp:get_els(Packet); - ACs -> - [#addresses{list = ACs}|xmpp:get_els(Packet)] - end, - Packet2 = xmpp:set_els(Packet, Els), - ToJID = stj(ToS), - ejabberd_router:route(xmpp:set_from_to(Packet2, From, ToJID)). - --spec append_dests([#dest{}], {[address()], [address()]} | [address()]) -> [address()]. -append_dests(_Dests, {Others, Addresses}) -> - Addresses ++ Others; -append_dests([], Addresses) -> Addresses; -append_dests([Dest | Dests], Addresses) -> - append_dests(Dests, [Dest#dest.address | Addresses]). - %%%------------------------- %%% Check relay %%%------------------------- --spec check_relay(binary(), binary(), [#group{}]) -> ok. +-spec check_relay(binary(), binary(), #{}) -> ok. check_relay(RS, LS, Gs) -> - case check_relay_required(RS, LS, Gs) of - false -> ok; - true -> throw(edrelay) + case lists:suffix(str:tokens(LS, <<".">>), + str:tokens(RS, <<".">>)) orelse + (maps:is_key(LS, Gs) andalso maps:size(Gs) == 1) of + true -> ok; + _ -> throw(edrelay) end. --spec check_relay_required(binary(), binary(), [#group{}]) -> boolean(). -check_relay_required(RServer, LServerS, Groups) -> - case lists:suffix(str:tokens(LServerS, <<".">>), - str:tokens(RServer, <<".">>)) of - true -> false; - false -> check_relay_required(LServerS, Groups) - end. - --spec check_relay_required(binary(), [#group{}]) -> boolean(). -check_relay_required(LServerS, Groups) -> - lists:any(fun (Group) -> Group#group.server /= LServerS - end, - Groups). - %%%------------------------- %%% Check protocol support: Send request %%%------------------------- @@ -1060,20 +920,6 @@ get_slimit_group(local, SLimits) -> get_slimit_group(remote, SLimits) -> SLimits#service_limits.remote. -fragment_dests(Dests, Limit_number) -> - {R, _} = lists:foldl(fun (Dest, {Res, Count}) -> - case Count of - Limit_number -> - Head2 = [Dest], {[Head2 | Res], 0}; - _ -> - [Head | Tail] = Res, - Head2 = [Dest | Head], - {[Head2 | Tail], Count + 1} - end - end, - {[[]], 0}, Dests), - R. - %%%------------------------- %%% Limits: XEP-0128 Service Discovery Extensions %%%------------------------- From a590e81922c2d9bd10e9bc3ceb0bab0f32cbb82c Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 17 Nov 2021 12:39:09 +0100 Subject: [PATCH 041/106] Add DIAGNOSTIC to rebar3 coveralls, this fails since some days ago --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7baa5790f..ac384e375 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -173,7 +173,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - rebar3 as test coveralls send + DIAGNOSTIC=1 rebar3 as test coveralls send curl -v -k https://coveralls.io/webhook \ --header "Content-Type: application/json" \ --data '{"repo_name":"$GITHUB_REPOSITORY", From 689749a5630c417fd52345394a7e8cd3c7ed1168 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 17 Nov 2021 13:22:34 +0100 Subject: [PATCH 042/106] Update Jose to 1.11.1 (the last in hex.pm correctly versioned) --- mix.exs | 2 +- rebar.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index 8debcd6a1..0f991a499 100644 --- a/mix.exs +++ b/mix.exs @@ -113,7 +113,7 @@ defmodule Ejabberd.MixProject do {:fast_yaml, "~> 1.0"}, {:idna, "~> 6.0"}, {:jiffy, "~> 1.0.5"}, - {:jose, "~> 1.8"}, + {:jose, "~> 1.11.1"}, {:lager, "~> 3.9.1"}, {:mqtree, "~> 1.0"}, {:p1_acme, "~> 1.0"}, diff --git a/rebar.config b/rebar.config index f0d916e81..5597b6c1e 100644 --- a/rebar.config +++ b/rebar.config @@ -38,7 +38,7 @@ {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.32"}}}, {idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "1.0.5"}}}, - {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.9.0"}}}, + {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}}, {lager, ".*", {git, "https://github.com/erlang-lager/lager", {tag, "3.9.1"}}}, {if_var_true, lua, {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.3"}}}}, From 15d3ebb8425cc545810e882e0fe8cbf8cc276fc3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 17 Nov 2021 16:50:14 +0100 Subject: [PATCH 043/106] Fix Dialyzer warning, old passwd tuple don't match current tuple definition --- src/ejabberd_auth_sql.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl index e51bf276c..50cc1902e 100644 --- a/src/ejabberd_auth_sql.erl +++ b/src/ejabberd_auth_sql.erl @@ -299,8 +299,8 @@ export(_Server) -> ["username=%(LUser)s", "server_host=%(LServer)s", "password=%(Password)s"])]; - (Host, #passwd{us = {LUser, LServer}, - password = {scram, StoredKey1, ServerKey, Salt, IterationCount}}) + (Host, {passwd, {LUser, LServer}, + {scram, StoredKey1, ServerKey, Salt, IterationCount}}) when LServer == Host -> Hash = sha, StoredKey = scram_hash_encode(Hash, StoredKey1), From 89ad8a55027bfbe5482f4b580cb5e8d74cca8b2f Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Tue, 23 Nov 2021 08:43:54 +0300 Subject: [PATCH 044/106] Add mod_conversejs --- src/mod_conversejs.erl | 148 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 src/mod_conversejs.erl diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl new file mode 100644 index 000000000..e8aede2c7 --- /dev/null +++ b/src/mod_conversejs.erl @@ -0,0 +1,148 @@ +%%%---------------------------------------------------------------------- +%%% File : mod_conversejs.erl +%%% Author : Alexey Shchepin +%%% Purpose : Implements REST API for ejabberd using JSON data +%%% Created : 8 Nov 2021 by Alexey Shchepin +%%% +%%% +%%% ejabberd, Copyright (C) 2002-2021 ProcessOne +%%% +%%% This program is free software; you can redistribute it and/or +%%% modify it under the terms of the GNU General Public License as +%%% published by the Free Software Foundation; either version 2 of the +%%% License, or (at your option) any later version. +%%% +%%% This program is distributed in the hope that it will be useful, +%%% but WITHOUT ANY WARRANTY; without even the implied warranty of +%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +%%% General Public License for more details. +%%% +%%% You should have received a copy of the GNU General Public License along +%%% with this program; if not, write to the Free Software Foundation, Inc., +%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +%%% +%%%---------------------------------------------------------------------- + +-module(mod_conversejs). + +-author('alexey@process-one.net'). + +-behaviour(gen_mod). + +-export([start/2, stop/1, reload/3, process/2, depends/2, + mod_opt_type/1, mod_options/1, mod_doc/0]). + +-include_lib("xmpp/include/xmpp.hrl"). +-include("logger.hrl"). +-include("ejabberd_http.hrl"). +-include("translate.hrl"). +-include("ejabberd_web_admin.hrl"). + +start(_Host, _Opts) -> + ok. + +stop(_Host) -> + ok. + +reload(_Host, _NewOpts, _OldOpts) -> + ok. + +depends(_Host, _Opts) -> + []. + +process([], #request{method = 'GET'}) -> + Host = ejabberd_config:get_myname(), + Domain = gen_mod:get_module_opt(Host, ?MODULE, default_domain), + Script = gen_mod:get_module_opt(Host, ?MODULE, conversejs_script), + CSS = gen_mod:get_module_opt(Host, ?MODULE, conversejs_css), + Init = [{<<"discover_connection_methods">>, false}, + {<<"jid">>, Domain}, + {<<"default_domain">>, Domain}, + {<<"domain_placeholder">>, Domain}, + {<<"view_mode">>, <<"fullscreen">>}], + Init2 = + case gen_mod:get_module_opt(Host, ?MODULE, websocket_url) of + undefined -> Init; + WSURL -> [{<<"websocket_url">>, WSURL} | Init] + end, + Init3 = + case gen_mod:get_module_opt(Host, ?MODULE, bosh_service_url) of + undefined -> Init2; + BoshURL -> [{<<"bosh_service_url">>, BoshURL} | Init2] + end, + {200, [html], + [<<"">>, + <<"">>, + <<"">>, + <<"">>, + <<"">>, + <<"">>, + <<"">>, + <<"">>, + <<"">>, + <<"">>, + <<"">>]}; +process(_, _) -> + ejabberd_web:error(not_found). + +mod_opt_type(bosh_service_url) -> + econf:either(undefined, econf:binary()); +mod_opt_type(websocket_url) -> + econf:either(undefined, econf:binary()); +mod_opt_type(conversejs_script) -> + econf:binary(); +mod_opt_type(conversejs_css) -> + econf:binary(); +mod_opt_type(default_domain) -> + econf:binary(). + +mod_options(_) -> + [{bosh_service_url, undefined}, + {websocket_url, undefined}, + {default_domain, ejabberd_config:get_myname()}, + {conversejs_script, <<"https://cdn.conversejs.org/8.0.1/dist/converse.min.js">>}, + {conversejs_css, <<"https://cdn.conversejs.org/8.0.1/dist/converse.min.css">>}]. + +mod_doc() -> + #{desc => + [?T("This module serves a simple Converse.js page."), "", + ?T("To use this module, in addition to adding it to the 'modules' " + "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " + "http://../listen-options/#request-handlers[request_handlers].")], + example => + ["listen:", + " -", + " port: 5280", + " module: ejabberd_http", + " request_handlers:", + " \"/websocket\": ejabberd_http_ws" + " \"/conversejs\": mod_conversejs", + "", + "modules:", + " mod_conversejs:", + " websocket_url: \"ws://example.org:5280/websocket\""], + opts => + [{websocket_url, + #{value => ?T("WebsocketURL"), + desc => + ?T("A websocket URL to which Converse.js can connect to.")}}, + {bosh_service_url, + #{value => ?T("BoshURL"), + desc => + ?T("BOSH service URL to which Converse.js can connect to.")}}, + {default_domain, + #{value => ?T("Domain"), + desc => + ?T("Specify a domain to act as the default for user JIDs.")}}, + {conversejs_script, + #{value => ?T("URL"), + desc => + ?T("Converse.js main script URL.")}}, + {conversejs_css, + #{value => ?T("URL"), + desc => + ?T("Converse.js CSS URL.")}}] + }. From 75b133d968be3e4914a86bd0db514fe2a62aea21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 29 Nov 2021 15:20:10 +0100 Subject: [PATCH 045/106] Update deps --- rebar.config | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rebar.config b/rebar.config index 5597b6c1e..3490d976c 100644 --- a/rebar.config +++ b/rebar.config @@ -30,11 +30,11 @@ {if_var_true, redis, {eredis, ".*", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, {if_var_true, sip, - {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.43"}}}}, + {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.44"}}}}, {if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.10"}}}}, {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.13"}}}, - {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.47"}}}, + {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.48"}}}, {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.32"}}}, {idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}}, {jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "1.0.5"}}}, @@ -43,12 +43,12 @@ {if_var_true, lua, {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.3"}}}}, {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.14"}}}, - {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.13"}}}, + {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.16"}}}, {if_var_true, mysql, {p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.19"}}}}, {p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.10"}}}, {if_var_true, pgsql, - {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.12"}}}}, + {p1_pgsql, ".*", {git, "https://github.com/processone/p1_pgsql", {tag, "1.1.16"}}}}, {p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.23"}}}, {pkix, ".*", {git, "https://github.com/processone/pkix", {tag, "1.0.8"}}}, {if_not_rebar3, %% Needed because modules are not fully migrated to new structure and mix @@ -59,7 +59,7 @@ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.27"}}}, {if_var_true, stun, {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.45"}}}}, - {xmpp, ".*", {git, "https://github.com/processone/xmpp", "a85dc699ff02471ecad08d85e3bed87c57c48aba"}}, + {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.5.6"}}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.12"}}} ]}. From ad3c91b86ef24da4a2a033065268198bfea6ff56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 29 Nov 2021 15:36:57 +0100 Subject: [PATCH 046/106] Update mix deps --- mix.exs | 4 ++-- mix.lock | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/mix.exs b/mix.exs index 0f991a499..fbed459a4 100644 --- a/mix.exs +++ b/mix.exs @@ -102,7 +102,7 @@ defmodule Ejabberd.MixProject do end defp deps do - [{:base64url, "~> 0.0.1"}, + [{:base64url, "~> 1.0"}, {:cache_tab, "~> 1.0"}, {:distillery, "~> 2.0"}, {:eimp, "~> 1.0"}, @@ -124,7 +124,7 @@ defmodule Ejabberd.MixProject do {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, {:stun, "~> 1.0"}, - {:xmpp, git: "https://github.com/processone/xmpp", ref: "a85dc699ff02471ecad08d85e3bed87c57c48aba", override: true}, + {:xmpp, ">= 1.5.5"}, {:yconf, "~> 1.0"}] ++ cond_deps() end diff --git a/mix.lock b/mix.lock index a5b158e0f..773cc2bbf 100644 --- a/mix.lock +++ b/mix.lock @@ -1,36 +1,36 @@ %{ "artificery": {:hex, :artificery, "0.4.3", "0bc4260f988dcb9dda4b23f9fc3c6c8b99a6220a331534fdf5bf2fd0d4333b02", [:mix], [], "hexpm", "12e95333a30e20884e937abdbefa3e7f5e05609c2ba8cf37b33f000b9ffc0504"}, - "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm", "fab09b20e3f5db886725544cbcf875b8e73ec93363954eb8a1a9ed834aa8c1f9"}, + "base64url": {:hex, :base64url, "1.0.1", "f8c7f2da04ca9a5d0f5f50258f055e1d699f0e8bf4cfdb30b750865368403cf6", [:rebar3], [], "hexpm", "f9b3add4731a02a9b0410398b475b33e7566a695365237a6bdee1bb447719f5c"}, "cache_tab": {:hex, :cache_tab, "1.0.29", "6c161988620b788d8df28c8f6af557571609c8e4b671dbadab295a4722cd501b", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "a02a638021cce91ed1a8628dcbb4795bf5c01c9d11db8c613065923142824ce9"}, "distillery": {:hex, :distillery, "2.1.1", "f9332afc2eec8a1a2b86f22429e068ef35f84a93ea1718265e740d90dd367814", [:mix], [{:artificery, "~> 0.2", [hex: :artificery, repo: "hexpm", optional: false]}], "hexpm", "bbc7008b0161a6f130d8d903b5b3232351fccc9c31a991f8fcbf2a12ace22995"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.13", "0c98163e7d04a15feb62000e1a891489feb29f3d10cb57d4f845c405852bbef8", [:mix], [], "hexpm", "d602c26af3a0af43d2f2645613f65841657ad6efc9f0e361c3b6c06b578214ba"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.17", "6f3c7e94170377ba45241d394389e800fb15adc5de51d0a3cd52ae766aafd63f", [:mix], [], "hexpm", "f93ac89c9feca61c165b264b5837bf82344d13bebc634cd575cb711e2e342023"}, "eimp": {:hex, :eimp, "1.0.21", "2e918a5dc9a1959ef8713a2360499e3baeee64cfd7881bd9d1f361ca9ddf07e8", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "998f58538f58aa0cff103414994d7ce56dc253e6576cd6fb40c1ead64aa73a28"}, "epam": {:hex, :epam, "1.0.12", "2a5625d4133bca4b3943791a3f723ba764455a461ae9b6ba5debb262efcf4b40", [:rebar3], [], "hexpm", "54c166c4459cef72f2990a3d89a8f0be27180fe0ab0f24b28ddcc3b815f49f7f"}, - "esip": {:hex, :esip, "1.0.43", "1cbdc073073f80b9b50e2759f66ca13a353eb4f874bcf92501bd4cd767e34d46", [:rebar3], [{:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.44", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "b2c758ae52c4588e0399c0b4ce550bfa56551a5a2f828a28389f2614797e4f4b"}, - "ex_doc": {:hex, :ex_doc, "0.25.0", "4070a254664ee5495c2f7cce87c2f43064a8752f7976f2de4937b65871b05223", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2d90883bd4f3d826af0bde7fea733a4c20adba1c79158e2330f7465821c8949b"}, + "esip": {:hex, :esip, "1.0.44", "dcf3a92e581210ab161d8a8201245df5a56058460b08d89cf3193d944adff71e", [:rebar3], [{:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.45", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "ea2086a1de95fe48f40b78251f74a6828799e318c55da346934e7d4fea675f04"}, + "ex_doc": {:hex, :ex_doc, "0.26.0", "1922164bac0b18b02f84d6f69cab1b93bc3e870e2ad18d5dacb50a9e06b542a3", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2775d66e494a9a48355db7867478ffd997864c61c65a47d31c4949459281c78d"}, "ezlib": {:hex, :ezlib, "1.0.10", "c1c24eb18944cfde55f0574e9922d5b0392fa864282f769f82b2ea15e54f6003", [:rebar3], [], "hexpm", "1d317f1d85373686199eb3b4164d3477e95033ac68e45a95ba18e7b7a8c23241"}, "fast_tls": {:hex, :fast_tls, "1.1.13", "828cdc75e1e8fce8158846d2b971d8b4fe2b2ddcc75b759e88d751079bf78afd", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d1f422af40c7777fe534496f508ee86515cb929ad10f7d1d56aa94ce899b44a0"}, - "fast_xml": {:hex, :fast_xml, "1.1.47", "bd1d6c081b69c7bce0d2f22b013c1b864ed2588d48f34e2156d9428f8f772c66", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "dd014c45247498effb9a28cf98cb716db79be635ad1e98c951240763119f24c7"}, + "fast_xml": {:hex, :fast_xml, "1.1.48", "d41d14015227999a2367264cc97ac1e6770285aab1dc69545ac4f822be01a2d2", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "afcf9b808c77599395d4bd22ed4560b3d82aa1a24ff5b65f3930fe72a423b3cf"}, "fast_yaml": {:hex, :fast_yaml, "1.0.32", "43f53a2c8572f2e4d66cd4e787fc6761b1c65b9132e42c511d8b9540b0989d65", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "7258e322739ff0824237ebe44cd158e0bf52cd27a15fe731cf92f4b4c70b913e"}, "goldrush": {:hex, :goldrush, "0.1.9", "f06e5d5f1277da5c413e84d5a2924174182fb108dabb39d5ec548b27424cd106", [:rebar3], [], "hexpm", "99cb4128cffcb3227581e5d4d803d5413fa643f4eb96523f77d9e6937d994ceb"}, "idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "4bdd305eb64e18b0273864920695cb18d7a2021f31a11b9c5fbcd9a253f936e2"}, "jiffy": {:hex, :jiffy, "1.0.5", "a69b58faf7123534c20e1b0b7ae97ac52079ca02ed4b6989b4b380179cd63a54", [:rebar3], [], "hexpm", "b617a53f46ae84f20d0c38951367dc947a2cf8cff922aa5c6ac6b64b8b052289"}, - "jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm", "6429c4fee52b2dda7861ee19a4f09c8c1ffa213bee3a1ec187828fde95d447ed"}, + "jose": {:hex, :jose, "1.11.1", "59da64010c69aad6cde2f5b9248b896b84472e99bd18f246085b7b9fe435dcdb", [:mix, :rebar3], [], "hexpm", "078f6c9fb3cd2f4cfafc972c814261a7d1e8d2b3685c0a76eb87e158efff1ac5"}, "lager": {:hex, :lager, "3.9.2", "4cab289120eb24964e3886bd22323cb5fefe4510c076992a23ad18cf85413d8c", [:rebar3], [{:goldrush, "0.1.9", [hex: :goldrush, repo: "hexpm", optional: false]}], "hexpm", "7f904d9e87a8cb7e66156ed31768d1c8e26eba1d54f4bc85b1aa4ac1f6340c28"}, "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.15.1", "b5888c880d17d1cc3e598f05cdb5b5a91b7b17ac4eaf5f297cb697663a1094dd", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "db68c173234b07ab2a07f645a5acdc117b9f99d69ebf521821d89690ae6c6ec8"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "mqtree": {:hex, :mqtree, "1.0.14", "d201a79b51a9232b80e764b4b77a866f7c30a90c7ac6205d71f391eb3ea7eb31", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "8626dac5e862b575eaf4836f0fc1be5a7c8435c378c5a309e34ee012d48b6f6e"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.1.0", "3a6fca1550363552e54c216debb6a9e95bd8d32348938e13de5eda962c0d7f89", [:mix], [], "hexpm", "08eb32d66b706e913ff748f11694b17981c0b04a33ef470e33e11b3d3ac8f54b"}, - "p1_acme": {:hex, :p1_acme, "1.0.13", "fec71df416004ce49e295f4846fe5ba3478b41fbe4f73a06b4a8fbc967d6e659", [:rebar3], [{:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.0.5", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.9.0", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.12", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "a2ce9d4904304df020c8e92e8577e0fc88f32623540656317c7e25440b4ac8d2"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.2.0", "b44d75e2a6542dcb6acf5d71c32c74ca88960421b6874777f79153bbbbd7dccc", [:mix], [], "hexpm", "52b2871a7515a5ac49b00f214e4165a40724cf99798d8e4a65e4fd64ebd002c1"}, + "p1_acme": {:hex, :p1_acme, "1.0.16", "88b84cc24c9b6eb87204ea53969ccd9b524dcd4142de632441fdd2859ccab778", [:rebar3], [{:base64url, "1.0.1", [hex: :base64url, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:jiffy, "1.0.5", [hex: :jiffy, repo: "hexpm", optional: false]}, {:jose, "1.11.1", [hex: :jose, repo: "hexpm", optional: false]}, {:yconf, "1.0.12", [hex: :yconf, repo: "hexpm", optional: false]}], "hexpm", "ec0ef380a7345c38b57899733f6fece97c337a3d44fd02cc8898f6a2491a38a8"}, "p1_mysql": {:hex, :p1_mysql, "1.0.19", "22f1be58397780a7d580a954e7af66cde32a29dee1a24ab2aa196272fc654a4a", [:rebar3], [], "hexpm", "88f6cdb510e8959c14b6ae84ccda04967e3de239228f859d8341da67949622b1"}, "p1_oauth2": {:hex, :p1_oauth2, "0.6.10", "09ba1fbd447b1f480b223903e36d0415f21be592a1b00db964eea01285749028", [:rebar3], [], "hexpm", "c79cb61ababee4a8c85409b7f4932035797c093aeef1f9f53985e512b26f2a64"}, "p1_pgsql": {:hex, :p1_pgsql, "1.1.12", "10ae79eeb35ea98c0424a8b6420542fef9e4469eb12ccf41475d10840c291e68", [:rebar3], [], "hexpm", "32203f779e01cf0353270df24833a1d831ad7cb3e3e8e35a7556dfa1f40948d5"}, "p1_utils": {:hex, :p1_utils, "1.0.23", "7f94466ada69bd982ea7bb80fbca18e7053e7d0b82c9d9e37621fa508587069b", [:rebar3], [], "hexpm", "47f21618694eeee5006af1c88731ad86b757161e7823c29b6f73921b571c8502"}, "pkix": {:hex, :pkix, "1.0.8", "98ea05243847fd4504f7c7a0cd82cecd1010ac327a082e1c674c5384006eae75", [:rebar3], [], "hexpm", "399508819501fab9d2e586dfa601b5ee3ef22b5612d3db58204dd2d089ef45d7"}, "stringprep": {:hex, :stringprep, "1.0.27", "02808c7024bc6285ca6a8a67e7addfc16f35dda55551a582c5181d8ea960e890", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "a5967b1144ca8002a58a03d16dd109fbd0bcdb82616cead2f983944314af6a00"}, - "stun": {:hex, :stun, "1.0.44", "30b6b774864b24b05ba901291abe583bff19081e7c4efb3361df50b781ec9d3b", [:rebar3], [{:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "e45bba816cbefff01d820e49e66814f450df25a7a468a70d68d1e64218d46520"}, + "stun": {:hex, :stun, "1.0.45", "3363fd4911f408c712c48baa3811548768dcd49a6c30d8f3c54c119bbbacd3ea", [:rebar3], [{:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "89774eb5500d5360dc0c1d0504b34afa891f91c807637309c449841ae91f7818"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, - "xmpp": {:hex, :xmpp, "1.5.4", "6cd8144b3fe04745dc2cb3e746d6f2a963bb283db48a61f159b49cbe3fab8623", [:rebar3], [{:ezlib, "1.0.10", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.47", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.27", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "3bc2b5cb24e52964fb11641422ce2b7ba7c261dd50080689a1cbe3d952a9db35"}, + "xmpp": {:hex, :xmpp, "1.5.6", "09259177a39c880d682817932f4da0537c471160fd43aa891ea9cb71cf827b52", [:rebar3], [{:ezlib, "1.0.10", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.48", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.27", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "59b7317c4077d3384f9a891e0517a591cdbd44a323260b835eafbede4f4eb12e"}, "yconf": {:hex, :yconf, "1.0.12", "78c119d39bb805207fcb7671cb884805d75ee89c9ec98632b678f90a597dee2c", [:rebar3], [{:fast_yaml, "1.0.32", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "12faa51c281e95bcb6abf185fd034a242209621a7bb04b6cc411c867b192e207"}, } From 0372878ba5e0c2a2fdfe445a2aae0973c5424fe6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 1 Dec 2021 10:22:41 +0100 Subject: [PATCH 047/106] Minor improvements in conversejs documentation --- src/mod_conversejs.erl | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index e8aede2c7..0b5d57a15 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -1,7 +1,7 @@ %%%---------------------------------------------------------------------- %%% File : mod_conversejs.erl %%% Author : Alexey Shchepin -%%% Purpose : Implements REST API for ejabberd using JSON data +%%% Purpose : Serve simple page for Converse.js XMPP web browser client %%% Created : 8 Nov 2021 by Alexey Shchepin %%% %%% @@ -103,23 +103,29 @@ mod_options(_) -> [{bosh_service_url, undefined}, {websocket_url, undefined}, {default_domain, ejabberd_config:get_myname()}, - {conversejs_script, <<"https://cdn.conversejs.org/8.0.1/dist/converse.min.js">>}, - {conversejs_css, <<"https://cdn.conversejs.org/8.0.1/dist/converse.min.css">>}]. + {conversejs_script, <<"https://cdn.conversejs.org/dist/converse.min.js">>}, + {conversejs_css, <<"https://cdn.conversejs.org/dist/converse.min.css">>}]. mod_doc() -> #{desc => - [?T("This module serves a simple Converse.js page."), "", + [?T("This module serves a simple page for the " + "https://conversejs.org/[Converse] XMPP web browser client."), "", ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " - "http://../listen-options/#request-handlers[request_handlers].")], + "http://../listen-options/#request-handlers[request_handlers]."), "", + ?T("You must also setup either the option 'websocket_url' or 'bosh_service_url'."), "", + ?T("By default, the options 'conversejs_css' and 'conversejs_script'" + " point to the public Converse.js client. Alternatively, you can" + " host the client locally using _`mod_http_fileserver`_.") + ], example => ["listen:", " -", " port: 5280", " module: ejabberd_http", " request_handlers:", - " \"/websocket\": ejabberd_http_ws" - " \"/conversejs\": mod_conversejs", + " /websocket: ejabberd_http_ws", + " /conversejs: mod_conversejs", "", "modules:", " mod_conversejs:", @@ -136,7 +142,9 @@ mod_doc() -> {default_domain, #{value => ?T("Domain"), desc => - ?T("Specify a domain to act as the default for user JIDs.")}}, + ?T("Specify a domain to act as the default for user JIDs. " + "The default value is the first domain defined in the " + "ejabberd configuration file.")}}, {conversejs_script, #{value => ?T("URL"), desc => From dab4c0cc107d8e829f2e68fa37605fa07b26f831 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 3 Dec 2021 12:16:20 +0100 Subject: [PATCH 048/106] New allow_modules option to restrict registration modules --- src/mod_register.erl | 26 ++++++++++++++++++++++---- src/mod_register_web.erl | 2 +- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/mod_register.erl b/src/mod_register.erl index 379318da6..0bb0978ef 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -32,11 +32,13 @@ -behaviour(gen_mod). -export([start/2, stop/1, reload/3, stream_feature_register/2, - c2s_unauthenticated_packet/2, try_register/4, + c2s_unauthenticated_packet/2, try_register/4, try_register/5, process_iq/1, send_registration_notifications/3, mod_opt_type/1, mod_options/1, depends/2, format_error/1, mod_doc/0]). +-deprecated({try_register, 4}). + -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). -include("translate.hrl"). @@ -283,7 +285,7 @@ try_register_or_set_password(User, Server, Password, _ when CaptchaSucceed -> case check_from(From, Server) of allow -> - case try_register(User, Server, Password, Source, Lang) of + case try_register(User, Server, Password, Source, ?MODULE, Lang) of ok -> xmpp:make_iq_result(IQ); {error, Error} -> @@ -328,6 +330,13 @@ try_set_password(User, Server, Password, #iq{lang = Lang, meta = M} = IQ) -> xmpp:make_error(IQ, xmpp:err_internal_server_error(format_error(Why), Lang)) end. +try_register(User, Server, Password, SourceRaw, Module) -> + Modules = mod_register_opt:allow_modules(Server), + case (Modules == all) orelse lists:member(Module, Modules) of + true -> try_register(User, Server, Password, SourceRaw); + false -> {error, eaccess} + end. + try_register(User, Server, Password, SourceRaw) -> case jid:is_nodename(User) of false -> @@ -363,8 +372,8 @@ try_register(User, Server, Password, SourceRaw) -> end end. -try_register(User, Server, Password, SourceRaw, Lang) -> - case try_register(User, Server, Password, SourceRaw) of +try_register(User, Server, Password, SourceRaw, Module, Lang) -> + case try_register(User, Server, Password, SourceRaw, Module) of ok -> JID = jid:make(User, Server), Source = may_remove_resource(SourceRaw), @@ -597,6 +606,8 @@ mod_opt_type(access_from) -> econf:acl(); mod_opt_type(access_remove) -> econf:acl(); +mod_opt_type(allow_modules) -> + econf:either(all, econf:list(econf:atom())); mod_opt_type(captcha_protected) -> econf:bool(); mod_opt_type(ip_access) -> @@ -623,6 +634,7 @@ mod_options(_Host) -> [{access, all}, {access_from, none}, {access_remove, all}, + {allow_modules, all}, {captcha_protected, false}, {ip_access, all}, {password_strength, 0}, @@ -661,6 +673,12 @@ mod_doc() -> desc => ?T("Specify rules to restrict access for user unregistration. " "By default any user is able to unregister their account.")}}, + {allow_modules, + #{value => "all | [Module, ...]", + desc => + ?T("List of modules that can register accounts, or 'all'. " + "The default value is 'all', which is equivalent to " + "something like '[mod_register, mod_register_web]'.")}}, {captcha_protected, #{value => "true | false", desc => diff --git a/src/mod_register_web.erl b/src/mod_register_web.erl index 1ac3b58dc..0cf4bcff8 100644 --- a/src/mod_register_web.erl +++ b/src/mod_register_web.erl @@ -521,7 +521,7 @@ register_account(Username, Host, Password, Ip) -> end. register_account2(Username, Host, Password, Ip) -> - case mod_register:try_register(Username, Host, Password, Ip) + case mod_register:try_register(Username, Host, Password, Ip, ?MODULE) of ok -> {success, ok, {Username, Host, Password}}; From 7fd0eefa3091e531639b9f13b853ce728e0f40a1 Mon Sep 17 00:00:00 2001 From: Badlop Date: Fri, 3 Dec 2021 12:53:58 +0100 Subject: [PATCH 049/106] Run make options --- src/mod_conversejs_opt.erl | 41 ++++++++++++++++++++++++++++++++++++++ src/mod_pubsub_opt.erl | 9 ++++++++- src/mod_register_opt.erl | 7 +++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/mod_conversejs_opt.erl diff --git a/src/mod_conversejs_opt.erl b/src/mod_conversejs_opt.erl new file mode 100644 index 000000000..9e53978ea --- /dev/null +++ b/src/mod_conversejs_opt.erl @@ -0,0 +1,41 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_conversejs_opt). + +-export([bosh_service_url/1]). +-export([conversejs_css/1]). +-export([conversejs_script/1]). +-export([default_domain/1]). +-export([websocket_url/1]). + +-spec bosh_service_url(gen_mod:opts() | global | binary()) -> 'undefined' | binary(). +bosh_service_url(Opts) when is_map(Opts) -> + gen_mod:get_opt(bosh_service_url, Opts); +bosh_service_url(Host) -> + gen_mod:get_module_opt(Host, mod_conversejs, bosh_service_url). + +-spec conversejs_css(gen_mod:opts() | global | binary()) -> binary(). +conversejs_css(Opts) when is_map(Opts) -> + gen_mod:get_opt(conversejs_css, Opts); +conversejs_css(Host) -> + gen_mod:get_module_opt(Host, mod_conversejs, conversejs_css). + +-spec conversejs_script(gen_mod:opts() | global | binary()) -> binary(). +conversejs_script(Opts) when is_map(Opts) -> + gen_mod:get_opt(conversejs_script, Opts); +conversejs_script(Host) -> + gen_mod:get_module_opt(Host, mod_conversejs, conversejs_script). + +-spec default_domain(gen_mod:opts() | global | binary()) -> binary(). +default_domain(Opts) when is_map(Opts) -> + gen_mod:get_opt(default_domain, Opts); +default_domain(Host) -> + gen_mod:get_module_opt(Host, mod_conversejs, default_domain). + +-spec websocket_url(gen_mod:opts() | global | binary()) -> 'undefined' | binary(). +websocket_url(Opts) when is_map(Opts) -> + gen_mod:get_opt(websocket_url, Opts); +websocket_url(Host) -> + gen_mod:get_module_opt(Host, mod_conversejs, websocket_url). + diff --git a/src/mod_pubsub_opt.erl b/src/mod_pubsub_opt.erl index 8db5532f6..cb3c014b9 100644 --- a/src/mod_pubsub_opt.erl +++ b/src/mod_pubsub_opt.erl @@ -11,6 +11,7 @@ -export([hosts/1]). -export([ignore_pep_from_offline/1]). -export([last_item_cache/1]). +-export([max_item_expire_node/1]). -export([max_items_node/1]). -export([max_nodes_discoitems/1]). -export([max_subscriptions_node/1]). @@ -68,7 +69,13 @@ last_item_cache(Opts) when is_map(Opts) -> last_item_cache(Host) -> gen_mod:get_module_opt(Host, mod_pubsub, last_item_cache). --spec max_items_node(gen_mod:opts() | global | binary()) -> non_neg_integer(). +-spec max_item_expire_node(gen_mod:opts() | global | binary()) -> 'infinity' | pos_integer(). +max_item_expire_node(Opts) when is_map(Opts) -> + gen_mod:get_opt(max_item_expire_node, Opts); +max_item_expire_node(Host) -> + gen_mod:get_module_opt(Host, mod_pubsub, max_item_expire_node). + +-spec max_items_node(gen_mod:opts() | global | binary()) -> 'unlimited' | non_neg_integer(). max_items_node(Opts) when is_map(Opts) -> gen_mod:get_opt(max_items_node, Opts); max_items_node(Host) -> diff --git a/src/mod_register_opt.erl b/src/mod_register_opt.erl index 53c6ca6ea..e7236424c 100644 --- a/src/mod_register_opt.erl +++ b/src/mod_register_opt.erl @@ -6,6 +6,7 @@ -export([access/1]). -export([access_from/1]). -export([access_remove/1]). +-export([allow_modules/1]). -export([captcha_protected/1]). -export([ip_access/1]). -export([password_strength/1]). @@ -31,6 +32,12 @@ access_remove(Opts) when is_map(Opts) -> access_remove(Host) -> gen_mod:get_module_opt(Host, mod_register, access_remove). +-spec allow_modules(gen_mod:opts() | global | binary()) -> 'all' | [atom()]. +allow_modules(Opts) when is_map(Opts) -> + gen_mod:get_opt(allow_modules, Opts); +allow_modules(Host) -> + gen_mod:get_module_opt(Host, mod_register, allow_modules). + -spec captcha_protected(gen_mod:opts() | global | binary()) -> boolean(). captcha_protected(Opts) when is_map(Opts) -> gen_mod:get_opt(captcha_protected, Opts); From 7897c3d0e170495615d40152df9dbddb8df5d2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 6 Dec 2021 15:07:59 +0100 Subject: [PATCH 050/106] Add workaround for bug in older erlang version in rest module --- src/rest.erl | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/rest.erl b/src/rest.erl index d724352f2..038ec1fb1 100644 --- a/src/rest.erl +++ b/src/rest.erl @@ -197,7 +197,18 @@ url(Url, Params) -> L = [<<"&", (iolist_to_binary(Key))/binary, "=", (misc:url_encode(Value))/binary>> || {Key, Value} <- Params], - <<$&, Encoded/binary>> = iolist_to_binary(L), + <<$&, Encoded0/binary>> = iolist_to_binary(L), + Encoded = + case erlang:function_exported(uri_string, normalize, 1) of + true -> + case uri_string:normalize("%25") of + "%" -> % This hack around bug in httpc >21 <23.2 + binary:replace(Encoded0, <<"%25">>, <<"%2525">>, [global]); + _ -> Encoded0 + end; + _ -> + Encoded0 + end, <>. url(Server, Path, Params) -> case binary:split(base_url(Server, Path), <<"?">>) of From 8d8a3177e15aac8e693aa20e8e51d6463e81163d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 6 Dec 2021 15:46:52 +0100 Subject: [PATCH 051/106] Eliminate xref warning from last commit --- mix.exs | 1 + rebar.config | 1 + src/rest.erl | 24 +++++++++++++----------- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/mix.exs b/mix.exs index fbed459a4..9da7885b5 100644 --- a/mix.exs +++ b/mix.exs @@ -87,6 +87,7 @@ defmodule Ejabberd.MixProject do if_version_below('23', [{:d, :USE_OLD_PG2}]) ++ if_version_below('24', [{:d, :COMPILER_REPORTS_ONLY_LINES}]) ++ if_version_below('24', [{:d, :SYSTOOLS_APP_DEF_WITHOUT_OPTIONAL}]) ++ + if_function_exported(:uri_string, :normalize, 1, [{:d, :HAVE_URI_STRING}]) if_function_exported(:erl_error, :format_exception, 6, [{:d, :HAVE_ERL_ERROR}]) defines = for {:d, value} <- result, do: {:d, value} result ++ [{:d, :ALL_DEFS, defines}] diff --git a/rebar.config b/rebar.config index 3490d976c..4be211e25 100644 --- a/rebar.config +++ b/rebar.config @@ -110,6 +110,7 @@ {if_var_true, sip, {d, 'SIP'}}, {if_var_true, stun, {d, 'STUN'}}, {if_have_fun, {erl_error, format_exception, 6}, {d, 'HAVE_ERL_ERROR'}}, + {if_have_fun, {uri_string, normalize, 1}, {d, 'HAVE_URI_STRING'}}, {src_dirs, [src, {if_rebar3, sql}, {if_var_true, tools, tools}, diff --git a/src/rest.erl b/src/rest.erl index 038ec1fb1..1bb5c5ef7 100644 --- a/src/rest.erl +++ b/src/rest.erl @@ -191,6 +191,18 @@ base_url(Server, Path) -> _ -> Url end. +-ifdef(HAVE_URI_STRING). +uri_hack(Str) -> + case uri_string:normalize("%25") of + "%" -> % This hack around bug in httpc >21 <23.2 + binary:replace(Str, <<"%25">>, <<"%2525">>, [global]); + _ -> Str + end. +-else. +uri_hack(Str) -> + Str. +-endif. + url(Url, []) -> Url; url(Url, Params) -> @@ -198,17 +210,7 @@ url(Url, Params) -> (misc:url_encode(Value))/binary>> || {Key, Value} <- Params], <<$&, Encoded0/binary>> = iolist_to_binary(L), - Encoded = - case erlang:function_exported(uri_string, normalize, 1) of - true -> - case uri_string:normalize("%25") of - "%" -> % This hack around bug in httpc >21 <23.2 - binary:replace(Encoded0, <<"%25">>, <<"%2525">>, [global]); - _ -> Encoded0 - end; - _ -> - Encoded0 - end, + Encoded = uri_hack(Encoded0), <>. url(Server, Path, Params) -> case binary:split(base_url(Server, Path), <<"?">>) of From 3f4423e996f19b5e395f59b17184cc021d6752a3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 6 Dec 2021 23:17:11 +0100 Subject: [PATCH 052/106] Use P1's coveralls-erl fork to handle unicode in git commit author --- rebar.config.script | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rebar.config.script b/rebar.config.script index efd51d6ba..c83390fb5 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -387,8 +387,8 @@ Rules = [ ]}]), []}, {[plugins], IsRebar3 and (os:getenv("GITHUB_ACTIONS") == "true"), AppendList([{coveralls, {git, - "https://github.com/RoadRunnr/coveralls-erl.git", - {branch, "feature/git-info"}}} ]), []}, + "https://github.com/processone/coveralls-erl.git", + {branch, "addjsonfile"}}} ]), []}, {[overrides], [post_hook_configure], SystemDeps == false, AppendList2(GenDepsConfigure), [], []}, {[ct_extra_params], [eunit_compile_opts], true, From 6b1f78e87aeb029b63cbc9669c9de0bd3e7169af Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Tue, 7 Dec 2021 10:12:14 +0100 Subject: [PATCH 053/106] Remove CTLLOCKDIR (/var/lock/ejabberdctl) from Makefile.in Flock'ing /var/lock/ejabberdctl by ejabberdctl was removed with f7d4aae64db8 ("Use UUID for ctl node name (#1021)"), however the according recipies in the Makefile where never removed. This commit does that. --- Makefile.in | 9 --------- rebar.config | 1 - rel/reltool.config.script | 1 - 3 files changed, 11 deletions(-) diff --git a/Makefile.in b/Makefile.in index fc9718006..ab0e9d967 100644 --- a/Makefile.in +++ b/Makefile.in @@ -68,9 +68,6 @@ LUADIR = $(PRIVDIR)/lua # /var/lib/ejabberd/ SPOOLDIR = $(DESTDIR)@localstatedir@/lib/ejabberd -# /var/lock/ejabberdctl -CTLLOCKDIR = $(DESTDIR)@localstatedir@/lock/ejabberdctl - # /var/log/ejabberd/ LOGDIR = $(DESTDIR)@localstatedir@/log/ejabberd @@ -313,11 +310,6 @@ install: copy-files $(CHOWN_COMMAND) -R @INSTALLUSER@ $(SPOOLDIR) >$(CHOWN_OUTPUT) chmod -R 750 $(SPOOLDIR) # - # ejabberdctl lock directory - $(INSTALL) -d -m 750 $(O_USER) $(CTLLOCKDIR) - $(CHOWN_COMMAND) -R @INSTALLUSER@ $(CTLLOCKDIR) >$(CHOWN_OUTPUT) - chmod -R 750 $(CTLLOCKDIR) - # # Log directory $(INSTALL) -d -m 750 $(O_USER) $(LOGDIR) $(CHOWN_COMMAND) -R @INSTALLUSER@ $(LOGDIR) >$(CHOWN_OUTPUT) @@ -366,7 +358,6 @@ uninstall-all: uninstall-binary rm -rf $(ETCDIR) rm -rf $(EJABBERDDIR) rm -rf $(SPOOLDIR) - rm -rf $(CTLLOCKDIR) rm -rf $(LOGDIR) clean: diff --git a/rebar.config b/rebar.config index 4be211e25..774c3af67 100644 --- a/rebar.config +++ b/rebar.config @@ -180,7 +180,6 @@ {overlay_vars, "vars.config"}, {extended_start_script, true}, {overlay, [{mkdir, "var/log/ejabberd"}, - {mkdir, "var/lock"}, {mkdir, "var/lib/ejabberd"}, {mkdir, "etc/ejabberd"}, {copy, "rel/files/erl", "\{\{erts_vsn\}\}/bin/erl"}, % in rebar2 this prepends erts- diff --git a/rel/reltool.config.script b/rel/reltool.config.script index 459077964..951d55d28 100644 --- a/rel/reltool.config.script +++ b/rel/reltool.config.script @@ -89,7 +89,6 @@ Sys = [{lib_dirs, []}, Overlay = [ {mkdir, "var/log/ejabberd"}, - {mkdir, "var/lock"}, {mkdir, "var/lib/ejabberd"}, {mkdir, "etc/ejabberd"}, {mkdir, "doc"}, From 68ea5834c8adb23052bcd7283b842c656b6db428 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 8 Dec 2021 16:21:42 +0100 Subject: [PATCH 054/106] Relax strictness in mix dependency versions --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 9da7885b5..924812666 100644 --- a/mix.exs +++ b/mix.exs @@ -125,7 +125,7 @@ defmodule Ejabberd.MixProject do {:pkix, "~> 1.0"}, {:stringprep, ">= 1.0.26"}, {:stun, "~> 1.0"}, - {:xmpp, ">= 1.5.5"}, + {:xmpp, "~> 1.5"}, {:yconf, "~> 1.0"}] ++ cond_deps() end From 0f9a0156c67b3ad3ccdaafaeee266778fa89073a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 8 Dec 2021 16:22:24 +0100 Subject: [PATCH 055/106] Handle mix version when running docker-ejabberd/ecs/build.sh latest --- mix.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/mix.exs b/mix.exs index 924812666..7669c519c 100644 --- a/mix.exs +++ b/mix.exs @@ -24,6 +24,7 @@ defmodule Ejabberd.MixProject do case config(:vsn) do :false -> "0.0.0" # ./configure wasn't run: vars.config not created '0.0' -> "0.0.0" # the full git repository wasn't downloaded + 'latest.0' -> "0.0.0" # running 'docker-ejabberd/ecs/build.sh latest' [_, _, ?., _, _] = x -> head = String.replace(:erlang.list_to_binary(x), ~r/0+([0-9])/, "\\1") <> From e10f2a9e47bb864b4011df9d2a38821ac4807a2f Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 8 Dec 2021 17:31:05 +0100 Subject: [PATCH 056/106] OTP 24 means whatever version provided by Actions... nowadays it's 24.1 --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac384e375..45a88eda9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,12 +25,12 @@ jobs: strategy: fail-fast: false matrix: - otp: ['19.3', '24.0'] + otp: ['19.3', '24'] include: - otp: '19.3' rebar: 2 os: ubuntu-18.04 - - otp: '24.0' + - otp: '24' rebar: 3 os: ubuntu-20.04 runs-on: ${{ matrix.os }} @@ -46,7 +46,7 @@ jobs: - name: Get previous Erlang/OTP uses: ErlGang/setup-erlang@master - if: matrix.otp != 24.0 + if: matrix.otp != 24 with: otp-version: ${{ matrix.otp }} @@ -169,7 +169,7 @@ jobs: run: find logs/ -name exunit.log -exec cat '{}' ';' - name: Send to coveralls - if: matrix.otp == 24.0 + if: matrix.otp == 24 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | From ca143c187301db7b44e7e946f3be5f9b297d2468 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 8 Dec 2021 18:14:43 +0100 Subject: [PATCH 057/106] Update man page --- man/ejabberd.yml.5 | 341 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 266 insertions(+), 75 deletions(-) diff --git a/man/ejabberd.yml.5 b/man/ejabberd.yml.5 index f25f57250..8fb59c381 100644 --- a/man/ejabberd.yml.5 +++ b/man/ejabberd.yml.5 @@ -2,12 +2,12 @@ .\" Title: ejabberd.yml .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 07/21/2021 +.\" Date: 12/08/2021 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "EJABBERD\&.YML" "5" "07/21/2021" "\ \&" "\ \&" +.TH "EJABBERD\&.YML" "5" "12/08/2021" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -82,7 +82,7 @@ All options can be changed in runtime by running \fIejabberdctl reload\-config\f .sp Some options can be specified for particular virtual host(s) only using \fIhost_config\fR or \fIappend_host_config\fR options\&. Such options are called \fIlocal\fR\&. Examples are \fImodules\fR, \fIauth_method\fR and \fIdefault_db\fR\&. The options that cannot be defined per virtual host are called \fIglobal\fR\&. Examples are \fIloglevel\fR, \fIcertfiles\fR and \fIlisten\fR\&. It is a configuration mistake to put \fIglobal\fR options under \fIhost_config\fR or \fIappend_host_config\fR section \- ejabberd will refuse to load such configuration\&. .sp -It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/21\&.07/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. +It is not recommended to write ejabberd\&.yml from scratch\&. Instead it is better to start from "default" configuration file available at https://github\&.com/processone/ejabberd/blob/21\&.12/ejabberd\&.yml\&.example\&. Once you get ejabberd running you can start changing configuration options to meet your requirements\&. .sp Note that this document is intended to provide comprehensive description of all configuration options that can be consulted to understand the meaning of a particular option, its format and possible values\&. It will be quite hard to understand how to configure ejabberd by reading this document only \- for this purpose the reader is recommended to read online Configuration Guide available at https://docs\&.ejabberd\&.im/admin/configuration\&. .SH "TOP LEVEL OPTIONS" @@ -316,14 +316,46 @@ means that the same username can be taken multiple times in anonymous login mode .PP \fBanonymous_protocol\fR: \fIlogin_anon | sasl_anon | both\fR .RS 4 +Define what anonymous protocol will be used: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} \fIlogin_anon\fR means that the anonymous login method will be used\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} \fIsasl_anon\fR means that the SASL Anonymous method will be used\&. -\fIboth\fR -means that SASL Anonymous and login anonymous are both enabled\&. The default value is -\fIsasl_anon\fR\&. .RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIboth\fR +means that SASL Anonymous and login anonymous are both enabled\&. +.RE +.RE +.sp +The default value is \fIsasl_anon\fR\&. .PP \fBapi_permissions\fR: \fI[Permission, \&.\&.\&.]\fR .RS 4 @@ -375,7 +407,7 @@ A list of authentication methods to use\&. If several methods are defined, authe This is used by the contributed module \fIejabberd_auth_http\fR that can be installed from the -\fIejabberd\-contrib\fR +ejabberd\-contrib Git repository\&. Please refer to that module\(cqs README file for details\&. .RE .sp @@ -383,14 +415,36 @@ Git repository\&. Please refer to that module\(cqs README file for details\&. .PP \fBauth_password_format\fR: \fIplain | scram\fR .RS 4 -The option defines in what format the users passwords are stored\&. +The option defines in what format the users passwords are stored: +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} \fIplain\fR: The password is stored as plain text in the database\&. This is risky because the passwords can be read if your database gets compromised\&. This is the default value\&. This format allows clients to authenticate using: the old Jabber Non\-SASL (XEP\-0078), SASL PLAIN, SASL DIGEST\-MD5, and SASL SCRAM\-SHA\-1\&. -\fIscram\fR: The password is not stored, only some information that allows to verify the hash provided by the client\&. It is impossible to obtain the original plain password from the stored information; for this reason, when this value is configured it cannot be changed to plain anymore\&. This format allows clients to authenticate using: SASL PLAIN and SASL SCRAM\-SHA\-1\&. +.RE +.sp +.RS 4 +.ie n \{\ +\h'-04'\(bu\h'+03'\c +.\} +.el \{\ +.sp -1 +.IP \(bu 2.3 +.\} +\fIscram\fR: The password is not stored, only some information that allows to verify the hash provided by the client\&. It is impossible to obtain the original plain password from the stored information; for this reason, when this value is configured it cannot be changed to plain anymore\&. This format allows clients to authenticate using: SASL PLAIN and SASL SCRAM\-SHA\-1\&. The default value is +\fIplain\fR\&. +.RE .RE .PP \fBauth_scram_hash\fR: \fIsha | sha256 | sha512\fR .RS 4 -Hash algorith that should be used to store password in SCRAM format\&. You shouldn\(cqt change this if you already have passwords generated with a different algorithm \- users that have such passwords will not be able to authenticate\&. +Hash algorith that should be used to store password in SCRAM format\&. You shouldn\(cqt change this if you already have passwords generated with a different algorithm \- users that have such passwords will not be able to authenticate\&. The default value is +\fIsha\fR\&. .RE .PP \fBauth_use_cache\fR: \fItrue | false\fR @@ -476,7 +530,7 @@ For server conections, this \fIca_file\fR option is overriden by the s2s_cafile \fBcache_life_time\fR: \fItimeout()\fR .RS 4 The time of a cached item to keep in cache\&. Once it\(cqs expired, the corresponding item is erased from cache\&. The default value is -\fIone hour\fR\&. Several modules have a similar option; and some core ejabberd parts support similar options too, see +\fI1 hour\fR\&. Several modules have a similar option; and some core ejabberd parts support similar options too, see \fIauth_cache_life_time\fR, \fIoauth_cache_life_time\fR, \fIrouter_cache_life_time\fR, and @@ -507,7 +561,9 @@ A maximum number of items (not memory!) in cache\&. The rule of thumb, for all t .PP \fBcaptcha_cmd\fR: \fIPath\fR .RS 4 -Full path to a script that generates CAPTCHA images\&. There is no default value: when this option is not set, CAPTCHA functionality is completely disabled\&. +Full path to a script that generates +CAPTCHA +images\&. There is no default value: when this option is not set, CAPTCHA functionality is completely disabled\&. .RE .PP \fBcaptcha_host\fR: \fIString\fR @@ -519,13 +575,17 @@ instead\&. .PP \fBcaptcha_limit\fR: \fIpos_integer() | infinity\fR .RS 4 -Maximum number of CAPTCHA generated images per minute for any given JID\&. The option is intended to protect the server from CAPTCHA DoS\&. The default value is +Maximum number of +CAPTCHA +generated images per minute for any given JID\&. The option is intended to protect the server from CAPTCHA DoS\&. The default value is \fIinfinity\fR\&. .RE .PP \fBcaptcha_url\fR: \fIURL\fR .RS 4 -An URL where CAPTCHA requests should be sent\&. NOTE: you need to configure +An URL where +CAPTCHA +requests should be sent\&. NOTE: you need to configure \fIrequest_handlers\fR for \fIejabberd_http\fR @@ -815,7 +875,8 @@ Path to the file that contains the JWK Key\&. The default value is .RS 4 The option defines the default language of server strings that can be seen by XMPP clients\&. If an XMPP client does not possess \fIxml:lang\fR -attribute, the specified language is used\&. +attribute, the specified language is used\&. The default value is +\fI"en"\fR\&. .RE .PP \fBldap_backups\fR: \fI[Host, \&.\&.\&.]\fR @@ -948,7 +1009,11 @@ section for details\&. \fBlog_rotate_count\fR: \fINumber\fR .RS 4 The number of rotated log files to keep\&. The default value is -\fI1\fR\&. +\fI1\fR, which means that only keeps +ejabberd\&.log\&.0, +error\&.log\&.0 +and +crash\&.log\&.0\&. .RE .PP \fBlog_rotate_size\fR: \fIpos_integer() | infinity\fR @@ -989,8 +1054,7 @@ seconds\&. .RS 4 This option can be used to tune tick time parameter of \fInet_kernel\fR\&. It tells Erlang VM how often nodes should check if intra\-node communication was not interrupted\&. This option must have identical value on all nodes, or it will lead to subtle bugs\&. Usually leaving default value of this is option is best, tweak it only if you know what you are doing\&. The default value is -\fI1\fR -minute\&. +\fI1 minute\fR\&. .RE .PP \fBnew_sql_schema\fR: \fItrue | false\fR @@ -998,7 +1062,7 @@ minute\&. Whether to use \fInew\fR SQL schema\&. All schemas are located at -https://github\&.com/processone/ejabberd/tree/21\&.07/sql\&. There are two schemas available\&. The default legacy schema allows to store one XMPP domain into one ejabberd database\&. The +https://github\&.com/processone/ejabberd/tree/21\&.12/sql\&. There are two schemas available\&. The default legacy schema allows to store one XMPP domain into one ejabberd database\&. The \fInew\fR schema allows to handle several XMPP domains in a single ejabberd database\&. Using this \fInew\fR @@ -1392,8 +1456,7 @@ if the latter is not set\&. \fBs2s_timeout\fR: \fItimeout()\fR .RS 4 A time to wait before closing an idle s2s connection\&. The default value is -\fI10\fR -minutes\&. +\fI10 minutes\fR\&. .RE .PP \fBs2s_tls_compression\fR: \fItrue | false\fR @@ -1795,24 +1858,7 @@ Details for some commands: \fIsrg\-create\fR: If you want to put a group Name with blankspaces, use the characters "\*(Aq and \*(Aq" to define when the Name starts and ends\&. See an example below\&. .RE .sp -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.br -.ps +1 -\fBAvailable options:\fR -.RS 4 -.PP -\fBmodule_resource\fR: \fIResource\fR -.RS 4 -Indicate the resource that the XMPP stanzas must use in the FROM or TO JIDs\&. This is only useful in the -\fIget_vcard*\fR -and -\fIset_vcard*\fR -commands\&. The default value is -\fImod_admin_extra\fR\&. -.RE -.RE +The module has no options\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -1835,8 +1881,7 @@ access_rules: vcard_set: \- allow: adminextraresource modules: - mod_admin_extra: - module_resource: "modadminextraf8x,31ad" + mod_admin_extra: {} mod_vcard: access_set: vcard_set .fi @@ -1884,7 +1929,7 @@ ejabberdctl srg\-create g1 example\&.org "\*(AqGroup number 1\*(Aq" this_is_g1 g .RE .SS "mod_admin_update_sql" .sp -This module can be used to update existing SQL database from the default to the new schema\&. Check the section Default and New Schemas for details\&. Please note that only PostgreSQL is supported\&. When the module is loaded use \fIupdate_sql\fR ejabberdctl command\&. +This module can be used to update existing SQL database from the default to the new schema\&. Check the section Default and New Schemas for details\&. Please note that only PostgreSQL is supported\&. When the module is loaded use \fIupdate_sql\fR API\&. .sp The module has no options\&. .SS "mod_announce" @@ -2321,6 +2366,78 @@ While a client is inactive, queue presence stanzas that indicate (un)availabilit The module provides server configuration functionality via XEP\-0050: Ad\-Hoc Commands\&. This module requires \fImod_adhoc\fR to be loaded\&. .sp The module has no options\&. +.SS "mod_conversejs" +.sp +This module serves a simple page for the Converse XMPP web browser client\&. +.sp +To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → request_handlers\&. +.sp +You must also setup either the option \fIwebsocket_url\fR or \fIbosh_service_url\fR\&. +.sp +By default, the options \fIconversejs_css\fR and \fIconversejs_script\fR point to the public Converse\&.js client\&. Alternatively, you can host the client locally using \fImod_http_fileserver\fR\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBAvailable options:\fR +.RS 4 +.PP +\fBbosh_service_url\fR: \fIBoshURL\fR +.RS 4 +BOSH service URL to which Converse\&.js can connect to\&. +.RE +.PP +\fBconversejs_css\fR: \fIURL\fR +.RS 4 +Converse\&.js CSS URL\&. +.RE +.PP +\fBconversejs_script\fR: \fIURL\fR +.RS 4 +Converse\&.js main script URL\&. +.RE +.PP +\fBdefault_domain\fR: \fIDomain\fR +.RS 4 +Specify a domain to act as the default for user JIDs\&. The default value is the first domain defined in the ejabberd configuration file\&. +.RE +.PP +\fBwebsocket_url\fR: \fIWebsocketURL\fR +.RS 4 +A websocket URL to which Converse\&.js can connect to\&. +.RE +.RE +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 5280 + module: ejabberd_http + request_handlers: + /websocket: ejabberd_http_ws + /conversejs: mod_conversejs + +modules: + mod_conversejs: + websocket_url: "ws://example\&.org:5280/websocket" +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_delegation" .sp This module is an implementation of XEP\-0355: Namespace Delegation\&. Only admin mode has been implemented by now\&. Namespace delegation allows external services to handle IQ using specific namespace\&. This may be applied for external PEP service\&. @@ -2479,11 +2596,11 @@ server_info: \- modules: all name: abuse\-addresses - urls: [mailto:abuse@shakespeare\&.lit] + urls: ["mailto:abuse@shakespeare\&.lit"] \- modules: [mod_muc] name: "Web chatroom logs" - urls: [http://www\&.example\&.org/muc\-logs] + urls: ["http://www\&.example\&.org/muc\-logs"] \- modules: [mod_disco] name: feedback\-addresses @@ -2563,13 +2680,40 @@ The number of C2S authentication failures to trigger the IP ban\&. The default v .sp This module provides a ReST API to call ejabberd commands using JSON data\&. .sp -To use this module, in addition to adding it to the \fImodules\fR section, you must also add it to \fIrequest_handlers\fR of some listener\&. +To use this module, in addition to adding it to the \fImodules\fR section, you must also enable it in \fIlisten\fR → \fIejabberd_http\fR → request_handlers\&. .sp To use a specific API version N, when defining the URL path in the request_handlers, add a \fIvN\fR\&. For example: \fI/api/v2: mod_http_api\fR .sp To run a command, send a POST request to the corresponding URL: \fIhttp://localhost:5280/api/\fR .sp The module has no options\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 5280 + module: ejabberd_http + request_handlers: + /api: mod_http_api + +modules: + mod_http_api: {} +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_http_fileserver" .sp This simple module serves files from the local disk over HTTP\&. @@ -2697,7 +2841,7 @@ modules: .sp This module allows for requesting permissions to upload a file via HTTP as described in XEP\-0363: HTTP File Upload\&. If the request is accepted, the client receives a URL for uploading the file and another URL from which that file can later be downloaded\&. .sp -In order to use this module, it must be configured as a \fIrequest_handler\fR for \fIejabberd_http\fR listener\&. +In order to use this module, it must be enabled in \fIlisten\fR → \fIejabberd_http\fR → request_handlers\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -2746,7 +2890,9 @@ This option defines the permission bits of uploaded files\&. The bits are specif .PP \fBget_url\fR: \fIURL\fR .RS 4 -This option specifies the initial part of the GET URLs used for downloading the files\&. By default, it is set to the same value as +This option specifies the initial part of the GET URLs used for downloading the files\&. The default value is +\fIundefined\fR\&. When this option is +\fIundefined\fR, this option is set to the same value as \fIput_url\fR\&. The keyword @HOST@ is replaced with the virtual host name\&. NOTE: if GET requests are handled by \fImod_http_upload\fR, the \fIget_url\fR @@ -2795,7 +2941,7 @@ A name of the service in the Service Discovery\&. This will only be displayed by .PP \fBput_url\fR: \fIURL\fR .RS 4 -This option specifies the initial part of the PUT URLs used for file uploads\&. The keyword @HOST@ is replaced with the virtual host name\&. NOTE: different virtual hosts cannot use the same PUT URL\&. The default value is "https://@HOST@:5443"\&. +This option specifies the initial part of the PUT URLs used for file uploads\&. The keyword @HOST@ is replaced with the virtual host name\&. NOTE: different virtual hosts cannot use the same PUT URL\&. The default value is "https://@HOST@:5443/upload"\&. .RE .PP \fBrm_on_unregister\fR: \fItrue | false\fR @@ -3530,21 +3676,24 @@ This option specifies who is allowed to administrate the Multi\-User Chat servic .PP \fBaccess_create\fR: \fIAccessName\fR .RS 4 -To configure who is allowed to create new rooms at the Multi\-User Chat service, this option can be used\&. By default any account in the local ejabberd server is allowed to create rooms\&. +To configure who is allowed to create new rooms at the Multi\-User Chat service, this option can be used\&. The default value is +\fIall\fR, which means everyone is allowed to create rooms\&. .RE .PP \fBaccess_mam\fR: \fIAccessName\fR .RS 4 To configure who is allowed to modify the \fImam\fR -room option\&. By default any account in the local ejabberd server is allowed to modify that option\&. +room option\&. The default value is +\fIall\fR, which means everyone is allowed to modify that option\&. .RE .PP \fBaccess_persistent\fR: \fIAccessName\fR .RS 4 To configure who is allowed to modify the \fIpersistent\fR -room option\&. By default any account in the local ejabberd server is allowed to modify that option\&. +room option\&. The default value is +\fIall\fR, which means everyone is allowed to modify that option\&. .RE .PP \fBaccess_register\fR: \fIAccessName\fR @@ -3825,7 +3974,8 @@ This option defines the number of service admins or room owners allowed to enter .PP \fBmax_users_presence\fR: \fINumber\fR .RS 4 -This option defines after how many users in the room, it is considered overcrowded\&. When a MUC room is considered overcrowed, presence broadcasts are limited to reduce load, traffic and excessive presence "storm" received by participants\&. +This option defines after how many users in the room, it is considered overcrowded\&. When a MUC room is considered overcrowed, presence broadcasts are limited to reduce load, traffic and excessive presence "storm" received by participants\&. The default value is +\fI1000\fR\&. .RE .PP \fBmin_message_interval\fR: \fINumber\fR @@ -4498,8 +4648,7 @@ This module implements support for XEP\-0199: XMPP Ping and periodic keepalives\ \fBping_ack_timeout\fR: \fItimeout()\fR .RS 4 How long to wait before deeming that a client has not answered a given server ping request\&. The default value is -\fI32\fR -seconds\&. +\fIundefined\fR\&. .RE .PP \fBping_interval\fR: \fItimeout()\fR @@ -4531,7 +4680,7 @@ means destroying the underlying connection, \fInone\fR means to do nothing\&. NOTE: when \fImod_stream_mgmt\fR -module is loaded and stream management is enabled by a client, killing the client connection doesn\(cqt mean killing the client session \- the session will be kept alive in order to give the client a chance to resume it\&. The default value is +is loaded and stream management is enabled by a client, killing the client connection doesn\(cqt mean killing the client session \- the session will be kept alive in order to give the client a chance to resume it\&. The default value is \fInone\fR\&. .RE .RE @@ -5136,10 +5285,16 @@ or \fIfalse\fR\&. If not defined, pubsub does not cache last items\&. On systems with not so many nodes, caching last items speeds up pubsub and allows to raise user connection rate\&. The cost is memory usage, as every item is stored in memory\&. .RE .PP -\fBmax_items_node\fR: \fIMaxItems\fR +\fBmax_item_expire_node\fR: \fItimeout() | infinity\fR +.RS 4 +Specify the maximum item epiry time\&. Default value is: +\fIinfinity\fR\&. +.RE +.PP +\fBmax_items_node\fR: \fInon_neg_integer() | infinity\fR .RS 4 Define the maximum number of items that can be stored in a node\&. Default value is: -\fI10\fR\&. +\fI1000\fR\&. .RE .PP \fBmax_nodes_discoitems\fR: \fIpos_integer() | infinity\fR @@ -5437,9 +5592,9 @@ The module depends on \fImod_push\fR\&. .PP \fBresume_timeout\fR: \fItimeout()\fR .RS 4 -This option specifies the period of time until the session of a disconnected push client times out\&. This timeout is only in effect as long as no push notification is issued\&. Once that happened, the resumption timeout configured for the +This option specifies the period of time until the session of a disconnected push client times out\&. This timeout is only in effect as long as no push notification is issued\&. Once that happened, the resumption timeout configured for \fImod_stream_mgmt\fR -module is restored\&. The default value is +is restored\&. The default value is \fI72\fR hours\&. .RE @@ -5499,7 +5654,7 @@ Change the password from an existing account on the server\&. Delete an existing account on the server\&. .RE .sp -This module reads also another option defined globally for the server: \fIregistration_timeout\fR\&. Please check that option documentation in the section with top\-level options\&. +This module reads also the top\-level \fIregistration_timeout\fR option defined globally for the server, so please check that option documentation too\&. .sp .it 1 an-trap .nr an-no-space-flag 1 @@ -5528,11 +5683,18 @@ doesn\(cqt allow to register new accounts from s2s or existing c2s sessions\&. Y Specify rules to restrict access for user unregistration\&. By default any user is able to unregister their account\&. .RE .PP +\fBallow_modules\fR: \fIall | [Module, \&.\&.\&.]\fR +.RS 4 +List of modules that can register accounts, or +\fIall\fR\&. The default value is +\fIall\fR, which is equivalent to something like +\fI[mod_register, mod_register_web]\fR\&. +.RE +.PP \fBcaptcha_protected\fR: \fItrue | false\fR .RS 4 -Protect registrations with CAPTCHA (see section -CAPTCHA -of the Configuration Guide)\&. The default is +Protect registrations with +CAPTCHA\&. The default is \fIfalse\fR\&. .RE .PP @@ -5551,7 +5713,8 @@ This option sets the minimum Shannon entropy for passwords\&. The value \fIEntropy\fR -is a number of bits of entropy\&. The recommended minimum is 32 bits\&. The default is 0, i\&.e\&. no checks are performed\&. +is a number of bits of entropy\&. The recommended minimum is 32 bits\&. The default is +\fI0\fR, i\&.e\&. no checks are performed\&. .RE .PP \fBredirect_url\fR: \fIURL\fR @@ -5610,13 +5773,40 @@ Change the password from an existing account on the server\&. Unregister an existing account on the server\&. .RE .sp -This module supports CAPTCHA image to register a new account\&. To enable this feature, configure the options \fIcaptcha_cmd\fR and \fIcaptcha_url\fR, which are documented in the section with top\-level options\&. +This module supports CAPTCHA to register a new account\&. To enable this feature, configure the top\-level \fIcaptcha_cmd\fR and top\-level \fIcaptcha_url\fR options\&. .sp -As an example usage, the users of the host \fIexample\&.org\fR can visit the page: \fIhttps://example\&.org:5281/register/\fR It is important to include the last / character in the URL, otherwise the subpages URL will be incorrect\&. +As an example usage, the users of the host \fIlocalhost\fR can visit the page: \fIhttps://localhost:5280/register/\fR It is important to include the last / character in the URL, otherwise the subpages URL will be incorrect\&. .sp -The module depends on \fImod_register\fR where all the configuration is performed\&. +This module is enabled in \fIlisten\fR → \fIejabberd_http\fR → request_handlers, no need to enable in \fImodules\fR\&. The module depends on \fImod_register\fR where all the configuration is performed\&. .sp The module has no options\&. +.sp +.it 1 an-trap +.nr an-no-space-flag 1 +.nr an-break-flag 1 +.br +.ps +1 +\fBExample:\fR +.RS 4 +.sp +.if n \{\ +.RS 4 +.\} +.nf +listen: + \- + port: 5280 + module: ejabberd_http + request_handlers: + /register: mod_register_web + +modules: + mod_register: {} +.fi +.if n \{\ +.RE +.\} +.RE .SS "mod_roster" .sp This module implements roster management as defined in RFC6121 Section 2\&. The module also adds support for XEP\-0237: Roster Versioning\&. @@ -5910,8 +6100,9 @@ option, but applied to this module only\&. .PP \fBdb_type\fR: \fImnesia | sql\fR .RS 4 -Define the type of storage where the module will create the tables and store user information\&. The default is the storage defined by the global option -\fIdefault_db\fR, or +Define the type of storage where the module will create the tables and store user information\&. The default is the storage defined by the top\-level +\fIdefault_db\fR +option, or \fImnesia\fR if omitted\&. If \fIsql\fR @@ -6521,7 +6712,8 @@ minute\&. .RS 4 Same as top\-level \fIcache_life_time\fR -option, but applied to this module only\&. +option, but applied to this module only\&. The default value is +\fI48 hours\fR\&. .RE .PP \fBcache_size\fR: \fIpos_integer() | infinity\fR @@ -6594,8 +6786,7 @@ This option defines which access rule will be used to control who is allowed to \fBcredentials_lifetime\fR: \fItimeout()\fR .RS 4 The lifetime of temporary credentials offered to clients\&. If ejabberd\(cqs built\-in TURN service is used, TURN relays allocated using temporary credentials will be terminated shortly after the credentials expired\&. The default value is -\fI12\fR -hours\&. Note that restarting the ejabberd node invalidates any temporary credentials offered before the restart unless a +\fI12 hours\fR\&. Note that restarting the ejabberd node invalidates any temporary credentials offered before the restart unless a \fIsecret\fR is specified (see below)\&. .RE @@ -7121,7 +7312,7 @@ The module depends on \fImod_vcard\fR\&. .ps -1 .br .sp -Nowadays XEP\-0153 is used mostly as "read\-only", i\&.e\&. modern clients don\(cqt publish their avatars inside vCards\&. Thus in the majority of cases the module is only used along with \fImod_avatar\fR module for providing backward compatibility\&. +Nowadays XEP\-0153 is used mostly as "read\-only", i\&.e\&. modern clients don\(cqt publish their avatars inside vCards\&. Thus in the majority of cases the module is only used along with \fImod_avatar\fR for providing backward compatibility\&. .sp .5v .RE .sp @@ -7189,13 +7380,13 @@ TODO ProcessOne\&. .SH "VERSION" .sp -This document describes the configuration file of ejabberd 21\&.04\&.131\&. Configuration options of other ejabberd versions may differ significantly\&. +This document describes the configuration file of ejabberd 21\&.12\&. Configuration options of other ejabberd versions may differ significantly\&. .SH "REPORTING BUGS" .sp Report bugs to https://github\&.com/processone/ejabberd/issues .SH "SEE ALSO" .sp -Default configuration file: https://github\&.com/processone/ejabberd/blob/21\&.07/ejabberd\&.yml\&.example +Default configuration file: https://github\&.com/processone/ejabberd/blob/21\&.12/ejabberd\&.yml\&.example .sp Main site: https://ejabberd\&.im .sp From d1bfd6c90d7e7d1bc033358e5b7e0570f03df084 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 2 Dec 2021 16:43:17 +0100 Subject: [PATCH 058/106] Annotate modules, options and command major changes in 21.12 --- src/ejabberd_commands.erl | 1 + src/mod_conversejs.erl | 1 + src/mod_pubsub.erl | 3 +++ src/mod_register.erl | 1 + 4 files changed, 6 insertions(+) diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index c00b1469a..ddf0d3c59 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -94,6 +94,7 @@ get_commands_spec() -> result_example = ok}, #ejabberd_commands{name = gen_markdown_doc_for_tags, tags = [documentation], desc = "Generates markdown documentation for ejabberd_commands", + note = "added in 21.12", module = ejabberd_commands_doc, function = generate_tags_md, args = [{file, binary}], result = {res, rescode}, diff --git a/src/mod_conversejs.erl b/src/mod_conversejs.erl index 0b5d57a15..8683d60ab 100644 --- a/src/mod_conversejs.erl +++ b/src/mod_conversejs.erl @@ -110,6 +110,7 @@ mod_doc() -> #{desc => [?T("This module serves a simple page for the " "https://conversejs.org/[Converse] XMPP web browser client."), "", + ?T("This module is available since ejabberd 21.12."), "", ?T("To use this module, in addition to adding it to the 'modules' " "section, you must also enable it in 'listen' -> 'ejabberd_http' -> " "http://../listen-options/#request-handlers[request_handlers]."), "", diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 43344986e..76092f1c6 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -4233,6 +4233,7 @@ delete_expired_items() -> get_commands_spec() -> [#ejabberd_commands{name = delete_old_pubsub_items, tags = [purge], desc = "Keep only NUMBER of PubSub items per node", + note = "added in 21.12", module = ?MODULE, function = delete_old_items, args_desc = ["Number of items to keep per node"], args = [{number, integer}], @@ -4242,6 +4243,7 @@ get_commands_spec() -> result_example = ok}, #ejabberd_commands{name = delete_expired_pubsub_items, tags = [purge], desc = "Delete expired PubSub items", + note = "added in 21.12", module = ?MODULE, function = delete_expired_items, args = [], result = {res, rescode}, @@ -4389,6 +4391,7 @@ mod_doc() -> "is memory usage, as every item is stored in memory.")}}, {max_item_expire_node, #{value => "timeout() | infinity", + note => "added in 21.12", desc => ?T("Specify the maximum item epiry time. Default value " "is: 'infinity'.")}}, diff --git a/src/mod_register.erl b/src/mod_register.erl index 0bb0978ef..b85efd57c 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -675,6 +675,7 @@ mod_doc() -> "By default any user is able to unregister their account.")}}, {allow_modules, #{value => "all | [Module, ...]", + note => "added in 21.12", desc => ?T("List of modules that can register accounts, or 'all'. " "The default value is 'all', which is equivalent to " From 333eaef6dcbc210ca833f3f3a06e3f65134566a6 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 9 Dec 2021 10:43:56 +0100 Subject: [PATCH 059/106] =?UTF-8?q?Update=20French=20translation=20(thanks?= =?UTF-8?q?=20to=20=C3=89frit=20and=20ButterflyOfFire)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- priv/msgs/fr.msg | 50 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/priv/msgs/fr.msg b/priv/msgs/fr.msg index f33d6115d..d35a2fbd5 100644 --- a/priv/msgs/fr.msg +++ b/priv/msgs/fr.msg @@ -5,11 +5,20 @@ {" (Add * to the end of field to match substring)"," (Ajouter * à la fin du champ pour correspondre à la sous-chaîne)"}. {" has set the subject to: "," a défini le sujet sur : "}. +{"# participants","# participants"}. {"A description of the node","Une description du nœud"}. {"A friendly name for the node","Un nom convivial pour le nœud"}. {"A password is required to enter this room","Un mot de passe est nécessaire pour accéder à ce salon"}. +{"A Web Page","Une page Web"}. {"Accept","Accepter"}. {"Access denied by service policy","L'accès au service est refusé"}. +{"Access model of authorize","Modèle d’accès de « autoriser »"}. +{"Access model of open","Modèle d’accès de « ouvrir »"}. +{"Access model of presence","Modèle d’accès de « présence »"}. +{"Access model of roster","Modèle d’accès de « liste »"}. +{"Access model of whitelist","Modèle d’accès de « liste blanche »"}. +{"Access model","Modèle d’accès"}. +{"Account doesn't exist","Le compte n’existe pas"}. {"Action on user","Action sur l'utilisateur"}. {"Add Jabber ID","Ajouter un Jabber ID"}. {"Add New","Ajouter"}. @@ -19,7 +28,9 @@ {"Administrator privileges required","Les droits d'administrateur sont nécessaires"}. {"All activity","Toute activité"}. {"All Users","Tous les utilisateurs"}. +{"Allow subscription","Autoriser l’abonnement"}. {"Allow this Jabber ID to subscribe to this pubsub node?","Autoriser ce Jabber ID à s'abonner à ce nœud PubSub ?"}. +{"Allow this person to register with the room?","Autoriser cette personne à enregistrer ce salon ?"}. {"Allow users to change the subject","Autoriser les utilisateurs à changer le sujet"}. {"Allow users to query other users","Autoriser les utilisateurs à envoyer des requêtes aux autres utilisateurs"}. {"Allow users to send invites","Autoriser les utilisateurs à envoyer des invitations"}. @@ -28,8 +39,25 @@ {"Allow visitors to send private messages to","Autoriser les visiteurs à envoyer des messages privés"}. {"Allow visitors to send status text in presence updates","Autoriser les visiteurs à envoyer un message d'état avec leur présence"}. {"Allow visitors to send voice requests","Permettre aux visiteurs d'envoyer des demandes de 'voice'"}. +{"An associated LDAP group that defines room membership; this should be an LDAP Distinguished Name according to an implementation-specific or deployment-specific definition of a group.","Un groupe LDAP associé qui définit l’adhésion à un salon ; cela devrait être un nom distingué LDAP selon la définition spécifique à l’implémentation ou au déploiement d’un groupe."}. {"Announcements","Annonces"}. +{"Answer associated with a picture","Réponse associée à une image"}. +{"Answer associated with a video","Réponse associée à une vidéo"}. +{"Answer associated with speech","Réponse associée à un discours"}. +{"Answer to a question","Réponse à une question"}. +{"Anyone in the specified roster group(s) may subscribe and retrieve items","N’importe qui dans le groupe de la liste spécifiée peut s’abonner et récupérer les items"}. +{"Anyone may associate leaf nodes with the collection","N’importe qui peut associer les feuilles avec la collection"}. +{"Anyone may publish","N’importe qui peut publier"}. +{"Anyone may subscribe and retrieve items","N’importe qui peut s’abonner et récupérer les items"}. +{"Anyone with a presence subscription of both or from may subscribe and retrieve items","N’importe qui avec un abonnement de présence peut s’abonner et récupérer les items"}. +{"Anyone with Voice","N’importe qui avec Voice"}. +{"Anyone","N’importe qui"}. {"April","Avril"}. +{"Attribute 'channel' is required for this request","L’attribut « channel » est requis pour la requête"}. +{"Attribute 'id' is mandatory for MIX messages","L’attribut « id » est obligatoire pour les messages MIX"}. +{"Attribute 'jid' is not allowed here","L’attribut « jid » n’est pas autorisé ici"}. +{"Attribute 'node' is not allowed here","L’attribut « node » n’est pas autorisé ici"}. +{"Attribute 'to' of stanza that triggered challenge","L’attribut « to » de la strophe qui a déclenché le défi"}. {"August","Août"}. {"Automatic node creation is not enabled","La creation implicite de nœud n'est pas disponible"}. {"Backup Management","Gestion des sauvegardes"}. @@ -43,10 +71,14 @@ {"Cannot remove active list","La liste active ne peut être supprimée"}. {"Cannot remove default list","La liste par défaut ne peut être supprimée"}. {"CAPTCHA web page","Page web de CAPTCHA"}. +{"Challenge ID","Identifiant du défi"}. {"Change Password","Modifier le mot de passe"}. {"Change User Password","Changer le mot de passe de l'utilisateur"}. {"Changing password is not allowed","La modification du mot de passe n'est pas autorisée"}. {"Changing role/affiliation is not allowed","La modification role/affiliation n'est pas autorisée"}. +{"Channel already exists","Ce canal existe déjà"}. +{"Channel does not exist","Le canal n’existe pas"}. +{"Channels","Canaux"}. {"Characters not allowed:","Caractères non autorisés :"}. {"Chatroom configuration modified","Configuration du salon modifiée"}. {"Chatroom is created","Le salon de discussion est créé"}. @@ -58,30 +90,39 @@ {"Choose storage type of tables","Choisissez un type de stockage pour les tables"}. {"Choose whether to approve this entity's subscription.","Choisissez d'approuver ou non l'abonnement de cette entité."}. {"City","Ville"}. +{"Client acknowledged more stanzas than sent by server","Le client accuse réception de plus de strophes que ce qui est envoyé par le serveur"}. {"Commands","Commandes"}. {"Conference room does not exist","Le salon de discussion n'existe pas"}. {"Configuration of room ~s","Configuration pour le salon ~s"}. {"Configuration","Configuration"}. {"Connected Resources:","Ressources connectées :"}. +{"Contact Addresses (normally, room owner or owners)","Adresses de contact (normalement les administrateurs du salon)"}. {"Country","Pays"}. {"CPU Time:","Temps CPU :"}. +{"Current Discussion Topic","Sujet de discussion courant"}. {"Database failure","Échec sur la base de données"}. {"Database Tables at ~p","Tables de base de données sur ~p"}. {"Database Tables Configuration at ","Configuration des tables de base de données sur "}. {"Database","Base de données"}. {"December","Décembre"}. {"Default users as participants","Les utilisateurs sont participant par défaut"}. +{"Delete content","Supprimer le contenu"}. {"Delete message of the day on all hosts","Supprimer le message du jour sur tous les domaines"}. {"Delete message of the day","Supprimer le message du jour"}. {"Delete Selected","Suppression des éléments sélectionnés"}. +{"Delete table","Supprimer la table"}. {"Delete User","Supprimer l'utilisateur"}. {"Deliver event notifications","Envoyer les notifications d'événement"}. {"Deliver payloads with event notifications","Inclure le contenu du message avec la notification"}. {"Description:","Description :"}. {"Disc only copy","Copie sur disque uniquement"}. +{"'Displayed groups' not added (they do not exist!): ","« Groupes affichés » non ajoutés (ils n’existent pas !) : "}. {"Displayed:","Affichés :"}. +{"Don't tell your password to anybody, not even the administrators of the XMPP server.","Ne révélez votre mot de passe à personne, pas même aux administrateurs du serveur XMPP."}. {"Dump Backup to Text File at ","Enregistrer la sauvegarde dans un fichier texte sur "}. {"Dump to Text File","Sauvegarder dans un fichier texte"}. +{"Duplicated groups are not allowed by RFC6121","Les groupes dupliqués ne sont pas autorisés par la RFC6121"}. +{"Dynamically specify a replyto of the item publisher","Spécifie dynamiquement un « réponse à » de l’item de l’éditeur"}. {"Edit Properties","Modifier les propriétés"}. {"Either approve or decline the voice request.","Accepter ou refuser la demande de voix."}. {"ejabberd MUC module","Module MUC ejabberd"}. @@ -116,6 +157,7 @@ {"Failed to parse HTTP response","Échec de lecture de la réponse HTTP"}. {"Failed to process option '~s'","Échec de traitement de l'option '~s'"}. {"Family Name","Nom de famille"}. +{"FAQ Entry","Entrée FAQ"}. {"February","Février"}. {"File larger than ~w bytes","Taille de fichier suppérieur à ~w octets"}. {"Fill in the form to search for any matching XMPP User","Complétez le formulaire pour rechercher un utilisateur XMPP correspondant"}. @@ -176,6 +218,7 @@ {"July","Juillet"}. {"June","Juin"}. {"Just created","Vient d'être créé"}. +{"Label:","Étiquette :"}. {"Last Activity","Dernière activité"}. {"Last login","Dernière connexion"}. {"Last month","Dernier mois"}. @@ -192,7 +235,6 @@ {"Make room public searchable","Rendre le salon public"}. {"Malformed username","Nom d'utilisateur invalide"}. {"March","Mars"}. -{"Max # of items to persist","Nombre maximum d'éléments à stocker"}. {"Max payload size in bytes","Taille maximum pour le contenu du message en octet"}. {"Maximum file size","Taille maximale du fichier"}. {"Maximum Number of History Messages Returned by Room","Nombre maximal de messages d'historique renvoyés par salle"}. @@ -274,6 +316,7 @@ {"Online Users:","Utilisateurs connectés :"}. {"Online Users","Utilisateurs en ligne"}. {"Online","En ligne"}. +{"Only admins can see this","Seuls les administrateurs peuvent voir cela"}. {"Only deliver notifications to available users","Envoyer les notifications uniquement aux utilisateurs disponibles"}. {"Only or tags are allowed","Seul le tag ou est autorisé"}. {"Only element is allowed in this query","Seul l'élément est autorisé dans cette requête"}. @@ -283,6 +326,7 @@ {"Only moderators can approve voice requests","Seuls les modérateurs peuvent accépter les requêtes voix"}. {"Only occupants are allowed to send messages to the conference","Seuls les occupants peuvent envoyer des messages à la conférence"}. {"Only occupants are allowed to send queries to the conference","Seuls les occupants sont autorisés à envoyer des requêtes à la conférence"}. +{"Only publishers may publish","Seuls les éditeurs peuvent publier"}. {"Only service administrators are allowed to send service messages","Seuls les administrateurs du service sont autoriser à envoyer des messages de service"}. {"Organization Name","Nom de l'organisation"}. {"Organization Unit","Unité de l'organisation"}. @@ -290,6 +334,7 @@ {"Outgoing s2s Connections:","Connexions s2s sortantes :"}. {"Owner privileges required","Les droits de propriétaire sont nécessaires"}. {"Packet","Paquet"}. +{"Participant","Participant"}. {"Password Verification","Vérification du mot de passe"}. {"Password Verification:","Vérification du mot de passe :"}. {"Password","Mot de passe"}. @@ -323,6 +368,9 @@ {"Remove User","Supprimer l'utilisateur"}. {"Remove","Supprimer"}. {"Replaced by new connection","Remplacé par une nouvelle connexion"}. +{"Request has timed out","La demande a expiré"}. +{"Request is ignored","La demande est ignorée"}. +{"Requested role","Rôle demandé"}. {"Resources","Ressources"}. {"Restart Service","Redémarrer le service"}. {"Restart","Redémarrer"}. From 1915f29d4b76b189ff9259582d25df8a93303416 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 9 Dec 2021 10:44:57 +0100 Subject: [PATCH 060/106] Update Chinese translation (thanks to Eric and 52871299hzy) --- priv/msgs/zh.msg | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/priv/msgs/zh.msg b/priv/msgs/zh.msg index 8f099b608..a3a400afb 100644 --- a/priv/msgs/zh.msg +++ b/priv/msgs/zh.msg @@ -4,7 +4,7 @@ %% https://docs.ejabberd.im/developer/extending-ejabberd/localization/ {" (Add * to the end of field to match substring)"," (在字段末添加*来匹配子串)"}. -{" has set the subject to: ","已将标题设置为: "}. +{" has set the subject to: "," 已将标题设置为: "}. {"# participants","# 个参与人"}. {"A description of the node","该节点的描述"}. {"A friendly name for the node","该节点的友好名称"}. @@ -23,7 +23,7 @@ {"Add Jabber ID","添加Jabber ID"}. {"Add New","添加新用户"}. {"Add User","添加用户"}. -{"Administration of ","管理"}. +{"Administration of ","管理 "}. {"Administration","管理"}. {"Administrator privileges required","需要管理员权限"}. {"All activity","所有活动"}. @@ -62,7 +62,7 @@ {"Automatic node creation is not enabled","未启用自动节点创建"}. {"Backup Management","备份管理"}. {"Backup of ~p","~p的备份"}. -{"Backup to File at ","备份文件位于"}. +{"Backup to File at ","备份文件位于 "}. {"Backup","备份"}. {"Bad format","格式错误"}. {"Birthday","出生日期"}. @@ -102,7 +102,7 @@ {"Current Discussion Topic","当前讨论话题"}. {"Database failure","数据库失败"}. {"Database Tables at ~p","位于~p的数据库表"}. -{"Database Tables Configuration at ","数据库表格配置位于"}. +{"Database Tables Configuration at ","数据库表格配置位于 "}. {"Database","数据库"}. {"December","十二月"}. {"Default users as participants","用户默认被视为参与人"}. @@ -119,12 +119,12 @@ {"'Displayed groups' not added (they do not exist!): ","'显示的群组' 未被添加 (它们不存在!): "}. {"Displayed:","已显示:"}. {"Don't tell your password to anybody, not even the administrators of the XMPP server.","不要将密码告诉任何人, 就算是XMPP服务器的管理员也不可以."}. -{"Dump Backup to Text File at ","转储备份到文本文件于"}. +{"Dump Backup to Text File at ","将备份转储到位于以下位置的文本文件 "}. {"Dump to Text File","转储到文本文件"}. {"Duplicated groups are not allowed by RFC6121","按照RFC6121的规则,不允许有重复的群组"}. {"Dynamically specify a replyto of the item publisher","为项目发布者动态指定一个 replyto"}. {"Edit Properties","编辑属性"}. -{"Either approve or decline the voice request.","接受或拒绝声音请求"}. +{"Either approve or decline the voice request.","接受或拒绝声音请求."}. {"ejabberd HTTP Upload service","ejabberd HTTP 上传服务"}. {"ejabberd MUC module","ejabberd MUC 模块"}. {"ejabberd Multicast service","ejabberd多重映射服务"}. @@ -194,10 +194,10 @@ {"Import Directory","导入目录"}. {"Import File","导入文件"}. {"Import user data from jabberd14 spool file:","从 jabberd14 Spool 文件导入用户数据:"}. -{"Import User from File at ","导入用户的文件位于"}. +{"Import User from File at ","从以下位置的文件导入用户 "}. {"Import users data from a PIEFXIS file (XEP-0227):","从 PIEFXIS 文件 (XEP-0227) 导入用户数据:"}. {"Import users data from jabberd14 spool directory:","从jabberd14 Spool目录导入用户数据:"}. -{"Import Users from Dir at ","导入用户的目录位于"}. +{"Import Users from Dir at ","从以下位置目录导入用户 "}. {"Import Users From jabberd14 Spool Files","从 jabberd14 Spool 文件导入用户"}. {"Improper domain part of 'from' attribute","不恰当的'from'属性域名部分"}. {"Improper message type","不恰当的消息类型"}. @@ -249,7 +249,6 @@ {"Malformed username","用户名无效"}. {"MAM preference modification denied by service policy","MAM偏好被服务策略拒绝"}. {"March","三月"}. -{"Max # of items to persist","允许持久化的最大内容条目数"}. {"Max payload size in bytes","最大有效负载字节数"}. {"Maximum file size","最大文件大小"}. {"Maximum Number of History Messages Returned by Room","房间返回的历史消息最大值"}. @@ -288,7 +287,7 @@ {"Never","从未"}. {"New Password:","新密码:"}. {"Nickname can't be empty","昵称不能为空"}. -{"Nickname Registration at ","昵称注册于"}. +{"Nickname Registration at ","昵称注册于 "}. {"Nickname ~s does not exist in the room","昵称~s不在该房间"}. {"Nickname","昵称"}. {"No address elements found","没有找到地址的各元素"}. @@ -326,6 +325,7 @@ {"Node ~p","节点~p"}. {"Nodeprep has failed","Nodeprep 已失效"}. {"Nodes","节点"}. +{"Node","节点"}. {"None","无"}. {"Not allowed","不允许"}. {"Not Found","没有找到"}. @@ -339,7 +339,6 @@ {"Number of Offline Messages","离线消息数量"}. {"Number of online users","在线用户数"}. {"Number of registered users","注册用户数"}. -{"Number of seconds after which to automatically purge items","自动清除项目要等待的秒数"}. {"Occupants are allowed to invite others","允许成员邀请其他人"}. {"Occupants May Change the Subject","成员可以修改主题"}. {"October","十月"}. @@ -428,7 +427,7 @@ {"Resources","资源"}. {"Restart Service","重启服务"}. {"Restart","重启"}. -{"Restore Backup from File at ","要恢复的备份文件位于"}. +{"Restore Backup from File at ","从以下位置的文件恢复备份 "}. {"Restore binary backup after next ejabberd restart (requires less memory):","在下次 ejabberd 重启后恢复二进制备份(需要的内存更少):"}. {"Restore binary backup immediately:","立即恢复二进制备份:"}. {"Restore plain text backup immediately:","立即恢复普通文本备份:"}. @@ -455,7 +454,7 @@ {"Search Results for ","搜索结果属于关键词 "}. {"Search the text","搜索文本"}. {"Search until the date","搜索截至日期"}. -{"Search users in ","搜索用户于"}. +{"Search users in ","在以下位置搜索用户 "}. {"Select All","全选"}. {"Send announcement to all online users on all hosts","发送通知给所有主机的在线用户"}. {"Send announcement to all online users","发送通知给所有在线用户"}. @@ -519,7 +518,6 @@ {"The JIDs of those with an affiliation of owner","隶属所有人的JID"}. {"The JIDs of those with an affiliation of publisher","隶属发布人的JID"}. {"The list of JIDs that may associate leaf nodes with a collection","可以将叶节点与集合关联的JID列表"}. -{"The maximum number of child nodes that can be associated with a collection","可以与集合关联的最大子节点数"}. {"The minimum number of milliseconds between sending any two notification digests","发送任何两个通知摘要之间的最小毫秒数"}. {"The name of the node","该节点的名称"}. {"The node is a collection node","该节点是集合节点"}. @@ -544,13 +542,12 @@ {"The type of node data, usually specified by the namespace of the payload (if any)","节点数据的类型, 如果有, 通常由有效负载的名称空间指定"}. {"The URL of an XSL transformation which can be applied to payloads in order to generate an appropriate message body element.","XSL转换的URL,可以将其应用于有效负载以生成适当的消息正文元素。"}. {"The URL of an XSL transformation which can be applied to the payload format in order to generate a valid Data Forms result that the client could display using a generic Data Forms rendering engine","XSL转换的URL, 可以将其应用于有效负载格式, 以生成有效的数据表单结果, 客户端可以使用通用数据表单呈现引擎来显示该结果"}. -{"The username is not valid","用户名无效"}. {"There was an error changing the password: ","修改密码出错: "}. {"There was an error creating the account: ","帐户创建出错: "}. {"There was an error deleting the account: ","帐户删除失败: "}. {"This is case insensitive: macbeth is the same that MacBeth and Macbeth.","此处不区分大小写: macbeth 与 MacBeth 和 Macbeth 是一样的."}. {"This page allows to register an XMPP account in this XMPP server. Your JID (Jabber ID) will be of the form: username@server. Please read carefully the instructions to fill correctly the fields.","本页面允许在此服务器上注册XMPP帐户. 你的JID (Jabber ID) 的形式如下: 用户名@服务器. 请仔细阅读说明并正确填写相应字段."}. -{"This page allows to unregister an XMPP account in this XMPP server.","此页面允许在此XMPP服务器上注销XMPP帐户"}. +{"This page allows to unregister an XMPP account in this XMPP server.","此页面允许在此 XMPP 服务器上注销 XMPP 帐户。"}. {"This room is not anonymous","此房间不是匿名房间"}. {"This service can not process the address: ~s","此服务无法处理地址: ~s"}. {"Thursday","星期四"}. @@ -565,7 +562,7 @@ {"Too many child elements","太多子元素"}. {"Too many elements","太多 元素"}. {"Too many elements","太多 元素"}. -{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","来自IP地址(~p)的(~s)失败认证太多. 该地址将在UTC时间~s被禁用."}. +{"Too many (~p) failed authentications from this IP address (~s). The address will be unblocked at ~s UTC","来自IP地址(~p)的(~s)失败认证太多。将在UTC时间 ~s 解除对该地址的封锁"}. {"Too many receiver fields were specified","指定的接收者字段太多"}. {"Too many unacked stanzas","未被确认的节太多"}. {"Too many users in this conference","该会议的用户太多"}. @@ -656,7 +653,7 @@ {"You need a client that supports x:data to register the nickname","您需要一个支持 x:data 的客户端来注册昵称"}. {"You need an x:data capable client to search","您需要一个兼容 x:data 的客户端来搜索"}. {"Your active privacy list has denied the routing of this stanza.","你的活跃私聊列表拒绝了在此房间进行路由分发."}. -{"Your contact offline message queue is full. The message has been discarded.","您的联系人离线消息队列已满. 消息已被丢弃"}. +{"Your contact offline message queue is full. The message has been discarded.","您的联系人离线消息队列已满。消息已被丢弃。"}. {"Your subscription request and/or messages to ~s have been blocked. To unblock your subscription request, visit ~s","您发送给~s的消息已被阻止. 要解除阻止, 请访问 ~s"}. {"Your XMPP account was successfully registered.","你的XMPP帐户注册成功."}. {"Your XMPP account was successfully unregistered.","你的XMPP帐户注销成功."}. From ed5ba1f645fcac53b236b90238ecd81be32d6d8b Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 9 Dec 2021 11:07:43 +0100 Subject: [PATCH 061/106] Update CHANGELOG.md to 21.12 --- CHANGELOG.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 13385b864..288931b5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,50 @@ +# Version 21.12 + +Commands +- `create_room_with_opts`: Fixed when using SQL storage +- `change_room_option`: Add missing fields from config inside `mod_muc_admin:change_options` +- piefxis: Fixed arguments of all commands + +Modules +- mod_caps: Don't forget caps on XEP-0198 resumption +- mod_conversejs: New module to serve a simple page for Converse.js +- mod_http_upload_quota: Avoid `max_days` race +- mod_muc: Support MUC hats (XEP-0317, conversejs/prosody compatible) +- mod_muc: Optimize MucSub processing +- mod_muc: Fix exception in mucsub {un}subscription events multicast handler +- mod_multicast: Improve and optimize multicast routing code +- mod_offline: Allow storing non-composing x:events in offline +- mod_ping: Send ping from server, not bare user JID +- mod_push: Fix handling of MUC/Sub messages +- mod_register: New allow_modules option to restrict registration modules +- mod_register_web: Handle unknown host gracefully +- mod_register_web: Use mod_register configured restrictions + +PubSub +- Add `delete_expired_pubsub_items` command +- Add `delete_old_pubsub_items` command +- Optimize publishing on large nodes (SQL) +- Support unlimited number of items +- Support `max_items=max` node configuration +- Bump default value for `max_items` limit from 10 to 1000 +- Use configured `max_items` by default +- node_flat: Avoid catch-all clauses for RSM +- node_flat_sql: Avoid catch-all clauses for RSM + +SQL +- Use `INSERT ... ON CONFLICT` in SQL_UPSERT for PostgreSQL >= 9.5 +- mod_mam export: assign MUC entries to the MUC service +- MySQL: Fix typo when creating index +- PgSQL: Add SASL auth support, PostgreSQL 14 +- PgSQL: Add missing SQL migration for table `push_session` +- PgSQL: Fix `vcard_search` definition in pgsql new schema + +Other +- `captcha-ng.sh`: "sort -R" command not POSIX, added "shuf" and "cat" as fallback +- Make s2s connection table cleanup more robust +- Update export/import of scram password to XEP-0227 1.1 +- Update Jose to 1.11.1 (the last in hex.pm correctly versioned) + # Version 21.07 Compilation From a94209a0e0de882e112400bf24c0a5721b8ae520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Thu, 9 Dec 2021 12:13:42 +0100 Subject: [PATCH 062/106] Update stun and esip dependencies --- mix.lock | 4 ++-- rebar.config | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mix.lock b/mix.lock index 773cc2bbf..d0edb619d 100644 --- a/mix.lock +++ b/mix.lock @@ -6,7 +6,7 @@ "earmark_parser": {:hex, :earmark_parser, "1.4.17", "6f3c7e94170377ba45241d394389e800fb15adc5de51d0a3cd52ae766aafd63f", [:mix], [], "hexpm", "f93ac89c9feca61c165b264b5837bf82344d13bebc634cd575cb711e2e342023"}, "eimp": {:hex, :eimp, "1.0.21", "2e918a5dc9a1959ef8713a2360499e3baeee64cfd7881bd9d1f361ca9ddf07e8", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "998f58538f58aa0cff103414994d7ce56dc253e6576cd6fb40c1ead64aa73a28"}, "epam": {:hex, :epam, "1.0.12", "2a5625d4133bca4b3943791a3f723ba764455a461ae9b6ba5debb262efcf4b40", [:rebar3], [], "hexpm", "54c166c4459cef72f2990a3d89a8f0be27180fe0ab0f24b28ddcc3b815f49f7f"}, - "esip": {:hex, :esip, "1.0.44", "dcf3a92e581210ab161d8a8201245df5a56058460b08d89cf3193d944adff71e", [:rebar3], [{:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.45", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "ea2086a1de95fe48f40b78251f74a6828799e318c55da346934e7d4fea675f04"}, + "esip": {:hex, :esip, "1.0.45", "2f21fb9750f7a505e6bbd43f6d48b0e879b808aba6c2224686c83f2bcd7a34bf", [:rebar3], [{:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stun, "1.0.47", [hex: :stun, repo: "hexpm", optional: false]}], "hexpm", "1f1eae69f2bd8d75f42c048409eabb4e3dc71ab6412fc5d998edbdade6ad5f75"}, "ex_doc": {:hex, :ex_doc, "0.26.0", "1922164bac0b18b02f84d6f69cab1b93bc3e870e2ad18d5dacb50a9e06b542a3", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2775d66e494a9a48355db7867478ffd997864c61c65a47d31c4949459281c78d"}, "ezlib": {:hex, :ezlib, "1.0.10", "c1c24eb18944cfde55f0574e9922d5b0392fa864282f769f82b2ea15e54f6003", [:rebar3], [], "hexpm", "1d317f1d85373686199eb3b4164d3477e95033ac68e45a95ba18e7b7a8c23241"}, "fast_tls": {:hex, :fast_tls, "1.1.13", "828cdc75e1e8fce8158846d2b971d8b4fe2b2ddcc75b759e88d751079bf78afd", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "d1f422af40c7777fe534496f508ee86515cb929ad10f7d1d56aa94ce899b44a0"}, @@ -29,7 +29,7 @@ "p1_utils": {:hex, :p1_utils, "1.0.23", "7f94466ada69bd982ea7bb80fbca18e7053e7d0b82c9d9e37621fa508587069b", [:rebar3], [], "hexpm", "47f21618694eeee5006af1c88731ad86b757161e7823c29b6f73921b571c8502"}, "pkix": {:hex, :pkix, "1.0.8", "98ea05243847fd4504f7c7a0cd82cecd1010ac327a082e1c674c5384006eae75", [:rebar3], [], "hexpm", "399508819501fab9d2e586dfa601b5ee3ef22b5612d3db58204dd2d089ef45d7"}, "stringprep": {:hex, :stringprep, "1.0.27", "02808c7024bc6285ca6a8a67e7addfc16f35dda55551a582c5181d8ea960e890", [:rebar3], [{:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "a5967b1144ca8002a58a03d16dd109fbd0bcdb82616cead2f983944314af6a00"}, - "stun": {:hex, :stun, "1.0.45", "3363fd4911f408c712c48baa3811548768dcd49a6c30d8f3c54c119bbbacd3ea", [:rebar3], [{:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "89774eb5500d5360dc0c1d0504b34afa891f91c807637309c449841ae91f7818"}, + "stun": {:hex, :stun, "1.0.47", "fae94c0dc7415263297e8f07f286f3355d327d8bf78b1b0743c9a5a492381f71", [:rebar3], [{:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "377d8487f4add85f6bc6ecdebdb4dcbcbe890e9462f27d6d31f3db1cf9b2cc9b"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"}, "xmpp": {:hex, :xmpp, "1.5.6", "09259177a39c880d682817932f4da0537c471160fd43aa891ea9cb71cf827b52", [:rebar3], [{:ezlib, "1.0.10", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.13", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.48", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.23", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.27", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "59b7317c4077d3384f9a891e0517a591cdbd44a323260b835eafbede4f4eb12e"}, "yconf": {:hex, :yconf, "1.0.12", "78c119d39bb805207fcb7671cb884805d75ee89c9ec98632b678f90a597dee2c", [:rebar3], [{:fast_yaml, "1.0.32", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "12faa51c281e95bcb6abf185fd034a242209621a7bb04b6cc411c867b192e207"}, diff --git a/rebar.config b/rebar.config index 774c3af67..179052d1c 100644 --- a/rebar.config +++ b/rebar.config @@ -30,7 +30,7 @@ {if_var_true, redis, {eredis, ".*", {git, "https://github.com/wooga/eredis", {tag, "v1.2.0"}}}}, {if_var_true, sip, - {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.44"}}}}, + {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.45"}}}}, {if_var_true, zlib, {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.10"}}}}, {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.13"}}}, @@ -58,7 +58,7 @@ {sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.13"}}}}, {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.27"}}}, {if_var_true, stun, - {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.45"}}}}, + {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.47"}}}}, {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.5.6"}}}, {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.12"}}} ]}. From 8b7da70b57096d5c88c4128877e8802a12057bd4 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Tue, 14 Dec 2021 09:54:00 +0300 Subject: [PATCH 063/106] Handle user removal in mod_muc --- sql/lite.new.sql | 1 + sql/lite.sql | 1 + sql/mssql.sql | 1 + sql/mysql.new.sql | 1 + sql/mysql.sql | 1 + sql/pg.new.sql | 1 + sql/pg.sql | 1 + src/mod_muc.erl | 39 +++++++++++++++++++++++++++++++++++++++ src/mod_muc_opt.erl | 7 +++++++ src/mod_muc_room.erl | 20 +++++++++++++++++++- src/mod_muc_sql.erl | 9 ++++++++- 11 files changed, 80 insertions(+), 2 deletions(-) diff --git a/sql/lite.new.sql b/sql/lite.new.sql index 96c880358..cb8add8a1 100644 --- a/sql/lite.new.sql +++ b/sql/lite.new.sql @@ -330,6 +330,7 @@ CREATE TABLE muc_room_subscribers ( ); CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(host, jid); +CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers(jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers(host, room, jid); CREATE TABLE motd ( diff --git a/sql/lite.sql b/sql/lite.sql index 087035d7f..56e8c9151 100644 --- a/sql/lite.sql +++ b/sql/lite.sql @@ -302,6 +302,7 @@ CREATE TABLE muc_room_subscribers ( ); CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers(host, jid); +CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers(jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers(host, room, jid); CREATE TABLE motd ( diff --git a/sql/mssql.sql b/sql/mssql.sql index bb7861527..f8b65edc4 100644 --- a/sql/mssql.sql +++ b/sql/mssql.sql @@ -150,6 +150,7 @@ CREATE TABLE [dbo].[muc_room_subscribers] ( CREATE UNIQUE CLUSTERED INDEX [muc_room_subscribers_host_room_jid] ON [muc_room_subscribers] (host, room, jid); CREATE INDEX [muc_room_subscribers_host_jid] ON [muc_room_subscribers] (host, jid); +CREATE INDEX [muc_room_subscribers_jid] ON [muc_room_subscribers] (jid); CREATE TABLE [dbo].[privacy_default_list] ( [username] [varchar] (250) NOT NULL, diff --git a/sql/mysql.new.sql b/sql/mysql.new.sql index 01aeffbc5..c4e021abf 100644 --- a/sql/mysql.new.sql +++ b/sql/mysql.new.sql @@ -347,6 +347,7 @@ CREATE TABLE muc_room_subscribers ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_subscribers(host, jid); +CREATE INDEX i_muc_room_subscribers_jid USING BTREE ON muc_room_subscribers(jid); CREATE TABLE motd ( username varchar(191) NOT NULL, diff --git a/sql/mysql.sql b/sql/mysql.sql index 7feaf5d0a..751552e19 100644 --- a/sql/mysql.sql +++ b/sql/mysql.sql @@ -319,6 +319,7 @@ CREATE TABLE muc_room_subscribers ( ) ENGINE=InnoDB CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE INDEX i_muc_room_subscribers_host_jid USING BTREE ON muc_room_subscribers(host, jid); +CREATE INDEX i_muc_room_subscribers_jid USING BTREE ON muc_room_subscribers(jid); CREATE TABLE motd ( username varchar(191) PRIMARY KEY, diff --git a/sql/pg.new.sql b/sql/pg.new.sql index 6a0743f23..5ffbedde3 100644 --- a/sql/pg.new.sql +++ b/sql/pg.new.sql @@ -495,6 +495,7 @@ CREATE TABLE muc_room_subscribers ( ); CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USING btree (host, jid); +CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers USING btree (jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers USING btree (host, room, jid); CREATE TABLE motd ( diff --git a/sql/pg.sql b/sql/pg.sql index 0e3d4c8b8..733856ede 100644 --- a/sql/pg.sql +++ b/sql/pg.sql @@ -320,6 +320,7 @@ CREATE TABLE muc_room_subscribers ( ); CREATE INDEX i_muc_room_subscribers_host_jid ON muc_room_subscribers USING btree (host, jid); +CREATE INDEX i_muc_room_subscribers_jid ON muc_room_subscribers USING btree (jid); CREATE UNIQUE INDEX i_muc_room_subscribers_host_room_jid ON muc_room_subscribers USING btree (host, room, jid); CREATE TABLE motd ( diff --git a/src/mod_muc.erl b/src/mod_muc.erl index b2ebc5c61..2853632cc 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -69,6 +69,7 @@ get_online_rooms_by_user/3, can_use_nick/4, get_subscribed_rooms/2, + remove_user/2, procname/2, route/1, unhibernate_room/3]). @@ -122,6 +123,8 @@ start(Host, Opts) -> case mod_muc_sup:start(Host) of {ok, _} -> + ejabberd_hooks:add(remove_user, Host, ?MODULE, + remove_user, 50), MyHosts = gen_mod:get_opt_hosts(Opts), Mod = gen_mod:db_mod(Opts, ?MODULE), RMod = gen_mod:ram_db_mod(Opts, ?MODULE), @@ -133,6 +136,8 @@ start(Host, Opts) -> end. stop(Host) -> + ejabberd_hooks:delete(remove_user, Host, ?MODULE, + remove_user, 50), Proc = mod_muc_sup:procname(Host), supervisor:terminate_child(ejabberd_gen_mod_sup, Proc), supervisor:delete_child(ejabberd_gen_mod_sup, Proc). @@ -1122,6 +1127,32 @@ count_online_rooms(ServerHost, Host) -> RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), RMod:count_online_rooms(ServerHost, Host). +-spec remove_user(binary(), binary()) -> ok. +remove_user(User, Server) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + Mod = gen_mod:db_mod(LServer, ?MODULE), + case erlang:function_exported(Mod, remove_user, 2) of + true -> + Mod:remove_user(LUser, LServer); + false -> + ok + end, + JID = jid:make(User, Server), + lists:foreach( + fun(Host) -> + lists:foreach( + fun({_, _, Pid}) -> + mod_muc_room:change_item( + Pid, JID, affiliation, none, <<"User removed">>), + mod_muc_room:change_item( + Pid, JID, role, none, <<"User removed">>) + end, + get_online_rooms(LServer, Host)) + end, + gen_mod:get_module_opt_hosts(LServer, mod_muc)), + ok. + opts_to_binary(Opts) -> lists:map( fun({title, Title}) -> @@ -1225,6 +1256,8 @@ mod_opt_type(user_message_shaper) -> econf:atom(); mod_opt_type(user_presence_shaper) -> econf:atom(); +mod_opt_type(cleanup_affiliations_on_start) -> + econf:bool(); mod_opt_type(default_room_options) -> econf:options( #{allow_change_subj => econf:bool(), @@ -1302,6 +1335,7 @@ mod_options(Host) -> {preload_rooms, true}, {hibernation_timeout, infinity}, {vcard, undefined}, + {cleanup_affiliations_on_start, false}, {default_room_options, [{allow_change_subj,true}, {allow_private_messages,true}, @@ -1580,6 +1614,11 @@ mod_doc() -> " -", " work: true", " street: Elm Street"]}]}}, + {cleanup_affiliations_on_start, + #{value => "true | false", + desc => + ?T("Remove affiliations for non-existing local users on startup. " + "The default value is 'false'.")}}, {default_room_options, #{value => ?T("Options"), desc => diff --git a/src/mod_muc_opt.erl b/src/mod_muc_opt.erl index 760a5d7c8..4b9e8b806 100644 --- a/src/mod_muc_opt.erl +++ b/src/mod_muc_opt.erl @@ -9,6 +9,7 @@ -export([access_mam/1]). -export([access_persistent/1]). -export([access_register/1]). +-export([cleanup_affiliations_on_start/1]). -export([db_type/1]). -export([default_room_options/1]). -export([hibernation_timeout/1]). @@ -73,6 +74,12 @@ access_register(Opts) when is_map(Opts) -> access_register(Host) -> gen_mod:get_module_opt(Host, mod_muc, access_register). +-spec cleanup_affiliations_on_start(gen_mod:opts() | global | binary()) -> boolean(). +cleanup_affiliations_on_start(Opts) when is_map(Opts) -> + gen_mod:get_opt(cleanup_affiliations_on_start, Opts); +cleanup_affiliations_on_start(Host) -> + gen_mod:get_module_opt(Host, mod_muc, cleanup_affiliations_on_start). + -spec db_type(gen_mod:opts() | global | binary()) -> atom(). db_type(Opts) when is_map(Opts) -> gen_mod:get_opt(db_type, Opts); diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index e8b0d1bce..ec1c551e4 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -306,7 +306,8 @@ init([Host, ServerHost, Access, Room, HistorySize, RoomShaper, Opts, QueueType]) room_shaper = Shaper}), add_to_log(room_existence, started, State), ejabberd_hooks:run(start_room, ServerHost, [ServerHost, Room, Host]), - {ok, normal_state, reset_hibernate_timer(State)}. + State1 = cleanup_affiliations(State), + {ok, normal_state, reset_hibernate_timer(State1)}. normal_state({route, <<"">>, #message{from = From, type = Type, lang = Lang} = Packet}, @@ -5337,6 +5338,23 @@ muc_subscribers_put(Subscriber, MUCSubscribers) -> subscriber_nodes = NewSubNodes}. +cleanup_affiliations(State) -> + case mod_muc_opt:cleanup_affiliations_on_start(State#state.server_host) of + true -> + Affiliations = + maps:filter( + fun({LUser, LServer, _}, _) -> + case ejabberd_router:is_my_host(LServer) of + true -> + ejabberd_auth:user_exists(LUser, LServer); + false -> + true + end + end, State#state.affiliations), + State#state{affiliations = Affiliations}; + false -> + State + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Detect messange stanzas that don't have meaningful content diff --git a/src/mod_muc_sql.erl b/src/mod_muc_sql.erl index 1310cde7b..8aa7ad62b 100644 --- a/src/mod_muc_sql.erl +++ b/src/mod_muc_sql.erl @@ -38,7 +38,7 @@ register_online_user/4, unregister_online_user/4, count_online_rooms_by_user/3, get_online_rooms_by_user/3, get_subscribed_rooms/3, get_rooms_without_subscribers/2, - find_online_room_by_pid/2]). + find_online_room_by_pid/2, remove_user/2]). -export([set_affiliation/6, set_affiliations/4, get_affiliation/5, get_affiliations/3, search_affiliation/4]). @@ -465,6 +465,13 @@ get_subscribed_rooms(LServer, Host, Jid) -> {error, db_failure} end. +remove_user(LUser, LServer) -> + SJID = jid:encode(jid:make(LUser, LServer)), + ejabberd_sql:sql_query( + LServer, + ?SQL("delete from muc_room_subscribers where jid=%(SJID)s")), + ok. + %%%=================================================================== %%% Internal functions %%%=================================================================== From 7e07cba406c46b2a682031a8a530425e8ecb5a1a Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 13 Dec 2021 18:22:59 +0100 Subject: [PATCH 064/106] Let get_all_rooms handle "global" argument, fixes rooms_unsued_... (#3726) --- src/mod_muc_admin.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index ce4665d7e..9952abd27 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -860,7 +860,14 @@ get_online_rooms(ServiceArg) -> || {RoomName, RoomHost, Pid} <- mod_muc:get_online_rooms(Host)] end, Hosts). -get_all_rooms(Host) -> +get_all_rooms(ServiceArg) -> + Hosts = find_services(ServiceArg), + lists:flatmap( + fun(Host) -> + get_all_rooms2(Host) + end, Hosts). + +get_all_rooms2(Host) -> ServerHost = ejabberd_router:host_of_route(Host), OnlineRooms = get_online_rooms(Host), OnlineMap = lists:foldl( From 24742031e943cd589c3a8e0d47901288678e7145 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 13 Dec 2021 13:48:26 +0100 Subject: [PATCH 065/106] Revert "Github Actions: use MD5 pass encryption to support PostgreSQL 14 (#3691)" This workaround is not needed anymore, thanks to p1_pgsql 3bf645b, included in p1_pgsql 1.1.13, and used in ejabberd 21.12. This reverts commit 964cb3aacab94fcf88f4a445fcf9bc6b414b35fc. --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 45a88eda9..08a6e4380 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,8 +52,6 @@ jobs: - name: Prepare databases run: | - sudo sed -i 's|#password_encryption.*|password_encryption = md5|g' /etc/postgresql/14/main/postgresql.conf - sudo sed -i 's|scram-sha-256|md5|g' /etc/postgresql/14/main/pg_hba.conf sudo systemctl start mysql.service sudo systemctl start postgresql.service mysql -u root -proot -e "CREATE USER 'ejabberd_test'@'localhost' From 42bdb501caf7039f71f3a26ae447db3bffaecb0a Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 18 Dec 2021 17:50:26 +0100 Subject: [PATCH 066/106] mod_stun_disco: Fix parsing of IPv6 listeners Don't crash if `mod_stun_disco` is used with `offer_local_services` and an IPv6 listener has an explicit `ip:` address configured. Thanks to Daniel Kenzelmann for reporting the issue. --- src/mod_stun_disco.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_stun_disco.erl b/src/mod_stun_disco.erl index 6e7592453..cbb671639 100644 --- a/src/mod_stun_disco.erl +++ b/src/mod_stun_disco.erl @@ -646,7 +646,7 @@ get_listener_ips(#{ip := {0, 0, 0, 0, 0, 0, 0, 1}} = Opts) -> {undefined, get_turn_ipv6_addr(Opts)}; get_listener_ips(#{ip := {_, _, _, _} = IP}) -> {IP, undefined}; -get_listener_ips(#{ip := {_, _, _, _, _,_, _, _, _} = IP}) -> +get_listener_ips(#{ip := {_, _, _, _, _, _, _, _} = IP}) -> {undefined, IP}. -spec get_turn_ipv4_addr(map()) -> inet:ip4_address() | undefined. From 7196f46730b233dcdc4d8eabfcdf2fd4104b2041 Mon Sep 17 00:00:00 2001 From: Linus Jahn Date: Sun, 19 Dec 2021 21:06:33 +0100 Subject: [PATCH 067/106] node_pep: Add config-node and multi-items features Fixes #3714. --- src/node_pep.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/node_pep.erl b/src/node_pep.erl index a30f8cb91..66431b948 100644 --- a/src/node_pep.erl +++ b/src/node_pep.erl @@ -81,10 +81,12 @@ features() -> [<<"create-nodes">>, <<"auto-create">>, <<"auto-subscribe">>, + <<"config-node">>, <<"delete-nodes">>, <<"delete-items">>, <<"filtered-notifications">>, <<"modify-affiliations">>, + <<"multi-items">>, <<"outcast-affiliation">>, <<"persistent-items">>, <<"publish">>, From 536beedeb66e6a54ea3631e09a441395f4b11527 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 20 Dec 2021 09:29:42 +0300 Subject: [PATCH 068/106] Accept more types of ejabberdctl commands arguments as JSON-encoded --- src/ejabberd_ctl.erl | 6 +++++- src/mod_http_api.erl | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 354e5ba2f..77595cd54 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -378,7 +378,11 @@ format_arg("", string) -> format_arg(Arg, string) -> NumChars = integer_to_list(length(Arg)), Parse = "~" ++ NumChars ++ "c", - format_arg2(Arg, Parse). + format_arg2(Arg, Parse); +format_arg(Arg, Format) -> + S = unicode:characters_to_binary(Arg, utf8), + JSON = jiffy:decode(S), + mod_http_api:format_arg(JSON, Format). format_arg2(Arg, Parse)-> {ok, [Arg2], _RemainingArguments} = io_lib:fread(Parse, Arg), diff --git a/src/mod_http_api.erl b/src/mod_http_api.erl index 427833584..023df39ca 100644 --- a/src/mod_http_api.erl +++ b/src/mod_http_api.erl @@ -30,6 +30,7 @@ -behaviour(gen_mod). -export([start/2, stop/1, reload/3, process/2, depends/2, + format_arg/2, mod_options/1, mod_doc/0]). -include_lib("xmpp/include/xmpp.hrl"). From a26c9d2475beaa9398d8815f861ed0b1025edc58 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 20 Dec 2021 09:31:01 +0300 Subject: [PATCH 069/106] Optimize user removal handling in mod_muc --- src/mod_muc.erl | 4 ++-- src/mod_muc_room.erl | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/mod_muc.erl b/src/mod_muc.erl index 2853632cc..72f386b00 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -1143,9 +1143,9 @@ remove_user(User, Server) -> fun(Host) -> lists:foreach( fun({_, _, Pid}) -> - mod_muc_room:change_item( + mod_muc_room:change_item_async( Pid, JID, affiliation, none, <<"User removed">>), - mod_muc_room:change_item( + mod_muc_room:change_item_async( Pid, JID, role, none, <<"User removed">>) end, get_online_rooms(LServer, Host)) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index ec1c551e4..25bc7d8b6 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -50,6 +50,7 @@ set_config/2, get_state/1, change_item/5, + change_item_async/5, config_reloaded/1, subscribe/4, unsubscribe/2, @@ -202,6 +203,11 @@ change_item(Pid, JID, Type, AffiliationOrRole, Reason) -> {error, notfound} end. +-spec change_item_async(pid(), jid(), affiliation | role, affiliation() | role(), binary()) -> ok. +change_item_async(Pid, JID, Type, AffiliationOrRole, Reason) -> + p1_fsm:send_all_state_event( + Pid, {process_item_change, {JID, Type, AffiliationOrRole, Reason}, undefined}). + -spec get_state(pid()) -> {ok, state()} | {error, notfound | timeout}. get_state(Pid) -> try p1_fsm:sync_send_all_state_event(Pid, get_state) @@ -675,6 +681,16 @@ handle_event({set_affiliations, Affiliations}, StateName, StateData) -> NewStateData = set_affiliations(Affiliations, StateData), {next_state, StateName, NewStateData}; +handle_event({process_item_change, Item, UJID}, StateName, StateData) -> + case process_item_change(Item, StateData, UJID) of + {error, _} -> + {next_state, StateName, StateData}; + StateData -> + {next_state, StateName, StateData}; + NSD -> + store_room(NSD), + {next_state, StateName, NSD} + end; handle_event(_Event, StateName, StateData) -> {next_state, StateName, StateData}. @@ -723,6 +739,8 @@ handle_sync_event({process_item_change, Item, UJID}, _From, StateName, StateData case process_item_change(Item, StateData, UJID) of {error, _} = Err -> {reply, Err, StateName, StateData}; + StateData -> + {reply, {ok, StateData}, StateName, StateData}; NSD -> store_room(NSD), {reply, {ok, NSD}, StateName, NSD} From fc34661b6f9fe8d506fcd51269c5cefe05c74350 Mon Sep 17 00:00:00 2001 From: Alexey Shchepin Date: Mon, 20 Dec 2021 09:37:47 +0300 Subject: [PATCH 070/106] Add subscribe_room_many command --- src/mod_muc_admin.erl | 44 ++++++++++++++++++++++++++++++++++++--- src/mod_muc_admin_opt.erl | 13 ++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 src/mod_muc_admin_opt.erl diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 9952abd27..21611c585 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -40,8 +40,11 @@ change_room_option/4, get_room_options/2, set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3, web_menu_main/2, web_page_main/2, web_menu_host/3, - subscribe_room/4, unsubscribe_room/2, get_subscribers/2, - web_page_host/3, mod_options/1, get_commands_spec/0, find_hosts/1]). + subscribe_room/4, subscribe_room_many/3, + unsubscribe_room/2, get_subscribers/2, + web_page_host/3, + mod_opt_type/1, mod_options/1, + get_commands_spec/0, find_hosts/1]). -include("logger.hrl"). -include_lib("xmpp/include/xmpp.hrl"). @@ -331,6 +334,25 @@ get_commands_spec() -> args = [{user, binary}, {nick, binary}, {room, binary}, {nodes, binary}], result = {nodes, {list, {node, string}}}}, + #ejabberd_commands{name = subscribe_room_many, tags = [muc_room], + desc = "Subscribe several users to a MUC conference", + module = ?MODULE, function = subscribe_room_many, + args_desc = ["Users JIDs and nicks", + "the room to subscribe", + "nodes separated by commas: ,"], + args_example = [[{"tom@localhost", "Tom"}, + {"jerry@localhost", "Jerry"}], + "room1@conference.localhost", + "urn:xmpp:mucsub:nodes:messages,urn:xmpp:mucsub:nodes:affiliations"], + args = [{users, {list, + {user, {tuple, + [{jid, binary}, + {nick, binary} + ]}} + }}, + {room, binary}, + {nodes, binary}], + result = {res, rescode}}, #ejabberd_commands{name = unsubscribe_room, tags = [muc_room], desc = "Unsubscribe from a MUC conference", module = ?MODULE, function = unsubscribe_room, @@ -1331,6 +1353,18 @@ subscribe_room(User, Nick, Room, Nodes) -> throw({error, "Malformed room JID"}) end. +subscribe_room_many(Users, Room, Nodes) -> + MaxUsers = mod_muc_admin_opt:subscribe_room_many_max_users(global), + if + length(Users) > MaxUsers -> + throw({error, "Too many users in subscribe_room_many command"}); + true -> + lists:foreach( + fun({User, Nick}) -> + subscribe_room(User, Nick, Room, Nodes) + end, Users) + end. + unsubscribe_room(User, Room) -> try jid:decode(Room) of #jid{luser = Name, lserver = Host} when Name /= <<"">> -> @@ -1413,7 +1447,11 @@ find_hosts(ServerHost) -> [] end. -mod_options(_) -> []. +mod_opt_type(subscribe_room_many_max_users) -> + econf:int(). + +mod_options(_) -> + [{subscribe_room_many_max_users, 50}]. mod_doc() -> #{desc => diff --git a/src/mod_muc_admin_opt.erl b/src/mod_muc_admin_opt.erl new file mode 100644 index 000000000..18ca64af7 --- /dev/null +++ b/src/mod_muc_admin_opt.erl @@ -0,0 +1,13 @@ +%% Generated automatically +%% DO NOT EDIT: run `make options` instead + +-module(mod_muc_admin_opt). + +-export([subscribe_room_many_max_users/1]). + +-spec subscribe_room_many_max_users(gen_mod:opts() | global | binary()) -> integer(). +subscribe_room_many_max_users(Opts) when is_map(Opts) -> + gen_mod:get_opt(subscribe_room_many_max_users, Opts); +subscribe_room_many_max_users(Host) -> + gen_mod:get_module_opt(Host, mod_muc_admin, subscribe_room_many_max_users). + From 0881c5941a638c065a92a2c72acbcbdb9891e01c Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Tue, 21 Dec 2021 00:01:17 +0100 Subject: [PATCH 071/106] rebar.config: Don't create extended start script We currently don't use the start script(s) generated by Relx. --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 179052d1c..dc89dc14e 100644 --- a/rebar.config +++ b/rebar.config @@ -178,7 +178,7 @@ {sys_config, "./rel/sys.config"}, {vm_args, "./rel/vm.args"}, {overlay_vars, "vars.config"}, - {extended_start_script, true}, + {extended_start_script, false}, {overlay, [{mkdir, "var/log/ejabberd"}, {mkdir, "var/lib/ejabberd"}, {mkdir, "etc/ejabberd"}, From aaa053829f6018a3a29ce2d340da7439f55dcdbf Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Thu, 23 Dec 2021 07:12:02 +0100 Subject: [PATCH 072/106] Revert "rebar.config: Don't create extended start script" This reverts commit 0881c5941a638c065a92a2c72acbcbdb9891e01c. The extended start had been enabled intentionally, as some commands (such as 'ping', 'status', or 'pid') actually do work. --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index dc89dc14e..179052d1c 100644 --- a/rebar.config +++ b/rebar.config @@ -178,7 +178,7 @@ {sys_config, "./rel/sys.config"}, {vm_args, "./rel/vm.args"}, {overlay_vars, "vars.config"}, - {extended_start_script, false}, + {extended_start_script, true}, {overlay, [{mkdir, "var/log/ejabberd"}, {mkdir, "var/lib/ejabberd"}, {mkdir, "etc/ejabberd"}, From 66bb4cbfa9277a01f8963b7f267062a2f7e4a550 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Dec 2021 13:30:44 +0100 Subject: [PATCH 073/106] Only provide Relx start script in dev profile That script serves a similar purpose to ejabberdctl to start ejabberd, but we can't guarantee it is completely equivalent to ejabberdctl. The prod profile must provide only the well-known script. The test profile provides the Relx script so we can experiment with it. --- rebar.config | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 179052d1c..02993d899 100644 --- a/rebar.config +++ b/rebar.config @@ -178,7 +178,6 @@ {sys_config, "./rel/sys.config"}, {vm_args, "./rel/vm.args"}, {overlay_vars, "vars.config"}, - {extended_start_script, true}, {overlay, [{mkdir, "var/log/ejabberd"}, {mkdir, "var/lib/ejabberd"}, {mkdir, "etc/ejabberd"}, @@ -193,6 +192,7 @@ {dev_mode, false}, {include_erts, true}, {include_src, true}, + {generate_start_script, false}, {overlay, [{copy, "sql/*", "lib/ejabberd-\{\{release_version\}\}/priv/sql/"}, {copy, "ejabberdctl.cfg.example", "etc/ejabberd/ejabberdctl.cfg"}, {copy, "ejabberd.yml.example", "etc/ejabberd/ejabberd.yml"}]}]}]}, @@ -201,6 +201,8 @@ {dev_mode, true}, {include_erts, true}, {include_src, false}, + {generate_start_script, true}, + {extended_start_script, true}, {overlay, [{copy, "ejabberdctl.cfg.example", "etc/ejabberd/ejabberdctl.cfg.example"}, {copy, "ejabberd.yml.example", "etc/ejabberd/ejabberd.yml.example"}, {copy, "test/ejabberd_SUITE_data/ca.pem", "etc/ejabberd/"}, From 200a842cb07e06e8dea30cc6d2db0fe588bd2b72 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 21 Dec 2021 18:33:54 +0100 Subject: [PATCH 074/106] Configure shellcheck to test with sh POSIX script --- .shellcheckrc | 1 + 1 file changed, 1 insertion(+) create mode 100644 .shellcheckrc diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 000000000..96e22629d --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1 @@ +shell=sh From f88aa49913866ae75e33ee5926b28483ee1cda35 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 22 Dec 2021 16:46:23 +0100 Subject: [PATCH 075/106] Give hint about what ejabberd.cfg.example sourcing --- .shellcheckrc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.shellcheckrc b/.shellcheckrc index 96e22629d..f9feb7e4b 100644 --- a/.shellcheckrc +++ b/.shellcheckrc @@ -1 +1,3 @@ +external-sources=true +source=ejabberdctl.cfg.example shell=sh From 1b22368f6b579ce1502fb24a36a556b0633fa445 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 21 Dec 2021 18:03:45 +0100 Subject: [PATCH 076/106] In POSIX sh, OSTYPE is undefined. ejabberd doesn't work natively in win anyway --- ejabberdctl.template | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index 4cab19977..730bfcae9 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -128,14 +128,6 @@ exec_iex() # usage debugwarning() { - if [ "$OSTYPE" != "cygwin" ] && [ "$OSTYPE" != "win32" ] ; then - if [ "a$TERM" = "a" ] || [ "$TERM" = "dumb" ] ; then - echo "Terminal type not supported." - echo "You may have to set the TERM environment variable to fix this." - exit 8 - fi - fi - if [ "$EJABBERD_BYPASS_WARNINGS" != "true" ] ; then echo "--------------------------------------------------------------------" echo "" From b5d8b224867699c59b45b79fa4ab8a130aa5ca1f Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 21 Dec 2021 18:09:18 +0100 Subject: [PATCH 077/106] In POSIX sh, RANDOM is undefined, use alternative --- ejabberdctl.template | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index 730bfcae9..7b7f25c51 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -198,8 +198,9 @@ help() uid() { uuid=$(uuidgen 2>/dev/null) + random=$(awk 'BEGIN { srand(); print int(rand()*32768) }' /dev/null) [ -z "$uuid" ] && [ -f /proc/sys/kernel/random/uuid ] && uuid=$(cat /proc/sys/kernel/random/uuid) - [ -z "$uuid" ] && uuid=$(printf "%X" "${RANDOM:-$$}$(date +%M%S)") + [ -z "$uuid" ] && uuid=$(printf "%X" "${random:-$$}$(date +%M%S)") uuid=$(printf '%s' $uuid | sed 's/^\(...\).*$/\1/') [ $# -eq 0 ] && echo "${uuid}-${ERLANG_NODE}" [ $# -eq 1 ] && echo "${uuid}-${1}-${ERLANG_NODE}" From 46324385f25d6810e8781fedd7c68aca90d7df98 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 21 Dec 2021 18:23:55 +0100 Subject: [PATCH 078/106] SCRIPT_DIR is used by relx releases --- ejabberdctl.template | 1 + 1 file changed, 1 insertion(+) diff --git a/ejabberdctl.template b/ejabberdctl.template index 7b7f25c51..adbaffa62 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -105,6 +105,7 @@ export ERL_MAX_ETS_TABLES export CONTRIB_MODULES_PATH export CONTRIB_MODULES_CONF_DIR export ERL_LIBS +export SCRIPT_DIR # run command either directly or via su $INSTALLUSER exec_cmd() From 4deff0513c519c5dcbde196a8752f05062cea25a Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 22 Dec 2021 13:17:31 +0100 Subject: [PATCH 079/106] Harmless warning, but let's prevent it anyway --- ejabberdctl.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index adbaffa62..c80d33da5 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -81,7 +81,7 @@ if [ -n "$INET_DIST_INTERFACE" ] ; then fi # if vm.args file exists in config directory, pass it to Erlang VM [ -f "$VMARGS" ] && ERLANG_OPTS="$ERLANG_OPTS -args_file $VMARGS" -ERL_LIBS={{libdir}} +ERL_LIBS='{{libdir}}' ERL_CRASH_DUMP="$LOGS_DIR"/erl_crash_$(date "+%Y%m%d-%H%M%S").dump ERL_INETRC="$ETC_DIR"/inetrc From fdc664a318e2b8e24ed197b70ba7c4471f9ec4f4 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 22 Dec 2021 13:52:13 +0100 Subject: [PATCH 080/106] The read value is useless, so use throwaway variable name --- ejabberdctl.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index c80d33da5..cd2cc3c3e 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -146,7 +146,7 @@ debugwarning() echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" echo " EJABBERD_BYPASS_WARNINGS=true" echo "Press return to continue" - read -r input + read -r _ echo "" fi } @@ -169,7 +169,7 @@ livewarning() echo "To bypass permanently this warning, add to ejabberdctl.cfg the line:" echo " EJABBERD_BYPASS_WARNINGS=true" echo "Press return to continue" - read -r input + read -r _ echo "" fi } From cde089ce9c25a0029d52a970d587b979080fa7e2 Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 22 Dec 2021 16:46:41 +0100 Subject: [PATCH 081/106] Disable some shellcheck warnings --- .shellcheckrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.shellcheckrc b/.shellcheckrc index f9feb7e4b..0b7131a2e 100644 --- a/.shellcheckrc +++ b/.shellcheckrc @@ -1,3 +1,4 @@ +disable=SC2016,SC2086,SC2089,SC2090 external-sources=true source=ejabberdctl.cfg.example shell=sh From d8be168cff8c5d447027ffbde36075c6c7a864f5 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Dec 2021 11:16:06 +0100 Subject: [PATCH 082/106] Use dollar notation instead of legacy backticked --- tools/captcha.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/captcha.sh b/tools/captcha.sh index 9fa4a52c4..59df7fcc6 100755 --- a/tools/captcha.sh +++ b/tools/captcha.sh @@ -21,7 +21,7 @@ if test -n ${BASH_VERSION:-''} ; then R=$RANDOM } else - for n in `od -A n -t u2 -N 48 /dev/urandom`; do RL="$RL$n "; done + for n in $(od -A n -t u2 -N 48 /dev/urandom); do RL="$RL$n "; done get_random () { R=${RL%% *} From f4e2d278ba2225bbb2db6c3f42fce8369d856f65 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Dec 2021 11:18:09 +0100 Subject: [PATCH 083/106] Dollar is unnecessary on arithmetic variables --- tools/captcha.sh | 50 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/tools/captcha.sh b/tools/captcha.sh index 59df7fcc6..a619626cf 100755 --- a/tools/captcha.sh +++ b/tools/captcha.sh @@ -30,54 +30,54 @@ else fi get_random -WAVE1_AMPLITUDE=$((2 + $R % 5)) +WAVE1_AMPLITUDE=$((2 + R % 5)) get_random -WAVE1_LENGTH=$((50 + $R % 25)) +WAVE1_LENGTH=$((50 + R % 25)) get_random -WAVE2_AMPLITUDE=$((2 + $R % 5)) +WAVE2_AMPLITUDE=$((2 + R % 5)) get_random -WAVE2_LENGTH=$((50 + $R % 25)) +WAVE2_LENGTH=$((50 + R % 25)) get_random -WAVE3_AMPLITUDE=$((2 + $R % 5)) +WAVE3_AMPLITUDE=$((2 + R % 5)) get_random -WAVE3_LENGTH=$((50 + $R % 25)) +WAVE3_LENGTH=$((50 + R % 25)) get_random -W1_LINE_START_Y=$((10 + $R % 40)) +W1_LINE_START_Y=$((10 + R % 40)) get_random -W1_LINE_STOP_Y=$((10 + $R % 40)) +W1_LINE_STOP_Y=$((10 + R % 40)) get_random -W2_LINE_START_Y=$((10 + $R % 40)) +W2_LINE_START_Y=$((10 + R % 40)) get_random -W2_LINE_STOP_Y=$((10 + $R % 40)) +W2_LINE_STOP_Y=$((10 + R % 40)) get_random -W3_LINE_START_Y=$((10 + $R % 40)) +W3_LINE_START_Y=$((10 + R % 40)) get_random -W3_LINE_STOP_Y=$((10 + $R % 40)) +W3_LINE_STOP_Y=$((10 + R % 40)) get_random -B1_LINE_START_Y=$(($R % 40)) +B1_LINE_START_Y=$((R % 40)) get_random -B1_LINE_STOP_Y=$(($R % 40)) +B1_LINE_STOP_Y=$((R % 40)) get_random -B2_LINE_START_Y=$(($R % 40)) +B2_LINE_START_Y=$((R % 40)) get_random -B2_LINE_STOP_Y=$(($R % 40)) -#B3_LINE_START_Y=$(($R % 40)) -#B3_LINE_STOP_Y=$(($R % 40)) +B2_LINE_STOP_Y=$((R % 40)) +#B3_LINE_START_Y=$((R % 40)) +#B3_LINE_STOP_Y=$((R % 40)) get_random -B1_LINE_START_X=$(($R % 20)) +B1_LINE_START_X=$((R % 20)) get_random -B1_LINE_STOP_X=$((100 + $R % 40)) +B1_LINE_STOP_X=$((100 + R % 40)) get_random -B2_LINE_START_X=$(($R % 20)) +B2_LINE_START_X=$((R % 20)) get_random -B2_LINE_STOP_X=$((100 + $R % 40)) -#B3_LINE_START_X=$(($R % 20)) -#B3_LINE_STOP_X=$((100 + $R % 40)) +B2_LINE_STOP_X=$((100 + R % 40)) +#B3_LINE_START_X=$((R % 20)) +#B3_LINE_STOP_X=$((100 + R % 40)) get_random -ROLL_X=$(($R % 40)) +ROLL_X=$((R % 40)) convert -size 180x60 xc:none -pointsize 40 \ \( -clone 0 -fill white \ From 84d23e60d1268edc155cf9b1f2a4515e8ab47efb Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Dec 2021 11:33:17 +0100 Subject: [PATCH 084/106] In POSIX sh, RANDOM is undefined: use always the other method --- tools/captcha.sh | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/tools/captcha.sh b/tools/captcha.sh index a619626cf..7885858a2 100755 --- a/tools/captcha.sh +++ b/tools/captcha.sh @@ -15,19 +15,12 @@ INPUT=$1 -if test -n ${BASH_VERSION:-''} ; then - get_random () - { - R=$RANDOM - } -else - for n in $(od -A n -t u2 -N 48 /dev/urandom); do RL="$RL$n "; done - get_random () - { - R=${RL%% *} - RL=${RL#* } - } -fi +for n in $(od -A n -t u2 -N 48 /dev/urandom); do RL="$RL$n "; done +get_random () +{ + R=${RL%% *} + RL=${RL#* } +} get_random WAVE1_AMPLITUDE=$((2 + R % 5)) From 694d80b7fa044abf3ddc47829130fcec0bb23fac Mon Sep 17 00:00:00 2001 From: Badlop Date: Wed, 22 Dec 2021 17:52:54 +0100 Subject: [PATCH 085/106] Test shell scripts in CI (#3738) --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08a6e4380..2cfc7a20b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,6 +44,14 @@ jobs: - uses: actions/checkout@v2 + - name: Test shell scripts + if: matrix.otp == 24 + run: | + shellcheck test/ejabberd_SUITE_data/gencerts.sh + shellcheck tools/captcha.sh + shellcheck ejabberd.init.template + shellcheck ejabberdctl.template + - name: Get previous Erlang/OTP uses: ErlGang/setup-erlang@master if: matrix.otp != 24 From e2b7717dd9897bbcda29b270195cd7a36eb295cf Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 23 Dec 2021 12:11:30 +0100 Subject: [PATCH 086/106] Workaround to support ShellCheck older than 0.8.0 --- .github/workflows/ci.yml | 2 +- ejabberdctl.template | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cfc7a20b..8b5a9c69c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: shellcheck test/ejabberd_SUITE_data/gencerts.sh shellcheck tools/captcha.sh shellcheck ejabberd.init.template - shellcheck ejabberdctl.template + shellcheck -x ejabberdctl.template - name: Get previous Erlang/OTP uses: ErlGang/setup-erlang@master diff --git a/ejabberdctl.template b/ejabberdctl.template index cd2cc3c3e..9bd76749c 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -62,6 +62,7 @@ done : "${EJABBERDCTL_CONFIG_PATH:="$ETC_DIR/ejabberdctl.cfg"}" # Allows passing extra Erlang command-line arguments in vm.args file : "${VMARGS:="$ETC_DIR/vm.args"}" +# shellcheck source=ejabberdctl.cfg.example [ -f "$EJABBERDCTL_CONFIG_PATH" ] && . "$EJABBERDCTL_CONFIG_PATH" [ -n "$ERLANG_NODE_ARG" ] && ERLANG_NODE="$ERLANG_NODE_ARG" [ "$ERLANG_NODE" = "${ERLANG_NODE%.*}" ] && S="-s" From 94a733c6669afb7b41fdafcb9bf0a4ac24a483f2 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Thu, 23 Dec 2021 14:24:27 +0100 Subject: [PATCH 087/106] Fix ERTS path in ejabberdctl with `rebar3 release` Rebar 2 expands {{erts-vsn}} to "erts-$vsn", Rebar 3 expands it to just "$vsn". Make sure `make rel` doesn't end up with a "$vsn" directory next to "erts-$vsn" (which happened when using Rebar 3), and make sure that ejabberdctl expects both "erl" and "epmd" to be installed below "erts-$vsn" (which it didn't when using Rebar 3). --- ejabberdctl.template | 1 + rebar.config | 2 +- vars.config.in | 5 +++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ejabberdctl.template b/ejabberdctl.template index 9bd76749c..679db7a73 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -12,6 +12,7 @@ ERLANG_NODE=ejabberd@localhost # define default environment variables [ -z "$SCRIPT" ] && SCRIPT=$0 SCRIPT_DIR="$(cd "$(dirname "$SCRIPT")" && pwd -P)" +ERTS_VSN="{{erts_vsn}}" ERL="{{erl}}" IEX="{{bindir}}/iex" EPMD="{{epmd}}" diff --git a/rebar.config b/rebar.config index 02993d899..80683b442 100644 --- a/rebar.config +++ b/rebar.config @@ -181,7 +181,7 @@ {overlay, [{mkdir, "var/log/ejabberd"}, {mkdir, "var/lib/ejabberd"}, {mkdir, "etc/ejabberd"}, - {copy, "rel/files/erl", "\{\{erts_vsn\}\}/bin/erl"}, % in rebar2 this prepends erts- + {copy, "rel/files/erl", "erts-\{\{erts_vsn\}\}/bin/erl"}, {template, "ejabberdctl.template", "bin/ejabberdctl"}, {copy, "inetrc", "etc/ejabberd/inetrc"}, {copy, "tools/captcha*.sh", "lib/ejabberd-\{\{release_version\}\}/priv/bin/"}, diff --git a/vars.config.in b/vars.config.in index 9b3ac7585..04024fd73 100644 --- a/vars.config.in +++ b/vars.config.in @@ -51,9 +51,10 @@ {release, true}. {release_dir, "${SCRIPT_DIR%/*}"}. {sysconfdir, "{{release_dir}}/etc"}. +{erts_dir, "{{release_dir}}/erts-${ERTS_VSN#erts-}"}. {installuser, "@INSTALLUSER@"}. -{erl, "{{release_dir}}/{{erts_vsn}}/bin/erl"}. -{epmd, "{{release_dir}}/{{erts_vsn}}/bin/epmd"}. +{erl, "{{erts_dir}}/bin/erl"}. +{epmd, "{{erts_dir}}/bin/epmd"}. {localstatedir, "{{release_dir}}/var"}. {libdir, "{{release_dir}}/lib"}. {docdir, "{{release_dir}}/doc"}. From 5ff45e0f00b6cbda6e47926c56cd7d2f69fbdba6 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Thu, 23 Dec 2021 14:46:53 +0100 Subject: [PATCH 088/106] ejabberdctl: Let shellcheck ignore unused ERTS_VSN The ERTS_VSN variable is only used for releases built with Rebar 3. --- ejabberdctl.template | 1 + 1 file changed, 1 insertion(+) diff --git a/ejabberdctl.template b/ejabberdctl.template index 679db7a73..408a4fe6d 100755 --- a/ejabberdctl.template +++ b/ejabberdctl.template @@ -12,6 +12,7 @@ ERLANG_NODE=ejabberd@localhost # define default environment variables [ -z "$SCRIPT" ] && SCRIPT=$0 SCRIPT_DIR="$(cd "$(dirname "$SCRIPT")" && pwd -P)" +# shellcheck disable=SC2034 ERTS_VSN="{{erts_vsn}}" ERL="{{erl}}" IEX="{{bindir}}/iex" From 38410974289f4ce046cb3865abe1bdcc0a3a95fd Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 13 Dec 2021 15:30:20 +0100 Subject: [PATCH 089/106] Clean actions steps, remove redundant ones --- .github/workflows/ci.yml | 59 ++++++++++------------------------------ 1 file changed, 15 insertions(+), 44 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b5a9c69c..c05c6f5c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,7 +89,6 @@ jobs: libsqlite3-dev libwebp-dev libyaml-dev - name: Prepare rebar - id: rebar run: | echo '{xref_ignores, [{eldap_filter_yecc, return_error, 2} ]}.' >>rebar.config @@ -101,23 +100,18 @@ jobs: mqtree, p1_acme, p1_mysql, p1_oauth2, p1_pgsql, p1_utils, pkix, sqlite3, stringprep, stun, xmpp, yconf]} ]}.' >>rebar.config echo '{ct_extra_params, "-verbosity 20"}.' >>rebar.config + echo "{ct_opts, [{verbosity, 20}, {keep_logs, 20}]}." >>rebar.config - - name: Cache rebar2 - if: matrix.rebar == 2 + - name: Cache rebar uses: actions/cache@v2 with: path: | deps/ dialyzer/ ebin/ + ~/.cache/rebar3/ key: ${{matrix.otp}}-${{matrix.rebar}}-${{hashFiles('rebar.config')}} - - name: Cache rebar3 - if: matrix.rebar == 3 - uses: actions/cache@v2 - with: - path: ~/.cache/rebar3/ - key: ${{matrix.otp}}-${{matrix.rebar}}-${{hashFiles('rebar.config')}} - name: Compile run: | @@ -151,28 +145,13 @@ jobs: grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log test $(find logs/ -empty -name error.log) - - name: View dialyzer report - run: cat logs/dialyzer.log - - - name: View full suite.log - run: cat logs/suite.log - - - name: View suite.log failures + - name: View logs failures if: failure() - run: cat logs/suite.log | awk - 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' - - - name: View full ejabberd.log - if: failure() - run: find logs/ -name ejabberd.log -exec cat '{}' ';' - - - name: View error.log - if: failure() - run: find logs/ -name error.log -exec cat '{}' ';' - - - name: View exunit.log - if: failure() - run: find logs/ -name exunit.log -exec cat '{}' ';' + run: | + cat logs/suite.log | awk \ + 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' + find logs/ -name error.log -exec cat '{}' ';' + find logs/ -name exunit.log -exec cat '{}' ';' - name: Send to coveralls if: matrix.otp == 24 @@ -218,21 +197,13 @@ jobs: grep 'TEST COMPLETE' logs/suite.log grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log test $(find logs/ -empty -name error.log) - - name: View full suite.log - run: cat logs/suite.log - - name: View suite.log failures + - name: View logs failures if: failure() - run: cat logs/suite.log | awk - 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' - - name: View full ejabberd.log - if: failure() - run: find logs/ -name ejabberd.log -exec cat '{}' ';' - - name: View error.log - if: failure() - run: find logs/ -name error.log -exec cat '{}' ';' - - name: View exunit.log - if: failure() - run: find logs/ -name exunit.log -exec cat '{}' ';' + run: | + cat logs/suite.log | awk \ + 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' + find logs/ -name error.log -exec cat '{}' ';' + find logs/ -name exunit.log -exec cat '{}' ';' binaries: name: Binaries From aa580b3f6efab3ae7c99b3d3f28d8b1cc0a6c15d Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 14 Dec 2021 13:52:16 +0100 Subject: [PATCH 090/106] Only care about new-schema results when new-schema tests were ran --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c05c6f5c2..6d6ef5984 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -188,8 +188,9 @@ jobs: TO ejabberd_test;" sudo sed -i 's|new_schema, false|new_schema, true|g' test/suite.erl - run: CT_BACKENDS=mysql,pgsql make test + id: ctnewschema - name: Check results - if: always() + if: always() && steps.ctnewschema.outcome != 'skipped' run: | [[ -d _build ]] && ln -s _build/test/logs/ logs \ || ln dialyzer/error.log logs/dialyzer.log @@ -198,7 +199,7 @@ jobs: grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log test $(find logs/ -empty -name error.log) - name: View logs failures - if: failure() + if: failure() && steps.ctnewschema.outcome != 'skipped' run: | cat logs/suite.log | awk \ 'BEGIN{RS="\n=case";FS="\n"} /=result\s*failed/ {print "=case" $0}' From 8a5d90797ae6d263838ead0ca70391885102d05e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 14 Dec 2021 20:00:10 +0100 Subject: [PATCH 091/106] Check dialyzer results in its step when using rebar 2 --- .github/workflows/ci.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6d6ef5984..b98ba24ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,16 +130,16 @@ jobs: - run: make hooks - run: make options - run: make xref - - run: make dialyzer + - run: | + make dialyzer + [ ${{ matrix.rebar }} = 3 ] && true \ + || { cat dialyzer/error.log ; test ! -s dialyzer/error.log ; } - run: make test - name: Check results if: always() run: | - [[ -d _build ]] && ln -s _build/test/logs/ logs \ - && ln `find _build/ -name "*dialyzer_warnings"` \ - logs/dialyzer.log \ - || ln dialyzer/error.log logs/dialyzer.log + [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true ln `find logs/ -name suite.log` logs/suite.log grep 'TEST COMPLETE' logs/suite.log grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log @@ -169,7 +169,7 @@ jobs: - name: Prepare new schema run: | [[ -d logs ]] && rm -rf logs - [[ -d _build/test/logs ]] && rm -rf _build/test/logs + [[ -d _build/test/logs ]] && rm -rf _build/test/logs || true mysql -u root -proot -e "DROP DATABASE ejabberd_test;" sudo -u postgres psql -c "DROP DATABASE ejabberd_test;" mysql -u root -proot -e "CREATE DATABASE ejabberd_test;" @@ -192,8 +192,7 @@ jobs: - name: Check results if: always() && steps.ctnewschema.outcome != 'skipped' run: | - [[ -d _build ]] && ln -s _build/test/logs/ logs \ - || ln dialyzer/error.log logs/dialyzer.log + [[ -d _build ]] && ln -s _build/test/logs/last/ logs || true ln `find logs/ -name suite.log` logs/suite.log grep 'TEST COMPLETE' logs/suite.log grep -q 'TEST COMPLETE,.* 0 failed' logs/suite.log From 8ccad7fadadc7ccedc4a4c9004838503d0f1c76d Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 13 Dec 2021 15:32:26 +0100 Subject: [PATCH 092/106] Publish CT logs and Cover on failure to an external GH Pages repo --- .github/workflows/ci.yml | 41 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b98ba24ed..8cd9a38c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -112,6 +112,14 @@ jobs: ~/.cache/rebar3/ key: ${{matrix.otp}}-${{matrix.rebar}}-${{hashFiles('rebar.config')}} + - name: Download test logs + if: matrix.otp == 24 && github.repository == 'processone/ejabberd' + continue-on-error: true + run: | + mkdir -p _build/test + curl -sSL https://github.com/processone/ecil/tarball/gh-pages | + tar -C _build/test --strip-components=1 --wildcards -xzf - + rm -rf _build/test/logs/last/ - name: Compile run: | @@ -134,7 +142,22 @@ jobs: make dialyzer [ ${{ matrix.rebar }} = 3 ] && true \ || { cat dialyzer/error.log ; test ! -s dialyzer/error.log ; } - - run: make test + + - name: Run tests + if: matrix.otp != 24 + run: make test + - name: Run tests (OTP 24) + if: matrix.otp == 24 + id: ct + run: | + (cd priv && ln -sf ../sql) + COMMIT=`echo $GITHUB_SHA | cut -c 1-7` + DATE=`date +%s` + REF_NAME=`echo $GITHUB_REF_NAME | tr "/" "_"` + NODENAME=$DATE@$GITHUB_RUN_NUMBER-$GITHUB_ACTOR-$REF_NAME-$COMMIT + LABEL=`git show -s --format=%s | cut -c 1-30` + rebar3 ct --name $NODENAME --label "$LABEL" + rebar3 cover - name: Check results if: always() @@ -166,6 +189,22 @@ jobs: "payload":{"build_num":$GITHUB_RUN_ID, "status":"done"}}' + - name: Upload test logs + if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd' + uses: peaceiris/actions-gh-pages@v3 + with: + publish_dir: _build/test + exclude_assets: '.github,lib,plugins' + external_repository: processone/ecil + deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} + keep_files: true + + - name: View ECIL address + if: always() && steps.ct.outcome == 'failure' && github.repository == 'processone/ejabberd' + run: | + CTRUN=`ls -la _build/test/logs/last | sed 's|.*-> ||'` + echo "::notice::View CT results: https://processone.github.io/ecil/logs/$CTRUN/" + - name: Prepare new schema run: | [[ -d logs ]] && rm -rf logs From 59c9500944a8ba363bc75047a3c35192883149a6 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Thu, 30 Dec 2021 21:17:11 +0100 Subject: [PATCH 093/106] mod_muc_room: Fix function name typo --- src/mod_muc_room.erl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 25bc7d8b6..aaf3e8895 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -1637,7 +1637,7 @@ do_get_affiliation_fallback(JID, StateData) -> -spec get_affiliations(state()) -> affiliations(). get_affiliations(#state{config = #config{persistent = false}} = StateData) -> - get_affiliations_callback(StateData); + get_affiliations_fallback(StateData); get_affiliations(StateData) -> Room = StateData#state.room, Host = StateData#state.host, @@ -1645,13 +1645,13 @@ get_affiliations(StateData) -> Mod = gen_mod:db_mod(ServerHost, mod_muc), case Mod:get_affiliations(ServerHost, Room, Host) of {error, _} -> - get_affiliations_callback(StateData); + get_affiliations_fallback(StateData); {ok, Affiliations} -> Affiliations end. --spec get_affiliations_callback(state()) -> affiliations(). -get_affiliations_callback(StateData) -> +-spec get_affiliations_fallback(state()) -> affiliations(). +get_affiliations_fallback(StateData) -> StateData#state.affiliations. -spec get_service_affiliation(jid(), state()) -> owner | none. From dc8196693837650c973834ed5f634e1f6e8fa1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Mon, 3 Jan 2022 13:51:49 +0100 Subject: [PATCH 094/106] Fix 'make update' when used with rebar 3.18 --- Makefile.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile.in b/Makefile.in index ab0e9d967..3066488f0 100644 --- a/Makefile.in +++ b/Makefile.in @@ -94,8 +94,10 @@ endif ifeq "$(MIX)" "mix" REBAR_VER:=6 +REBAR_VER_318:=0 else REBAR_VER:=$(shell $(REBAR) --version | awk -F '[ .]' '/rebar / {print $$2}') +REBAR_VER_318:=$(shell $(REBAR) --version | awk -F '[ .]' '/rebar / {print ($$2 == 3 && $$3 >= 18 ? 1 : 0)}') endif ifeq "$(REBAR_VER)" "6" @@ -115,7 +117,11 @@ else ifeq "$(REBAR_VER)" "3" SKIPDEPS= LISTDEPS=tree +ifeq "$(REBAR_VER_318)" "1" + UPDATEDEPS=upgrade --all +else UPDATEDEPS=upgrade +endif DEPSPATTERN="s/ (.*//; /^ / s/.* \([a-z0-9_]*\).*/\1/p;" DEPSBASE=_build DEPSDIR=$(DEPSBASE)/default/lib From 7c9b47fb78aa964885136c00c649e474b7ff6453 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 4 Jan 2022 15:55:24 +0100 Subject: [PATCH 095/106] Fix "make rel" when using mix, broken after 94a733c66 --- mix.exs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index 7669c519c..b4b71aa8f 100644 --- a/mix.exs +++ b/mix.exs @@ -215,6 +215,7 @@ defmodule Ejabberd.MixProject do epmd: config(:epmd), bindir: Path.join([config(:release_dir), "releases", version()]), release_dir: config(:release_dir), + erts_dir: config(:erts_dir), erts_vsn: "erts-#{release.erts_version}" ] ro = "rel/overlays" @@ -240,7 +241,9 @@ defmodule Ejabberd.MixProject do execute.("sed -e 's|{{\\(\[_a-z\]*\\)}}|<%= @\\1 %>|g' ejabberdctl.template > ejabberdctl.example1") Mix.Generator.copy_template("ejabberdctl.example1", "ejabberdctl.example2", assigns) - execute.("sed -e 's|{{\\(\[_a-z\]*\\)}}|<%= @\\1 %>|g' ejabberdctl.example2 > ejabberdctl.example3") + execute.("sed -e 's|{{\\(\[_a-z\]*\\)}}|<%= @\\1 %>|g' ejabberdctl.example2> ejabberdctl.example2a") + Mix.Generator.copy_template("ejabberdctl.example2a", "ejabberdctl.example2b", assigns) + execute.("sed -e 's|{{\\(\[_a-z\]*\\)}}|<%= @\\1 %>|g' ejabberdctl.example2b > ejabberdctl.example3") execute.("sed -e 's|ERLANG_NODE=ejabberd@localhost|ERLANG_NODE=ejabberd|g' ejabberdctl.example3 > ejabberdctl.example4") execute.("sed -e 's|INSTALLUSER=|ERL_OPTIONS=\"-setcookie \\$\\(cat \"\\${SCRIPT_DIR%/*}/releases/COOKIE\")\"\\nINSTALLUSER=|g' ejabberdctl.example4 > ejabberdctl.example5") Mix.Generator.copy_template("ejabberdctl.example5", "#{ro}/bin/ejabberdctl", assigns) @@ -248,6 +251,8 @@ defmodule Ejabberd.MixProject do File.rm("ejabberdctl.example1") File.rm("ejabberdctl.example2") + File.rm("ejabberdctl.example2a") + File.rm("ejabberdctl.example2b") File.rm("ejabberdctl.example3") File.rm("ejabberdctl.example4") File.rm("ejabberdctl.example5") From 61dc89a06826ae922db6433f1a354923c3737d2d Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 4 Jan 2022 21:22:11 +0100 Subject: [PATCH 096/106] Update Luerl to 1.0.0, now available in Hex.pm (rvirding/luerl#142) --- mix.exs | 2 +- rebar.config | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mix.exs b/mix.exs index b4b71aa8f..64f94345c 100644 --- a/mix.exs +++ b/mix.exs @@ -147,7 +147,7 @@ defmodule Ejabberd.MixProject do for {:true, dep} <- [{config(:pam), {:epam, "~> 1.0"}}, {config(:redis), {:eredis, "~> 1.2.0"}}, {config(:zlib), {:ezlib, "~> 1.0"}}, - {config(:lua), {:luerl, "~> 0.3.1"}}, + {config(:lua), {:luerl, "~> 1.0"}}, {config(:sqlite), {:sqlite3, "~> 1.1"}}], do: dep end diff --git a/rebar.config b/rebar.config index 80683b442..e1b1b6903 100644 --- a/rebar.config +++ b/rebar.config @@ -41,7 +41,7 @@ {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}}, {lager, ".*", {git, "https://github.com/erlang-lager/lager", {tag, "3.9.1"}}}, {if_var_true, lua, - {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.3"}}}}, + {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.0.0"}}}}, {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.14"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.16"}}}, {if_var_true, mysql, @@ -63,7 +63,7 @@ {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.12"}}} ]}. -{gitonly_deps, [elixir, luerl]}. +{gitonly_deps, [elixir]}. {if_var_true, latest_deps, {floating_deps, [cache_tab, From 79ddde3040555b7fa656e38d221fdee11b90df54 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 4 Jan 2022 21:46:40 +0100 Subject: [PATCH 097/106] Workaround to support rebar2 git tag "1.0" and rebar3 hex version "1.0.0" --- rebar.config | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index e1b1b6903..0c18d9e7d 100644 --- a/rebar.config +++ b/rebar.config @@ -41,7 +41,13 @@ {jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.11.1"}}}, {lager, ".*", {git, "https://github.com/erlang-lager/lager", {tag, "3.9.1"}}}, {if_var_true, lua, - {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.0.0"}}}}, + {if_not_rebar3, + {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.0"}}} + }}, + {if_var_true, lua, + {if_rebar3, + {luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "1.0.0"}}} + }}, {mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.14"}}}, {p1_acme, ".*", {git, "https://github.com/processone/p1_acme", {tag, "1.0.16"}}}, {if_var_true, mysql, From cc7ebb86b4c8582231753b2bef3d440c84d17878 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 4 Jan 2022 23:05:42 +0100 Subject: [PATCH 098/106] Fix Dialyzer, related to Luerl API update from 0.3 to 1.0 --- src/prosody2ejabberd.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl index 3992a4034..8f5c35f84 100644 --- a/src/prosody2ejabberd.erl +++ b/src/prosody2ejabberd.erl @@ -118,7 +118,7 @@ eval_file(Path) -> case luerl:eval(NewData, State1) of {ok, _} = Res -> Res; - {error, Why} = Err -> + {error, Why, _} = Err -> ?ERROR_MSG("Failed to eval ~ts: ~p", [Path, Why]), Err end; From 9ba20d26cb67a0c6adcfb7d5fbbb3ba23c6cc9fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Chmielowski?= Date: Wed, 5 Jan 2022 16:43:55 +0100 Subject: [PATCH 099/106] Add better descripion of subscribe_room_many command --- src/mod_muc_admin.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 21611c585..6e533a3df 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -336,6 +336,7 @@ get_commands_spec() -> result = {nodes, {list, {node, string}}}}, #ejabberd_commands{name = subscribe_room_many, tags = [muc_room], desc = "Subscribe several users to a MUC conference", + longdesc = "This command accept up to 50 users at once (this is configurable with `subscribe_room_many_max_users` option)", module = ?MODULE, function = subscribe_room_many, args_desc = ["Users JIDs and nicks", "the room to subscribe", From 03a11c63bd9450a857b5c2d60c0c7337280918e3 Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 11 Jan 2022 17:19:12 +0100 Subject: [PATCH 100/106] Fix login when generating client id, keep connection record (#3593) --- src/mod_mqtt_session.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mod_mqtt_session.erl b/src/mod_mqtt_session.erl index ca025e3d2..e7737804e 100644 --- a/src/mod_mqtt_session.erl +++ b/src/mod_mqtt_session.erl @@ -1134,8 +1134,8 @@ is_expired(#publish{meta = Meta, properties = Props} = Pkt) -> %%% Authentication %%%=================================================================== -spec parse_credentials(connect()) -> {ok, jid:jid()} | {error, reason_code()}. -parse_credentials(#connect{client_id = <<>>}) -> - parse_credentials(#connect{client_id = p1_rand:get_string()}); +parse_credentials(#connect{client_id = <<>>} = C) -> + parse_credentials(C#connect{client_id = p1_rand:get_string()}); parse_credentials(#connect{username = <<>>, client_id = ClientID}) -> Host = ejabberd_config:get_myname(), JID = case jid:make(ClientID, Host) of From ce14c28faf2ca47e14a3dcdaf9e336940ca89ca0 Mon Sep 17 00:00:00 2001 From: Badlop Date: Mon, 10 Jan 2022 16:45:35 +0100 Subject: [PATCH 101/106] Fix version when this command was really updated --- src/mod_muc_admin.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 6e533a3df..d18ba5f07 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -284,7 +284,7 @@ get_commands_spec() -> #ejabberd_commands{name = send_direct_invitation, tags = [muc_room], desc = "Send a direct invitation to several destinations", - longdesc = "Since ejabberd 20.10, this command is " + longdesc = "Since ejabberd 20.12, this command is " "asynchronous: the API call may return before the " "server has send all the invitations.\n\n" "Password and Message can also be: none. " From 1ce3bd256bdb79aa4e7943d58d7c2e92529613b7 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 13 Jan 2022 19:27:01 +0100 Subject: [PATCH 102/106] Update section URLs in ldap documentation --- src/mod_shared_roster_ldap.erl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index 08fbe8793..e842ab261 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -689,9 +689,9 @@ mod_doc() -> ?T("- Connection parameters: The module also accepts the " "connection parameters, all of which default to the top-level " "parameter of the same name, if unspecified. " - "See http://../database-ldap/#ldap-connection[LDAP Connection] " + "See http://../ldap/#ldap-connection[LDAP Connection] " "section for more information about them."), "", - ?T("Check also the http://../database-ldap/#configuration-examples" + ?T("Check also the http://../ldap/#ldap-examples" "[Configuration examples] section to get details about " "retrieving the roster, " "and configuration examples including Flat DIT and Deep DIT.")], @@ -721,13 +721,13 @@ mod_doc() -> "name of roster entries (usually full names of people in " "the roster). See also the parameters 'ldap_userdesc' and " "'ldap_useruid'. For more information check the LDAP " - "http://../database-ldap/#filters[Filters] section.")}}, + "http://../ldap/#filters[Filters] section.")}}, {ldap_filter, #{desc => ?T("Additional filter which is AND-ed together " "with \"User Filter\" and \"Group Filter\". " "For more information check the LDAP " - "http://../database-ldap/#filters[Filters] section.")}}, + "http://../ldap/#filters[Filters] section.")}}, %% Attributes: {ldap_groupattr, #{desc => @@ -785,7 +785,7 @@ mod_doc() -> #{desc => ?T("A regex for extracting user ID from the value of the " "attribute named by 'ldap_memberattr'. Check the LDAP " - "http://../database-ldap/#control-parameters" + "http://../ldap/#control-parameters" "[Control Parameters] section.")}}, {ldap_auth_check, #{value => "true | false", From 1fb908b70f8651f7074d8f1beefc5d4d2baf470b Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 13 Jan 2022 19:27:53 +0100 Subject: [PATCH 103/106] Document option subscrube_room_many_max_users introduced in fc34661b6 --- src/mod_muc_admin.erl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index d18ba5f07..ac2d887fe 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -1459,4 +1459,11 @@ mod_doc() -> [?T("This module provides commands to administer local MUC " "services and their MUC rooms. It also provides simple " "WebAdmin pages to view the existing rooms."), "", - ?T("This module depends on _`mod_muc`_.")]}. + ?T("This module depends on _`mod_muc`_.")], + opts => + [{subscribe_room_many_max_users, + #{value => ?T("Number"), + desc => + ?T("How many users can be subscribed to a room at once using " + "the 'subscribe_room_many' command. " + "The default value is '50'.")}}]}. From a9ac10e876da1bfde0b7c3bbc4039745e4e79368 Mon Sep 17 00:00:00 2001 From: Badlop Date: Thu, 13 Jan 2022 19:28:50 +0100 Subject: [PATCH 104/106] Document that 'unregister' command deletes data associated with the account --- src/ejabberd_admin.erl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 14305e84b..9e72c7b36 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -181,6 +181,8 @@ get_commands_spec() -> result = {res, restuple}}, #ejabberd_commands{name = unregister, tags = [accounts], desc = "Unregister a user", + longdesc = "This deletes the authentication and all the " + "data associated to the account (roster, vcard...).", policy = admin, module = ?MODULE, function = unregister, args_desc = ["Username", "Local vhost served by ejabberd"], From 8e88fa3884a7a396a33b69fa2ea3d805e1d49501 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Sat, 15 Jan 2022 18:18:24 +0100 Subject: [PATCH 105/106] mod_shared_roster: Normalize JID on unset_presence Don't forget to normalize the JID handed over from ejabberd_sm on presence-unavailable. Without normalization, mod_shared_roster might fail to look up the storage backend for the given host name, for example. Fixes #3752. --- src/mod_shared_roster.erl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl index 13ff90466..358a8df32 100644 --- a/src/mod_shared_roster.erl +++ b/src/mod_shared_roster.erl @@ -870,12 +870,15 @@ c2s_self_presence(Acc) -> Acc. -spec unset_presence(binary(), binary(), binary(), binary()) -> ok. -unset_presence(LUser, LServer, Resource, Status) -> +unset_presence(User, Server, Resource, Status) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + LResource = jid:resourceprep(Resource), Resources = ejabberd_sm:get_user_resources(LUser, LServer), ?DEBUG("Unset_presence for ~p @ ~p / ~p -> ~p " "(~p resources)", - [LUser, LServer, Resource, Status, length(Resources)]), + [LUser, LServer, LResource, Status, length(Resources)]), case length(Resources) of 0 -> lists:foreach( From 0f2d36dc533b03dca55b9dfda78956f7d4a2de37 Mon Sep 17 00:00:00 2001 From: Holger Weiss Date: Mon, 17 Jan 2022 19:08:36 +0100 Subject: [PATCH 106/106] mod_pubsub: Allow for limiting item_expire value If mod_pubsub's 'max_item_expire_node' option is specified, reject node configurations with an 'item_expire' value that exceeds the specified limit. --- src/mod_pubsub.erl | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl index 76092f1c6..d161ec10c 100644 --- a/src/mod_pubsub.erl +++ b/src/mod_pubsub.erl @@ -3512,17 +3512,24 @@ decode_node_config(undefined, _, _) -> decode_node_config(#xdata{fields = Fs}, Host, Lang) -> try Config = pubsub_node_config:decode(Fs), - Max = get_max_items_node(Host), - case {check_opt_range(max_items, Config, Max), + MaxItems = get_max_items_node(Host), + MaxExpiry = get_max_item_expire_node(Host), + case {check_opt_range(max_items, Config, MaxItems), + check_opt_range(item_expire, Config, MaxExpiry), check_opt_range(max_payload_size, Config, ?MAX_PAYLOAD_SIZE)} of - {true, true} -> + {true, true, true} -> Config; - {true, false} -> + {true, true, false} -> erlang:error( {pubsub_node_config, {bad_var_value, <<"pubsub#max_payload_size">>, ?NS_PUBSUB_NODE_CONFIG}}); - {false, _} -> + {true, false, _} -> + erlang:error( + {pubsub_node_config, + {bad_var_value, <<"pubsub#item_expire">>, + ?NS_PUBSUB_NODE_CONFIG}}); + {false, _, _} -> erlang:error( {pubsub_node_config, {bad_var_value, <<"pubsub#max_items">>, @@ -3568,9 +3575,11 @@ decode_get_pending(#xdata{fields = Fs}, Lang) -> end. -spec check_opt_range(atom(), [proplists:property()], - non_neg_integer() | unlimited) -> boolean(). + non_neg_integer() | unlimited | infinity) -> boolean(). check_opt_range(_Opt, _Opts, unlimited) -> true; +check_opt_range(_Opt, _Opts, infinity) -> + true; check_opt_range(Opt, Opts, Max) -> case proplists:get_value(Opt, Opts, Max) of max -> true;