diff --git a/big_tests/tests/cluster_commands_SUITE.erl b/big_tests/tests/cluster_commands_SUITE.erl index 1e1e3bcc1c7..3f325613ade 100644 --- a/big_tests/tests/cluster_commands_SUITE.erl +++ b/big_tests/tests/cluster_commands_SUITE.erl @@ -37,21 +37,20 @@ all() -> [{group, clustered}, - {group, mnesia}, {group, clustering_two}, {group, clustering_three}]. groups() -> [{clustered, [], [one_to_one_message]}, {clustering_two, [], clustering_two_tests()}, - {clustering_three, [], clustering_three_tests()}, - {mnesia, [], [set_master_test]}]. + {clustering_three, [], clustering_three_tests()}]. suite() -> require_rpc_nodes([mim, mim2, mim3]) ++ escalus:suite(). clustering_two_tests() -> - [join_successful_prompt, + [commands_without_args, + join_successful_prompt, join_successful_force, leave_successful_prompt, leave_successful_force, @@ -184,33 +183,16 @@ one_to_one_message(ConfigIn) -> Stanza2 = escalus:wait_for_stanza(Alice, 5000), escalus:assert(is_chat_message, [<<"Oh hi!">>], Stanza2) end). -%%-------------------------------------------------------------------- -%% mnesia tests -%%-------------------------------------------------------------------- - -set_master_test(ConfigIn) -> - %% To ensure that passwd table exists. - %% We also need at least two nodes for set_master to work. - catch distributed_helper:rpc(mim(), ejabberd_auth_internal, start, [host_type(mim1)]), - catch distributed_helper:rpc(mim2(), ejabberd_auth_internal, start, [host_type(mim2)]), - - TableName = passwd, - NodeList = rpc_call(mnesia, system_info, [running_db_nodes]), - mongooseimctl("set_master", ["self"], ConfigIn), - [MasterNode] = rpc_call(mnesia, table_info, [TableName, master_nodes]), - true = lists:member(MasterNode, NodeList), - RestNodesList = lists:delete(MasterNode, NodeList), - OtherNode = hd(RestNodesList), - mongooseimctl("set_master", [atom_to_list(OtherNode)], ConfigIn), - [OtherNode] = rpc_call(mnesia, table_info, [TableName, master_nodes]), - mongooseimctl("set_master", ["self"], ConfigIn), - [MasterNode] = rpc_call(mnesia, table_info, [TableName, master_nodes]). - %%-------------------------------------------------------------------- %% Manage cluster commands tests %%-------------------------------------------------------------------- +commands_without_args(Config) -> + {Res1, 1} = mongooseimctl_interactive("join_cluster", [], "yes\n", Config), + ?assertMatch({match, _}, re:run(Res1, "This command requires one argument: other node's name")), + {Res2, 1} = mongooseimctl_interactive("remove_from_cluster", [], "yes\n",Config), + ?assertMatch({match, _}, re:run(Res2, "This command requires one argument: other node's name")). join_successful_prompt(Config) -> %% given diff --git a/big_tests/tests/gdpr_SUITE.erl b/big_tests/tests/gdpr_SUITE.erl index f563d9df085..8eecfe7eb95 100644 --- a/big_tests/tests/gdpr_SUITE.erl +++ b/big_tests/tests/gdpr_SUITE.erl @@ -5,6 +5,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("escalus/include/escalus.hrl"). -include_lib("exml/include/exml.hrl"). +-include_lib("eunit/include/eunit.hrl"). -include("muc_light.hrl"). -export([suite/0, all/0, groups/0]). @@ -52,15 +53,13 @@ remove_pubsub_push_node/1, remove_pubsub_pep_node/1 ]). --export([ - data_is_not_retrieved_for_missing_user/1 - ]). -import(mongooseimctl_helper, [mongooseimctl/3]). -import(distributed_helper, [mim/0, subhost_pattern/1, rpc/4]). -import(muc_light_helper, [room_bin_jid/1]). -import(domain_helper, [host_type/0]). -import(config_parser_helper, [default_mod_config/1, mod_config/2]). +-import(graphql_helper, [execute_command/4, get_ok_value/2]). -define(ROOM, <<"tt1">>). @@ -74,7 +73,6 @@ suite() -> all() -> [ {group, retrieve_personal_data}, - {group, retrieve_negative}, {group, remove_personal_data} ]. @@ -111,9 +109,6 @@ groups() -> dont_retrieve_other_user_private_xml, retrieve_multiple_private_xmls ]}, - {retrieve_negative, [], [ - data_is_not_retrieved_for_missing_user - ]}, {retrieve_personal_data_mam, [], [ {group, retrieve_personal_data_mam_rdbms}, {group, retrieve_personal_data_mam_cassandra}, @@ -181,11 +176,13 @@ init_per_suite(Config) -> #{node := MimNode} = distributed_helper:mim(), Config1 = [{{ejabberd_cwd, MimNode}, get_mim_cwd()} | dynamic_modules:save_modules(host_type(), Config)], muc_helper:load_muc(), - escalus:init_per_suite(Config1). + Config2 = graphql_helper:init_admin_cli(Config1), + escalus:init_per_suite(Config2). end_per_suite(Config) -> delete_files(), escalus_fresh:clean(), + graphql_helper:clean(), dynamic_modules:restore_modules(Config), escalus:end_per_suite(Config). @@ -478,7 +475,7 @@ remove_vcard(Config) -> = escalus:send_and_wait(Alice, escalus_stanza:vcard_update(AliceFields)), escalus:assert(is_iq_result, AliceSetResultStanza), - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), assert_personal_data_via_rpc(Alice, [{vcard,["jid","vcard"],[]}]) @@ -500,7 +497,7 @@ remove_private(Config) -> <<"Something to declare">>}]}]), %% Remove Alice - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), %% Expect Alice's data to be gone assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"], []}]) @@ -518,7 +515,7 @@ dont_remove_other_user_private_xml(Config) -> send_and_assert_private_stanza(Bob, BobNS, BobContent), %% Remove Alice - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), %% Expect Alice's data to be gone assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"], []}]), @@ -559,7 +556,7 @@ remove_multiple_private_xmls(Config) -> Alice, Config, "private", ExpectedHeader, ExpectedItems), %% Remove Alice - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), %% Expect all of Alice's data to be gone assert_personal_data_via_rpc(Alice, [{private, ["ns","xml"], []}]) @@ -587,7 +584,7 @@ remove_roster(Config) -> #{ "jid" => [{contains, AliceU}, {contains, AliceS}] } ], - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), assert_personal_data_via_rpc(Alice, [{roster, expected_header(mod_roster), []}]), retrieve_and_validate_personal_data( @@ -795,7 +792,7 @@ remove_mam_pm(Config) -> #{"message" => [{contains, Msg2}], "from" => [{jid, BobJID}]} ], - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), assert_personal_data_via_rpc(Alice, [{mam_pm, ExpectedHeader, []}]), @@ -990,7 +987,7 @@ remove_offline(Config) -> [host_type(), AliceU, AliceS, 10]) end, 3), - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), assert_personal_data_via_rpc( Alice, [{offline, ["timestamp","from", "to", "packet"],[]}]) @@ -1082,7 +1079,7 @@ remove_pubsub_subscriptions(Config) -> pubsub_tools:create_node(Alice, Node, []), pubsub_tools:subscribe(Bob, Node, []), - {0, _} = unregister(Bob, Config), + unregister(Bob, Config), assert_personal_data_via_rpc(Bob, [{pubsub_payloads,["node_name","item_id","payload"],[]}, @@ -1106,7 +1103,7 @@ remove_pubsub_dont_remove_flat_pubsub_node(Config) -> Node1 = {_,NodeName} = pubsub_tools:pubsub_node_with_num(1), pubsub_tools:create_nodes([{Alice, Node1, []}]), - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), assert_personal_data_via_rpc(Alice, [{pubsub_payloads,["node_name","item_id","payload"],[]}, @@ -1133,7 +1130,7 @@ remove_pubsub_push_node(Config) -> escalus:send(Bob, PublishIQ), escalus:assert(is_iq_result, escalus:wait_for_stanza(Bob)), - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), assert_personal_data_via_rpc(Alice, [{pubsub_payloads,["node_name","item_id","payload"],[]}, {pubsub_nodes,["node_name","type"],[]}, @@ -1149,7 +1146,7 @@ remove_pubsub_pep_node(Config) -> {Alice, PepNode, []} ]), - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), assert_personal_data_via_rpc(Alice, [{pubsub_payloads,["node_name","item_id","payload"],[]}, {pubsub_nodes,["node_name","type"],[]}, @@ -1164,7 +1161,7 @@ remove_pubsub_dont_remove_node_when_only_publisher(Config) -> AffChange = [{Bob, <<"publish-only">>}], pubsub_tools:set_affiliations(Alice, Node1, AffChange, []), - {0, _} = unregister(Bob, Config), + unregister(Bob, Config), assert_personal_data_via_rpc(Alice, [{pubsub_payloads,["node_name","item_id","payload"],[]}, @@ -1209,7 +1206,7 @@ remove_pubsub_all_data(Config) -> pubsub_tools:publish(Bob, BobToNode3, Node3, [{with_payload, {true, BinItem4}}]), pubsub_tools:receive_item_notification(Alice, BobToNode3, Node3, []), - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), [{pubsub_payloads,["node_name","item_id","payload"], AlicePayloads}, {pubsub_nodes,["node_name","type"], AliceNodes}, @@ -1431,7 +1428,7 @@ remove_inbox(Config) -> ExpectedHeader = ["jid", "content", "unread_count", "timestamp"], - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), assert_personal_data_via_rpc(Alice, [{inbox, ExpectedHeader, []}]), @@ -1457,7 +1454,7 @@ remove_inbox_muclight(Config) -> ExpectedHeader = ["jid", "content", "unread_count", "timestamp"], - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), %% MUC Light affiliations are also stored in inbox %% 1. Added to the room @@ -1499,7 +1496,7 @@ remove_inbox_muc(Config) -> ExpectedHeader = ["jid", "content", "unread_count", "timestamp"], - {0, _} = unregister(Alice, Config), + unregister(Alice, Config), escalus:wait_for_stanza(Bob), assert_personal_data_via_rpc(Alice, [{inbox, ExpectedHeader, []}]), @@ -1543,18 +1540,13 @@ retrieve_logs(Config) -> mongoose_helper:successful_rpc(net_kernel, connect_node, [MIM2NodeName]), mongoose_helper:successful_rpc(MIM2Node, error_logger, error_msg, ["event=disturbance_in_the_force, jid=~s", [JID]]), - Dir = request_and_unzip_personal_data(User, Domain, Config), + Dir = request_and_unzip_personal_data(list_to_binary(User), list_to_binary(Domain), + Config), Filename = filename:join(Dir, "logs-" ++ atom_to_list(MIM2NodeName) ++ ".txt"), {ok, Content} = file:read_file(Filename), {match, _} = re:run(Content, "disturbance_in_the_force") end). -%% ------------------------- Data retrieval - Negative case ------------------------- - -data_is_not_retrieved_for_missing_user(Config) -> - {Filename, 1, _} = retrieve_personal_data("non-person", "oblivion", Config), - {error, _} = file:read_file_info(Filename). - %% ------------------------------------------------------------- %% Internal functions %% ------------------------------------------------------------- @@ -1695,7 +1687,9 @@ retrieve_all_personal_data(Client, Config) -> request_and_unzip_personal_data(User, Domain, Config). request_and_unzip_personal_data(User, Domain, Config) -> - {Filename, 0, _} = retrieve_personal_data(User, Domain, Config), + {Filename, Res} = retrieve_personal_data(User, Domain, Config), + ParsedResult = get_ok_value([data, gdpr, retrievePersonalData], Res), + ?assertEqual(<<"Data retrieved">>, ParsedResult), FullPath = get_mim_cwd() ++ "/" ++ Filename, Dir = make_dir_name(Filename, User), ct:log("extracting logs ~s", [Dir]), @@ -1709,15 +1703,18 @@ make_dir_name(Filename, User) when is_list(User) -> retrieve_personal_data(User, Domain, Config) -> Filename = random_filename(Config), - {CommandOutput, Code} = mongooseimctl("retrieve_personal_data", - [User, Domain, Filename], Config), - {Filename, Code, CommandOutput}. + Vars = #{<<"username">> => User, <<"domain">> => Domain, + <<"resultFilepath">> => list_to_binary(Filename)}, + Result = execute_command(<<"gdpr">>, <<"retrievePersonalData">>, Vars, Config), + {Filename, Result}. unregister(Client, Config) -> - User = escalus_client:username(Client), - Domain = escalus_client:server(Client), - {CommandOutput, Code} = mongooseimctl("unregister", [User, Domain], Config), - {Code, CommandOutput}. + User = escalus_client:full_jid(Client), + Path = [data, account, removeUser, message], + Vars = #{<<"user">> => User}, + Resp = execute_command(<<"account">>, <<"removeUser">>, Vars, Config), + ?assertNotEqual(nomatch, binary:match(get_ok_value(Path, Resp), + <<"successfully unregister">>)). random_filename(Config) -> TCName = atom_to_list(?config(tc_name, Config)), diff --git a/big_tests/tests/graphql_account_SUITE.erl b/big_tests/tests/graphql_account_SUITE.erl index 7c4ff5e1238..a444302f9e5 100644 --- a/big_tests/tests/graphql_account_SUITE.erl +++ b/big_tests/tests/graphql_account_SUITE.erl @@ -44,7 +44,8 @@ admin_account_tests() -> admin_check_password_non_existing_user, admin_check_password_hash, admin_check_password_hash_non_existing_user, - admin_check_plain_password_hash, + admin_check_plain_password_hash_md5, + admin_check_plain_password_hash_sha, admin_check_user, admin_check_non_existing_user, admin_register_user, @@ -135,7 +136,8 @@ domain_admin_clean_users(Config) -> init_per_testcase(admin_register_user = C, Config) -> Config1 = [{user, {<<"gql_admin_registration_test">>, domain_helper:domain()}} | Config], escalus:init_per_testcase(C, Config1); -init_per_testcase(admin_check_plain_password_hash = C, Config) -> +init_per_testcase(C, Config) when C =:= admin_check_plain_password_hash_md5; + C =:= admin_check_plain_password_hash_sha -> {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), case lists:member(ejabberd_auth_ldap, AuthMods) of true -> @@ -176,7 +178,8 @@ end_per_testcase(admin_register_user = C, Config) -> {Username, Domain} = proplists:get_value(user, Config), rpc(mim(), mongoose_account_api, unregister_user, [Username, Domain]), escalus:end_per_testcase(C, Config); -end_per_testcase(admin_check_plain_password_hash, Config) -> +end_per_testcase(C, Config) when C =:= admin_check_plain_password_hash_md5; + C =:= admin_check_plain_password_hash_sha -> mongoose_helper:restore_config(Config), escalus:delete_users(Config, escalus:get_users([carol])); end_per_testcase(admin_register_user_limit_error = C, Config) -> @@ -297,12 +300,17 @@ admin_check_password_hash_non_existing_user(Config) -> Resp3 = check_password_hash(?EMPTY_NAME_JID, EmptyHash, Method, Config), get_coercion_err_msg(Resp3). -admin_check_plain_password_hash(Config) -> +admin_check_plain_password_hash_md5(Config) -> + admin_check_password_hash(Config, <<"md5">>, fun get_md5/1). + +admin_check_plain_password_hash_sha(Config) -> + admin_check_password_hash(Config, <<"sha">>, fun get_sha/1). + +admin_check_password_hash(Config, Method, HashFun) -> UserJID = escalus_users:get_jid(Config, carol), Password = lists:last(escalus_users:get_usp(Config, carol)), - Method = <<"md5">>, - Hash = list_to_binary(get_md5(Password)), - WrongHash = list_to_binary(get_md5(<<"wrong password">>)), + Hash = list_to_binary(HashFun(Password)), + WrongHash = list_to_binary(HashFun(<<"wrong password">>)), Path = [data, account, checkPasswordHash], % A correct hash Resp = check_password_hash(UserJID, Hash, Method, Config), @@ -490,8 +498,9 @@ admin_import_users_http(Config) -> <<"notAllowed">> => null}, get_ok_value([data, account, importUsers], Resp2)), Domain = domain_helper:domain(), + JID = mongoose_helper:make_jid(<<"john">>, Domain), mongoose_helper:wait_until(fun() -> - rpc(mim(), mongoose_account_api, check_account, [<<"john">>, Domain]) + rpc(mim(), mongoose_account_api, check_account, [JID]) end, {ok, io_lib:format("User ~s exists", [<<"john@", Domain/binary>>])}, #{time_left => timer:seconds(20), @@ -608,6 +617,10 @@ get_md5(AccountPass) -> lists:flatten([io_lib:format("~.16B", [X]) || X <- binary_to_list(crypto:hash(md5, AccountPass))]). +get_sha(AccountPass) -> + lists:flatten([io_lib:format("~.16B", [X]) + || X <- binary_to_list(crypto:hash(sha, AccountPass))]). + %% Commands user_unregister(User, Config) -> diff --git a/big_tests/tests/graphql_domain_SUITE.erl b/big_tests/tests/graphql_domain_SUITE.erl index 23fa77d458f..67353ad4536 100644 --- a/big_tests/tests/graphql_domain_SUITE.erl +++ b/big_tests/tests/graphql_domain_SUITE.erl @@ -30,7 +30,10 @@ groups() -> domain_tests() -> [create_domain, unknown_host_type_error_formatting, - static_domain_error_formatting, + add_static_domain_error_formatting, + remove_static_domain_error_formatting, + enable_static_domain_error_formatting, + disable_static_domain_error_formatting, domain_duplicate_error_formatting, domain_not_found_error_formatting_after_mutation_disable_domain, domain_not_found_error_formatting_after_mutation_enable_domain, @@ -116,11 +119,26 @@ unknown_host_type_error_formatting(Config) -> Result2 = get_domains_by_host_type(HostType, Config), ?assertEqual(<<"Unknown host type">>, get_err_msg(Result2)). -static_domain_error_formatting(Config) -> +add_static_domain_error_formatting(Config) -> DomainName = <<"localhost">>, Result = add_domain(DomainName, ?HOST_TYPE, Config), ?assertEqual(<<"Domain is static">>, get_err_msg(Result)). +remove_static_domain_error_formatting(Config) -> + DomainName = <<"localhost">>, + Result = remove_domain(DomainName, ?HOST_TYPE, Config), + ?assertEqual(<<"Domain is static">>, get_err_msg(Result)). + +enable_static_domain_error_formatting(Config) -> + DomainName = <<"localhost">>, + Result = enable_domain(DomainName, Config), + ?assertEqual(<<"Domain is static">>, get_err_msg(Result)). + +disable_static_domain_error_formatting(Config) -> + DomainName = <<"localhost">>, + Result = disable_domain(DomainName, Config), + ?assertEqual(<<"Domain is static">>, get_err_msg(Result)). + domain_duplicate_error_formatting(Config) -> DomainName = ?EXAMPLE_DOMAIN, Result = add_domain(DomainName, ?SECOND_HOST_TYPE, Config), diff --git a/big_tests/tests/graphql_last_SUITE.erl b/big_tests/tests/graphql_last_SUITE.erl index 6289d8a6ec8..af365823f77 100644 --- a/big_tests/tests/graphql_last_SUITE.erl +++ b/big_tests/tests/graphql_last_SUITE.erl @@ -608,7 +608,7 @@ set_last(UserJID, DateTime, Config) -> check_account(User) -> {Username, LServer} = jid:to_lus(user_to_jid(User)), - rpc(mim(), mongoose_account_api, check_account, [Username, LServer]). + rpc(mim(), mongoose_account_api, check_account, [mongoose_helper:make_jid(Username, LServer)]). assert_err_msg(Contains, Res) -> ?assertNotEqual(nomatch, binary:match(get_err_msg(Res), Contains)). diff --git a/big_tests/tests/graphql_server_SUITE.erl b/big_tests/tests/graphql_server_SUITE.erl index 288c2f55c1d..a068d28a89f 100644 --- a/big_tests/tests/graphql_server_SUITE.erl +++ b/big_tests/tests/graphql_server_SUITE.erl @@ -45,6 +45,7 @@ clustering_tests() -> join_unsuccessful, leave_but_no_cluster, join_twice, + leave_twice, remove_dead_from_cluster, remove_alive_from_cluster, remove_node_test, @@ -108,7 +109,8 @@ end_per_testcase(set_and_get_loglevel_test = CaseName, Config) -> escalus:end_per_testcase(CaseName, Config); end_per_testcase(CaseName, Config) when CaseName == join_successful orelse CaseName == join_successful_http - orelse CaseName == join_twice -> + orelse CaseName == join_twice + orelse CaseName == leave_twice -> remove_node_from_cluster(mim2(), Config), escalus:end_per_testcase(CaseName, Config); end_per_testcase(CaseName, Config) when CaseName == remove_alive_from_cluster @@ -172,6 +174,13 @@ join_twice(Config) -> ?assertEqual(<<"already_joined">>, get_err_code(join_cluster(atom_to_binary(Node2), Config))), distributed_helper:verify_result(RPCSpec2, add). +leave_twice(Config) -> + #{node := Node2} = RPCSpec2 = mim2(), + join_cluster(atom_to_binary(Node2), Config), + get_ok_value([], leave_cluster(Config)), + distributed_helper:verify_result(RPCSpec2, remove), + ?assertEqual(<<"not_in_cluster">>, get_err_code(leave_cluster(Config))). + remove_dead_from_cluster(Config) -> % given Timeout = timer:seconds(60), @@ -247,7 +256,7 @@ remove_dead_from_cluster_http(Config) -> ok = rpc(Node3#{timeout => Timeout}, mongoose_cluster, join, [Node1Nodename]), %% when distributed_helper:stop_node(Node3Nodename, Config), - F2 = fun() -> + F2 = fun() -> test == rpc(Node1#{timeout => Timeout}, mongoose_config, get_opt, [listen, test]) end, mongoose_helper:wait_until(F2, false, #{sleep_time => 200, name => wait_for_mim1, @@ -281,8 +290,8 @@ remove_alive_from_cluster_http(Config) -> ensure_node_started(Node) -> Timeout = timer:seconds(60), - F = fun() -> - case rpc(Node#{timeout => Timeout}, mongoose_server_api, status, []) of + F = fun() -> + case rpc(Node#{timeout => Timeout}, mongoose_server_api, status, []) of {ok, {true, _, _}} -> true; _Other -> false end diff --git a/big_tests/tests/metrics_roster_SUITE.erl b/big_tests/tests/metrics_roster_SUITE.erl index c2502a09500..36c6374df2e 100644 --- a/big_tests/tests/metrics_roster_SUITE.erl +++ b/big_tests/tests/metrics_roster_SUITE.erl @@ -72,6 +72,7 @@ init_per_suite(Config) -> [{mongoose_metrics, MongooseMetrics} | escalus:init_per_suite(Config)]. end_per_suite(Config) -> + escalus_fresh:clean(), escalus:end_per_suite(Config). init_per_group(_GroupName, Config) -> diff --git a/big_tests/tests/mongooseimctl_SUITE.erl b/big_tests/tests/mongooseimctl_SUITE.erl index c4c0f0722e5..7bbdbef2bfb 100644 --- a/big_tests/tests/mongooseimctl_SUITE.erl +++ b/big_tests/tests/mongooseimctl_SUITE.erl @@ -20,70 +20,9 @@ -include_lib("exml/include/exml.hrl"). -include_lib("eunit/include/eunit.hrl"). --import(mongooseimctl_helper, [mongooseimctl/3, rpc_call/3]). --import(mongoose_helper, [auth_modules/0]). --import(distributed_helper, [mim/0, - require_rpc_nodes/1, - rpc/4]). --import(domain_helper, [host_type/0, domain/0]). --import(config_parser_helper, [config/2, mod_config/2]). - --define(HTTP_UPLOAD_FILENAME, "tmp.txt"). --define(HTTP_UPLOAD_FILESIZE, "5"). --define(HTTP_UPLOAD_TIMEOUT, "666"). --define(HTTP_UPLOAD_PARAMS(ContentType), ?HTTP_UPLOAD_PARAMS(?HTTP_UPLOAD_FILENAME, - ?HTTP_UPLOAD_FILESIZE, - ContentType, - ?HTTP_UPLOAD_TIMEOUT)). --define(HTTP_UPLOAD_PARAMS_WITH_FILESIZE(X), ?HTTP_UPLOAD_PARAMS(?HTTP_UPLOAD_FILENAME, X, - "", ?HTTP_UPLOAD_TIMEOUT)). --define(HTTP_UPLOAD_PARAMS_WITH_TIMEOUT(X), ?HTTP_UPLOAD_PARAMS(?HTTP_UPLOAD_FILENAME, - ?HTTP_UPLOAD_FILESIZE, "", X)). --define(HTTP_UPLOAD_PARAMS(FileName, FileSize, ContentType, Timeout), - [domain(), FileName, FileSize, ContentType, Timeout]). - --define(CTL_ERROR(Messsage), "Error: \"" ++ Messsage ++ "\"\n"). --define(HTTP_UPLOAD_NOT_ENABLED_ERROR, ?CTL_ERROR("mod_http_upload is not loaded for this host")). --define(HTTP_UPLOAD_FILESIZE_ERROR, ?CTL_ERROR("size must be positive integer")). --define(HTTP_UPLOAD_TIMEOUT_ERROR, ?CTL_ERROR("timeout must be positive integer")). - --define(S3_BUCKET_URL, "http://localhost:9000/mybucket/"). --define(S3_REGION, "eu-east-25"). --define(S3_ACCESS_KEY_ID, "AKIAIAOAONIULXQGMOUA"). - -%%Prefix MUST be a constant string, otherwise it results in compilation error --define(GET_URL(Prefix, Sting), fun() -> Prefix ++ URL = Sting, URL end()). - -%% The following is an example presigned URL: -%% -%% https://s3.amazonaws.com/examplebucket/test.txt -%% ?X-Amz-Algorithm=AWS4-HMAC-SHA256 -%% &X-Amz-Credential=/20130721/us-east-1/s3/aws4_request -%% &X-Amz-Date=20130721T201207Z -%% &X-Amz-Expires=86400 -%% &X-Amz-SignedHeaders=host -%% &X-Amz-Signature= -%% -%% for more details see -%% https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html --define(S3_BASE_URL_REGEX, "^"?S3_BUCKET_URL".+/"?HTTP_UPLOAD_FILENAME). --define(S3_ALGORITHM_REGEX, "[?&]X-Amz-Algorithm=AWS4-HMAC-SHA256(&|$)"). --define(S3_CREDENTIAL_REGEX, - % X-Amz-Credential=////aws4_request - "[?&]X-Amz-Credential="?S3_ACCESS_KEY_ID"%2F[0-9]{8}%2F"?S3_REGION"%2Fs3%2Faws4_request(&|$)"). --define(S3_DATE_REGEX, "X-Amz-Date=[0-9]{8}T[0-9]{6}Z(&|$)"). --define(S3_EXPIRATION_REGEX, "[?&]X-Amz-Expires="?HTTP_UPLOAD_TIMEOUT"(&|$)"). --define(S3_SIGNED_HEADERS, "[?&]X-Amz-SignedHeaders=content-length%3Bhost(&|$)"). --define(S3_SIGNED_HEADERS_WITH_ACL, - "[?&]X-Amz-SignedHeaders=content-length%3Bhost%3Bx-amz-acl(&|$)"). --define(S3_SIGNED_HEADERS_WITH_CONTENT_TYPE, - "[?&]X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost(&|$)"). --define(S3_SIGNED_HEADERS_WITH_CONTENT_TYPE_AND_ACL, - "[?&]X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost%3Bx-amz-acl(&|$)"). --define(S3_SIGNATURE_REGEX, "[?&]X-Amz-Signature=[^&]+(&|$)"). - - --record(offline_msg, {us, timestamp, expire, from, to, packet, permanent_fields = []}). +-import(mongooseimctl_helper, [mongooseimctl/3]). +-import(distributed_helper, [mim/0, require_rpc_nodes/1]). +-import(domain_helper, [domain/0]). %%-------------------------------------------------------------------- %% Suite configuration @@ -91,99 +30,24 @@ all() -> [ - {group, accounts}, - {group, sessions}, - {group, vcard}, - {group, roster}, - {group, roster_advanced}, - {group, last}, - {group, private}, - {group, stanza}, - {group, stats}, - {group, basic}, - {group, upload}, {group, graphql}, {group, help}, {group, server} ]. groups() -> - [{accounts, [sequence], accounts()}, - {sessions, [sequence], sessions()}, - {vcard, [sequence], vcard()}, - {roster, [sequence], roster()}, - {last, [sequence], last()}, - {private, [sequence], private()}, - {stanza, [sequence], stanza()}, - {roster_advanced, [sequence], roster_advanced()}, - {basic, [sequence], basic()}, - {stats, [sequence], stats()}, - {upload, [], upload()}, - {upload_with_acl, [], upload_enabled()}, - {upload_without_acl, [], upload_enabled()}, - {graphql, [], graphql()}, + [{graphql, [], graphql()}, {help, [], help()}, {server, [], server()}]. -basic() -> - [simple_register, - simple_unregister, - register_twice, - backup_restore_mnesia, - restore_mnesia_wrong, - dump_and_load, - load_mnesia_wrong, - dump_table, - get_loglevel, - remove_old_messages_test, - remove_expired_messages_test]. - -accounts() -> [change_password, check_password_hash, check_password, - check_account, ban_account, num_active_users, delete_old_users, - delete_old_users_vhost]. - -sessions() -> [num_resources_num, kick_session, status, - sessions_info, set_presence]. - -vcard() -> [vcard_rw, vcard2_rw, vcard2_multi_rw]. - -roster() -> [rosteritem_rw, - presence_after_add_rosteritem, - push_roster, - push_roster_all, - push_roster_alltoall]. - -roster_advanced() -> [process_rosteritems_list_simple, - process_rosteritems_list_nomatch, - process_rosteritems_list_advanced1, - process_rosteritems_list_advanced2, - process_rosteritems_delete_advanced, - process_rosteritems_delete_advanced2]. - -last() -> [set_last]. - -private() -> [private_rw]. - -stanza() -> [send_message, send_message_wrong_jid, send_stanza, send_stanzac2s_wrong]. - -stats() -> [stats_global, stats_host]. - -upload() -> - [upload_not_enabled, upload_wrong_filesize, upload_wrong_timeout, - {group, upload_with_acl}, {group, upload_without_acl}]. - -upload_enabled() -> - [upload_returns_correct_urls_without_content_type, - upload_returns_correct_urls_with_content_type, - real_upload_without_content_type, - real_upload_with_content_type]. - graphql() -> [graphql_wrong_arguments_number, can_execute_admin_queries_with_permissions, can_handle_execution_error, graphql_error_unknown_command_with_args, graphql_error_unknown_command_without_args, + graphql_error_unknown_category_with_args, + graphql_error_unknown_category_without_args, graphql_no_command, graphql_error_invalid_args, graphql_error_invalid_arg_value, @@ -194,8 +58,7 @@ graphql() -> graphql_command]. help() -> - [default_help, - old_help]. + [default_help]. server() -> [server_status, @@ -205,955 +68,19 @@ suite() -> require_rpc_nodes([mim]) ++ escalus:suite(). init_per_suite(Config) -> - TemplatePath = filename:join(?config(mim_data_dir, Config), "roster.template"), - AuthMods = auth_modules(), Node = mim(), Config1 = ejabberd_node_utils:init(Node, Config), - Config2 = escalus:init_per_suite([{ctl_auth_mods, AuthMods}, - {roster_template, TemplatePath} | Config1]), - Backend = mongoose_helper:mnesia_or_rdbms_backend(), - LastOpts = mod_config(mod_last, #{backend => Backend}), - dynamic_modules:ensure_modules(domain_helper:host_type(), [{mod_last, LastOpts}]), - dynamic_modules:ensure_modules(domain_helper:secondary_host_type(), [{mod_last, LastOpts}]), - prepare_roster_template(TemplatePath, domain()), - %% dump_and_load requires at least one mnesia table - %% ensure, that passwd table is available - catch rpc_call(ejabberd_auth_internal, start, [host_type()]), - escalus:create_users(Config2, escalus:get_users([alice, mike, bob, kate])). + Config1. -prepare_roster_template(TemplatePath, Domain) -> - {ok, [RosterIn]} = file:consult(TemplatePath ++ ".in"), - DomainStr = binary_to_list(Domain), - Roster = [{User, DomainStr, Group, Name} || {User, Group, Name} <- RosterIn], - FormattedRoster = io_lib:format("~tp.~n", [Roster]), - file:write_file(TemplatePath, FormattedRoster). - -end_per_suite(Config) -> - Config1 = lists:keydelete(ctl_auth_mods, 1, Config), - delete_users(Config1), - dynamic_modules:stop(domain_helper:host_type(), mod_last), - dynamic_modules:stop(domain_helper:secondary_host_type(), mod_last), - escalus:end_per_suite(Config1). - -init_per_group(basic, Config) -> - Backend = mongoose_helper:mnesia_or_rdbms_backend(), - OfflineOpts = mod_config(mod_offline, #{backend => Backend}), - dynamic_modules:ensure_modules(domain_helper:host_type(), - [{mod_offline, OfflineOpts}]), - Config; -init_per_group(private, Config) -> - Backend = mongoose_helper:mnesia_or_rdbms_backend(), - PrivateOpts = mod_config(mod_private, #{backend => Backend, iqdisc => one_queue}), - dynamic_modules:ensure_modules(domain_helper:host_type(), [{mod_private, PrivateOpts}]), - Config; -init_per_group(vcard, Config) -> - case rpc(mim(), gen_mod, get_module_opt, [host_type(), mod_vcard, backend, mnesia]) of - ldap -> - {skip, vcard_set_not_supported_with_ldap}; - _ -> - Config - end; -init_per_group(roster_advanced, Config) -> - case rpc(mim(), gen_mod, get_module_opt, [host_type(), mod_roster, backend, mnesia]) of - mnesia -> - Config; - _ -> - {skip, command_process_rosteritems_supports_only_mnesia} - end; -init_per_group(upload_without_acl, Config) -> - dynamic_modules:start(host_type(), mod_http_upload, mod_http_upload_config(false)), - [{with_acl, false} | Config]; -init_per_group(upload_with_acl, Config) -> - dynamic_modules:start(host_type(), mod_http_upload, mod_http_upload_config(true)), - [{with_acl, true} | Config]; -init_per_group(_GroupName, Config) -> - Config. - -mod_http_upload_config(AddAcl) -> - config([modules, mod_http_upload], - #{max_file_size => 1234, - s3 => #{bucket_url => <>, - add_acl => AddAcl, - region => <>, - access_key_id => <>, - secret_access_key => <<"CG5fGqG0/n6NCPJ10FylpdgRnuV52j8IZvU7BSj8">> - } - }). - -end_per_group(basic, Config) -> - dynamic_modules:stop(domain_helper:host_type(), mod_offline), - Config; -end_per_group(private, Config) -> - dynamic_modules:stop(domain_helper:host_type(), mod_private), - Config; -end_per_group(Rosters, Config) when (Rosters == roster) or (Rosters == roster_advanced) -> - TemplatePath = escalus_config:get_config(roster_template, Config), - RegUsers = [atom_to_list(U) || {U, _} <- escalus_config:get_config(escalus_users, Config)], - {ok, [Roster]} = file:consult(TemplatePath), - C = fun({U, S, _, _}) -> - case lists:member(U, RegUsers) of - true -> - SB = string_to_binary(S), - UB = string_to_binary(U), - Acc = mongoose_helper:new_mongoose_acc(SB), - rpc(mim(), mongoose_hooks, remove_user, [Acc, SB, UB]); - _ -> - ok - end - end, - lists:foreach(C, Roster), - Config; -end_per_group(UploadGroup, Config) when UploadGroup =:= upload_without_acl; - UploadGroup =:= upload_with_acl -> - dynamic_modules:stop(host_type(), mod_http_upload), - Config; -end_per_group(_GroupName, Config) -> - Config. - -get_registered_users() -> - rpc(mim(), ejabberd_auth, get_vh_registered_users, [domain()]). +end_per_suite(_Config) -> + ok. -init_per_testcase(CaseName, Config) - when CaseName == delete_old_users_vhost - orelse CaseName == stats_global - orelse CaseName == stats_host -> - {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), - case lists:member(ejabberd_auth_ldap, AuthMods) of - true -> {skip, "not supported for LDAP"}; - false -> escalus:init_per_testcase(CaseName, Config) - end; -init_per_testcase(check_password_hash, Config) -> - {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), - case lists:member(ejabberd_auth_ldap, AuthMods) of - true -> - {skip, not_fully_supported_with_ldap}; - false -> - AuthOpts = mongoose_helper:auth_opts_with_password_format(plain), - Config1 = mongoose_helper:backup_and_set_config_option(Config, {auth, host_type()}, - AuthOpts), - Config2 = escalus:create_users(Config1, escalus:get_users([carol])), - escalus:init_per_testcase(check_password_hash, Config2) - end; -init_per_testcase(delete_old_users, Config) -> - {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), - case lists:member(ejabberd_auth_ldap, AuthMods) of - true -> {skip, not_fully_supported_with_ldap}; - false -> escalus:init_per_testcase(delete_old_users, Config) - end; -init_per_testcase(CaseName, Config) when CaseName == real_upload_without_content_type; - CaseName == real_upload_with_content_type -> - case mongoose_helper:should_minio_be_running(Config) of - true -> escalus:init_per_testcase(CaseName, Config); - false -> {skip, "minio is not running"} - end; init_per_testcase(CaseName, Config) -> escalus:init_per_testcase(CaseName, Config). -end_per_testcase(delete_old_users, Config) -> - Users = escalus_users:get_users([alice, bob, kate, mike]), - lists:foreach(fun({_User, UserSpec}) -> - {Username, Domain, Pass} = get_user_data(UserSpec, Config), - JID = mongoose_helper:make_jid(Username, Domain), - rpc(mim(), ejabberd_auth, try_register, [JID, Pass]) - end, Users), - escalus:end_per_testcase(delete_old_users, Config); -end_per_testcase(check_password_hash, Config) -> - mongoose_helper:restore_config(Config), - escalus:delete_users(Config, escalus:get_users([carol])); end_per_testcase(CaseName, Config) -> - %% Because kick_session fails with unexpected stanza received: - %% - %% TODO: Remove when escalus learns how to automatically deal - %% with 'unavailable' stanzas on client stop. - mongoose_helper:kick_everyone(), escalus:end_per_testcase(CaseName, Config). -%%-------------------------------------------------------------------- -%% http upload tests -%%-------------------------------------------------------------------- -upload_not_enabled(Config) -> - Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS("text/plain"), Config), - ?assertEqual({?HTTP_UPLOAD_NOT_ENABLED_ERROR, 1}, Ret). - -upload_wrong_filesize(Config) -> - Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_FILESIZE("0"), Config), - ?assertEqual({?HTTP_UPLOAD_FILESIZE_ERROR, 1}, Ret), - Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_FILESIZE("-1"), Config), - ?assertEqual({?HTTP_UPLOAD_FILESIZE_ERROR, 1}, Ret). - -upload_wrong_timeout(Config) -> - Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_TIMEOUT("0"), Config), - ?assertEqual({?HTTP_UPLOAD_TIMEOUT_ERROR, 1}, Ret), - Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_TIMEOUT("-1"), Config), - ?assertEqual({?HTTP_UPLOAD_TIMEOUT_ERROR, 1}, Ret). - -upload_returns_correct_urls_with_content_type(Config) -> - upload_returns_correct_urls(Config, "text/plain"). - -upload_returns_correct_urls_without_content_type(Config) -> - upload_returns_correct_urls(Config, ""). - -real_upload_with_content_type(Config) -> - real_upload(Config, "text/plain"). - -real_upload_without_content_type(Config) -> - real_upload(Config, ""). - -upload_returns_correct_urls(Config, ContentType) -> - HttpUploadParams = ?HTTP_UPLOAD_PARAMS(ContentType), - {Output, 0} = mongooseimctl("http_upload", HttpUploadParams, Config), - {PutURL, GetURL} = get_urls(Output), - WithACL = proplists:get_value(with_acl, Config), - check_urls(PutURL, GetURL, WithACL, ContentType). - -get_urls(Output) -> - [PutStr, GetStr | _] = string:split(Output, "\n", all), - PutURL = ?GET_URL("PutURL: ", PutStr), - GetURL = ?GET_URL("GetURL: ", GetStr), - {PutURL, GetURL}. - -check_urls(PutURL, GetURL, WithACL, ContentType) -> - check_bucket_url_and_filename(put, PutURL), - check_bucket_url_and_filename(get, GetURL), - check_substring(?S3_ALGORITHM_REGEX, PutURL), - check_substring(?S3_CREDENTIAL_REGEX, PutURL), - check_substring(?S3_DATE_REGEX, PutURL), - check_substring(?S3_EXPIRATION_REGEX, PutURL), - SignedHeadersRegex = signed_headers_regex(WithACL, ContentType), - check_substring(SignedHeadersRegex, PutURL), - check_substring(?S3_SIGNATURE_REGEX, PutURL). - -check_bucket_url_and_filename(Type, Url) -> - UrlRegex = case Type of - get -> ?S3_BASE_URL_REGEX"$"; - put -> ?S3_BASE_URL_REGEX"\?.*" - end, - ct:log("check_bucket_url_and_filename type=~p url=~p regex=~p", - [Type, Url, UrlRegex]), - ?assertMatch({match, [{0, _}]}, re:run(Url, UrlRegex)). - -check_substring(SubString, String) -> - ?assertMatch({match, [_]}, re:run(String, SubString, [global])). - -signed_headers_regex(false, "") -> ?S3_SIGNED_HEADERS; -signed_headers_regex(false, _) -> ?S3_SIGNED_HEADERS_WITH_CONTENT_TYPE; -signed_headers_regex(true, "") -> ?S3_SIGNED_HEADERS_WITH_ACL; -signed_headers_regex(true, _) -> ?S3_SIGNED_HEADERS_WITH_CONTENT_TYPE_AND_ACL. - -real_upload(Config, ContentType) -> - #{node := Node} = mim(), - BinPath = distributed_helper:bin_path(Node, Config), - UploadScript = filename:join(?config(mim_data_dir, Config), "test_file_upload.sh"), - Ret = mongooseimctl_helper:run(UploadScript, [ContentType], [{cd, BinPath}]), - ?assertMatch({_, 0}, Ret), - ok. -%%-------------------------------------------------------------------- -%% service_admin_extra_accounts tests -%%-------------------------------------------------------------------- - -change_password(Config) -> - {User, Domain, OldPassword} = get_user_data(alice, Config), - mongooseimctl("change_password", [User, Domain, <>], Config), - {error, {connection_step_failed, _, _}} = escalus_client:start_for(Config, alice, <<"newres">>), - mongooseimctl("change_password", [User, Domain, OldPassword], Config), - {ok, Alice2} = escalus_client:start_for(Config, alice, <<"newres2">>), - escalus_client:stop(Config, Alice2). - -check_password_hash(Config) -> - {User, Domain, Pass} = get_user_data(carol, Config), - MD5Hash = get_md5(Pass), - MD5HashBad = get_md5(<>), - SHAHash = get_sha(Pass), - - {_, 0} = mongooseimctl("check_password_hash", [User, Domain, MD5Hash, "md5"], Config), - {_, ErrCode} = mongooseimctl("check_password_hash", [User, Domain, MD5HashBad, "md5"], Config), - true = (ErrCode =/= 0), %% Must return code other than 0 - {_, 0} = mongooseimctl("check_password_hash", [User, Domain, SHAHash, "sha"], Config). - -check_password(Config) -> - {User, Domain, Pass} = get_user_data(alice, Config), - MetricName = [backends, auth, check_password], - OldValue = get_metric(MetricName), - {_, 0} = mongooseimctl("check_password", [User, Domain, Pass], Config), - {_, ErrCode} = mongooseimctl("check_password", [User, Domain, <>], Config), - ValidatorFn = fun(NewValue) -> OldValue =/= NewValue end, - mongoose_helper:wait_until( - fun() -> get_metric(MetricName) end, - ValidatorFn, #{name => ?FUNCTION_NAME}), - true = (ErrCode =/= 0). %% Must return code other than 0 - -get_metric(MetricName) -> - HostType = domain_helper:host_type(mim), - HostTypePrefix = domain_helper:make_metrics_prefix(HostType), - {ok, Value} = rpc(mim(), mongoose_metrics, get_metric_value, [[HostTypePrefix | MetricName]]), - Value. - -check_account(Config) -> - {User, Domain, _Pass} = get_user_data(alice, Config), - - {_, 0} = mongooseimctl("check_account", [User, Domain], Config), - {_, ErrCode} = mongooseimctl("check_account", [<>, Domain], Config), - true = (ErrCode =/= 0). %% Must return code other than 0 - -ban_account(Config) -> - {User, Domain, Pass} = get_user_data(mike, Config), - - {ok, Mike} = escalus_client:start_for(Config, mike, <<"newres">>), - {_, 0} = mongooseimctl("ban_account", [User, Domain, "SomeReason"], Config), - escalus:assert(is_stream_error, [<<"conflict">>, <<"SomeReason">>], - escalus:wait_for_stanza(Mike)), - {error, {connection_step_failed, _, _}} = escalus_client:start_for(Config, mike, <<"newres2">>), - mongooseimctl("change_password", [User, Domain, Pass], Config), - escalus_connection:wait_for_close(Mike, 1000), - escalus_cleaner:remove_client(Config, Mike). - -num_active_users(Config) -> - %% Given some users with recorded last activity timestamps - {AliceName, Domain, _} = get_user_data(alice, Config), - {MikeName, Domain, _} = get_user_data(mike, Config), - {Mega, Secs, _} = os:timestamp(), - Now = Mega * 1000000 + Secs, - set_last(AliceName, Domain, Now), - set_last(MikeName, Domain, Now), - {SLastActiveBefore, _} = mongooseimctl("num_active_users", [Domain, "5"], Config), - %% When we artificially remove a user's last activity timestamp in the given period - TenDaysAgo = Now - 864000, - set_last(MikeName, Domain, TenDaysAgo), - %% Then we expect that the number of active users in the last 5 days is one less - %% than before the change above - {SLastActiveAfter, _} = mongooseimctl("num_active_users", [Domain, "5"], Config), - NLastActiveBefore = list_to_integer(string:strip(SLastActiveBefore, both, $\n)), - NLastActiveAfter = list_to_integer(string:strip(SLastActiveAfter, both, $\n)), - NLastActiveAfter = NLastActiveBefore - 1. - -delete_old_users(Config) -> - {AliceName, Domain, _} = get_user_data(alice, Config), - {BobName, Domain, _} = get_user_data(bob, Config), - {KateName, Domain, _} = get_user_data(kate, Config), - {MikeName, Domain, _} = get_user_data(mike, Config), - - {Mega, Secs, _} = os:timestamp(), - Now = Mega*1000000+Secs, - set_last(AliceName, Domain, Now), - set_last(BobName, Domain, Now), - set_last(MikeName, Domain, Now), - set_last(KateName, Domain, 0), - - {_, 0} = mongooseimctl("delete_old_users", ["10"], Config), - {_, 0} = mongooseimctl("check_account", [AliceName, Domain], Config), - {_, ErrCode} = mongooseimctl("check_account", [KateName, Domain], Config), - true = (ErrCode =/= 0). %% Must return code other than 0 - -delete_old_users_vhost(Config) -> - {AliceName, Domain, _} = get_user_data(alice, Config), - {KateName, Domain, KatePass} = get_user_data(kate, Config), - SecDomain = ct:get_config({hosts, mim, secondary_domain}), - - {Mega, Secs, _} = os:timestamp(), - Now = Mega*1000000+Secs, - set_last(AliceName, Domain, Now-86400*30), - - {_, 0} = mongooseimctl("register_identified", [KateName, SecDomain, KatePass], Config), - {_, 0} = mongooseimctl("check_account", [KateName, SecDomain], Config), - {_, 0} = mongooseimctl("delete_old_users_vhost", [SecDomain, "10"], Config), - {_, 0} = mongooseimctl("check_account", [AliceName, Domain], Config), - {_, ErrCode} = mongooseimctl("check_account", [KateName, SecDomain], Config), - true = (ErrCode =/= 0). %% Must return code other than 0 - -%%-------------------------------------------------------------------- -%% service_admin_extra_accounts tests -%%-------------------------------------------------------------------- - -%% Checks both num_resources and resource_num -num_resources_num(Config) -> - escalus:story(Config, [{alice, 3}, {bob, 1}], fun(_, Alice2, _, _) -> - {Username, Domain, _} = get_user_data(alice, Config), - ResName = binary_to_list(escalus_client:resource(Alice2)) ++ "\n", - - {"3\n", _} = mongooseimctl("num_resources", [Username, Domain], Config), - {ResName, _} = mongooseimctl("resource_num", [Username, Domain, "2"], Config) - end). - -kick_session(Config) -> - escalus:story(Config, [{alice, 1}], fun(Alice) -> - Username = escalus_client:username(Alice), - Domain = escalus_client:server(Alice), - Resource = escalus_client:resource(Alice), - Args = [Username, Domain, Resource, "Because I can!"], - - {_, 0} = mongooseimctl("kick_session", Args, Config), - Stanza = escalus:wait_for_stanza(Alice), - escalus:assert(is_stream_error, [<<"conflict">>, <<"Because I can!">>], Stanza) - end). - -status(Config) -> - escalus:story(Config, [{alice, 1}, {mike, 1}, {bob, 1}], fun(User1, User2, User3) -> - PriDomain = escalus_client:server(User1), - SecDomain = ct:get_config({hosts, mim, secondary_domain}), - AwayPresence = escalus_stanza:presence_show(<<"away">>), - escalus_client:send(User2, AwayPresence), - - {"2\n", _} = mongooseimctl("status_num", ["available"], Config), - - {"2\n", _} = mongooseimctl("status_num_host", [PriDomain, "available"], Config), - {"0\n", _} = mongooseimctl("status_num_host", [SecDomain, "available"], Config), - - {StatusList, _} = mongooseimctl("status_list", ["available"], Config), - match_user_status([User1, User3], StatusList), - - {StatusList2, _} = mongooseimctl("status_list_host", - [PriDomain, "available"], Config), - match_user_status([User1, User3], StatusList2), - {[], _} = mongooseimctl("status_list_host", [SecDomain, "available"], Config) - end). - -sessions_info(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(User1, User2, User3) -> - Username1 = escalus_client:username(User1), - PriDomain = escalus_client:server(User1), - SecDomain = ct:get_config({hosts, mim, secondary_domain}), - AwayPresence = escalus_stanza:presence_show(<<"away">>), - escalus_client:send(User2, AwayPresence), - - {UserList, _} = mongooseimctl("connected_users_info", [], Config), - match_user_info([User1, User2, User3], UserList), - - {UserList2, _} = mongooseimctl("connected_users_vhost", [PriDomain], Config), - match_user_info([User1, User2, User3], UserList2), - {[], _} = mongooseimctl("connected_users_vhost", [SecDomain], Config), - - {UserList3, _} = mongooseimctl("user_sessions_info", - [Username1, PriDomain], Config), - match_user_info([User1], UserList3) - end). - -set_presence(Config) -> - escalus:story(Config, [{alice, 1}], fun(Alice) -> - Username = escalus_client:username(Alice), - Domain = escalus_client:server(Alice), - Resource = escalus_client:resource(Alice), - - {_, 0} = mongooseimctl("set_presence", - [Username, Domain, Resource, - "available", "away", "mystatus", "10"], - Config), - Presence = escalus:wait_for_stanza(Alice), - escalus:assert(is_presence_with_show, [<<"away">>], Presence), - escalus:assert(is_presence_with_status, [<<"mystatus">>], Presence), - escalus:assert(is_presence_with_priority, [<<"10">>], Presence) - end). - -%%-------------------------------------------------------------------- -%% service_admin_extra_vcard tests -%%-------------------------------------------------------------------- - -vcard_rw(Config) -> - {Username, Domain, _} = get_user_data(alice, Config), - - {_, ExitCode} = mongooseimctl("get_vcard", [Username, Domain, "NICKNAME"], Config), - true = (ExitCode /= 0), - - {_, 0} = mongooseimctl("set_vcard", [Username, Domain, "NICKNAME", "SomeNickname"], Config), - {"SomeNickname\n", 0} = mongooseimctl("get_vcard", [Username, Domain, "NICKNAME"], Config). - -vcard2_rw(Config) -> - {Username, Domain, _} = get_user_data(alice, Config), - - {_, ExitCode} = mongooseimctl("get_vcard2", [Username, Domain, "ORG", "ORGNAME"], Config), - true = (ExitCode /= 0), - - {_, 0} = mongooseimctl("set_vcard2", [Username, Domain, "ORG", "ORGNAME", "ESL"], Config), - {"ESL\n", 0} = mongooseimctl("get_vcard2", [Username, Domain, "ORG", "ORGNAME"], Config). - -vcard2_multi_rw(Config) -> - {Username, Domain, _} = get_user_data(alice, Config), - - {_, ExitCode} = mongooseimctl("get_vcard2_multi", [Username, Domain, "ORG", "ORGUNIT"], Config), - true = (ExitCode /= 0), - - Args = [Username, Domain, "ORG", "ORGUNIT", "sales;marketing"], - {_, 0} = mongooseimctl("set_vcard2_multi", Args, Config), - {OrgUnits0, 0} = mongooseimctl("get_vcard2_multi", - [Username, Domain, "ORG", "ORGUNIT"], Config), - OrgUnits = string:tokens(OrgUnits0, "\n"), - 2 = length(OrgUnits), - true = (lists:member("sales", OrgUnits) andalso lists:member("marketing", OrgUnits)). - -%%-------------------------------------------------------------------- -%% service_admin_extra_vcard tests -%%-------------------------------------------------------------------- - -rosteritem_rw(Config) -> - escalus:story(Config, [{alice, 1}], fun(Alice) -> - BobJid = escalus_users:get_jid(Config, bob), - MikeJid = escalus_users:get_jid(Config, mike), - - {AliceName, Domain, _} = get_user_data(alice, Config), - {BobName, Domain, _} = get_user_data(bob, Config), - {MikeName, Domain, _} = get_user_data(mike, Config), - - {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), - {_, 0} = mongooseimctl("add_rosteritem", - [AliceName, Domain, MikeName, - Domain, "My Mike", - "My Group", "both"], Config), - - [Push1, Push2] = escalus:wait_for_stanzas(Alice, 2), % Check roster broadcasts - escalus:assert(is_roster_set, Push1), - escalus:assert(roster_contains, [BobJid], Push1), - escalus:assert(is_roster_set, Push2), - escalus:assert(roster_contains, [MikeJid], Push2), - - {Items1, 0} = mongooseimctl("get_roster", [AliceName, Domain], Config), - match_roster([{BobName, Domain, "MyBob", "MyGroup", "both"}, - {MikeName, Domain, "MyMike", "MyGroup", "both"}], Items1), - - escalus:send(Alice, escalus_stanza:roster_get()), - Roster1 = escalus:wait_for_stanza(Alice), - escalus:assert(is_roster_result, Roster1), - escalus:assert(roster_contains, [BobJid], Roster1), - escalus:assert(roster_contains, [MikeJid], Roster1), - - {_, 0} = mongooseimctl("delete_rosteritem", - [AliceName, Domain, BobName, Domain], - Config), - - Push3 = escalus:wait_for_stanza(Alice), - escalus:assert(is_roster_set, Push3), - escalus:assert(roster_contains, [BobJid], Push3), - - {Items2, 0} = mongooseimctl("get_roster", [AliceName, Domain], Config), - match_roster([{MikeName, Domain, "MyMike", "MyGroup", "both"}], Items2), - - escalus:send(Alice, escalus_stanza:roster_remove_contact(MikeJid)), % cleanup - escalus:wait_for_stanzas(Alice, 2, 5000) - end). - -presence_after_add_rosteritem(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> - BobJid = escalus_users:get_jid(Config, bob), - {AliceName, Domain, _} = get_user_data(alice, Config), - {BobName, Domain, _} = get_user_data(bob, Config), - - {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), - - escalus:send(Alice, escalus_stanza:presence(<<"available">>)), - escalus:assert(is_presence, escalus:wait_for_stanza(Bob)), - - escalus:send(Alice, escalus_stanza:roster_remove_contact(BobJid)), % cleanup - %% Wait for stanzas, so they would not end up in the next story - escalus:wait_for_stanzas(Alice, 3, 5000) - end). - -push_roster(Config) -> - escalus:story(Config, [{alice, 1}], fun(Alice) -> - BobJid = escalus_users:get_jid(Config, bob), - {AliceName, Domain, _} = get_user_data(alice, Config), - TemplatePath = escalus_config:get_config(roster_template, Config), - - {_, 0} = mongooseimctl("push_roster", [TemplatePath, AliceName, Domain], Config), - escalus:send(Alice, escalus_stanza:roster_get()), - Roster1 = escalus:wait_for_stanza(Alice), - escalus:assert(is_roster_result, Roster1), - escalus:assert(roster_contains, [BobJid], Roster1), - - escalus:send(Alice, escalus_stanza:roster_remove_contact(BobJid)), % cleanup - escalus:wait_for_stanzas(Alice, 2, 5000) - end). - -process_rosteritems_list_simple(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> - %% given - Action = "list", - Subs = "any", - Asks = "any", - User = escalus_client:short_jid(Alice), - Contact = string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), - {AliceName, Domain, _} = get_user_data(alice, Config), - {BobName, Domain, _} = get_user_data(bob, Config), - %% when - {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), - escalus:assert(is_roster_set, [], escalus:wait_for_stanza(Alice)), - escalus_assert:has_no_stanzas(Alice), - {R, 0} = mongooseimctl("process_rosteritems", [Action, Subs, Asks, User, Contact], Config), - %% then - {match, _} = re:run(R, ".*Matches:.*" ++ Contact ++ ".*"), - {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, BobName, Domain], Config) - end). - -process_rosteritems_list_nomatch(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> - %% given - Action = "list", - Subs = "from:both", - Asks = "any", - User = escalus_client:short_jid(Alice), - Contact =string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), - {AliceName, Domain, _} = get_user_data(alice, Config), - {BobName, Domain, _} = get_user_data(bob, Config), - {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, BobName, - Domain, "MyBob", "MyGroup", "to"], Config), - escalus:assert(is_roster_set, [], escalus:wait_for_stanza(Alice)), - escalus_assert:has_no_stanzas(Alice), - %% when - {R, 0} = mongooseimctl("process_rosteritems", [Action, Subs, Asks, User, Contact], Config), - %% then - nomatch = re:run(R, ".*Matches:.*" ++ Contact ++ ".*"), - {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, BobName, Domain], Config) - end). - -process_rosteritems_list_advanced1(Config) -> - escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> - %% given - Action = "list", - Subs = "from:both", - Asks = "any", - User = escalus_client:short_jid(Alice), - {AliceName, Domain, _} = get_user_data(alice, Config), - {MikeName, Domain, _} = get_user_data(mike, Config), - {KateName, Domain, _} = get_user_data(kate, Config), - ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), - ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), - ContactsRegexp = ContactMike ++ ":" ++ - string:substr(binary_to_list(KateName), 1, 2) ++ - ".*@.*", - - {_, 0} = add_rosteritem2(AliceName, Domain, MikeName, Domain, Config), - {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, - Domain, "BestFriend", "MyGroup", "both"], Config), - escalus:assert_many([is_roster_set, is_roster_set], - escalus:wait_for_stanzas(Alice, 2)), - escalus_assert:has_no_stanzas(Alice), - %% when - {R, 0} = mongooseimctl("process_rosteritems", - [Action, Subs, Asks, User, ContactsRegexp], - Config), - %% then - {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), - {match, _} = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), - {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), - {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) - end). - -process_rosteritems_delete_advanced(Config) -> - escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> - %% given - Action = "delete", - Subs = "from", - Asks = "any", - User = escalus_client:short_jid(Alice), - {AliceName, Domain, _} = get_user_data(alice, Config), - {MikeName, Domain, _} = get_user_data(mike, Config), - {KateName, Domain, _} = get_user_data(kate, Config), - ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), - ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), - ContactsRegexp = ".*" ++ string:substr(ContactMike, 3) ++ - ":" ++ string:substr(ContactKate, 1, 2) ++ - "@" ++ binary_to_list(Domain), - {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, MikeName, - Domain, "DearMike", "MyGroup", "from"], Config), - {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, - Domain, "Friend", "MyGroup", "from"], Config), - escalus:assert_many([is_roster_set, is_roster_set], - escalus:wait_for_stanzas(Alice, 2)), - escalus_assert:has_no_stanzas(Alice), - %% when - {R, 0} = mongooseimctl("process_rosteritems", - [Action, Subs, Asks, User, ContactsRegexp], - Config), - %% then - {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), - nomatch = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), - {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), - {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) - end). - -process_rosteritems_list_advanced2(Config) -> - escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> - %% given - Action = "list", - Subs = "any", - Asks = "any", - User = escalus_client:short_jid(Alice), - {AliceName, Domain, _} = get_user_data(alice, Config), - {MikeName, Domain, _} = get_user_data(mike, Config), - {KateName, Domain, _} = get_user_data(kate, Config), - ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), - ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), - ContactsRegexp = ".*e@lo.*", - {_, 0} = add_rosteritem2(AliceName, Domain, MikeName, Domain, Config), - {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, - Domain, "KateFromSchool", - "MyGroup", "from"], Config), - escalus:assert_many([is_roster_set, is_roster_set], - escalus:wait_for_stanzas(Alice, 2)), - escalus_assert:has_no_stanzas(Alice), - %% when - {R, 0} = mongooseimctl("process_rosteritems", - [Action, Subs, Asks, User, ContactsRegexp], - Config), - %% then - {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), - {match, _} = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), - {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), - {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) - end). - -process_rosteritems_delete_advanced2(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}, {mike, 1}, {kate, 1}], - fun(Alice, Bob, Mike, Kate) -> - %% given - Action = "delete", - Subs = "to:from", - Asks = "any", - User = "al.c[e]@.*host:((b[o]b)|(mike))@loc.*t2", - {AliceName, Domain, _} = get_user_data(alice, Config), - {BobName, Domain, _} = get_user_data(bob, Config), - {MikeName, Domain, _} = get_user_data(mike, Config), - {KateName, Domain, _} = get_user_data(kate, Config), - ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), - ContactKate = string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), - ContactBob = string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), - ContactsReg = ".ik[ea]@localho+.*:k@loc.*st:(alice)+@.*:no", - {_, 0} = mongooseimctl("add_rosteritem", - [AliceName, Domain, MikeName, - Domain, "DearMike", "MyGroup", "to"], - Config), - {_, 0} = mongooseimctl("add_rosteritem", - [AliceName, Domain, KateName, - Domain, "HateHerSheHasSoNiceLegs", - "MyGroup", "to"], Config), - {_, 0} = mongooseimctl("add_rosteritem", [BobName, Domain, AliceName, - Domain, "Girlfriend", "MyGroup", "from"], Config), - escalus:assert_many([is_roster_set, is_roster_set, is_presence], - escalus:wait_for_stanzas(Alice, 3)), - escalus:assert(is_roster_set, [], escalus:wait_for_stanza(Bob)), - escalus_assert:has_no_stanzas(Alice), - escalus_assert:has_no_stanzas(Bob), - - %% when - {R, 0} = mongooseimctl("process_rosteritems", - [Action, Subs, Asks, User, ContactsReg], - Config), - %% then - {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), - nomatch = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), - nomatch = re:run(R, ".*Matches:.*" ++ ContactBob ++ ".*"), - {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), - {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config), - {_, 0} = mongooseimctl("delete_rosteritem", [BobName, Domain, AliceName, Domain], Config) - end). - -push_roster_all(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> - TemplatePath = escalus_config:get_config(roster_template, Config), - - {_, 0} = mongooseimctl("push_roster_all", [TemplatePath], Config), - - escalus:send(Alice, escalus_stanza:roster_get()), - Roster1 = escalus:wait_for_stanza(Alice), - escalus:assert(is_roster_result, Roster1), - BobJid = escalus_client:short_jid(Bob), - escalus:assert(roster_contains, [BobJid], Roster1), - - escalus:send(Bob, escalus_stanza:roster_get()), - Roster2 = escalus:wait_for_stanza(Bob), - escalus:assert(is_roster_result, Roster2), - AliceJid = escalus_client:short_jid(Alice), - escalus:assert(roster_contains, [AliceJid], Roster2), - - escalus:send_and_wait(Alice, escalus_stanza:roster_remove_contact(bob)), % cleanup - escalus:send_and_wait(Bob, escalus_stanza:roster_remove_contact(alice)) % cleanup - end). - -push_roster_alltoall(Config) -> - escalus:story(Config, [{alice, 1}], fun(Alice) -> - BobJid = escalus_users:get_jid(Config, bob), - MikeJid = escalus_users:get_jid(Config, mike), - KateJid = escalus_users:get_jid(Config, kate), - {_, Domain, _} = get_user_data(alice, Config), - - {_, 0} = mongooseimctl("push_roster_alltoall", [Domain, "MyGroup"], Config), - - escalus:send(Alice, escalus_stanza:roster_get()), - Roster = escalus:wait_for_stanza(Alice), - - escalus:assert(is_roster_result, Roster), - escalus:assert(roster_contains, [BobJid], Roster), - escalus:assert(roster_contains, [MikeJid], Roster), - escalus:assert(roster_contains, [KateJid], Roster) - end). - -%%-------------------------------------------------------------------- -%% service_admin_extra_last tests -%%-------------------------------------------------------------------- - -set_last(Config) -> - escalus:story(Config, [{alice, 1}], fun(Alice) -> - BobJid = escalus_users:get_jid(Config, bob), - {AliceName, Domain, _} = get_user_data(alice, Config), - {BobName, Domain, _} = get_user_data(bob, Config), - - {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), - {_, 0} = mongooseimctl("add_rosteritem", - [BobName, Domain, AliceName, - Domain, "MyAlice", "MyGroup", "both"], - Config), - - escalus:wait_for_stanza(Alice), % ignore push - - Now = os:system_time(second), - TS = integer_to_list(Now - 7200), - {_, 0} = mongooseimctl("set_last", [BobName, Domain, TS, "Status"], Config), - escalus:send(Alice, escalus_stanza:last_activity(BobJid)), - LastAct = escalus:wait_for_stanza(Alice), - escalus:assert(is_last_result, LastAct), - Seconds = list_to_integer(binary_to_list( - exml_query:path(LastAct, [{element, <<"query">>}, - {attr, <<"seconds">>}]))), - true = (( (Seconds > 7100) andalso (Seconds < 7300) ) orelse Seconds), - - {_, 0} = mongooseimctl("delete_rosteritem", - [AliceName, Domain, BobName, Domain], - Config), % cleanup - {_, 0} = mongooseimctl("delete_rosteritem", - [BobName, Domain, AliceName, Domain], - Config) - end). - -%%-------------------------------------------------------------------- -%% service_admin_extra_private tests -%%-------------------------------------------------------------------- - -private_rw(Config) -> - {AliceName, Domain, _} = get_user_data(alice, Config), - XmlEl1 = "1", - XmlEl2 = "2", - - {_, 0} = mongooseimctl("private_set", [AliceName, Domain, XmlEl1], Config), - {_, 0} = mongooseimctl("private_set", [AliceName, Domain, XmlEl2], Config), - - {Result, 0} = mongooseimctl("private_get", - [AliceName, Domain, "secretinfo", "nejmspejs"], - Config), - {ok, #xmlel{ name = <<"secretinfo">>, attrs = [{<<"xmlns">>, <<"nejmspejs">>}], - children = [#xmlcdata{ content = <<"1">> }]}} = exml:parse(list_to_binary(Result)). - -%%-------------------------------------------------------------------- -%% service_admin_extra_stanza tests -%%-------------------------------------------------------------------- - -send_message(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 2}], fun(Alice, Bob1, Bob2) -> - {_, 0} = mongooseimctl("send_message_chat", [escalus_client:full_jid(Alice), - escalus_client:full_jid(Bob1), - "Hi Bob!"], Config), - Stanza1 = escalus:wait_for_stanza(Bob1), - escalus:assert(is_chat_message, [<<"Hi Bob!">>], Stanza1), - - {_, 0} = mongooseimctl("send_message_headline", - [escalus_client:full_jid(Alice), - escalus_client:short_jid(Bob1), - "Subj", "Hi Bob!!"], Config), - Stanza2 = escalus:wait_for_stanza(Bob1), - Stanza3 = escalus:wait_for_stanza(Bob2), - escalus:assert(is_headline_message, [<<"Subj">>, <<"Hi Bob!!">>], Stanza2), - escalus:assert(is_headline_message, [<<"Subj">>, <<"Hi Bob!!">>], Stanza3) - end). - -send_message_wrong_jid(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> - {_, Err1} = mongooseimctl("send_message_chat", ["@@#$%!!.§§£", - escalus_client:full_jid(Bob), - "Hello bobby!"], Config), - {_, Err2} = mongooseimctl("send_message_headline", ["%%@&@&@==//\///", - escalus_client:short_jid(Bob), - "Subj", "Are - you there?"], - Config), - true = Err1 =/= 0, - true = Err2 =/= 0, - escalus_assert:has_no_stanzas(Alice), - escalus_assert:has_no_stanzas(Bob) - end). - -send_stanza(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> - Domain = escalus_client:server(Alice), - Resource = escalus_client:resource(Alice), - {BobName, _, _} = get_user_data(bob, Config), - BobJID = <>, - - Stanza = create_stanza(Alice, BobJID), - {_, 0} = mongooseimctl("send_stanza_c2s", - [BobName, Domain, Resource, Stanza], - Config), - - Message = escalus:wait_for_stanza(Alice), - escalus:assert(is_chat_message, [<<"Hi">>], Message), - escalus:assert(is_stanza_from, [Bob], Message) - end). - -send_stanzac2s_wrong(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> - Domain = escalus_client:server(Alice), - Resource = escalus_client:resource(Alice), - WrongBobName = "bobby_the_great", - {BobName, _, _} = get_user_data(bob, Config), - BobJID = <>, - Stanza = create_stanza(Alice, BobJID), - StanzaWrong = <<"">>, - {_, Err} = mongooseimctl("send_stanza_c2s", - [WrongBobName, Domain, Resource, Stanza], - Config), - {_, Err2} = mongooseimctl("send_stanza_c2s", - [BobName, Domain, Resource, StanzaWrong], - Config), - - true = Err =/= 0, - true = Err2 =/= 0, - escalus_assert:has_no_stanzas(Alice) - end). - -create_stanza(Name1, JID2) -> - exml:to_binary(escalus_stanza:from(escalus_stanza:chat_to(Name1, "Hi"), JID2)). - -%%-------------------------------------------------------------------- -%% service_admin_extra_stats tests -%%-------------------------------------------------------------------- - -stats_global(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}], fun(_Alice, _Bob) -> - RegisteredCount = length(escalus_config:get_config(escalus_users, Config, [])), - Registered = integer_to_list(RegisteredCount) ++ "\n", - - {UpTime, 0} = mongooseimctl("stats", ["uptimeseconds"], Config), - _ = list_to_integer(string:strip(UpTime, both, $\n)), - {Registered, 0} = mongooseimctl("stats", ["registeredusers"], Config), - - {"2\n", 0} = mongooseimctl("stats", ["onlineusersnode"], Config), - - {"2\n", 0} = mongooseimctl("stats", ["onlineusers"], Config) - end). - -stats_host(Config) -> - escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, _Bob) -> - RegisteredCount = length(escalus_config:get_config(escalus_users, Config, [])), - Registered = integer_to_list(RegisteredCount) ++ "\n", - - PriDomain = escalus_client:server(Alice), - SecDomain = ct:get_config({hosts, mim, secondary_domain}), - - {Registered, 0} = mongooseimctl("stats_host", - ["registeredusers", PriDomain], Config), - {"0\n", 0} = mongooseimctl("stats_host", ["registeredusers", SecDomain], Config), - - {"2\n", 0} = mongooseimctl("stats_host", ["onlineusers", PriDomain], Config), - {"0\n", 0} = mongooseimctl("stats_host", ["onlineusers", SecDomain], Config) - end). - %%-------------------------------------------------------------------- %% mongoose_graphql tests %%-------------------------------------------------------------------- @@ -1197,6 +124,16 @@ graphql_error_unknown_command_without_args(Config) -> ?assertMatch({match, _}, re:run(Res, "Unknown command")), expect_existing_commands(Res). +graphql_error_unknown_category_with_args(Config) -> + {Res, 1} = mongooseimctl("cafe", ["makeCoffee"], Config), + ?assertMatch({match, _}, re:run(Res, "Unknown category")), + expect_category_list(Res). + +graphql_error_unknown_category_without_args(Config) -> + {Res, 1} = mongooseimctl("cafe", [], Config), + ?assertMatch({match, _}, re:run(Res, "Unknown category")), + expect_category_list(Res). + graphql_no_command(Config) -> %% Not an error - lists commands in the given category {Res, 0} = mongooseimctl("account", [], Config), @@ -1252,18 +189,7 @@ default_help(Config) -> #{node := Node} = mim(), CtlCmd = distributed_helper:ctl_path(Node, Config), {Res, 2} = mongooseimctl_helper:run(CtlCmd, []), - %% Expect category list and no deprecated command list - ?assertMatch({match, _}, re:run(Res, "Usage")), - ?assertMatch({match, _}, re:run(Res, "account\s+Account management")), - ?assertMatch(nomatch, re:run(Res, "add_rosteritem\s+Add an item")). - -old_help(Config) -> - {Res, 2} = mongooseimctl("help", [], Config), - %% Expect deprecated command list and no category list - ?assertMatch({match, _}, re:run(Res, "The following commands are deprecated")), - ?assertMatch({match, _}, re:run(Res, "Usage")), - ?assertMatch(nomatch, re:run(Res, "account\s+Account management")), - ?assertMatch({match, _}, re:run(Res, "add_rosteritem")). + expect_category_list(Res). %%----------------------------------------------------------------- %% Server management tests @@ -1279,297 +205,10 @@ server_is_started(Config) -> %% Expect only whitespace ?assertMatch(nomatch, re:run(Res, "\S")). -%%----------------------------------------------------------------- -%% Improve coverage -%%----------------------------------------------------------------- - -simple_register(Config) -> - %% given - Domain = domain(), - {Name, Password} = {<<"tyler">>, <<"durden">>}, - %% when - {R1, 0} = mongooseimctl("registered_users", [Domain], Config), - Before = length(string:tokens(R1, "\n")), - {_, 0} = mongooseimctl("register", [Domain, Password], Config), - {_, 0} = mongooseimctl("register_identified", [Name, Domain, Password], Config), - - {R2, 0} = mongooseimctl("registered_users", [Domain], Config), - After = length(string:tokens(R2, "\n")), - %% then - 2 = After - Before. - -simple_unregister(Config) -> - %% given - Domain = domain(), - {Name, _} = {<<"tyler">>, <<"durden">>}, - %% when - {_, 0} = mongooseimctl("unregister", [Name, Domain], Config), - {R2, 0} = mongooseimctl("registered_users", [Domain], Config), - %% then - nomatch = re:run(R2, ".*(" ++ binary_to_list(Name) ++ ").*"). - -register_twice(Config) -> - %% given - Domain = domain(), - {Name, Password} = {<<"tyler">>, <<"durden">>}, - %% when - {_, 0} = mongooseimctl("register_identified", [Name, Domain, Password], Config), - {R, Code} = mongooseimctl("register_identified", [Name, Domain, Password], Config), - %% then - {match, _} = re:run(R, ".*(already registered).*"), - true = (Code =/= 0), - {_, 0} = mongooseimctl("unregister", [Name, Domain], Config). - -backup_restore_mnesia(Config) -> - %% given - TableName = passwd, - TableSize = rpc_call(mnesia, table_info, [TableName, size]), - %% Table passwd should not be empty - FileName = "backup_mnesia.bup", - %% when - {R, 0} = mongooseimctl("backup", [FileName], Config), - nomatch = re:run(R, ".+"), - rpc_call(mnesia, clear_table, [TableName]), - 0 = rpc_call(mnesia, table_info, [TableName, size]), - {R2, 0} = mongooseimctl("restore", [FileName], Config), - %% then - nomatch = re:run(R2, ".+"), - TableSize = rpc_call(mnesia, table_info, [TableName, size]). - -restore_mnesia_wrong(Config) -> - FileName = "file that doesnt exist13123.bup", - {R2, _} = mongooseimctl("restore", [FileName], Config), - {match, Code} = re:run(R2, ".+"), - true = (Code =/= 0). - -dump_and_load(Config) -> - FileName = "dump.bup", - TableName = passwd, - %% Table passwd should not be empty - TableSize = rpc_call(mnesia, table_info, [TableName, size]), - {DumpReturns, 0} = mongooseimctl("dump", [FileName], Config), - ct:log("DumpReturns ~p", [DumpReturns]), - {ok, DumpData} = rpc_call(file, consult, [FileName]), - ct:log("DumpData ~p", [DumpData]), - rpc_call(mnesia, clear_table, [TableName]), - 0 = rpc_call(mnesia, table_info, [TableName, size]), - {R, 0} = mongooseimctl("load", [FileName], Config), - ct:log("LoadReturns ~p", [R]), - {match, _} = re:run(R, ".+"), - TableSize = rpc_call(mnesia, table_info, [TableName, size]). - -load_mnesia_wrong(Config) -> - FileName = "file that doesnt existRHCP.bup", - {R2, Code} = mongooseimctl("restore", [FileName], Config), - {match, _} = re:run(R2, ".+"), - true = (Code =/= 0). - -dump_table(Config) -> - FileName = "dump.mn", - TableName = passwd, - %% Table passwd should not be empty - TableSize = rpc_call(mnesia, table_info, [TableName, size]), - {_, 0} = mongooseimctl("dump_table", [FileName, atom_to_list(TableName)], Config), - rpc_call(mnesia, clear_table, [TableName]), - 0 = rpc_call(mnesia, table_info, [TableName, size]), - {R, 0} = mongooseimctl("load", [FileName], Config), - {match, _} = re:run(R, ".+"), - TableSize = rpc_call(mnesia, table_info, [TableName, size]). - -get_loglevel(Config) -> - {R, 0} = mongooseimctl("get_loglevel", [], Config), - LogLevel = rpc_call(mongoose_logs, get_global_loglevel, []), - Regexp = io_lib:format("global loglevel is \(.\)\{1,2\}, which means '~p'", [LogLevel]), - {match, _} = re:run(R, Regexp, [{capture, first}]). - -remove_old_messages_test(Config) -> - escalus:story(Config, [{alice, 1}], fun(_) -> - %% given - JidA = nick_to_jid(alice, Config), - JidB = nick_to_jid(bob, Config), - JidRecordAlice = jid:from_binary(JidA), - JidRecordBob = jid:from_binary(JidB), - Domain = domain(), - Msg1 = escalus_stanza:chat_to(<<"bob@", Domain/binary>>, - "Hi, how are you? Its old message!"), - Msg2 = escalus_stanza:chat_to(<<"bob@", Domain/binary>>, - "Hello its new message!"), - OldTimestamp = fallback_timestamp(10, os:system_time(microsecond)), - OfflineOld = generate_offline_message(JidRecordAlice, JidRecordBob, Msg1, OldTimestamp), - OfflineNew = generate_offline_message(JidRecordAlice, JidRecordBob, Msg2, os:system_time(microsecond)), - {LUser, LServer} = jid:to_lus(JidRecordBob), - HostType = host_type(), - rpc_call(mod_offline_backend, write_messages, [host_type(), LUser, LServer, [OfflineOld, OfflineNew]]), - %% when - {_, 0} = mongooseimctl("delete_old_messages", [LServer, "1"], Config), - {ok, SecondList} = rpc_call(mod_offline_backend, pop_messages, [HostType, JidRecordBob]), - %% then - 1 = length(SecondList) - end). - -remove_expired_messages_test(Config) -> - escalus:story(Config, [{mike, 1}], fun(_) -> - %% given - JidA = nick_to_jid(mike, Config), - JidB = nick_to_jid(kate, Config), - JidRecordMike = jid:from_binary(JidA), - JidRecordKate = jid:from_binary(JidB), - Domain = domain(), - Msg1 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Rolling stones"), - Msg2 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Arctic monkeys!"), - Msg3 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "More wine..."), - Msg4 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "kings of leon"), - OldTimestamp = fallback_timestamp(10, os:system_time(microsecond)), - ExpirationTime = fallback_timestamp(2, os:system_time(microsecond)), - ExpirationTimeFuture= fallback_timestamp(-5, os:system_time(microsecond)), - OfflineOld = generate_offline_expired_message(JidRecordMike, - JidRecordKate, Msg1, - OldTimestamp, - ExpirationTime), - OfflineNow = generate_offline_expired_message(JidRecordMike, - JidRecordKate, Msg2, os:system_time(microsecond), ExpirationTime), - OfflineFuture = generate_offline_expired_message(JidRecordMike, - JidRecordKate, Msg3, os:system_time(microsecond), ExpirationTimeFuture), - OfflineFuture2 = generate_offline_expired_message(JidRecordMike, - JidRecordKate, Msg4, - OldTimestamp, - ExpirationTimeFuture), - {LUser, LServer} = jid:to_lus(JidRecordKate), - Args = [OfflineOld, OfflineNow, OfflineFuture, OfflineFuture2], - HostType = host_type(), - rpc_call(mod_offline_backend, write_messages, [HostType, LUser, LServer, Args]), - %% when - {_, 0} = mongooseimctl("delete_expired_messages", [LServer], Config), - {ok, SecondList} = rpc_call(mod_offline_backend, pop_messages, [HostType, JidRecordKate]), - %% then - 2 = length(SecondList) - end). - %%----------------------------------------------------------------- %% Helpers %%----------------------------------------------------------------- - -nick_to_jid(UserName, Config) when is_atom(UserName) -> - UserSpec = escalus_users:get_userspec(Config, UserName), - escalus_utils:jid_to_lower(escalus_users:get_jid(Config, UserSpec)). - -generate_offline_message(From, To, Msg, TimeStamp) -> - {LUser, LServer} = jid:to_lus(To), - #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, expire = never, - from = From, to = To, packet = Msg}. - -generate_offline_expired_message(From, To, Msg, TimeStamp, ExpirationTime) -> - {LUser, LServer} = jid:to_lus(To), - #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, - expire = ExpirationTime, from = From, to = To, packet = Msg}. - - -fallback_timestamp(HowManyDays, TS_MicroSeconds) -> - HowManySeconds = HowManyDays * 86400, - HowManyMicroSeconds = erlang:convert_time_unit(HowManySeconds, second, microsecond), - TS_MicroSeconds - HowManyMicroSeconds. - -get_user_data(User, Config) when is_atom(User) -> - get_user_data(escalus_users:get_options(Config, User, <<"newres">>), Config); -get_user_data(User, _Config) -> - {_, Password} = lists:keyfind(password, 1, User), - {_, Username} = lists:keyfind(username, 1, User), - {_, Domain} = lists:keyfind(server, 1, User), - {Username, Domain, Password}. - -get_md5(AccountPass) -> - lists:flatten([io_lib:format("~.16B", [X]) - || X <- binary_to_list(crypto:hash(md5, AccountPass))]). -get_sha(AccountPass) -> - lists:flatten([io_lib:format("~.16B", [X]) - || X <- binary_to_list(crypto:hash(sha, AccountPass))]). - -set_last(User, Domain, TStamp) -> - rpc(mim(), mod_last, store_last_info, - [host_type(), escalus_utils:jid_to_lower(User), Domain, TStamp, <<>>]). - -delete_users(_Config) -> - lists:foreach(fun({User, Domain}) -> - JID = mongoose_helper:make_jid(User, Domain), - rpc(mim(), ejabberd_auth, remove_user, [JID]) - end, get_registered_users()). - -%%----------------------------------------------------------------- -%% Predicates -%%----------------------------------------------------------------- - -match_user_status(Users, StatusTxt) -> - Statuses = string:tokens(StatusTxt, "\n"), - true = (length(Users) == length(Statuses)), - match_user_status2(Users, Statuses). - -match_user_status2([], _) -> - true; -match_user_status2([User | UserR], Statuses) -> - Username = binary_to_list(escalus_client:username(User)), - Domain = binary_to_list(escalus_client:server(User)), - Resource = binary_to_list(escalus_client:resource(User)), - - true = lists:any(fun(Status) -> - [Username, Domain, Resource] - =:= - lists:sublist(string:tokens(Status, "\t"), 1, 3) - end, Statuses), - match_user_status2(UserR, Statuses). - -match_user_info(Users, UsersTxt) -> - UsersInfo = string:tokens(UsersTxt, "\n"), - case length(Users) == length(UsersInfo) of - true -> - ok; - false -> - ct:fail(#{what => match_user_info_failed, - users => Users, user_info => UsersInfo}) - end, - match_user_info2(Users, UsersInfo). - -match_user_info2([], _) -> - true; -match_user_info2([User | UserR], UsersInfo) -> - Username = binary_to_list(escalus_client:username(User)), - Domain = binary_to_list(escalus_client:server(User)), - Resource = binary_to_list(escalus_client:resource(User)), - FullJID = Username ++ "@" ++ Domain ++ "/" ++ Resource, - - true = lists:any(fun(UserInfo) -> - string:str(UserInfo, string:to_lower(FullJID)) =:= 1 - end, UsersInfo), - match_user_info2(UserR, UsersInfo). - -match_roster(ItemsValid, Items) -> - ItemsTokens = [ string:tokens(ItemToken, "\t") || ItemToken <- string:tokens(Items, "\n") ], - - true = (length(ItemsValid) == length(ItemsTokens)), - true = lists:all(fun({Username, Domain, _Nick, _Group, _Sub}) -> - JID = escalus_utils:jid_to_lower(<>), - lists:any(fun - ([RosterJID, _Nick, _Sub, "none", _Group]) -> - JID =:= escalus_utils:jid_to_lower(list_to_binary(RosterJID)); - (_) -> - false - end, ItemsTokens) - end, ItemsValid). - -string_to_binary(List) -> - case erlang:system_info(otp_release) of - [$R|_] -> - list_to_binary(List); - _ -> - unicode:characters_to_binary(List) - end. - -add_rosteritem1(UserName1, Domain, UserName2, Config) -> - mongooseimctl("add_rosteritem", - [UserName1, Domain, UserName2, - Domain, "MyBob", "MyGroup", "both"], Config). - -add_rosteritem2(Name1, Domain1, Name2, Domain2, Config) -> - mongooseimctl("add_rosteritem", - [Name1, Domain1, Name2, - Domain2, "DearMike", "MyGroup", "both"], Config). +expect_category_list(Res) -> + ?assertMatch({match, _}, re:run(Res, "Usage")), + ?assertMatch({match, _}, re:run(Res, "account\s+Account management")). diff --git a/big_tests/tests/oauth_SUITE.erl b/big_tests/tests/oauth_SUITE.erl index d43edc3b813..6eff6bd03ea 100644 --- a/big_tests/tests/oauth_SUITE.erl +++ b/big_tests/tests/oauth_SUITE.erl @@ -37,7 +37,6 @@ all() -> {group, token_login}, {group, token_revocation}, {group, provision_token}, - {group, commands}, {group, cleanup}, {group, sasl_mechanisms} ]. @@ -47,8 +46,6 @@ groups() -> {token_login, [sequence], token_login_tests()}, {token_revocation, [sequence], token_revocation_tests()}, {provision_token, [], [provision_token_login]}, - {commands, [], [revoke_token_cmd_when_no_token, - revoke_token_cmd]}, {cleanup, [], [token_removed_on_user_removal]}, {sasl_mechanisms, [], [check_for_oauth_with_mod_auth_token_not_loaded, check_for_oauth_with_mod_auth_token_loaded]} @@ -93,11 +90,7 @@ end_per_suite(Config) -> dynamic_modules:restore_modules(Config), escalus:end_per_suite(Config). -init_per_group(GroupName, Config0) -> - Config = case GroupName of - commands -> ejabberd_node_utils:init(Config0); - _ -> Config0 - end, +init_per_group(GroupName, Config) -> AuthOpts = mongoose_helper:auth_opts_with_password_format(password_format(GroupName)), HostType = domain_helper:host_type(), Config1 = mongoose_helper:backup_and_set_config_option(Config, {auth, HostType}, AuthOpts), @@ -286,21 +279,6 @@ get_owner_seqno_to_revoke(Config, User) -> revoke_token(Owner) -> rpc(mim(), mod_auth_token, revoke, [domain_helper:host_type(), Owner]). -revoke_token_cmd_when_no_token(Config) -> - %% given existing user with no token - %% when revoking token - R = mimctl(Config, ["revoke_token", escalus_users:get_jid(Config, bob)]), - %% then no token was found - "Error: \"User or token not found\"\n" = R. - -revoke_token_cmd(Config) -> - %% given existing user and token present in the database - _Tokens = request_tokens_once_logged_in_impl(Config, bob), - %% when - R = mimctl(Config, ["revoke_token", escalus_users:get_jid(Config, bob)]), - %% then - "Revoked\n" = R. - token_removed_on_user_removal(Config) -> %% given existing user with token and XMPP (de)registration available _Tokens = request_tokens_once_logged_in_impl(Config, bob), diff --git a/big_tests/tests/service_domain_db_SUITE.erl b/big_tests/tests/service_domain_db_SUITE.erl index 8d5530a6294..2df7952c748 100644 --- a/big_tests/tests/service_domain_db_SUITE.erl +++ b/big_tests/tests/service_domain_db_SUITE.erl @@ -4,8 +4,9 @@ -include_lib("eunit/include/eunit.hrl"). -compile([export_all, nowarn_export_all]). --import(distributed_helper, [mim/0, mim2/0, mim3/0, require_rpc_nodes/1, rpc/4]). --import(mongooseimctl_helper, [mongooseimctl/3]). +-import(distributed_helper, [mim/0, mim2/0, mim3/0, require_rpc_nodes/1, rpc/4, + remove_node_from_cluster/2]). +-import(graphql_helper, [execute_command/4]). -import(domain_rest_helper, [set_invalid_creds/1, @@ -87,28 +88,6 @@ db_cases() -> [ db_keeps_syncing_after_cluster_join, db_gaps_are_getting_filled_automatically, db_event_could_appear_with_lower_id, - cli_can_insert_domain, - cli_can_disable_domain, - cli_can_enable_domain, - cli_can_delete_domain, - cli_cannot_delete_domain_without_correct_type, - cli_cannot_insert_domain_twice_with_another_host_type, - cli_cannot_insert_domain_with_unknown_host_type, - cli_cannot_delete_domain_with_unknown_host_type, - cli_cannot_enable_missing_domain, - cli_cannot_disable_missing_domain, - cli_cannot_insert_domain_when_it_is_static, - cli_cannot_delete_domain_when_it_is_static, - cli_cannot_enable_domain_when_it_is_static, - cli_cannot_disable_domain_when_it_is_static, - cli_insert_domain_fails_if_db_fails, - cli_insert_domain_fails_if_service_disabled, - cli_delete_domain_fails_if_db_fails, - cli_delete_domain_fails_if_service_disabled, - cli_enable_domain_fails_if_db_fails, - cli_enable_domain_fails_if_service_disabled, - cli_disable_domain_fails_if_db_fails, - cli_disable_domain_fails_if_service_disabled, {group, rest_with_auth}, {group, rest_without_auth}]. @@ -269,12 +248,16 @@ init_per_testcase2(TestcaseName, Config) Mods = [{mod_mam, mam_helper:config_opts(#{pm => #{}})}], dynamic_modules:ensure_modules(HostType, Mods), escalus:init_per_testcase(TestcaseName, Config); +init_per_testcase2(db_keeps_syncing_after_cluster_join, Config) -> + graphql_helper:init_admin_cli(Config); init_per_testcase2(_, Config) -> Config. end_per_testcase2(TestcaseName, Config) when TestcaseName =:= rest_delete_domain_cleans_data_from_mam -> escalus:end_per_testcase(TestcaseName, Config); +end_per_testcase2(db_keeps_syncing_after_cluster_join, _Config) -> + graphql_helper:clean(); end_per_testcase2(_, Config) -> Config. @@ -730,112 +713,6 @@ db_event_could_appear_with_lower_id(_Config) -> mongoose_helper:wait_until(F, lists:seq(40, 50), #{time_left => timer:seconds(15)}). -cli_can_insert_domain(Config) -> - {"Added\n", 0} = - mongooseimctl("insert_domain", [<<"example.db">>, <<"type1">>], Config), - {ok, #{host_type := <<"type1">>, status := enabled}} = - select_domain(mim(), <<"example.db">>). - -cli_can_disable_domain(Config) -> - mongooseimctl("insert_domain", [<<"example.db">>, <<"type1">>], Config), - mongooseimctl("disable_domain", [<<"example.db">>], Config), - {ok, #{host_type := <<"type1">>, status := disabled}} = - select_domain(mim(), <<"example.db">>). - -cli_can_enable_domain(Config) -> - mongooseimctl("insert_domain", [<<"example.db">>, <<"type1">>], Config), - mongooseimctl("disable_domain", [<<"example.db">>], Config), - mongooseimctl("enable_domain", [<<"example.db">>], Config), - {ok, #{host_type := <<"type1">>, status := enabled}} = - select_domain(mim(), <<"example.db">>). - -cli_can_delete_domain(Config) -> - mongooseimctl("insert_domain", [<<"example.db">>, <<"type1">>], Config), - mongooseimctl("delete_domain", [<<"example.db">>, <<"type1">>], Config), - {error, not_found} = select_domain(mim(), <<"example.db">>). - -cli_cannot_delete_domain_without_correct_type(Config) -> - {"Added\n", 0} = - mongooseimctl("insert_domain", [<<"example.db">>, <<"type1">>], Config), - {"Error: \"Wrong host type was provided\"\n", 1} = - mongooseimctl("delete_domain", [<<"example.db">>, <<"type2">>], Config), - {ok, _} = select_domain(mim(), <<"example.db">>). - -cli_cannot_insert_domain_twice_with_another_host_type(Config) -> - {"Added\n", 0} = - mongooseimctl("insert_domain", [<<"example.db">>, <<"type1">>], Config), - {"Error: \"Domain already exists\"\n", 1} = - mongooseimctl("insert_domain", [<<"example.db">>, <<"type2">>], Config). - -cli_cannot_insert_domain_with_unknown_host_type(Config) -> - {"Error: \"Unknown host type\"\n", 1} = - mongooseimctl("insert_domain", [<<"example.db">>, <<"type6">>], Config). - -cli_cannot_delete_domain_with_unknown_host_type(Config) -> - {"Error: \"Unknown host type\"\n", 1} = - mongooseimctl("delete_domain", [<<"example.db">>, <<"type6">>], Config). - -cli_cannot_enable_missing_domain(Config) -> - {"Error: \"Given domain does not exist\"\n", 1} = - mongooseimctl("enable_domain", [<<"example.db">>], Config). - -cli_cannot_disable_missing_domain(Config) -> - {"Error: \"Given domain does not exist\"\n", 1} = - mongooseimctl("disable_domain", [<<"example.db">>], Config). - -cli_cannot_insert_domain_when_it_is_static(Config) -> - {"Error: \"Domain is static\"\n", 1} = - mongooseimctl("insert_domain", [<<"example.cfg">>, <<"type1">>], Config). - -cli_cannot_delete_domain_when_it_is_static(Config) -> - {"Error: \"Domain is static\"\n", 1} = - mongooseimctl("delete_domain", [<<"example.cfg">>, <<"type1">>], Config). - -cli_cannot_enable_domain_when_it_is_static(Config) -> - {"Error: \"Domain is static\"\n", 1} = - mongooseimctl("enable_domain", [<<"example.cfg">>], Config). - -cli_cannot_disable_domain_when_it_is_static(Config) -> - {"Error: \"Domain is static\"\n", 1} = - mongooseimctl("disable_domain", [<<"example.cfg">>], Config). - -cli_insert_domain_fails_if_db_fails(Config) -> - Res = mongooseimctl("insert_domain", [<<"example.db">>, <<"type1">>], Config), - assert_cli_db_error(Res). - -cli_insert_domain_fails_if_service_disabled(Config) -> - service_disabled(mim()), - {"Error: \"Dynamic domains service is disabled\"\n", 1} = - mongooseimctl("insert_domain", [<<"example.db">>, <<"type1">>], Config). - -cli_delete_domain_fails_if_db_fails(Config) -> - {ok, _} = insert_domain(mim(), <<"example.db">>, <<"type1">>), - Res = mongooseimctl("delete_domain", [<<"example.db">>, <<"type1">>], Config), - assert_cli_db_error(Res). - -cli_delete_domain_fails_if_service_disabled(Config) -> - service_disabled(mim()), - {"Error: \"Dynamic domains service is disabled\"\n", 1} = - mongooseimctl("delete_domain", [<<"example.db">>, <<"type1">>], Config). - -cli_enable_domain_fails_if_db_fails(Config) -> - Res = mongooseimctl("enable_domain", [<<"example.db">>], Config), - assert_cli_db_error(Res). - -cli_enable_domain_fails_if_service_disabled(Config) -> - service_disabled(mim()), - {"Error: \"Dynamic domains service is disabled\"\n", 1} = - mongooseimctl("enable_domain", [<<"example.db">>], Config). - -cli_disable_domain_fails_if_db_fails(Config) -> - Res = mongooseimctl("disable_domain", [<<"example.db">>], Config), - assert_cli_db_error(Res). - -cli_disable_domain_fails_if_service_disabled(Config) -> - service_disabled(mim()), - {"Error: \"Dynamic domains service is disabled\"\n", 1} = - mongooseimctl("disable_domain", [<<"example.db">>], Config). - rest_can_insert_domain(Config) -> {{<<"204">>, _}, _} = rest_put_domain(Config, <<"example.db">>, <<"type1">>), @@ -1254,19 +1131,15 @@ force_check_for_updates(Node) -> ensure_nodes_know_each_other() -> pong = rpc(mim2(), net_adm, ping, [maps:get(node, mim())]). -maybe_setup_meck(TC) when TC =:= rest_insert_domain_fails_if_db_fails; - TC =:= cli_insert_domain_fails_if_db_fails -> +maybe_setup_meck(rest_insert_domain_fails_if_db_fails) -> ok = rpc(mim(), meck, new, [mongoose_domain_sql, [passthrough, no_link]]), ok = rpc(mim(), meck, expect, [mongoose_domain_sql, insert_domain, 2, {error, {db_error, simulated_db_error}}]); -maybe_setup_meck(TC) when TC =:= rest_delete_domain_fails_if_db_fails; - TC =:= cli_delete_domain_fails_if_db_fails -> +maybe_setup_meck(rest_delete_domain_fails_if_db_fails) -> ok = rpc(mim(), meck, new, [mongoose_domain_sql, [passthrough, no_link]]), ok = rpc(mim(), meck, expect, [mongoose_domain_sql, delete_domain, 2, {error, {db_error, simulated_db_error}}]); -maybe_setup_meck(TC) when TC =:= rest_enable_domain_fails_if_db_fails; - TC =:= cli_enable_domain_fails_if_db_fails; - TC =:= cli_disable_domain_fails_if_db_fails -> +maybe_setup_meck(rest_enable_domain_fails_if_db_fails) -> ok = rpc(mim(), meck, new, [mongoose_domain_sql, [passthrough, no_link]]), ok = rpc(mim(), meck, expect, [mongoose_domain_sql, set_status, 2, {error, {db_error, simulated_db_error}}]); @@ -1286,17 +1159,11 @@ maybe_teardown_meck(_) -> rpc(mim(), meck, unload, []). leave_cluster(Config) -> - Cmd = "leave_cluster", - #{node := Node} = distributed_helper:mim(), - Args = ["--force"], - mongooseimctl_helper:mongooseimctl(Node, Cmd, Args, Config). + execute_command(<<"server">>, <<"leaveCluster">>, #{}, Config). join_cluster(Config) -> - Cmd = "join_cluster", - #{node := Node} = distributed_helper:mim(), #{node := Node2} = distributed_helper:mim2(), - Args = ["--force", atom_to_list(Node2)], - mongooseimctl_helper:mongooseimctl(Node, Cmd, Args, Config). + execute_command(<<"server">>, <<"joinCluster">>, #{<<"node">> => Node2}, Config). assert_domains_are_equal(HostType) -> Domains1 = lists:sort(get_domains_by_host_type(mim(), HostType)), @@ -1306,9 +1173,6 @@ assert_domains_are_equal(HostType) -> false -> ct:fail({Domains1, Domains2}) end. -assert_cli_db_error({Msg, 1}) -> - ?assertMatch([_|_], string:find(Msg, "simulated_db_error")). - assert_rest_db_error({Result, Msg}) -> ?assertEqual({<<"500">>, <<"Internal Server Error">>}, Result), ?assertEqual(<<>>, Msg). % shouldn't leak out the DB error, it's in the logs anyway diff --git a/doc/configuration/Services.md b/doc/configuration/Services.md index 9bab0aab02a..a43b3afaa02 100644 --- a/doc/configuration/Services.md +++ b/doc/configuration/Services.md @@ -1,57 +1,21 @@ Some functionalities in MongooseIM are provided by "services". -A service is similar to a module, but while a module is started for every -host type and may have global or specific configuration, a service is started +A service is similar to a module, but while a module is started for every +host type and may have global or specific configuration, a service is started only once with global configuration. Currently, three modules are categorised as "service providers". Eventually the modules which are not specific for a host type will be refactored to be services. -* **Syntax:** Each service is specified in its own `services.*` section. +* **Syntax:** Each service is specified in its own `services.*` section. * **Default:** None - each service needs to be enabled explicitly. Typical services are already specified in the example configuration file. -* **Example:** A configuration of the `service_admin_extra` service. +* **Example:** A configuration of the `service_domain_db` service. ```toml -[services.service_admin_extra] - submods = ["node", "account", "sessions", "vcard", "gdpr", "upload", "roster", - "last", "private", "stanza", "stats"] +[services.service_domain_db] + event_cleaning_interval = 1000 + event_max_age = 5000 ``` -## service_admin_extra - -This service provides additional commands to the mongooseimctl script. - -!!! Warning - This service is deprecated. - The commands are still supported, but they **will be removed** soon. - You should use the new GraphQL-based command line interface instead. - -### `services.service_admin_extra.submods` -* **Syntax:** Array of strings representing function groups added by `service_admin_extra`. -* **Default:** All submodules: `["node", "account", "sessions", "vcard", "gdpr", - "upload", "roster", "last", "private", "stanza", "stats", "domain"]` -* **Example:** `submods = ["stats", "gdpr"]` - -The commands are bundled in the following groups: - -* `accounts`: Adds `change_password`, `check_password_hash`, `delete_old_users`, - `delete_old_users_vhost`, `ban_account`, `num_active_users`, `check_account`, - `check_password` -* `last`: Adds `set_last` -* `node`: Adds `load_config`, `get_cookie`, `remove_node` -* `private`: Adds `private_get`, `private_set` -* `roster`: Adds `add_rosteritem`, `delete_rosteritem`, `process_rosteritems`, - `get_roster`, `push_roster`, `push_roster_all`, `push_roster_alltoall` -* `sessions`: Adds `num_resources`, `resource_num`, `kick_session`, `status_num_host`, - `status_num`, `status_list_host`, `status_list`, `connected_users_info`, - `connected_users_vhost`, `user_sessions_info`, `set_presence` -* `stanza`: Adds `send_message_chat`, `send_message_headline`, `send_stanza_c2s` -* `stats`: Adds `stats`, `stats_host` -* `vcard`: Adds `get_vcard`, `get_vcard2`, `get_vcard2_multi`, `set_vcard`, - `set_vcard2`, `set_vcard2_multi` -* `gdpr`: Adds `retrieve_personal_data` -* `upload` : Adds `http_upload` -* `domain` : Adds `insert_domain`, `delete_domain`, `enable_domain`, `disable_domain` - ## service_mongoose_system_metrics MongooseIM system metrics are being gathered to analyse the trends and needs of our users, improve MongooseIM, and get to know where to focus our efforts. @@ -63,7 +27,7 @@ See [System Metrics Privacy Policy](../operation-and-maintenance/System-Metrics- * **Example:** `report = true` An explicit acknowledgement that the metrics are gathered and reported. -When this option is not specified, the reports are gathered, and a notification +When this option is not specified, the reports are gathered, and a notification appears in logs on startup. Enabling this option silences the notification reminder that metrics are gathered. When this option is set to `false`, System Metrics Service is not started and metrics are not collected. @@ -132,17 +96,13 @@ The number of seconds after an event must be deleted from the `domain_events` ta ## Example configuration ```toml -[services.service_admin_extra] - submods = ["node", "account", "sessions", "vcard", "gdpr", "upload", "roster", - "last", "private", "stanza", "stats"] - [services.service_mongoose_system_metrics] report = true initial_report = 300_000 periodic_report = 108_000_000 tracking_id.id = "G-123456789" tracking_id.secret = "Secret" - + [services.service_domain_db] db_pool = "global" event_cleaning_interval = 1800 diff --git a/doc/configuration/general.md b/doc/configuration/general.md index 6e60fa9b83d..f0761fca87b 100644 --- a/doc/configuration/general.md +++ b/doc/configuration/general.md @@ -86,38 +86,7 @@ When using MSSQL or PostgreSQL databases, this option allows MongooseIM to optim ## Access management -User access rules are configured mainly in the [`acl`](acl.md) and [`access`](access.md) sections. Here you can find some additional options. - -### `general.mongooseimctl_access_commands` - -!!! Warning - This option is deprecated. The commands are still supported, but they **will be removed** soon. - You should use the new GraphQL-based command line interface instead. - -* **Syntax:** TOML table, whose **keys** are the names of the access rules defined in the [`access`](access.md) config section and **values** specify allowed administration commands. Each value is a table with the following nested options: - * `commands`: optional, a list of strings representing the allowed commands. When not specified, all commands are allowed. - * `argument_restrictions`: optional, a table whose keys are the argument names, and the values are strings representing the allowed values. When not specified, there are no restrictions. -* **Default:** not set - -By default, all admin operations are permitted with the `mongooseimctl` command without authentication. You can change that by setting this option for a specific access rule. When the rule returns the value `"allow"`, the user is permitted to use the specified commands with the optional restrictions. - -**Example 1.** Allow administrators to execute all commands without any restrictions: - -```toml - [general.mongooseimctl_access_commands.admin] -``` - -The `admin` rule needs to be defined in the `access` section. - -**Example 2.** Allow local users to execute the `join_cluster` command, but only if the `node` argument is equal to `mongooseim@prime`: - -```toml - [general.mongooseimctl_access_commands.local] - commands = ["join_cluster"] - argument_restrictions.node = "mongooseim@prime" -``` - -The `local` rule needs to be defined in the `access` section. +User access rules are configured mainly in the [`acl`](acl.md) and [`access`](access.md) sections. ## Security diff --git a/doc/migrations/6.1.0_6.2.0.md b/doc/migrations/6.1.0_6.2.0.md index da8dda0f7dd..e2d73520ab8 100644 --- a/doc/migrations/6.1.0_6.2.0.md +++ b/doc/migrations/6.1.0_6.2.0.md @@ -4,3 +4,7 @@ So far MongooseIM has been using the internal Mnesia database to replicate the i Now there is an option to use [CETS](https://github.com/esl/cets/) instead. Mnesia is still used by default, so you don't need to change your configuration file. If you want to switch to CETS, see [`internal_databases`](../configuration/internal-databases.md). + +# Transition to New CLI Commands + +Legacy CLI commands previously marked as deprecated have now been removed. The users are encouraged to explore the new GraphQL-based CLI. It is recommended to transition to the new CLI commands **prior to the next system upgrade**. The configuration options `general.mongooseimctl_access_commands` and `services.service_admin_extra` related to the legacy CLI were also removed. **You need to remove them** from your configuration file unless you have already done so. diff --git a/include/ejabberd_commands.hrl b/include/ejabberd_commands.hrl deleted file mode 100644 index 9cc9ee67e1e..00000000000 --- a/include/ejabberd_commands.hrl +++ /dev/null @@ -1,34 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% -%%% ejabberd, Copyright (C) 2002-2011 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 -%%% -%%%---------------------------------------------------------------------- - --record(ejabberd_commands, {name :: atom(), - tags = [], - %% Description of the command - desc = "", - longdesc = "", - module, - function, - %% Describe the accepted arguments. - %% This way the function that calls the command can format the arguments - %% before calling - args = [], - result = rescode - }). - diff --git a/rel/files/mongooseim.toml b/rel/files/mongooseim.toml index e66f67b7a96..8e9a95c4cce 100644 --- a/rel/files/mongooseim.toml +++ b/rel/files/mongooseim.toml @@ -189,8 +189,6 @@ # tls.server_name_indication.enabled = false {{/outgoing_pools}} -[services.service_admin_extra] - [services.service_mongoose_system_metrics] {{#service_domain_db}} diff --git a/src/admin_extra/service_admin_extra.erl b/src/admin_extra/service_admin_extra.erl deleted file mode 100644 index 31b3fd16025..00000000000 --- a/src/admin_extra/service_admin_extra.erl +++ /dev/null @@ -1,67 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : service_admin_extra.erl -%%% Author : Badlop , Piotr Nosek -%%% Purpose : Contributed administrative functions and commands -%%% Created : 10 Aug 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra). --author('badlop@process-one.net'). - --behaviour(mongoose_service). - --include("mongoose_config_spec.hrl"). - --export([start/1, stop/0, config_spec/0]). - --define(SUBMODS, [node, accounts, sessions, vcard, roster, last, - private, stanza, stats, gdpr, upload, domain - %, srg %% Disabled until we add mod_shared_roster - ]). - -%%% -%%% gen_mod -%%% - --spec start(mongoose_service:options()) -> ok. -start(#{submods := Submods}) -> - lists:foreach(fun(Submod) -> - ejabberd_commands:register_commands((mod_name(Submod)):commands()) - end, Submods). - --spec stop() -> ok. -stop() -> - lists:foreach(fun(Submod) -> - ejabberd_commands:unregister_commands((mod_name(Submod)):commands()) - end, ?SUBMODS). - --spec config_spec() -> mongoose_config_spec:config_section(). -config_spec() -> - #section{ - items = #{<<"submods">> => #list{items = #option{type = atom, - validate = {enum, ?SUBMODS}}, - validate = unique} - }, - defaults = #{<<"submods">> => ?SUBMODS} - }. - -mod_name(ModAtom) -> - list_to_existing_atom(atom_to_list(?MODULE) ++ "_" ++ atom_to_list(ModAtom)). diff --git a/src/admin_extra/service_admin_extra_accounts.erl b/src/admin_extra/service_admin_extra_accounts.erl deleted file mode 100644 index 8e0d875b43e..00000000000 --- a/src/admin_extra/service_admin_extra_accounts.erl +++ /dev/null @@ -1,161 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : service_admin_extra_accounts.erl -%%% Author : Badlop , Piotr Nosek -%%% Purpose : Contributed administrative functions and commands -%%% Created : 10 Aug 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra_accounts). --author('badlop@process-one.net'). - --export([ - commands/0, - - %% Accounts - set_password/3, - check_password_hash/4, - delete_old_users/1, - delete_old_users_for_domain/2, - ban_account/3, - num_active_users/2, - check_account/2, - check_password/3]). - --ignore_xref([ - commands/0, set_password/3, check_password_hash/4, - delete_old_users/1, delete_old_users_for_domain/2, - ban_account/3, num_active_users/2, check_account/2, check_password/3 -]). - --include("ejabberd_commands.hrl"). - -%%% -%%% Register commands -%%% - --spec commands() -> [ejabberd_commands:cmd(), ...]. -commands() -> - [ - #ejabberd_commands{name = change_password, tags = [accounts], - desc = "Change the password of an account", - module = ?MODULE, function = set_password, - args = [{user, binary}, {host, binary}, {newpass, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = check_password_hash, tags = [accounts], - desc = "Check if the password hash is correct", - longdesc = "Allowed hash methods: md5, sha.", - module = ?MODULE, function = check_password_hash, - args = [{user, binary}, {host, binary}, {passwordhash, string}, - {hashmethod, string}], - result = {res, restuple}}, - #ejabberd_commands{name = delete_old_users, tags = [accounts, purge], - desc = "Delete users that didn't log in last days, or that never logged", - module = ?MODULE, function = delete_old_users, - args = [{days, integer}], - result = {res, restuple}}, - #ejabberd_commands{name = delete_old_users_vhost, tags = [accounts, purge], - desc = "Delete users that didn't log in last days in vhost," - " or that never logged", - module = ?MODULE, function = delete_old_users_for_domain, - args = [{host, binary}, {days, integer}], - result = {res, restuple}}, - #ejabberd_commands{name = ban_account, tags = [accounts], - desc = "Ban an account: kick sessions and set random password", - module = ?MODULE, function = ban_account, - args = [{user, binary}, {host, binary}, {reason, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = num_active_users, tags = [accounts, stats], - desc = "Get number of users active in the last days", - module = ?MODULE, function = num_active_users, - args = [{host, binary}, {days, integer}], - result = {res, restuple}}, - #ejabberd_commands{name = check_account, tags = [accounts], - desc = "Check if an account exists or not", - module = ?MODULE, function = check_account, - args = [{user, binary}, {host, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = check_password, tags = [accounts], - desc = "Check if a password is correct", - module = ?MODULE, function = check_password, - args = [{user, binary}, {host, binary}, {password, binary}], - result = {res, restuple}} - ]. - -%%% -%%% Accounts -%%% - --spec set_password(jid:user(), jid:server(), binary()) -> - mongoose_account_api:change_password_result(). -set_password(User, Host, Password) -> - mongoose_account_api:change_password(User, Host, Password). - --spec check_password(jid:user(), jid:server(), binary()) -> - mongoose_account_api:check_password_result(). -check_password(User, Host, Password) -> - mongoose_account_api:check_password(User, Host, Password). - --spec check_account(jid:user(), jid:server()) -> mongoose_account_api:check_account_result(). -check_account(User, Host) -> - mongoose_account_api:check_account(User, Host). - --spec check_password_hash(jid:user(), jid:server(), string(), string()) -> - mongoose_account_api:check_password_hash_result(). -check_password_hash(User, Host, PasswordHash, HashMethod) -> - mongoose_account_api:check_password_hash(User, Host, PasswordHash, HashMethod). - --spec num_active_users(jid:lserver(), integer()) -> {ok | domain_not_found, iolist()}. -num_active_users(Domain, Days) -> - Timestamp2 = days_to_timestamp(Days), - case mod_last_api:count_active_users(Domain, Timestamp2) of - {ok, Num} -> {ok, integer_to_list(Num)}; - Res -> Res - end. - --spec delete_old_users(integer()) -> {ok, iolist()}. -delete_old_users(Days) -> - Timestamp = days_to_timestamp(Days), - {ok, OldUsers} = mod_last_api:remove_old_users(Timestamp), - {ok, format_deleted_users(OldUsers)}. - --spec delete_old_users_for_domain(jid:server(), integer()) -> {ok | domain_not_found, iolist()}. -delete_old_users_for_domain(Domain, Days) -> - Timestamp = days_to_timestamp(Days), - case mod_last_api:remove_old_users(Domain, Timestamp) of - {ok, OldUsers} -> - {ok, format_deleted_users(OldUsers)}; - Error -> - Error - end. - --spec ban_account(jid:user(), jid:server(), binary()) -> - mongoose_account_api:change_password_result(). -ban_account(User, Host, ReasonText) -> - mongoose_account_api:ban_account(User, Host, ReasonText). - -%% Internal - -days_to_timestamp(Days) -> - erlang:system_time(second) - Days * 86400. - -format_deleted_users(Users) -> - Users2 = lists:join(", ", [jid:to_binary(JID) || {JID, _} <- Users]), - io_lib:format("Deleted ~p users: ~s", [length(Users), Users2]). diff --git a/src/admin_extra/service_admin_extra_gdpr.erl b/src/admin_extra/service_admin_extra_gdpr.erl deleted file mode 100644 index cf7b14f39c7..00000000000 --- a/src/admin_extra/service_admin_extra_gdpr.erl +++ /dev/null @@ -1,20 +0,0 @@ --module(service_admin_extra_gdpr). - --include("ejabberd_commands.hrl"). --include("mongoose_logger.hrl"). --include("jlib.hrl"). - --export([commands/0]). --ignore_xref([commands/0]). - --spec commands() -> [ejabberd_commands:cmd()]. -commands() -> [ - #ejabberd_commands{name = retrieve_personal_data, tags = [gdpr], - desc = "Retrieve user's presonal data.", - longdesc = "Retrieves all personal data from MongooseIM for a given user. Example:\n" - " mongooseimctl retrieve_personal_data alice localhost /home/mim/alice.smith.zip ", - module = gdpr_api, - function = retrieve_all, - args = [{username, binary}, {domain, binary}, {path, binary}], - result = {res, rescode}} - ]. diff --git a/src/admin_extra/service_admin_extra_last.erl b/src/admin_extra/service_admin_extra_last.erl deleted file mode 100644 index 97909efcdc2..00000000000 --- a/src/admin_extra/service_admin_extra_last.erl +++ /dev/null @@ -1,73 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : service_admin_extra_last.erl -%%% Author : Badlop , Piotr Nosek -%%% Purpose : Contributed administrative functions and commands -%%% Created : 10 Aug 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra_last). --author('badlop@process-one.net'). - --export([ - commands/0, - set_last/4 - ]). - --ignore_xref([commands/0, set_last/4]). - --include("mongoose.hrl"). --include("ejabberd_commands.hrl"). --include("mod_roster.hrl"). --include("jlib.hrl"). --include_lib("exml/include/exml.hrl"). - -%%% -%%% Register commands -%%% - --spec commands() -> [ejabberd_commands:cmd(), ...]. -commands() -> - [ - #ejabberd_commands{name = set_last, tags = [last], - desc = "Set last activity information", - longdesc = "Timestamp is the seconds since" - "1970-01-01 00:00:00 UTC, for example: date +%s", - module = ?MODULE, function = set_last, - args = [{user, binary}, {host, binary}, - {timestamp, integer}, {status, binary}], - result = {res, restuple}} - ]. - -%%% -%%% Last Activity -%%% - --spec set_last(jid:user(), jid:server(), _, _) -> {Res, string()} when - Res :: ok | user_does_not_exist. -set_last(User, Server, Timestamp, Status) -> - JID = jid:make_bare(User, Server), - case mod_last_api:set_last(JID, Timestamp, Status) of - {ok, #{timestamp := Timestamp, status := Status}} -> - {ok, io_lib:format("Last activity for user ~s is set as ~B with status ~s", - [jid:to_binary(JID), Timestamp, Status])}; - Error -> - Error - end. diff --git a/src/admin_extra/service_admin_extra_node.erl b/src/admin_extra/service_admin_extra_node.erl deleted file mode 100644 index 3b8fdbaacb5..00000000000 --- a/src/admin_extra/service_admin_extra_node.erl +++ /dev/null @@ -1,52 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : service_admin_extra_node.erl -%%% Author : Badlop , Piotr Nosek -%%% Purpose : Contributed administrative functions and commands -%%% Created : 10 Aug 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra_node). --author('badlop@process-one.net'). - --export([commands/0]). - --ignore_xref([commands/0, load_config/1]). - --include("ejabberd_commands.hrl"). - -%%% -%%% Register commands -%%% - --spec commands() -> [ejabberd_commands:cmd(), ...]. -commands() -> - [ - #ejabberd_commands{name = get_cookie, tags = [erlang], - desc = "Get the Erlang cookie of this node", - module = mongoose_server_api, function = get_cookie, - args = [], - result = {res, restuple}}, - #ejabberd_commands{name = remove_node, tags = [erlang], - desc = "Remove a MongooseIM node from Mnesia clustering config", - module = mongoose_server_api, function = remove_node, - args = [{node, string}], - result = {res, rescode}} - ]. diff --git a/src/admin_extra/service_admin_extra_private.erl b/src/admin_extra/service_admin_extra_private.erl deleted file mode 100644 index df75c3c0388..00000000000 --- a/src/admin_extra/service_admin_extra_private.erl +++ /dev/null @@ -1,93 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : service_admin_extra_private.erl -%%% Author : Badlop , Piotr Nosek -%%% Purpose : Contributed administrative functions and commands -%%% Created : 10 Aug 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra_private). --author('badlop@process-one.net'). - --export([ - commands/0, - private_get/4, - private_set/3 - ]). - --ignore_xref([ - commands/0, private_get/4, private_set/3 -]). - --include("ejabberd_commands.hrl"). - -%%% -%%% Register commands -%%% - --spec commands() -> [ejabberd_commands:cmd(), ...]. -commands() -> - [ - #ejabberd_commands{name = private_get, tags = [private], - desc = "Get some information from a user private storage", - module = ?MODULE, function = private_get, - args = [{user, binary}, {host, binary}, {element, binary}, {ns, binary}], - result = {content, string}}, - #ejabberd_commands{name = private_set, tags = [private], - desc = "Set to the user private storage", - module = ?MODULE, function = private_set, - args = [{user, binary}, {host, binary}, {element, binary}], - result = {res, restuple}} - ]. - -%%% -%%% Private Storage -%%% - -%% Example usage: -%% $ mongooseimctl private_set badlop localhost "\Cluth\" -%% $ mongooseimctl private_get badlop localhost aa bb -%% Cluth - --spec private_get(jid:user(), jid:server(), binary(), binary()) -> - {not_found, string()} | string(). -private_get(Username, Host, Element, Ns) -> - JID = jid:make_bare(Username, Host), - case mod_private_api:private_get(JID, Element, Ns) of - {ok, Xml} -> exml:to_list(Xml); - Error -> Error - end. - --spec private_set(jid:user(), jid:server(), - ElementString :: binary()) -> {Res, string()} when - Res :: ok | not_found | parse_error. -private_set(Username, Host, ElementString) -> - case exml:parse(ElementString) of - {error, Error} -> - String = io_lib:format("Error found parsing the element: '~ts' Error: ~ts", - [ElementString, Error]), - {parse_error, String}; - {ok, Xml} -> - JID = jid:make_bare(Username, Host), - case mod_private_api:private_set(JID, Xml) of - {ok, _} -> {ok, ""}; - Error -> Error - end - end. diff --git a/src/admin_extra/service_admin_extra_roster.erl b/src/admin_extra/service_admin_extra_roster.erl deleted file mode 100644 index 2c8fb77ec7c..00000000000 --- a/src/admin_extra/service_admin_extra_roster.erl +++ /dev/null @@ -1,541 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : service_admin_extra_roster.erl -%%% Author : Badlop , Piotr Nosek -%%% Purpose : Contributed administrative functions and commands -%%% Created : 10 Aug 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra_roster). --author('badlop@process-one.net'). --export([ - commands/0, - add_rosteritem/7, - delete_rosteritem/4, - process_rosteritems/5, - get_roster/2, - push_roster/3, - push_roster_all/1, - push_alltoall/2 - ]). - --ignore_xref([ - commands/0, add_rosteritem/7, delete_rosteritem/4, process_rosteritems/5, - get_roster/2, push_roster/3, push_roster_all/1, push_alltoall/2 -]). - --include("mongoose.hrl"). --include("ejabberd_commands.hrl"). --include("mod_roster.hrl"). --include("jlib.hrl"). --include_lib("exml/include/exml.hrl"). - --type simple_roster() :: {User :: jid:user(), - Server :: jid:server(), - Group :: binary(), - Nick :: binary()}. --type jids_nick_subs_ask_grp() :: {Jids :: list(), - Nick :: binary(), - Subs :: subs(), - _Ask, - _Group}. --type subs() :: atom() | binary(). --type push_action() :: remove - | {add, Nick :: binary(), Subs :: subs(), - Group :: binary() | string()}. - --type delete_action() :: {'delete', Subs :: [atom()], Asks :: [atom()], - [jid:user()], Contacts :: [binary()]}. --type list_action() :: {'list', Subs :: [atom()], Asks :: [atom()], - [jid:user()], Contacts :: [binary()]}. - - -%%% -%%% Register commands -%%% - --spec commands() -> [ejabberd_commands:cmd(), ...]. -commands() -> - [ - #ejabberd_commands{name = add_rosteritem, tags = [roster], - desc = "Add an item to a user's roster (supports RDBMS)", - module = ?MODULE, function = add_rosteritem, - args = [{localuser, binary}, {localserver, binary}, - {user, binary}, {server, binary}, - {nick, binary}, {group, binary}, - {subs, binary}], - result = {res, restuple}}, - %%{"", "subs= none, from, to or both"}, - %%{"", "example: add-roster peter localhost mike server.com MiKe Employees both"}, - %%{"", "will add mike@server.com to peter@localhost roster"}, - #ejabberd_commands{name = delete_rosteritem, tags = [roster], - desc = "Delete an item from a user's roster (supports RDBMS)", - module = ?MODULE, function = delete_rosteritem, - args = [{localuser, binary}, {localserver, binary}, - {user, binary}, {server, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = process_rosteritems, tags = [roster], - desc = "List or delete rosteritems that" - " match filtering options (Mnesia only!)", - longdesc = "Explanation of each argument:\n" - " - action: what to do with each rosteritem that " - "matches all the filtering options\n" - " - subs: subscription type\n" - " - asks: pending subscription\n" - " - users: the JIDs of the local user\n" - " - contacts: the JIDs of the contact in the roster\n" - "\n" - "Allowed values in the arguments:\n" - " ACTION = list | delete\n" - " SUBS = SUB[:SUB]* | any\n" - " SUB = none | from | to | both\n" - " ASKS = ASK[:ASK]* | any\n" - " ASK = none | out | in\n" - " USERS = JID[:JID]* | any\n" - " CONTACTS = JID[:JID]* | any\n" - " JID = characters valid in a JID, and can use the " - "Regular expression syntax:" - " http://www.erlang.org/doc/man/re.html#id212737\n" - "\n" - "This example will list roster items with subscription " - "'none', 'from' or 'to' that have any ask property, of " - "local users which JID is in the virtual host " - "'example.org' and that the contact JID is either a " - "bare server name (without user part) or that has a " - "user part and the server part contains the word 'icq'" - ":\n list none:from:to any *@example.org *:*@*icq*", - module = ?MODULE, function = process_rosteritems, - args = [{action, string}, {subs, string}, - {asks, string}, {users, string}, - {contacts, string}], - result = {res, binary}}, - #ejabberd_commands{name = get_roster, tags = [roster], - desc = "Get roster of a local user", - module = ?MODULE, function = get_roster, - args = [{user, binary}, {host, binary}], - result = {contacts, {list, {contact, {tuple, [ - {jid, binary}, - {nick, binary}, - {subscription, binary}, - {ask, binary}, - {group, binary} - ]}}}}}, - #ejabberd_commands{name = push_roster, tags = [roster], - desc = "Push template roster from file to a user", - module = ?MODULE, function = push_roster, - args = [{file, string}, {user, binary}, {host, binary}], - result = {res, rescode}}, - #ejabberd_commands{name = push_roster_all, tags = [roster], - desc = "Push template roster from file to all those users", - module = ?MODULE, function = push_roster_all, - args = [{file, string}], - result = {res, rescode}}, - #ejabberd_commands{name = push_roster_alltoall, tags = [roster], - desc = "Add all the users to all the users of Host in Group", - module = ?MODULE, function = push_alltoall, - args = [{host, binary}, {group, binary}], - result = {res, rescode}} - ]. - -%%% -%%% Roster -%%% - --spec add_rosteritem(LocalUser :: jid:user(), - LocalServer :: jid:server(), - User :: jid:user(), - Server :: jid:server(), - Nick :: binary(), - Group :: binary() | string(), - Subs :: subs()) -> {Res, string()} when - Res :: user_doest_not_exist | error | bad_subs | ok. -add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) -> - LocalJID = jid:make_bare(LocalUser, LocalServer), - case ejabberd_auth:does_user_exist(LocalJID) of - true -> - RemoteJID = jid:make_bare(User, Server), - case subscribe(LocalJID, RemoteJID, Nick, Group, Subs, []) of - ok -> - do_add_rosteritem(LocalJID, RemoteJID, Nick, Group, Subs); - {error, Reason} -> - {error, io_lib:format("~p", [Reason])} - end; - false -> - {user_does_not_exist, - io_lib:format("Cannot add the item because user ~s@~s does not exist", - [LocalUser, LocalServer])} - end. - -do_add_rosteritem(LocalJID, RemoteJID, Nick, Group, Subs) -> - case lists:member(Subs, possible_subs_binary()) of - true -> - push_roster_item(LocalJID, RemoteJID, {add, Nick, Subs, Group}), - {ok, io_lib:format("Added the item to the roster of ~s", [jid:to_binary(LocalJID)])}; - false -> - {bad_subs, io_lib:format("Sub ~s is incorrect." - " Choose one of the following:~nnone~nfrom~nto~nboth", - [binary_to_list(Subs)])} - end. - - -%% @doc returns result of mnesia or rdbms transaction --spec subscribe(LocalJID :: jid:jid(), - RemoteJID :: jid:jid(), - Nick :: binary(), - Group :: binary() | string(), - Subs :: subs(), - _Xattrs :: [jlib:binary_pair()]) -> ok | {error, any()}. -subscribe(LocalJID, RemoteJID, Nick, Group, SubscriptionS, _Xattrs) -> - ItemEl = build_roster_item(RemoteJID, {add, Nick, SubscriptionS, Group}), - QueryEl = #xmlel{ name = <<"query">>, - attrs = [{<<"xmlns">>, <<"jabber:iq:roster">>}], - children = [ItemEl]}, - {ok, HostType} = mongoose_domain_api:get_domain_host_type(LocalJID#jid.lserver), - mod_roster:set_items(HostType, LocalJID, QueryEl). - - --spec delete_rosteritem(LocalUser :: jid:user(), - LocalServer :: jid:server(), - User :: jid:user(), - Server :: jid:server()) -> {Res, string()} when - Res :: ok | error | user_does_not_exist. -delete_rosteritem(LocalUser, LocalServer, User, Server) -> - LocalJID = jid:make_bare(LocalUser, LocalServer), - case ejabberd_auth:does_user_exist(LocalJID) of - true -> - RemoteJID = jid:make_bare(User, Server), - case unsubscribe(LocalJID, RemoteJID) of - ok -> - push_roster_item(LocalJID, RemoteJID, remove), - {ok, io_lib:format("The item removed from roster of ~s", - [jid:to_binary(LocalJID)])}; - {error, Reason} -> - {error, io_lib:format("~p", [Reason])} - end; - false -> - {user_does_not_exist, - io_lib:format("Cannot delete the item because user ~s@~s doest not exist", - [LocalUser, LocalServer])} - end. - - -%% @doc returns result of mnesia or rdbms transaction --spec unsubscribe(LocalJID :: jid:jid(), RemoteJID :: jid:jid()) -> ok | {error, any()}. -unsubscribe(LocalJID, RemoteJID) -> - ItemEl = build_roster_item(RemoteJID, remove), - QueryEl = #xmlel{ name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER}], - children = [ItemEl]}, - {ok, HostType} = mongoose_domain_api:get_domain_host_type(LocalJID#jid.lserver), - mod_roster:set_items(HostType, LocalJID, QueryEl). - -%% ----------------------------- -%% Get Roster -%% ----------------------------- - --spec get_roster(jid:user(), jid:server()) -> - [jids_nick_subs_ask_grp()]. -get_roster(User, Server) -> - UserJID = jid:make_bare(User, Server), - {ok, HostType} = mongoose_domain_api:get_domain_host_type(UserJID#jid.lserver), - Acc = mongoose_acc:new(#{location => ?LOCATION, - host_type => HostType, - lserver => UserJID#jid.lserver, - element => undefined}), - Acc2 = mongoose_hooks:roster_get(Acc, UserJID), - Items = mongoose_acc:get(roster, items, [], Acc2), - make_roster(Items). - - -%% @doc Note: if a contact is in several groups, the contact is returned -%% several times, each one in a different group. --spec make_roster([mod_roster:roster()]) -> [jids_nick_subs_ask_grp()]. -make_roster(Roster) -> - lists:foldl( - fun(Item, Res) -> - JIDS = jid:to_binary(Item#roster.jid), - Nick = Item#roster.name, - Subs = atom_to_list(Item#roster.subscription), - Ask = atom_to_list(Item#roster.ask), - Groups = case Item#roster.groups of - [] -> [""]; - Gs -> Gs - end, - ItemsX = [{JIDS, Nick, Subs, Ask, Group} - || Group <- Groups], - ItemsX ++ Res - end, - [], - Roster). - - -%%----------------------------- -%% Push Roster from file -%%----------------------------- - --spec push_roster(file:name(), jid:user(), jid:server()) -> 'ok'. -push_roster(File, User, Server) -> - {ok, [Roster]} = file:consult(File), - subscribe_roster({User, Server, <<"">>, User}, roster_list_to_binary(Roster)). - - --spec push_roster_all(file:name()) -> 'ok'. -push_roster_all(File) -> - {ok, [Roster]} = file:consult(File), - subscribe_all(roster_list_to_binary(Roster)). - - --spec roster_list_to_binary([mod_roster:roster()]) -> [simple_roster()]. -roster_list_to_binary(Roster) -> - [{ - mongoose_bin:string_to_binary(Usr), - mongoose_bin:string_to_binary(Srv), - mongoose_bin:string_to_binary(Grp), - mongoose_bin:string_to_binary(Nick)} || {Usr, Srv, Grp, Nick} <- Roster]. - - --spec subscribe_all([simple_roster()]) -> 'ok'. -subscribe_all(Roster) -> - subscribe_all(Roster, Roster). -subscribe_all([], _) -> - ok; -subscribe_all([User1 | Users], Roster) -> - subscribe_roster(User1, Roster), - subscribe_all(Users, Roster). - - --spec subscribe_roster(simple_roster(), [simple_roster()]) -> 'ok'. -subscribe_roster(_, []) -> - ok; -%% Do not subscribe a user to itself -subscribe_roster({Name, Server, Group, Nick}, [{Name, Server, _, _} | Roster]) -> - subscribe_roster({Name, Server, Group, Nick}, Roster); -%% Subscribe Name2 to Name1 -subscribe_roster({Name1, Server1, Group1, Nick1}, [{Name2, Server2, Group2, Nick2} | Roster]) -> - subscribe(jid:make_bare(Name1, Server1), - jid:make_bare(Name2, Server2), - Nick2, Group2, <<"both">>, []), - subscribe_roster({Name1, Server1, Group1, Nick1}, Roster). - - --spec push_alltoall(jid:server(), binary()) -> 'ok'. -push_alltoall(S, G) -> - Users = ejabberd_auth:get_vh_registered_users(S), - Users2 = build_list_users(G, Users, []), - subscribe_all(Users2), - ok. - - --spec build_list_users(Group :: binary(), - [jid:simple_bare_jid()], - Res :: [simple_roster()]) -> []. -build_list_users(_Group, [], Res) -> - Res; -build_list_users(Group, [{User, Server}|Users], Res) -> - build_list_users(Group, Users, [{User, Server, Group, User}|Res]). - - -%% @doc Push to the roster of account LU@LS the contact U@S. -%% The specific action to perform is defined in Action. --spec push_roster_item(jid:jid(), jid:jid(), Action :: push_action()) -> 'ok'. -push_roster_item(JID, #jid{luser = U, lserver = S} = RemJID, Action) -> - lists:foreach(fun(R) -> - RJID = jid:replace_resource(JID, R), - SubsAtom = action_to_subscription(Action), - mod_roster:broadcast_item(RJID, {U, S, <<>>}, SubsAtom), - Item = build_roster_item(RemJID, Action), - ResIQ = build_iq_roster_push(Item), - ejabberd_router:route(RJID, RJID, ResIQ) - end, ejabberd_sm:get_user_resources(JID)). - --spec build_roster_item(jid:jid(), push_action()) -> exml:element(). -build_roster_item(#jid{lresource = <<>>} = JID, {add, Nick, Subs, Group}) -> - #xmlel{ name = <<"item">>, - attrs = [{<<"jid">>, jid:to_binary(JID)}, - {<<"name">>, Nick}, - {<<"subscription">>, Subs}], - children = [#xmlel{name = <<"group">>, children = [#xmlcdata{content = Group}]}] - }; -build_roster_item(#jid{lresource = <<>>} = JID, remove) -> - #xmlel{ name = <<"item">>, - attrs = [{<<"jid">>, jid:to_binary(JID)}, - {<<"subscription">>, <<"remove">>}]}. - --spec build_iq_roster_push(jlib:xmlcdata() | exml:element()) -> exml:element(). -build_iq_roster_push(Item) -> - #xmlel{ name = <<"iq">>, - attrs = [{<<"type">>, <<"set">>}, {<<"id">>, <<"push">>}], - children = [#xmlel{ name = <<"query">>, - attrs = [{<<"xmlns">>, ?NS_ROSTER}], - children = [Item]}] }. - --spec action_to_subscription(push_action()) -> atom(). -action_to_subscription({add, _Nick, Subs, _Group}) -> - list_to_existing_atom(binary_to_list(Subs)); -action_to_subscription(remove) -> - none. - -%%----------------------------- -%% Purge roster items -%%----------------------------- - --spec process_rosteritems(Act :: string(), SubsS :: string(), AsksS :: string(), - UsersS :: string(), ContactsS :: string()) -> binary(). -process_rosteritems(ActionS, SubsS, AsksS, UsersS, ContactsS) -> - Action = case ActionS of - "list" -> list; - "delete" -> delete - end, - - Subs = lists:foldl( - fun(any, _) -> [none, from, to, both]; - (Sub, Subs) -> [Sub | Subs] - end, - [], - [list_to_existing_atom(S) || S <- string:tokens(SubsS, ":")] - ), - - Asks = lists:foldl( - fun(any, _) -> [none, out, in]; - (Ask, Asks) -> [Ask | Asks] - end, - [], - [list_to_existing_atom(S) || S <- string:tokens(AsksS, ":")] - ), - - Users = lists:foldl( - fun(<<"any">>, _) -> [<<".*">>, <<".*@.*">>]; - (U, Us) -> [U | Us] - end, - [], - [mongoose_bin:string_to_binary(S) || S <- string:tokens(UsersS, ":")] - ), - - Contacts = lists:foldl( - fun(<<"any">>, _) -> [<<".*">>, <<".*@.*">>]; - (U, Us) -> [U | Us] - end, - [], - [mongoose_bin:string_to_binary(S) || S <- string:tokens(ContactsS, ":")] - ), - - case validate_regexps(Users ++ Contacts) of - <<>> -> - Options = {Action, Subs, Asks, Users, Contacts}, - - case mnesia:table_info(roster, size) of - 0 -> - <<"Roster table is empty.\n">>; - NumRosteritems -> - Msg1 = <<"There are ", (integer_to_binary(NumRosteritems))/binary, - " roster items in total.\n">>, - Key = mnesia:dirty_first(roster), - Msg2 = rip(Key, Options, <<>>), - <> - end; - ErrorMsg -> - ErrorMsg - end. - -validate_regexps(ListOfRegexps) -> - lists:foldl( - fun(RegExp, MsgAcc) -> - case re:compile(RegExp) of - {ok, _} -> MsgAcc; - {error, Error} -> - NewErr = iolist_to_binary(io_lib:format("Wrong regexp ~p: ~p~n", - [RegExp, Error])), - <> - end - end, <<>>, ListOfRegexps). - --spec rip('$end_of_table' | any(), delete_action() | list_action(), binary()) -> binary(). -rip('$end_of_table', _Options, Acc) -> - Acc; -rip(Key, Options, Acc) -> - KeyNext = mnesia:dirty_next(roster, Key), - {Action, _, _, _, _} = Options, - Msg = case decide_rip(Key, Options) of - true -> - apply_action(Action, Key); - false -> - <<>> - end, - rip(KeyNext, Options, <>). - -apply_action(list, Key) -> - {User, Server, JID} = Key, - {RUser, RServer, _} = JID, - <<"Matches: ", User/binary, "@", Server/binary, " ", RUser/binary, "@", RServer/binary, "\n">>; -apply_action(delete, Key) -> - Msg = apply_action(list, Key), - mnesia:dirty_delete(roster, Key), - Msg. - -decide_rip(Key, {_Action, Subs, Asks, User, Contact}) -> - case catch mnesia:dirty_read(roster, Key) of - [RI] -> - lists:member(RI#roster.subscription, Subs) - andalso lists:member(RI#roster.ask, Asks) - andalso decide_rip_jid(RI#roster.us, User) - andalso decide_rip_jid(RI#roster.jid, Contact); - _ -> - false - end. - -%% Returns true if the server of the JID is included in the servers -decide_rip_jid({UName, UServer, _UResource}, MatchList) -> - decide_rip_jid({UName, UServer}, MatchList); -decide_rip_jid({UName, UServer}, MatchList) -> - lists:any( - fun(MatchString) -> - MJID = jid:from_binary(MatchString), - MName = MJID#jid.luser, - MServer = MJID#jid.lserver, - IsServer = is_regexp_match(UServer, MServer), - case {MName, UName} of - {<<>>, <<>>} -> IsServer; - {<<>>, _} -> false; - _ -> IsServer andalso is_regexp_match(UName, MName) - end - end, - MatchList). - -is_regexp_match(String, RegExp) -> - case catch re:run(String, RegExp) of - nomatch -> - false; - {match, List} -> - Size = length(binary_to_list(String)), - case lists:member({0, Size}, List) of - true -> - true; - false -> - false - end; - Error -> - ?LOG_ERROR(#{what => regexp_match_failed, - regex => RegExp, string => String, reason => Error}), - false - end. - -possible_subs_binary() -> - [<<"none">>, <<"from">>, <<"to">>, <<"both">>]. - diff --git a/src/admin_extra/service_admin_extra_sessions.erl b/src/admin_extra/service_admin_extra_sessions.erl deleted file mode 100644 index 90d6608102f..00000000000 --- a/src/admin_extra/service_admin_extra_sessions.erl +++ /dev/null @@ -1,229 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : service_admin_extra_sessions.erl -%%% Author : Badlop , Piotr Nosek -%%% Purpose : Contributed administrative functions and commands -%%% Created : 10 Aug 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra_sessions). --author('badlop@process-one.net'). - --export([ - commands/0, - num_resources/2, - resource_num/3, - kick_session/4, - status_num/2, status_num/1, - status_list/2, status_list/1, - connected_users_info/0, - connected_users_info/1, - set_presence/7, - user_sessions_info/2 - ]). - --ignore_xref([ - commands/0, num_resources/2, resource_num/3, kick_session/4, - status_num/2, status_num/1, status_list/2, status_list/1, - connected_users_info/0, connected_users_info/1, set_presence/7, - user_sessions_info/2 -]). - --include_lib("jid/include/jid.hrl"). --include("ejabberd_commands.hrl"). - --type status() :: mongoose_session_api:status(). - -%%% -%%% Register commands -%%% - --spec commands() -> [ejabberd_commands:cmd(), ...]. -commands() -> - SessionDisplay = {list, - {sessions, {tuple, - [{jid, string}, - {connection, string}, - {ip, string}, - {port, integer}, - {priority, integer}, - {node, string}, - {uptime, integer} - ]}}}, - - UserStatusDisplay = {list, - {userstatus, {tuple, - [{user, string}, - {host, string}, - {resource, string}, - {priority, integer}, - {status, string} - ]}}}, - - [ - #ejabberd_commands{name = num_resources, tags = [session], - desc = "Get the number of resources of a user", - module = ?MODULE, function = num_resources, - args = [{user, binary}, {host, binary}], - result = {resources, integer}}, - #ejabberd_commands{name = resource_num, tags = [session], - desc = "Resource string of a session number", - module = ?MODULE, function = resource_num, - args = [{user, binary}, {host, binary}, {num, integer}], - result = {res, restuple}}, - #ejabberd_commands{name = kick_session, tags = [session], - desc = "Kick a user session", - module = ?MODULE, function = kick_session, - args = [{user, binary}, {host, binary}, - {resource, binary}, {reason, binary}], - result = {res, integer}}, - #ejabberd_commands{name = status_num_host, tags = [session, stats], - desc = "Number of logged users with this status in host", - module = ?MODULE, function = status_num, - args = [{host, binary}, {status, binary}], - result = {users, integer}}, - #ejabberd_commands{name = status_num, tags = [session, stats], - desc = "Number of logged users with this status", - module = ?MODULE, function = status_num, - args = [{status, binary}], - result = {users, integer}}, - #ejabberd_commands{name = status_list_host, tags = [session], - desc = "List of users logged in host with their statuses", - module = ?MODULE, function = status_list, - args = [{host, binary}, {status, binary}], - result = {users, UserStatusDisplay}}, - #ejabberd_commands{name = status_list, tags = [session], - desc = "List of logged users with this status", - module = ?MODULE, function = status_list, - args = [{status, binary}], - result = {users, UserStatusDisplay}}, - #ejabberd_commands{name = connected_users_info, - tags = [session], - desc = "List all established sessions and their information", - module = ?MODULE, function = connected_users_info, - args = [], - result = {connected_users_info, SessionDisplay}}, - #ejabberd_commands{name = connected_users_vhost, - tags = [session], - desc = "Get the list of established sessions in a vhost", - module = ?MODULE, function = connected_users_info, - args = [{host, binary}], - result = {connected_users_vhost, SessionDisplay}}, - #ejabberd_commands{name = user_sessions_info, - tags = [session], - desc = "Get information about all sessions of a user", - module = ?MODULE, function = user_sessions_info, - args = [{user, binary}, {host, binary}], - result = {user_sessions_info, SessionDisplay}}, - - #ejabberd_commands{name = set_presence, - tags = [session], - desc = "Set presence of a session", - module = ?MODULE, function = set_presence, - args = [{user, binary}, {host, binary}, - {resource, binary}, {type, binary}, - {show, binary}, {status, binary}, - {priority, binary}], - result = {res, rescode}} - ]. - -%%% -%%% Sessions -%%% - --spec num_resources(jid:user(), jid:server()) -> non_neg_integer(). -num_resources(User, Host) -> - JID = jid:make_bare(User, Host), - {ok, Value} = mongoose_session_api:num_resources(JID), - Value. - --spec resource_num(jid:user(), jid:server(), integer()) -> mongoose_session_api:res_number_result(). -resource_num(User, Host, Num) -> - JID = jid:make_bare(User, Host), - mongoose_session_api:get_user_resource(JID, Num). - --spec kick_session(jid:user(), jid:server(), jid:resource(), null | binary()) -> - {ok, mongoose_session_api:kick_user_result()} | {no_session | user_not_found, binary()}. -kick_session(User, Server, Resource, ReasonText) -> - mongoose_session_api:kick_session(jid:make(User, Server, Resource), ReasonText). - --spec status_num(jid:server(), status()) -> non_neg_integer(). -status_num(Host, Status) -> - {ok, Value} = mongoose_session_api:num_status_users(Host, Status), - Value. - --spec status_num(status()) -> non_neg_integer(). -status_num(Status) -> - {ok, Value} = mongoose_session_api:num_status_users(Status), - Value. - --spec status_list(jid:server(), status()) -> [tuple()]. -status_list(Host, Status) -> - {ok, Sessions} = mongoose_session_api:list_status_users(Host, Status), - format_status_users(Sessions). - --spec status_list(binary()) -> [tuple()]. -status_list(Status) -> - {ok, Sessions} = mongoose_session_api:list_status_users(Status), - format_status_users(Sessions). - --spec connected_users_info() -> [tuple()]. -connected_users_info() -> - {ok, Sessions} = mongoose_session_api:list_sessions(), - format_sessions(Sessions). - --spec connected_users_info(jid:server()) -> [tuple()]. -connected_users_info(Host) -> - {ok, Sessions} = mongoose_session_api:list_sessions(Host), - format_sessions(Sessions). - --spec set_presence(jid:user(), jid:server(), jid:resource(), - Type :: binary(), Show :: binary(), Status :: binary(), - Prio :: binary()) -> ok. -set_presence(User, Host, Resource, Type, Show, Status, Priority) -> - JID = jid:make(User, Host, Resource), - mongoose_session_api:set_presence(JID, Type, Show, Status, Priority), - ok. - --spec user_sessions_info(jid:user(), jid:server()) -> [tuple()]. -user_sessions_info(User, Host) -> - JID = jid:make_bare(User, Host), - {ok, Sessions} = mongoose_session_api:list_user_sessions(JID), - format_sessions(Sessions). - -% Internal - -format_sessions(Sessions) -> - lists:map(fun(S) -> format_session(S) end, Sessions). - -format_session({USR, Conn, Address, Prio, Node, Uptime}) -> - {IP, Port} = from_address(Address), - {jid:to_binary(USR), atom_to_list(Conn), IP, Port, Prio, atom_to_list(Node), Uptime}. - -from_address(undefined) -> - {undefined, undefined}; -from_address({IP, Port}) -> - {inet:ntoa(IP), Port}. - -format_status_users(Sessions) -> - lists:map(fun(S) -> format_status_user(S) end, Sessions). - -format_status_user({#jid{luser = User, lserver = Server, lresource = Resource}, Prio, Status}) -> - {User, Server, Resource, Prio, Status}. diff --git a/src/admin_extra/service_admin_extra_stanza.erl b/src/admin_extra/service_admin_extra_stanza.erl deleted file mode 100644 index 2d4702ffd25..00000000000 --- a/src/admin_extra/service_admin_extra_stanza.erl +++ /dev/null @@ -1,187 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : service_admin_extra_stanza.erl -%%% Author : Badlop , Piotr Nosek -%%% Purpose : Contributed administrative functions and commands -%%% Created : 10 Aug 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra_stanza). --author('badlop@process-one.net'). - --export([ - commands/0, - - send_message_headline/4, - send_message_chat/3, - send_stanza_c2s/4 - ]). - --ignore_xref([commands/0, send_message_chat/3, send_message_headline/4, send_stanza_c2s/4]). - --include("mongoose.hrl"). --include("ejabberd_commands.hrl"). --include("jlib.hrl"). --include_lib("exml/include/exml.hrl"). - -%%% -%%% Register commands -%%% - --spec commands() -> [ejabberd_commands:cmd(), ...]. -commands() -> - [ - #ejabberd_commands{name = send_message_chat, tags = [stanza], - desc = "Send a chat message to a local or remote bare of full JID", - module = ?MODULE, function = send_message_chat, - args = [{from, binary}, {to, binary}, {body, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = send_message_headline, tags = [stanza], - desc = "Send a headline message to a local or remote bare of full JID", - module = ?MODULE, function = send_message_headline, - args = [{from, binary}, {to, binary}, - {subject, binary}, {body, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = send_stanza_c2s, tags = [stanza], - desc = "Send a stanza as if sent from a c2s session", - module = ?MODULE, function = send_stanza_c2s, - args = [{user, binary}, {host, binary}, - {resource, binary}, {stanza, binary}], - result = {res, restuple}} - ]. - -%%% -%%% Stanza -%%% - -%% @doc Send a chat message to a Jabber account. --spec send_message_chat(From :: binary(), To :: binary(), - Body :: binary() | string()) -> {Res, string()} when - Res :: bad_jid | ok. -send_message_chat(From, To, Body) -> - Packet = build_packet(message_chat, [Body]), - send_packet_all_resources(From, To, Packet). - - -%% @doc Send a headline message to a Jabber account. --spec send_message_headline(From :: binary(), To :: binary(), - Subject:: binary() | string(), - Body :: binary() | string()) -> {Res, string()} when - Res :: ok | bad_jid. -send_message_headline(From, To, Subject, Body) -> - Packet = build_packet(message_headline, [Subject, Body]), - send_packet_all_resources(From, To, Packet). - - -%% @doc Send a packet to a Jabber account. -%% If a resource was specified in the JID, the packet is sent only to that -%% specific resource. -%% If no resource was specified in the JID, and the user is remote or local but -%% offline, the packet is sent to the bare JID. -%% If the user is local and is online in several resources, the packet is sent -%% to all its resources. --spec send_packet_all_resources( - FromJIDStr :: binary() | jid:jid(), - ToJIDString :: binary() | jid:jid(), - exml:element()) -> - {Res, string()} when - Res :: bad_jid | ok. -send_packet_all_resources(FromJIDString, ToJIDString, Packet) when is_binary(FromJIDString) -> - case jid:from_binary(FromJIDString) of - error -> - {bad_jid, "Sender JID is invalid"}; - FromJID -> - send_packet_all_resources(FromJID, ToJIDString, Packet), - {ok, ""} - end; -send_packet_all_resources(FromJID, ToJIDString, Packet) when is_binary(ToJIDString) -> - case jid:from_binary(ToJIDString) of - error -> - {bad_jid, "Receiver JID is invalid"}; - ToJID -> - send_packet_all_resources(FromJID, ToJID, Packet), - {ok, ""} - end; -send_packet_all_resources(#jid{} = FromJID, #jid{} = ToJID, Packet) -> - case ToJID#jid.lresource of - <<>> -> - send_packet_all_resources_2(FromJID, ToJID, Packet), - {ok, ""}; - _Res -> - route_packet(FromJID, ToJID, Packet), - {ok, ""} - end. - - --spec send_packet_all_resources_2(FromJID :: jid:jid(), - ToJID :: jid:jid(), - exml:element()) -> ok. -send_packet_all_resources_2(FromJID, ToJID, Packet) -> - case ejabberd_sm:get_user_resources(ToJID) of - [] -> - route_packet(FromJID, ToJID, Packet); - ToResources -> - lists:foreach( - fun(ToResource) -> - route_packet(FromJID, jid:replace_resource(ToJID, ToResource), Packet) - end, - ToResources) - end. - - --spec route_packet(jid:jid(), jid:jid(), exml:element()) -> mongoose_acc:t(). -route_packet(FromJID, ToJID, Packet) -> - ejabberd_router:route(FromJID, ToJID, Packet). - - --spec build_packet('message_chat' | 'message_headline', - SubjectBody :: [binary() | string(), ...]) -> exml:element(). -build_packet(message_chat, [Body]) -> - #xmlel{ name = <<"message">>, - attrs = [{<<"type">>, <<"chat">>}, {<<"id">>, mongoose_bin:gen_from_crypto()}], - children = [#xmlel{ name = <<"body">>, children = [#xmlcdata{content = Body}]}] - }; -build_packet(message_headline, [Subject, Body]) -> - #xmlel{ name = <<"message">>, - attrs = [{<<"type">>, <<"headline">>}, {<<"id">>, mongoose_bin:gen_from_crypto()}], - children = [#xmlel{ name = <<"subject">>, children = [#xmlcdata{content = Subject}]}, - #xmlel{ name = <<"body">>, children = [#xmlcdata{content = Body}]} - ] - }. - - --spec send_stanza_c2s(jid:user(), jid:server(), jid:resource(), - Stanza :: binary()) -> {Res, string()} when - Res :: user_does_not_exist | bad_stanza | ok. -send_stanza_c2s(Username, Host, Resource, Stanza) -> - C2sPid = ejabberd_sm:get_session_pid(jid:make(Username, Host, Resource)), - case C2sPid of - none -> - {user_does_not_exist, - io_lib:format("User ~s@~s/~s does not exist", [Username, Host, Resource])}; - _ -> - case exml:parse(Stanza) of - {ok, XmlEl} -> - C2sPid ! XmlEl, - {ok, "Stanza has been sent"}; - {error, _} -> - {bad_stanza, "Stanza is incorrect"} - end - end. diff --git a/src/admin_extra/service_admin_extra_stats.erl b/src/admin_extra/service_admin_extra_stats.erl deleted file mode 100644 index be4bb0c397a..00000000000 --- a/src/admin_extra/service_admin_extra_stats.erl +++ /dev/null @@ -1,56 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : service_admin_extra_stats.erl -%%% Author : Badlop , Piotr Nosek -%%% Purpose : Contributed administrative functions and commands -%%% Created : 10 Aug 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra_stats). --author('badlop@process-one.net'). - - --export([commands/0]). - --ignore_xref([commands/0, stats/1, stats/2]). - --include("mongoose.hrl"). --include("ejabberd_commands.hrl"). - -%%% -%%% Register commands -%%% - --spec commands() -> [ejabberd_commands:cmd(), ...]. -commands() -> - [ - #ejabberd_commands{name = stats, tags = [stats], - desc = "Get statistical value:" - " registeredusers onlineusers onlineusersnode uptimeseconds", - module = stats_api, function = stats_mongooseimctl, - args = [{name, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = stats_host, tags = [stats], - desc = "Get statistical value for this host:" - " registeredusers onlineusers", - module = stats_api, function = stats_mongooseimctl, - args = [{name, binary}, {host, binary}], - result = {res, restuple}} - ]. diff --git a/src/admin_extra/service_admin_extra_upload.erl b/src/admin_extra/service_admin_extra_upload.erl deleted file mode 100644 index 34c97b2404c..00000000000 --- a/src/admin_extra/service_admin_extra_upload.erl +++ /dev/null @@ -1,23 +0,0 @@ --module(service_admin_extra_upload). - --include("ejabberd_commands.hrl"). - --export([commands/0]). - --ignore_xref([commands/0, get_urls/5]). - --spec commands() -> [ejabberd_commands:cmd()]. -commands() -> [ - #ejabberd_commands{ - name = http_upload, - tags = [http, upload], - desc = "Generate upload/download URLs for the file", - longdesc = "Returns upload/download URLs generated by mod_http_upload. Example:\n" - " mongooseimctl http_upload localhost tmp.txt 5 '' 60", - module = mod_http_upload_api, - function = get_urls_mongooseimctl, - args = [{domain, binary}, {file_name, binary}, {size, integer}, - {content_type, binary}, {timeout, integer}], - result = {res, restuple} - } -]. diff --git a/src/admin_extra/service_admin_extra_vcard.erl b/src/admin_extra/service_admin_extra_vcard.erl deleted file mode 100644 index 4901d127a70..00000000000 --- a/src/admin_extra/service_admin_extra_vcard.erl +++ /dev/null @@ -1,265 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File : service_admin_extra_vcard.erl -%%% Author : Badlop , Piotr Nosek -%%% Purpose : Contributed administrative functions and commands -%%% Created : 10 Aug 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2008 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(service_admin_extra_vcard). --author('badlop@process-one.net'). - --export([ - commands/0, - - get_vcard/3, - get_vcard/4, - set_vcard/4, - set_vcard/5 -]). - --ignore_xref([commands/0, get_vcard/3, get_vcard/4, set_vcard/5, set_vcard/4, set_vcard/5]). - --include("mongoose.hrl"). --include("ejabberd_commands.hrl"). --include("mod_roster.hrl"). --include("jlib.hrl"). --include_lib("exml/include/exml.hrl"). - -%%% -%%% Register commands -%%% - --spec commands() -> [ejabberd_commands:cmd(), ...]. -commands() -> - Vcard1FieldsString = "Some vcard field names in get/set_vcard are:\n" - " FN - Full Name\n" - " NICKNAME - Nickname\n" - " BDAY - Birthday\n" - " TITLE - Work: Position\n" - " ROLE - Work: Role", - - Vcard2FieldsString = "Some vcard field names and subnames in get/set_vcard2 are:\n" - " N FAMILY - Family name\n" - " N GIVEN - Given name\n" - " N MIDDLE - Middle name\n" - " ADR CTRY - Address: Country\n" - " ADR LOCALITY - Address: City\n" - " EMAIL USERID - E-Mail Address\n" - " ORG ORGNAME - Work: Company\n" - " ORG ORGUNIT - Work: Department", - - VcardXEP = "For a full list of vCard fields check XEP-0054: vcard-temp at " - "http://www.xmpp.org/extensions/xep-0054.html", - - [ - #ejabberd_commands{name = get_vcard, tags = [vcard], - desc = "Get content from a vCard field", - longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" - ++ VcardXEP, - module = ?MODULE, function = get_vcard, - args = [{user, binary}, {host, binary}, {name, binary}], - result = {content, binary}}, - #ejabberd_commands{name = get_vcard2, tags = [vcard], - desc = "Get content from a vCard field", - longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" - ++ VcardXEP, - module = ?MODULE, function = get_vcard, - args = [{user, binary}, {host, binary}, - {name, binary}, {subname, binary}], - result = {content, binary}}, - #ejabberd_commands{name = get_vcard2_multi, tags = [vcard], - desc = "Get multiple contents from a vCard field", - longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" - ++ VcardXEP, - module = ?MODULE, function = get_vcard, - args = [{user, binary}, {host, binary}, - {name, binary}, {subname, binary}], - result = {contents, {list, {value, binary}}}}, - #ejabberd_commands{name = set_vcard, tags = [vcard], - desc = "Set content in a vCard field", - longdesc = Vcard1FieldsString ++ "\n" ++ Vcard2FieldsString ++ "\n\n" - ++ VcardXEP, - module = ?MODULE, function = set_vcard, - args = [{user, binary}, {host, binary}, - {name, binary}, {content, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = set_vcard2, tags = [vcard], - desc = "Set content in a vCard subfield", - longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" - ++ VcardXEP, - module = ?MODULE, function = set_vcard, - args = [{user, binary}, {host, binary}, {name, binary}, - {subname, binary}, {content, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = set_vcard2_multi, tags = [vcard], - desc = "Set multiple contents in a vCard subfield", - longdesc = Vcard2FieldsString ++ "\n\n" ++ Vcard1FieldsString ++ "\n" - ++ VcardXEP, - module = ?MODULE, function = set_vcard, - args = [{user, binary}, {host, binary}, {name, binary}, - {subname, binary}, {contents, {list, binary}}], - result = {res, restuple}} - ]. - -%%% -%%% Vcard -%%% --spec get_vcard(jid:user(), jid:server(), any()) - -> {error, string()} | [binary()]. -get_vcard(User, Host, Name) -> - JID = jid:make_bare(User, Host), - case ejabberd_auth:does_user_exist(JID) of - true -> - get_vcard_content(JID, [Name]); - false -> - {error, io_lib:format("User ~s@~s does not exist", [User, Host])} - end. - --spec get_vcard(jid:user(), jid:server(), any(), any()) - -> {error, string()} | [binary()]. -get_vcard(User, Host, Name, Subname) -> - JID = jid:make_bare(User, Host), - case ejabberd_auth:does_user_exist(JID) of - true -> - get_vcard_content(JID, [Name, Subname]); - false -> - {error, io_lib:format("User ~s@~s does not exist", [User, Host])} - end. - --spec set_vcard(jid:user(), jid:server(), [binary()], - binary() | [binary()]) -> {ok, string()} | {user_does_not_exist, string()}. -set_vcard(User, Host, Name, SomeContent) -> - JID = jid:make_bare(User, Host), - case ejabberd_auth:does_user_exist(JID) of - true -> - set_vcard_content(JID, [Name], SomeContent); - false -> - {user_does_not_exist, io_lib:format("User ~s@~s does not exist", [User, Host])} - end. - --spec set_vcard(jid:user(), jid:server(), [binary()], [binary()], - binary() | [binary()]) -> {ok, string()} | {user_does_not_exist, string()}. -set_vcard(User, Host, Name, Subname, SomeContent) -> - JID = jid:make_bare(User, Host), - case ejabberd_auth:does_user_exist(JID) of - true -> - set_vcard_content(JID, [Name, Subname], SomeContent); - false -> - {user_does_not_exist, io_lib:format("User ~s@~s does not exist", [User, Host])} - end. - - -%% -%% Internal vcard - --spec get_vcard_content(jid:jid(), any()) -> - {error, string()} | list(binary()). -get_vcard_content(#jid{lserver = LServer} = NoResJID, Data) -> - JID = jid:replace_resource(NoResJID, atom_to_binary(?MODULE)), - IQ = #iq{type = get, xmlns = ?NS_VCARD, sub_el = []}, - {ok, HostType} = mongoose_domain_api:get_domain_host_type(LServer), - Acc = mongoose_acc:new(#{ location => ?LOCATION, - from_jid => JID, - to_jid => JID, - lserver => JID#jid.lserver, - host_type => HostType, - element => jlib:iq_to_xml(IQ) }), - Extra = #{}, - {_, IQr} = mod_vcard:process_sm_iq(Acc, JID, JID, IQ, Extra), - case IQr#iq.sub_el of - [#xmlel{} = A1] -> - case get_vcard(Data, A1) of - [] -> - {error, "Value not found in vcard"}; - ElemList -> - [exml_query:cdata(Elem) || Elem <- ElemList] - end; - _ -> - {error, "Vcard not found"} - end. - - --spec get_vcard([binary()], exml:element()) -> [exml:element()]. -get_vcard([Data1, Data2], A1) -> - A2List = exml_query:subelements(A1, Data1), - lists:flatten([get_vcard([Data2], A2) || A2 <- A2List]); -get_vcard([Data], A1) -> - exml_query:subelements(A1, Data). - --spec set_vcard_content(jid:jid(), Data :: [binary()], - ContentList :: binary() | [binary()]) -> {ok, string()}. -set_vcard_content(JID, D, SomeContent) when is_binary(SomeContent) -> - set_vcard_content(JID, D, [SomeContent]); -set_vcard_content(JID, Data, ContentList) -> - IQ = #iq{type = get, xmlns = ?NS_VCARD, sub_el = []}, - {ok, HostType} = mongoose_domain_api:get_domain_host_type(JID#jid.lserver), - Acc = mongoose_acc:new(#{ location => ?LOCATION, - from_jid => JID, - to_jid => JID, - lserver => JID#jid.lserver, - host_type => HostType, - element => jlib:iq_to_xml(IQ) }), - - Extra = #{}, - {Acc1, IQr} = mod_vcard:process_sm_iq(Acc, JID, JID, IQ, Extra), - - %% Get old vcard - A4 = case IQr#iq.sub_el of - [A1] -> - {_, _, _, A2} = A1, - update_vcard_els(Data, ContentList, A2); - _ -> - update_vcard_els(Data, ContentList, []) - end, - - %% Build new vcard - SubEl = #xmlel{name = <<"vCard">>, attrs = [{<<"xmlns">>, <<"vcard-temp">>}], children = A4}, - IQ2 = #iq{type = set, sub_el = SubEl}, - Extra = #{}, - mod_vcard:process_sm_iq(Acc1, JID, JID, IQ2, Extra), - {ok, ""}. - --spec update_vcard_els(Data :: [binary(), ...], - ContentList :: [binary() | string()], - Els :: [jlib:xmlcdata() | exml:element()] - ) -> [jlib:xmlcdata() | exml:element()]. -update_vcard_els(Data, ContentList, Els1) -> - Els2 = lists:keysort(2, Els1), - [Data1 | Data2] = Data, - NewEls = case Data2 of - [] -> - [#xmlel{ name = Data1, children = [#xmlcdata{content = Content}] } - || Content <- ContentList]; - [D2] -> - OldEl = case lists:keysearch(Data1, 2, Els2) of - {value, A} -> A; - false -> #xmlel{ name = Data1 } - end, - ContentOld1 = OldEl#xmlel.children, - Content2 = [#xmlel{ name = D2, children = [#xmlcdata{content=Content}]} - || Content <- ContentList], - ContentOld2 = [A || {_, X, _, _} = A <- ContentOld1, X/=D2], - ContentOld3 = lists:keysort(2, ContentOld2), - ContentNew = lists:keymerge(2, Content2, ContentOld3), - [#xmlel{ name = Data1, children = ContentNew}] - end, - Els3 = lists:keydelete(Data1, 2, Els2), - lists:keymerge(2, NewEls, Els3). diff --git a/src/config/mongoose_config_spec.erl b/src/config/mongoose_config_spec.erl index a2b5c5011d0..53279d9a448 100644 --- a/src/config/mongoose_config_spec.erl +++ b/src/config/mongoose_config_spec.erl @@ -189,9 +189,6 @@ general() -> <<"route_subdomains">> => #option{type = atom, validate = {enum, [s2s]}, wrap = host_config}, - <<"mongooseimctl_access_commands">> => #section{ - items = #{default => ctl_access_rule()}, - wrap = global_config}, <<"routing_modules">> => #list{items = #option{type = atom, validate = module}, process = fun xmpp_router:expand_routing_modules/1, @@ -220,22 +217,10 @@ general_defaults() -> <<"component_backend">> => mnesia, <<"s2s_backend">> => mnesia, <<"rdbms_server_type">> => generic, - <<"mongooseimctl_access_commands">> => #{}, <<"routing_modules">> => mongoose_router:default_routing_modules(), <<"replaced_wait_timeout">> => 2000, <<"hide_service_name">> => false}. -ctl_access_rule() -> - #section{ - items = #{<<"commands">> => #list{items = #option{type = atom, - validate = non_empty}}, - <<"argument_restrictions">> => - #section{items = #{default => #option{type = string}}} - }, - defaults = #{<<"commands">> => all, - <<"argument_restrictions">> => #{}} - }. - %% path: general.domain_certfile domain_cert() -> #section{ @@ -733,8 +718,7 @@ services() -> }. configurable_services() -> - [service_admin_extra, - service_mongoose_system_metrics, + [service_mongoose_system_metrics, service_domain_db]. %% path: (host_config[].)modules diff --git a/src/domain/service_admin_extra_domain.erl b/src/domain/service_admin_extra_domain.erl deleted file mode 100644 index 97640d2085d..00000000000 --- a/src/domain/service_admin_extra_domain.erl +++ /dev/null @@ -1,71 +0,0 @@ -%% Domain actions for mongooseimctl --module(service_admin_extra_domain). --export([commands/0, - insert_domain/2, - delete_domain/2, - enable_domain/1, - disable_domain/1]). - --ignore_xref([ - commands/0, insert_domain/2, delete_domain/2, enable_domain/1, disable_domain/1 -]). - --include("ejabberd_commands.hrl"). - --type cmd_result() :: {ok, string()} | {error, string()}. - --spec commands() -> [ejabberd_commands:cmd()]. -commands() -> - [ - #ejabberd_commands{name = insert_domain, tags = [domain], - desc = "Insert a domain", - module = ?MODULE, function = insert_domain, - args = [{domain, binary}, {host_type, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = delete_domain, tags = [domain], - desc = "Delete a domain", - module = ?MODULE, function = delete_domain, - args = [{domain, binary}, {host_type, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = enable_domain, tags = [domain], - desc = "Enable a domain", - module = ?MODULE, function = enable_domain, - args = [{domain, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = disable_domain, tags = [domain], - desc = "Disable a domain", - module = ?MODULE, function = disable_domain, - args = [{domain, binary}], - result = {res, restuple}} - ]. - --spec insert_domain(binary(), binary()) -> cmd_result(). -insert_domain(Domain, HostType) -> - SDomain = jid:nameprep(Domain), - call_api(fun() -> mongoose_domain_api:insert_domain(SDomain, HostType) end, "Added"). - --spec delete_domain(binary(), binary()) -> cmd_result(). -delete_domain(Domain, HostType) -> - SDomain = jid:nameprep(Domain), - call_api(fun() -> mongoose_domain_api:delete_domain(SDomain, HostType) end, "Deleted"). - --spec enable_domain(binary()) -> cmd_result(). -enable_domain(Domain) -> - SDomain = jid:nameprep(Domain), - call_api(fun() -> mongoose_domain_api:enable_domain(SDomain) end, "Enabled"). - --spec disable_domain(binary()) -> cmd_result(). -disable_domain(Domain) -> - SDomain = jid:nameprep(Domain), - call_api(fun() -> mongoose_domain_api:disable_domain(SDomain) end, "Disabled"). - -call_api(F, OkMsg) -> - case service_domain_db:enabled() of - true -> - case F() of - {ok, _} -> {ok, OkMsg}; - {_Reason, ErrMsg} -> {error, ErrMsg} - end; - false -> - {error, <<"Dynamic domains service is disabled">>} - end. diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index 8e4f010d3e0..d52e8c07478 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -26,149 +26,20 @@ -module(ejabberd_admin). -author('mickael.remond@process-one.net'). --export([start/0, stop/0, - %% Server +-export([%% Server status/0, %% Accounts - register/3, register/2, unregister/2, - registered_users/1, + register/3, unregister/2, import_users/1, %% Purge DB - delete_expired_messages/1, delete_old_messages/2, remove_from_cluster/1]). -ignore_xref([ - backup_mnesia/1, delete_expired_messages/1, delete_old_messages/2, - dump_mnesia/1, dump_table/2, - import_users/1, install_fallback_mnesia/1, - load_mnesia/1, mnesia_change_nodename/4, - register/2, register/3, registered_users/1, remove_from_cluster/1, - restore_mnesia/1, status/0, - stop/0, unregister/2]). + import_users/1, + register/3, remove_from_cluster/1, + status/0, unregister/2]). -include("mongoose.hrl"). --include("ejabberd_commands.hrl"). - -start() -> - ejabberd_commands:register_commands(commands()). - -stop() -> - ejabberd_commands:unregister_commands(commands()). - -%%% -%%% ejabberd commands -%%% - --spec commands() -> [ejabberd_commands:cmd()]. -commands() -> - [ - %% The commands status, stop and restart are implemented also in ejabberd_ctl - %% They are defined here so that other interfaces can use them too - #ejabberd_commands{name = status, tags = [server], - desc = "Get status of the ejabberd server", - module = ?MODULE, function = status, - args = [], result = {res, restuple}}, - #ejabberd_commands{name = restart, tags = [server], - desc = "Restart ejabberd gracefully", - module = init, function = restart, - args = [], result = {res, rescode}}, - #ejabberd_commands{name = get_loglevel, tags = [logs, server], - desc = "Get the current loglevel", - module = mongoose_server_api, function = get_loglevel_mongooseimctl, - args = [], - result = {res, restuple}}, - #ejabberd_commands{name = register, tags = [accounts], - desc = "Register a user", - module = ?MODULE, function = register, - args = [{host, binary}, {password, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = register_identified, tags = [accounts], - desc = "Register a user with a specific jid", - module = ?MODULE, function = register, - args = [{user, binary}, {host, binary}, {password, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = unregister, tags = [accounts], - desc = "Unregister a user", - module = ?MODULE, function = unregister, - args = [{user, binary}, {host, binary}], - result = {res, restuple}}, - #ejabberd_commands{name = registered_users, tags = [accounts], - desc = "List all registered users in HOST", - module = ?MODULE, function = registered_users, - args = [{host, binary}], - result = {users, {list, {user_jid, binary}}}}, - #ejabberd_commands{name = import_users, tags = [accounts], - desc = "Import users from CSV file", - module = ?MODULE, function = import_users, - args = [{file, string}], - result = {summary, {list, {res, {tuple, - [{reason, binary}, - {users, {list, {user, binary}}}]}}}}}, - #ejabberd_commands{name = delete_expired_messages, tags = [purge], - desc = "Delete expired offline messages from database", - module = ?MODULE, function = delete_expired_messages, - args = [{host, binary}], result = {res, restuple}}, - #ejabberd_commands{name = delete_old_messages, tags = [purge], - desc = "Delete offline messages older than DAYS", - module = ?MODULE, function = delete_old_messages, - args = [{host, binary}, {days, integer}], result = {res, restuple}}, - - #ejabberd_commands{name = set_master, tags = [mnesia], - desc = "Set master node of the clustered Mnesia tables", - longdesc = "If you provide as nodename \"self\", this " - "node will be set as its own master.", - module = mnesia_api, function = set_master, - args = [{nodename, atom}], result = {res, restuple}}, - #ejabberd_commands{name = mnesia_change_nodename, tags = [mnesia], - desc = "Change the erlang node name in a backup file", - module = mnesia_api, function = mnesia_change_nodename, - args = [{oldnodename, atom}, {newnodename, atom}, - {oldbackup, string}, {newbackup, string}], - result = {res, restuple}}, - #ejabberd_commands{name = backup, tags = [mnesia], - desc = "Store the database to backup file (only Mnesia)", - module = mnesia_api, function = backup_mnesia, - args = [{file, string}], result = {res, restuple}}, - #ejabberd_commands{name = restore, tags = [mnesia], - desc = "Restore the database from backup file (only Mnesia)", - module = mnesia_api, function = restore_mnesia, - args = [{file, string}], result = {res, restuple}}, - #ejabberd_commands{name = dump, tags = [mnesia], - desc = "Dump the database to text file (only Mnesia)", - module = mnesia_api, function = dump_mnesia, - args = [{file, string}], result = {res, restuple}}, - #ejabberd_commands{name = dump_table, tags = [mnesia], - desc = "Dump a table to text file (only Mnesia)", - module = mnesia_api, function = dump_table, - args = [{file, string}, {table, string}], result = {res, restuple}}, - #ejabberd_commands{name = load, tags = [mnesia], - desc = "Restore the database from text file (only Mnesia)", - module = mnesia_api, function = load_mnesia, - args = [{file, string}], result = {res, restuple}}, - #ejabberd_commands{name = install_fallback, tags = [mnesia], - desc = "Install the database from a fallback file (only Mnesia)", - module = mnesia_api, function = install_fallback_mnesia, - args = [{file, string}], result = {res, restuple}}, - - #ejabberd_commands{name = join_cluster, tags = [server], - desc = "Join the node to a cluster. Call it from the joining node. - Use `-f` or `--force` flag to avoid question prompt and force join the node", - module = mongoose_server_api, function = join_cluster, - args = [{node, string}], - result = {res, restuple}}, - #ejabberd_commands{name = leave_cluster, tags = [server], - desc = "Leave a cluster. Call it from the node that is going to leave. - Use `-f` or `--force` flag to avoid question prompt and force leave the node from cluster", - module = mongoose_server_api, function = leave_cluster, - args = [], - result = {res, restuple}}, - #ejabberd_commands{name = remove_from_cluster, tags = [server], - desc = "Remove dead node from the cluster. Call it from the member of the cluster. - Use `-f` or `--force` flag to avoid question prompt and force remove the node", - module = mongoose_server_api, function = remove_from_cluster, - args = [{node, string}], - result = {res, restuple}} - ]. %%% %%% Commands @@ -227,12 +98,6 @@ remove_rpc_alive_node(AliveNode) -> %%% Account management %%% --spec register(Host :: jid:server(), - Password :: binary()) -> mongoose_account_api:register_result(). -register(Host, Password) -> - {Result, _} = mongoose_account_api:register_generated_user(Host, Password), - Result. - -spec register(User :: jid:user(), Host :: jid:server(), Password :: binary()) -> mongoose_account_api:register_result(). @@ -244,30 +109,7 @@ register(User, Host, Password) -> unregister(User, Host) -> mongoose_account_api:unregister_user(User, Host). - --spec registered_users(Host :: jid:server()) -> mongoose_account_api:list_user_result(). -registered_users(Host) -> - mongoose_account_api:list_users(Host). - -spec import_users(file:filename()) -> [{binary(), jid:user() | binary()}]. import_users(Filename) -> {ok, Result} = mongoose_import_users:run(Filename), maps:to_list(Result). - -%%% -%%% Purge DB -%%% - --spec delete_expired_messages(binary()) -> {ok, iolist()} | {error, iolist()}. -delete_expired_messages(Domain) -> - case mod_offline_api:delete_expired_messages(jid:nameprep(Domain)) of - {ok, _} = Result -> Result; - {_, Message} -> {error, Message} - end. - --spec delete_old_messages(binary(), Days :: integer()) -> {ok, iolist()} | {error, iolist()}. -delete_old_messages(Domain, Days) -> - case mod_offline_api:delete_old_messages(jid:nameprep(Domain), Days) of - {ok, _} = Result -> Result; - {_, Message} -> {error, Message} - end. diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl index 95e6bf22f3e..78d84bb1d58 100644 --- a/src/ejabberd_app.erl +++ b/src/ejabberd_app.erl @@ -61,7 +61,6 @@ do_start() -> mongoose_internal_databases:init(), mongoose_graphql:init(), translate:start(), - ejabberd_commands:init(), mongoose_graphql_commands:start(), mongoose_router:start(), mongoose_logs:set_global_loglevel(mongoose_config:get_opt(loglevel)), @@ -78,7 +77,6 @@ do_start() -> mongoose_modules:start(), service_mongoose_system_metrics:verify_if_configured(), mongoose_listener:start(), - ejabberd_admin:start(), mongoose_metrics:init_mongooseim_metrics(), gen_hook:reload_hooks(), update_status_file(started), diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl deleted file mode 100644 index 360b1dda646..00000000000 --- a/src/ejabberd_commands.erl +++ /dev/null @@ -1,473 +0,0 @@ -%%%---------------------------------------------------------------------- -%%% File : ejabberd_commands.erl -%%% Author : Badlop -%%% Purpose : Management of ejabberd commands -%%% Created : 20 May 2008 by Badlop -%%% -%%% -%%% ejabberd, Copyright (C) 2002-2011 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 -%%% -%%%---------------------------------------------------------------------- - -%%% @headerfile "ejabberd_commands.hrl" - -%%% @doc Management of ejabberd commands. -%%% -%%% An ejabberd command is an abstract function identified by a name, -%%% with a defined number and type of calling arguments and type of -%%% result, that can be defined in any Erlang module and executed -%%% using any valid frontend. -%%% -%%% -%%% == Define a new ejabberd command == -%%% -%%% ejabberd commands can be defined and registered in -%%% any Erlang module. -%%% -%%% Some commands are procedures; and their purpose is to perform an -%%% action in the server, so the command result is only some result -%%% code or result tuple. Other commands are inspectors, and their -%%% purpose is to gather some information about the server and return -%%% a detailed response: it can be integer, string, atom, tuple, list -%%% or a mix of those ones. -%%% -%%% The arguments and result of an ejabberd command are strictly -%%% defined. The number and format of the arguments provided when -%%% calling an ejabberd command must match the definition of that -%%% command. The format of the result provided by an ejabberd command -%%% must be exactly its definition. For example, if a command is said -%%% to return an integer, it must always return an integer (except in -%%% case of a crash). -%%% -%%% If you are developing an Erlang module that will run inside -%%% ejabberd and you want to provide a new ejabberd command to -%%% administer some task related to your module, you only need to: -%%% implement a function, define the command, and register it. -%%% -%%% -%%% === Define a new ejabberd command === -%%% -%%% An ejabberd command is defined using the Erlang record -%%% 'ejabberd_commands'. This record has several elements that you -%%% must define. Note that 'tags', 'desc' and 'longdesc' are optional. -%%% -%%% For example let's define an ejabberd command 'pow' that gets the -%%% integers 'base' and 'exponent'. Its result will be an integer -%%% 'power': -%%% -%%%
#ejabberd_commands{name = pow, tags = [test],
-%%%                 desc = "Return the power of base for exponent",
-%%%                 longdesc = "This is an example command. The formula is:\n"
-%%%                 "  power = base ^ exponent",
-%%%                 module = ?MODULE, function = pow,
-%%%                 args = [{base, integer}, {exponent, integer}],
-%%%                 result = {power, integer}}
-%%% -%%% -%%% === Implement the function associated to the command === -%%% -%%% Now implement a function in your module that matches the arguments -%%% and result of the ejabberd command. -%%% -%%% For example the function calc_power gets two integers Base and -%%% Exponent. It calculates the power and rounds to an integer: -%%% -%%%
calc_power(Base, Exponent) ->
-%%%    PowFloat = math:pow(Base, Exponent),
-%%%    round(PowFloat).
-%%% -%%% Since this function will be called by ejabberd_commands, it must be exported. -%%% Add to your module: -%%%
-export([calc_power/2]).
-%%% -%%% Only some types of result formats are allowed. -%%% If the format is defined as 'rescode', then your function must return: -%%% ok | true | atom() -%%% where the atoms ok and true as considered positive answers, -%%% and any other response atom is considered negative. -%%% -%%% If the format is defined as 'restuple', then the command must return: -%%% {rescode(), string()} -%%% -%%% If the format is defined as '{list, something()}', then the command -%%% must return a list of something(). -%%% -%%% -%%% === Register the command === -%%% -%%% Define this function and put inside the #ejabberd_command you -%%% defined in the beginning: -%%% -%%%
commands() ->
-%%%    [
-%%%
-%%%    ].
-%%% -%%% You need to include this header file in order to use the record: -%%% -%%%
-include("ejabberd_commands.hrl").
-%%% -%%% When your module is initialized or started, register your commands: -%%% -%%%
ejabberd_commands:register_commands(commands()), 
-%%% -%%% And when your module is stopped, unregister your commands: -%%% -%%%
ejabberd_commands:unregister_commands(commands()), 
-%%% -%%% That's all! Now when your module is started, the command will be -%%% registered and any frontend can access it. For example: -%%% -%%%
$ mongooseimctl help pow
-%%%
-%%%   Command Name: pow
-%%%
-%%%   Arguments: base::integer
-%%%              exponent::integer
-%%%
-%%%   Returns: power::integer
-%%%
-%%%   Tags: test
-%%%
-%%%   Description: Return the power of base for exponent
-%%%
-%%% This is an example command. The formula is:
-%%%  power = base ^ exponent
-%%%
-%%% $ mongooseimctl pow 3 4
-%%% 81
-%%% 
-%%% -%%% -%%% == Execute an ejabberd command == -%%% -%%% ejabberd commands are mean to be executed using any valid -%%% frontend. An ejabberd commands is implemented in a regular Erlang -%%% function, so it is also possible to execute this function in any -%%% Erlang module, without dealing with the associated ejabberd -%%% commands. -%%% -%%% -%%% == Frontend to ejabberd commands == -%%% -%%% Currently there is one frontend to ejabberd commands: the shell -%%% script - mongooseimctl -%%% -%%% === mongooseimctl as a frontend to ejabberd commands === -%%% -%%% It is possible to use mongooseimctl to get documentation of any -%%% command. But mongooseimctl does not support all the argument types -%%% allowed in ejabberd commands, so there are some ejabberd commands -%%% that cannot be executed using mongooseimctl. -%%% -%%% Also note that the mongooseimctl shell administration script also -%%% manages mongooseimctl commands, which are unrelated to ejabberd -%%% commands and can only be executed using mongooseimctl. -%%% -%%% TODO: consider this feature: -%%% All commands are catched. If an error happens, return the restuple: -%%% {error, flattened error string} -%%% This means that ecomm call APIs ejabberd_ctl need to allows this. - - --module(ejabberd_commands). --author('badlop@process-one.net'). - --export([init/0, - list_commands/0, - get_command_format/1, - get_command_definition/1, - get_tags_commands/0, - register_commands/1, - unregister_commands/1, - execute_command/2, - execute_command/4 - ]). - --ignore_xref([execute_command/2]). - --include("ejabberd_commands.hrl"). --include("mongoose.hrl"). - -%% Allowed types for arguments are integer, string, tuple and list. --type atype() :: integer | string | atom | binary | {tuple, [aterm()]} | {list, aterm()}. - -%% A rtype is either an atom or a tuple with two elements. --type rtype() :: integer | string | atom | binary | {tuple, [rterm()]} - | {list, rterm()} | rescode | restuple. - -%% An argument term is a tuple with the term name and the term type. --type aterm() :: {Name::atom(), Type::atype()}. - -%% A result term is a tuple with the term name and the term type. --type rterm() :: {Name::atom(), Type::rtype()}. - --type cmd() :: #ejabberd_commands{ - name :: atom(), - tags :: [atom()], - desc :: string(), - longdesc :: string(), - module :: module(), - function :: atom(), - args :: [ejabberd_commands:aterm()], - result :: ejabberd_commands:rterm() - }. - --type auth() :: {User :: binary(), Server :: binary(), Password :: binary()} | noauth. - --type cmd_error() :: command_unknown | account_unprivileged - | invalid_account_data | no_auth_provided. --type access_commands() :: #{acl:rule_name() => command_rules()}. --type command_rules() :: #{commands := all | [atom()], - argument_restrictions := argument_restrictions()}. - -%% Currently only string arguments can have restrictions --type argument_restrictions() :: #{ArgName :: atom() => Value :: string()}. - --type list_cmd() :: {Name::atom(), Args::[aterm()], Desc::string()}. - --export_type([rterm/0, - aterm/0, - cmd/0, - auth/0, - access_commands/0, - list_cmd/0]). - -init() -> - case ets:info(ejabberd_commands) of - undefined -> - ets:new(ejabberd_commands, [named_table, set, public, - {keypos, #ejabberd_commands.name}]); - _ -> - ok - end. - -%% @doc Register ejabberd commands. If a command is already registered, a -%% warning is printed and the old command is preserved. --spec register_commands([cmd()]) -> ok. -register_commands(Commands) -> - lists:foreach( - fun(Command) -> - Inserted = ets:insert_new(ejabberd_commands, Command), - ?LOG_IF(warning, not Inserted, - #{what => register_command_duplicate, - text => <<"This command is already defined">>, - command => Command}) - end, - Commands). - -%% @doc Unregister ejabberd commands. --spec unregister_commands([cmd()]) -> ok. -unregister_commands(Commands) -> - lists:foreach( - fun(Command) -> - ets:delete_object(ejabberd_commands, Command) - end, - Commands). - -%% @doc Get a list of all the available commands, arguments and description. --spec list_commands() -> [list_cmd()]. -list_commands() -> - Commands = ets:match(ejabberd_commands, - #ejabberd_commands{name = '$1', - args = '$2', - desc = '$3', - _ = '_'}), - [{A, B, C} || [A, B, C] <- Commands]. - -%% @doc Get the format of arguments and result of a command. --spec get_command_format(Name::atom()) -> {Args::[aterm()], Result::rterm()} - | {error, command_unknown}. -get_command_format(Name) -> - Matched = ets:match(ejabberd_commands, - #ejabberd_commands{name = Name, - args = '$1', - result = '$2', - _ = '_'}), - case Matched of - [] -> - {error, command_unknown}; - [[Args, Result]] -> - {Args, Result} - end. - -%% @doc Get the definition record of a command. --spec get_command_definition(Name::atom()) -> cmd() | 'command_not_found'. -get_command_definition(Name) -> - case ets:lookup(ejabberd_commands, Name) of - [E] -> E; - [] -> command_not_found - end. - -%% @doc Execute a command. --spec execute_command(Name :: atom(), - Arguments :: list() - ) -> Result :: term() | {error, command_unknown}. -execute_command(Name, Arguments) -> - execute_command(#{}, noauth, Name, Arguments). - --spec execute_command(AccessCommands :: access_commands(), - Auth :: auth(), - Name :: atom(), - Arguments :: [term()] - ) -> Result :: term() | {error, cmd_error()}. -execute_command(AccessCommands, Auth, Name, Arguments) -> - case ets:lookup(ejabberd_commands, Name) of - [Command] -> - try check_access_commands(AccessCommands, Auth, Name, Command, Arguments) of - ok -> execute_command2(Command, Arguments) - catch - {error, Error} -> {error, Error} - end; - [] -> {error, command_unknown} - end. - -%% @private -execute_command2(Command, Arguments) -> - Module = Command#ejabberd_commands.module, - Function = Command#ejabberd_commands.function, - ?LOG_DEBUG(#{what => execute_command, - command_module => Module, - command_function => Function, - command_args => Arguments}), - apply(Module, Function, Arguments). - -%% @doc Get all the tags and associated commands. --spec get_tags_commands() -> [{Tag::string(), [CommandName::string()]}]. -get_tags_commands() -> - CommandTags = ets:match(ejabberd_commands, - #ejabberd_commands{ - name = '$1', - tags = '$2', - _ = '_'}), - Dict = lists:foldl( - fun([CommandNameAtom, CTags], D) -> - CommandName = atom_to_list(CommandNameAtom), - case CTags of - [] -> - orddict:append("untagged", CommandName, D); - _ -> - lists:foldl( - fun(TagAtom, DD) -> - Tag = atom_to_list(TagAtom), - orddict:append(Tag, CommandName, DD) - end, - D, - CTags) - end - end, - orddict:new(), - CommandTags), - orddict:to_list(Dict). - -%% ----------------------------- -%% Access verification -%% ----------------------------- - -%% @doc Check access is allowed to that command. -%% At least one AccessCommand must be satisfied. -%% May throw {error, account_unprivileged | invalid_account_data} --spec check_access_commands(AccessCommands :: access_commands(), - Auth :: auth(), - Method :: atom(), - Command :: tuple(), - Arguments :: [any()] - ) -> ok | none(). -check_access_commands(AccessCommands, _Auth, _Method, _Command, _Arguments) - when AccessCommands =:= #{} -> ok; -check_access_commands(AccessCommands, Auth, Method, Command, Arguments) -> - AccessCommandsAllowed = - maps:filter( - fun(Access, CommandSpec) -> - case check_access(Access, Auth) of - true -> - check_access_command(Command, CommandSpec, Method, Arguments); - false -> - false - end - end, - AccessCommands), - case AccessCommandsAllowed =:= #{} of - true -> throw({error, account_unprivileged}); - false -> ok - end. - -%% @private -%% May throw {error, invalid_account_data} --spec check_auth(auth()) -> {ok, jid:jid()} | no_return(). -check_auth({User, Server, Password}) -> - %% Check the account exists and password is valid - JID = jid:make_bare(User, Server), - AccountPass = ejabberd_auth:get_password_s(JID), - AccountPassMD5 = get_md5(AccountPass), - case Password of - AccountPass -> {ok, JID}; - AccountPassMD5 -> {ok, JID}; - _ -> throw({error, invalid_account_data}) - end. - --spec get_md5(iodata()) -> string(). -get_md5(AccountPass) -> - lists:flatten([io_lib:format("~.16B", [X]) - || X <- binary_to_list(crypto:hash(md5, AccountPass))]). - --spec check_access(Access :: acl:rule_name(), Auth :: auth()) -> boolean(). -check_access(all, _) -> - true; -check_access(_, noauth) -> - false; -check_access(Access, Auth) -> - {ok, JID} = check_auth(Auth), - %% Check this user has access permission - {_, LServer} = jid:to_lus(JID), - {ok, HostType} = mongoose_domain_api:get_domain_host_type(LServer), - case acl:match_rule(HostType, LServer, Access, JID) of - allow -> true; - deny -> false - end. - --spec check_access_command(cmd(), command_rules(), atom(), [any()]) -> boolean(). -check_access_command(Command, CommandRules, Method, Arguments) -> - #{commands := Commands, argument_restrictions := ArgumentRestrictions} = CommandRules, - case Commands == all orelse lists:member(Method, Commands) of - true -> check_access_arguments(Command, ArgumentRestrictions, Arguments); - false -> false - end. - --spec check_access_arguments(Command :: cmd(), - Restrictions :: [any()], - Args :: [any()]) -> boolean(). -check_access_arguments(Command, ArgumentRestrictions, Arguments) -> - ArgumentsTagged = tag_arguments(Command#ejabberd_commands.args, Arguments), - lists:all( - fun({ArgName, ArgValue}) -> - case ArgumentRestrictions of - %% If there is a restriction, check the value is acceptable - #{ArgName := ArgAllowedValue} -> ArgValue =:= ArgAllowedValue; - #{} -> true - end - end, ArgumentsTagged). - --spec tag_arguments(ArgsDefs :: [{atom(), integer() | string() | {_, _}}], - Args :: [any()] ) -> [{_, _}]. -tag_arguments(ArgsDefs, Args) -> - lists:zipwith( - fun({ArgName, _ArgType}, ArgValue) -> - {ArgName, ArgValue} - end, - ArgsDefs, - Args). diff --git a/src/ejabberd_ctl.erl b/src/ejabberd_ctl.erl index 4df6e3571b7..80ebf418f39 100644 --- a/src/ejabberd_ctl.erl +++ b/src/ejabberd_ctl.erl @@ -35,29 +35,19 @@ %%% Note: strings cannot have blankspaces %%% %%% Does not support commands that have arguments with ctypes: list, tuple -%%% -%%% TODO: Update the guide -%%% TODO: Mention this in the release notes -%%% Note: the commands with several words use now the underline: _ -%%% It is still possible to call the commands with dash: - -%%% but this is deprecated, and may be removed in a future version. -module(ejabberd_ctl). -author('alexey@process-one.net'). -export([start/0, - process/1, - process2/2]). + process/1]). --ignore_xref([process/1, process2/2, start/0]). +-ignore_xref([process/1, start/0]). -include("ejabberd_ctl.hrl"). --include("ejabberd_commands.hrl"). -include("mongoose_logger.hrl"). --type format() :: integer | string | atom | binary | {list, format()}. --type format_type() :: binary() | string() | char() | node(). -type cmd() :: {CallString :: string(), Args :: [string()], Desc :: string()}. -define(ASCII_SPACE_CHARACTER, $\s). @@ -130,12 +120,16 @@ process(["stop"]) -> process(["restart"]) -> init:restart(), ?STATUS_SUCCESS; -process(["mnesia"]) -> - ?PRINT("~p~n", [mnesia:system_info(all)]), - ?STATUS_SUCCESS; -process(["mnesia", "info"]) -> - mnesia:info(), - ?STATUS_SUCCESS; +process(["join_cluster", Arg]) when is_list(Arg) -> + join_cluster(Arg); +process(["join_cluster" | _]) -> + cluster_command_usage(); +process(["leave_cluster"]) -> + leave_cluster(); +process(["remove_from_cluster", Arg]) when is_list(Arg) -> + remove_from_cluster(Arg); +process(["remove_from_cluster" | _]) -> + cluster_command_usage(); process(["graphql", Arg]) when is_list(Arg) -> Doc = list_to_binary(Arg), Ep = mongoose_graphql:get_endpoint(admin), @@ -144,42 +138,13 @@ process(["graphql", Arg]) when is_list(Arg) -> process(["graphql" | _]) -> ?PRINT("This command requires one string type argument!\n", []), ?STATUS_ERROR; - -%% @doc The arguments --long and --dual are not documented because they are -%% automatically selected depending in the number of columns of the shell -process(["help" | Mode]) -> - {MaxC, ShCode} = get_shell_info(), - case Mode of - [] -> - print_usage_old(dual, MaxC, ShCode), - ?STATUS_USAGE; - ["--dual"] -> - print_usage_old(dual, MaxC, ShCode), - ?STATUS_USAGE; - ["--long"] -> - print_usage_old(long, MaxC, ShCode), - ?STATUS_USAGE; - ["--tags"] -> - print_usage_tags(MaxC, ShCode), - ?STATUS_SUCCESS; - ["--tags", Tag] -> - print_usage_tags(Tag, MaxC, ShCode), - ?STATUS_SUCCESS; - ["help"] -> - print_usage_help(MaxC, ShCode), - ?STATUS_SUCCESS; - [CmdString | _] -> - CmdStringU = re:replace(CmdString, "-", "_", [global, {return, list}]), - print_usage_commands(CmdStringU, MaxC, ShCode), - ?STATUS_SUCCESS - end; process(Args) -> case mongoose_graphql_commands:process(Args) of #{status := executed, result := Result} -> handle_graphql_result(Result); - #{status := error, reason := Reason} when Reason =:= no_args; - Reason =:= unknown_category -> - run_command(Args); % Fallback to the old commands + #{status := error, reason := no_args} = Ctx -> + print_usage(Ctx), + ?STATUS_USAGE; #{status := error} = Ctx -> ?PRINT(error_message(Ctx) ++ "\n\n", []), print_usage(Ctx), @@ -194,6 +159,8 @@ error_message(#{reason := unknown_command, command := Command}) -> io_lib:format("Unknown command '~s'", [Command]); error_message(#{reason := invalid_args}) -> "Could not parse the command arguments"; +error_message(#{reason := unknown_category}) -> + "Unknown category"; error_message(#{reason := {unknown_arg, ArgName}, command := Command}) -> io_lib:format("Unknown argument '~s' for command '~s'", [ArgName, Command]); error_message(#{reason := {invalid_arg_value, ArgName, ArgValue}, command := Command}) -> @@ -207,7 +174,10 @@ error_message(#{reason := {missing_args, MissingArgs}, command := Command}) -> print_usage(#{category := Category, command := Command, args_spec := ArgsSpec}) -> print_usage_command(Category, Command, ArgsSpec); print_usage(#{category := Category, commands := Commands}) -> - print_usage_category(Category, Commands). + print_usage_category(Category, Commands); +print_usage(_) -> + {MaxC, ShCode} = get_shell_info(), + print_usage(MaxC, ShCode). handle_graphql_result({ok, Result}) -> JSONResult = mongoose_graphql_response:term_to_pretty_json(Result), @@ -222,227 +192,9 @@ handle_graphql_result({error, Reason}) -> ?PRINT("~s\n", [JSONResult]), ?STATUS_ERROR. -run_command(Args) -> - AccessCommands = get_accesscommands(), - {String, Code} = process2(Args, AccessCommands), - case String of - [] -> ok; - _ -> - io:format("~s~n", [String]) - end, - Code. - --spec process2(Args :: [string()], AccessCommands :: ejabberd_commands:access_commands()) -> - {String::string(), Code::integer()}. -process2(["--auth", User, Server, Pass | Args], AccessCommands) -> - process2(Args, {list_to_binary(User), list_to_binary(Server), list_to_binary(Pass)}, - AccessCommands); -process2(Args, AccessCommands) -> - process2(Args, noauth, AccessCommands). - - -%% @private -process2(Args, Auth, AccessCommands) -> - case try_call_command(Args, Auth, AccessCommands) of - {String, wrong_command_arguments} when is_list(String) -> - io:format(lists:flatten(["\n" | String]++["\n"])), - [CommandString | _] = Args, - process(["help" | [CommandString]]), - {lists:flatten(String), ?STATUS_ERROR}; - {String, Code} when is_list(String) and is_integer(Code) -> - {lists:flatten(String), Code}; - String when is_list(String) -> - {lists:flatten(String), ?STATUS_SUCCESS}; - Other -> - {"Erroneous result: " ++ io_lib:format("~p", [Other]), ?STATUS_ERROR} - end. - - --spec get_accesscommands() -> ejabberd_commands:access_commands(). -get_accesscommands() -> - mongoose_config:get_opt(mongooseimctl_access_commands). - - -%%----------------------------- -%% Command calling -%%----------------------------- --spec try_call_command(Args :: [string()], - Auth :: ejabberd_commands:auth(), - AccessCommands :: ejabberd_commands:access_commands() - ) -> string() | integer() | {string(), integer()} | {string(), wrong_command_arguments}. -try_call_command([], _, _) -> - print_usage(), - {"", ?STATUS_USAGE}; -try_call_command(Args, Auth, AccessCommands) -> - try call_command(Args, Auth, AccessCommands) of - {error, command_unknown} -> - {io_lib:format("Error: command ~p not known.", [hd(Args)]), ?STATUS_ERROR}; - Res -> - Res - catch - A:Why:Stack -> - {io_lib:format("Problem '~p ~p' occurred executing the command.~nStacktrace: ~p", [A, Why, Stack]), ?STATUS_ERROR} - end. - - --spec call_command(Args :: [string()], - Auth :: ejabberd_commands:auth(), - AccessCommands :: ejabberd_commands:access_commands() - ) -> string() | integer() | {string(), integer()} - | {string(), wrong_command_arguments} | {error, command_unknown}. -call_command([CmdString | Args], Auth, AccessCommands) -> - CmdStringU = re:replace(CmdString, "-", "_", [global, {return, list}]), - Command = list_to_atom(CmdStringU), - case ejabberd_commands:get_command_format(Command) of - {error, command_unknown} -> - {error, command_unknown}; - {ArgsFormat, ResultFormat} -> - case (catch format_args(Args, ArgsFormat)) of - ArgsFormatted when is_list(ArgsFormatted) -> - Result = ejabberd_commands:execute_command(AccessCommands, Auth, Command, - ArgsFormatted), - format_result(Result, ResultFormat); - {'EXIT', {function_clause, [{lists, zip, [A1, A2], _FileInfo} | _]}} -> - {NumCompa, TextCompa} = - case {length(A1), length(A2)} of - {L1, L2} when L1 < L2 -> {L2-L1, "less argument"}; - {L1, L2} when L1 > L2 -> {L1-L2, "more argument"} - end, - {io_lib:format("Error: the command ~p requires ~p ~s.", - [CmdString, NumCompa, TextCompa]), - wrong_command_arguments} - end - end. - - %%----------------------------- %% Format arguments %%----------------------------- - -%% @private --spec format_args(Args :: [any()], - ArgsFormat :: [format()]) -> [any()]. -format_args(Args, ArgsFormat) -> - lists:foldl( - fun({{_ArgName, ArgFormat}, Arg}, Res) -> - Formatted = format_arg(Arg, ArgFormat), - Res ++ [Formatted] - end, - [], - lists:zip(ArgsFormat, Args)). - - -%% @private --spec format_arg(string(), format()) -> format_type(). -format_arg(Arg, integer) -> - format_arg2(Arg, "~d"); -format_arg("", string) -> - ""; -format_arg(Arg, string) -> - NumChars = integer_to_list(string:len(Arg)), - Parse = "~" ++ NumChars ++ "c", - format_arg2(Arg, Parse); -format_arg(Arg, binary) -> - list_to_binary(format_arg(Arg, string)); -format_arg(Arg, {list, Type}) -> - [format_arg(Token, Type) || Token <- string:tokens(Arg, ";")]; -format_arg("self", atom) -> - node(); -format_arg(Arg, atom) -> - list_to_atom(Arg). - -%% @private --spec format_arg2(Arg :: string(), - Parse :: nonempty_string() - ) -> [[any()] | char()] | char(). -format_arg2(Arg, Parse)-> - {ok, [Arg2], _RemainingArguments} = io_lib:fread(Parse, Arg), - Arg2. - -%%----------------------------- -%% Format result -%%----------------------------- - -format_error(Error) -> - try - io_lib:format("\"~ts\"", [Error]) - catch _ -> - io_lib:format("~p", [Error]) - end. - --spec format_result(In :: tuple() | atom() | integer() | string() | binary(), - {_, 'atom'|'integer'|'string'|'binary'} - ) -> string() | {string(), _}. -format_result({Atom, Error}, _) when is_atom(Atom), Atom =/= ok -> - {io_lib:format("Error: ~ts", [format_error(Error)]), make_status(error)}; -format_result(Atom, {_Name, atom}) -> - io_lib:format("~p", [Atom]); -format_result(Int, {_Name, integer}) -> - io_lib:format("~p", [Int]); -format_result(String, {_Name, string}) -> - io_lib:format("~s", [String]); -format_result(Binary, {_Name, binary}) -> - io_lib:format("~s", [Binary]); -format_result(Code, {_Name, rescode}) -> - {"", make_status(Code)}; -format_result({Code, Text}, {_Name, restuple}) -> - {io_lib:format("~s", [Text]), make_status(Code)}; -%% The result is a list of something: [something()] -format_result([], {_Name, {list, _ElementsDef}}) -> - ""; -format_result([FirstElement | Elements], {_Name, {list, ElementsDef}}) -> - %% Start formatting the first element - [format_result(FirstElement, ElementsDef) | - %% If there are more elements, put always first a newline character - lists:map( - fun(Element) -> - ["\n" | format_result(Element, ElementsDef)] - end, - Elements)]; -%% The result is a tuple with several elements: {something1(), something2(), ...} -%% NOTE: the elements in the tuple are separated with tabular characters, -%% if a string is empty, it will be difficult to notice in the shell, -%% maybe a different separation character should be used, like ;;? -format_result(ElementsTuple, {_Name, {tuple, ElementsDef}}) -> - ElementsList = tuple_to_list(ElementsTuple), - [{FirstE, FirstD} | ElementsAndDef] = lists:zip(ElementsList, ElementsDef), - [format_result(FirstE, FirstD) | - lists:map( - fun({Element, ElementDef}) -> - ["\t" | format_result(Element, ElementDef)] - end, - ElementsAndDef)]; -format_result({ok, List}, ListDef) -> - format_result(List, ListDef). - --spec make_status(ok | true | _) -> 0 | 1. -make_status(ok) -> ?STATUS_SUCCESS; -make_status(true) -> ?STATUS_SUCCESS; -make_status(_Error) -> ?STATUS_ERROR. - - --spec get_list_commands() - -> [{Call :: string(), Args :: [string()], Desc :: string()}]. -get_list_commands() -> - try ejabberd_commands:list_commands() of - Commands -> - [tuple_command_help(Command) - || {N, _, _}=Command <- Commands, - %% Don't show again those commands, because they are already - %% announced by ejabberd_ctl itself - N /= status, N /= stop, N /= restart] - catch - exit:_ -> - [] - end. - - --spec tuple_command_help(ejabberd_commands:list_cmd()) -> cmd(). -tuple_command_help({Name, Args, Desc}) -> - Arguments = [atom_to_list(ArgN) || {ArgN, _ArgF} <- Args], - CallString = atom_to_list(Name), - {CallString, Arguments, Desc}. - format_status([{node, Node}, {internal_status, IS}, {provided_status, PS}, {mongoose_status, MS}, {os_pid, OSPid}, {uptime, UptimeHMS}, {dist_proto, DistProto}, {logs, LogFiles}]) -> @@ -476,54 +228,33 @@ format_status([{node, Node}, {internal_status, IS}, {provided_status, PS}, print_usage() -> {MaxC, ShCode} = get_shell_info(), - print_usage(dual, MaxC, ShCode). + print_usage(MaxC, ShCode). --spec print_usage(dual | long, MaxC :: integer(), ShCode :: boolean()) -> ok. -print_usage(HelpMode, MaxC, ShCode) -> +-spec print_usage(MaxC :: integer(), ShCode :: boolean()) -> ok. +print_usage(MaxC, ShCode) -> ?PRINT(["Usage: ", ?B("mongooseimctl"), " [", ?U("category"), "] ", ?U("command"), " [", ?U("arguments"), "]\n\n" "Most MongooseIM commands are grouped into the following categories:\n"], []), - print_categories(HelpMode, MaxC, ShCode), + print_categories(MaxC, ShCode), ?PRINT(["\nTo list the commands in a particular category:\n mongooseimctl ", ?U("category"), "\n"], []), ?PRINT(["\nThe following basic system management commands do not have a category:\n"], []), - print_usage_commands(HelpMode, MaxC, ShCode, basic_commands()). - --spec print_usage_old(dual | long, MaxC :: integer(), ShCode :: boolean()) -> ok. -print_usage_old(HelpMode, MaxC, ShCode) -> - ?PRINT(["The following commands are deprecated and ", ?B("will be removed"), " soon.\n" - "To learn about the new commands, run 'mongooseimctl' without any arguments.\n\n"], []), - AllCommands = basic_commands() ++ get_list_commands(), - ?PRINT( - ["Usage: ", ?B("mongooseimctl"), " [--node ", ?U("nodename"), "] [--auth ", - ?U("user"), " ", ?U("host"), " ", ?U("password"), "] ", - ?U("command"), " [", ?U("options"), "]\n" - "\n" - "Available commands in this MongooseIM node:\n"], []), - print_usage_commands(HelpMode, MaxC, ShCode, AllCommands), - ?PRINT( - ["\n" - "Examples:\n" - " mongooseimctl restart\n" - " mongooseimctl --node mongooseim@host restart\n"], - []). + print_usage_commands(MaxC, ShCode, basic_commands()). -spec basic_commands() -> [cmd()]. basic_commands() -> [{"status", [], "Get MongooseIM status"}, {"stop", [], "Stop MongooseIM"}, {"restart", [], "Restart MongooseIM"}, - {"help", ["[--tags [tag] | com?*]"], "Show help for the deprecated commands"}, - {"mnesia", ["[info]"], "Show information about Mnesia database management system"}, {"graphql", ["query"], "Execute GraphQL query or mutation"}]. --spec print_categories(dual | long, MaxC :: integer(), ShCode :: boolean()) -> ok. -print_categories(HelpMode, MaxC, ShCode) -> +-spec print_categories(MaxC :: integer(), ShCode :: boolean()) -> ok. +print_categories(MaxC, ShCode) -> SortedSpecs = lists:sort(maps:to_list(mongoose_graphql_commands:get_specs())), Categories = [{binary_to_list(Category), [], binary_to_list(Desc)} || {Category, #{desc := Desc}} <- SortedSpecs], - print_usage_commands(HelpMode, MaxC, ShCode, Categories). + print_usage_commands(MaxC, ShCode, Categories). -spec print_usage_category(mongoose_graphql_commands:category(), mongoose_graphql_commands:command_map()) -> ok. @@ -534,7 +265,7 @@ print_usage_category(Category, Commands) -> "The following commands are available in the category '", Category, "':\n"], []), CmdSpec = [{binary_to_list(Command), [], binary_to_list(Desc)} || {Command, #{desc := Desc}} <- maps:to_list(Commands)], - print_usage_commands(dual, MaxC, ShCode, CmdSpec), + print_usage_commands(MaxC, ShCode, CmdSpec), ?PRINT(["\nTo list the arguments for a particular command:\n" " mongooseimctl ", Category, " ", ?U("command"), " --help", "\n"], []). @@ -551,16 +282,15 @@ print_usage_command(Category, Command, ArgsSpec) -> %% This will be replaced with new logic when old commands are dropped Args = [{binary_to_list(Name), [], mongoose_graphql_commands:wrap_type(Wrap, Type)} || #{name := Name, type := Type, wrap := Wrap} <- ArgsSpec], - print_usage_commands(dual, MaxC, ShCode, Args), + print_usage_commands(MaxC, ShCode, Args), ?PRINT(["\nScalar values do not need quoting unless they contain special characters or spaces.\n" "Complex input types are passed as JSON maps or lists, depending on the type.\n" "When a type is followed by '!', the corresponding argument is required.\n"], []). --spec print_usage_commands(HelpMode :: 'dual' | 'long', - MaxC :: integer(), +-spec print_usage_commands(MaxC :: integer(), ShCode :: boolean(), Commands :: [cmd(), ...]) -> 'ok'. -print_usage_commands(HelpMode, MaxC, ShCode, Commands) -> +print_usage_commands(MaxC, ShCode, Commands) -> CmdDescsSorted = lists:keysort(1, Commands), %% What is the length of the largest command? @@ -585,7 +315,7 @@ print_usage_commands(HelpMode, MaxC, ShCode, Commands) -> %% For each command in the list of commands %% Convert its definition to a line - FmtCmdDescs = format_command_lines(CmdArgsLenDescsSorted, MaxCmdLen, MaxC, ShCode, HelpMode), + FmtCmdDescs = format_command_lines(CmdArgsLenDescsSorted, MaxCmdLen, MaxC, ShCode), ?PRINT([FmtCmdDescs], []). %% @doc Get some info about the shell: how many columns of width and guess if @@ -658,228 +388,30 @@ join(L, [Word | Words], LenLastSeg, LastSeg, ResSeg) -> -spec format_command_lines(CALD :: [{[any()], [any()], number(), _}, ...], MaxCmdLen :: integer(), MaxC :: integer(), - ShCode :: boolean(), - 'dual' | 'long') -> [[any(), ...], ...]. -format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, dual) - when MaxC - MaxCmdLen < 40 -> - %% If the space available for descriptions is too narrow, enforce long help mode - format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, long); -format_command_lines(CALD, MaxCmdLen, MaxC, ShCode, dual) -> + ShCode :: boolean()) -> [[any(), ...], ...]. +format_command_lines(CALD, MaxCmdLen, MaxC, ShCode) when MaxC - MaxCmdLen < 40 -> + % Long mode lists:map( - fun({Cmd, Args, CmdArgsL, Desc}) -> - DescFmt = prepare_description(MaxCmdLen+4, MaxC, Desc), - [" ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], string:chars(?ASCII_SPACE_CHARACTER, MaxCmdLen - CmdArgsL + 1), - DescFmt, "\n"] - end, CALD); -format_command_lines(CALD, _MaxCmdLen, MaxC, ShCode, long) -> + fun({Cmd, Args, _CmdArgsL, Desc}) -> + DescFmt = prepare_description(8, MaxC, Desc), + ["\n ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], "\n", " ", + DescFmt, "\n"] + end, CALD); + +format_command_lines(CALD, MaxCmdLen, MaxC, ShCode) -> + % Dual mode lists:map( - fun({Cmd, Args, _CmdArgsL, Desc}) -> - DescFmt = prepare_description(8, MaxC, Desc), - ["\n ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], "\n", " ", - DescFmt, "\n"] - end, CALD). - -%%----------------------------- -%% Print Tags -%%----------------------------- - -print_usage_tags(MaxC, ShCode) -> - ?PRINT("Available tags and commands:", []), - TagsCommands = ejabberd_commands:get_tags_commands(), - lists:foreach( - fun({Tag, Commands} = _TagCommands) -> - ?PRINT(["\n\n ", ?B(Tag), "\n "], []), - Words = lists:sort(Commands), - Desc = prepare_long_line(5, MaxC, Words), - ?PRINT(Desc, []) - end, - TagsCommands), - ?PRINT("\n\n", []). - - -print_usage_tags(Tag, MaxC, ShCode) -> - ?PRINT(["Available commands with tag ", ?B(Tag), ":", "\n"], []), - HelpMode = long, - TagsCommands = ejabberd_commands:get_tags_commands(), - CommandsNames = case lists:keysearch(Tag, 1, TagsCommands) of - {value, {Tag, CNs}} -> CNs; - false -> [] - end, - CommandsList = lists:map( - fun(NameString) -> - C = ejabberd_commands:get_command_definition(list_to_atom(NameString)), - #ejabberd_commands{name = Name, - args = Args, - desc = Desc} = C, - tuple_command_help({Name, Args, Desc}) - end, - CommandsNames), - print_usage_commands(HelpMode, MaxC, ShCode, CommandsList), - ?PRINT("\n", []). - - -%%----------------------------- -%% Print usage of 'help' command -%%----------------------------- - -print_usage_help(MaxC, ShCode) -> - LongDesc = - ["The special 'help' mongooseimctl command provides help of MongooseIM commands.\n\n" - "The format is:\n ", ?B("mongooseimctl"), " ", ?B("help"), " [", ?B("--tags"), " ", ?U("[tag]"), " | ", ?U("com?*"), "]\n\n" - "The optional arguments:\n" - " ", ?B("--tags"), " Show all tags and the names of commands in each tag\n" - " ", ?B("--tags"), " ", ?U("tag"), " Show description of commands in this tag\n" - " ", ?U("command"), " Show detailed description of the command\n" - " ", ?U("com?*"), " Show detailed description of commands that match this glob.\n" - " You can use ? to match a simple character, \n" - " and * to match several characters.\n" - "\n", - "Some example usages:\n", - " mongooseimctl help\n", - " mongooseimctl help --tags\n", - " mongooseimctl help --tags accounts\n", - " mongooseimctl help register\n", - " mongooseimctl help regist*\n", - "\n", - "Please note that 'mongooseimctl help' shows all MongooseIM commands, \n", - "even those that cannot be used in the shell with mongooseimctl.\n", - "Those commands can be identified because the description starts with: *"], - ArgsDef = [], - C = #ejabberd_commands{ - name = help, - desc = "Show help of MongooseIM commands", - longdesc = lists:flatten(LongDesc), - args = ArgsDef, - module = none, - function = none, - result = {help, string}}, - print_usage_command("help", C, MaxC, ShCode). - + fun({Cmd, Args, CmdArgsL, Desc}) -> + DescFmt = prepare_description(MaxCmdLen+4, MaxC, Desc), + [" ", ?B(Cmd), " ", [[?U(Arg), " "] || Arg <- Args], + string:chars(?ASCII_SPACE_CHARACTER, MaxCmdLen - CmdArgsL + 1), + DescFmt, "\n"] + end, CALD). %%----------------------------- %% Print usage command %%----------------------------- --spec print_usage_commands(CmdSubString :: string(), MaxC :: integer(), ShCode :: boolean()) -> ok. -print_usage_commands(CmdSubString, MaxC, ShCode) -> - %% Get which command names match this substring - AllCommandsNames = [atom_to_list(Name) || {Name, _, _} <- ejabberd_commands:list_commands()], - Cmds = filter_commands(AllCommandsNames, CmdSubString), - case Cmds of - [] -> io:format("Error: no command found that match: ~p~n", [CmdSubString]); - _ -> print_usage_commands2(lists:sort(Cmds), MaxC, ShCode) - end. - - -print_usage_commands2(Cmds, MaxC, ShCode) -> - %% Then for each one print it - lists:mapfoldl( - fun(Cmd, Remaining) -> - print_usage_command_old(Cmd, MaxC, ShCode), - case Remaining > 1 of - true -> ?PRINT([" ", lists:duplicate(MaxC, 126), " \n"], []); - false -> ok - end, - {ok, Remaining-1} - end, - length(Cmds), - Cmds). - - -filter_commands(All, SubString) -> - case lists:member(SubString, All) of - true -> [SubString]; - false -> filter_commands_regexp(All, SubString) - end. - - -filter_commands_regexp(All, Glob) -> - RegExp = xmerl_regexp:sh_to_awk(Glob), - lists:filter( - fun(Command) -> - case re:run(Command, RegExp, [{capture, none}]) of - match -> - true; - nomatch -> - false - end - end, - All). - - --spec print_usage_command_old(Cmd :: string(), MaxC :: integer(), ShCode :: boolean()) -> ok. -print_usage_command_old(Cmd, MaxC, ShCode) -> - Name = list_to_atom(Cmd), - case ejabberd_commands:get_command_definition(Name) of - command_not_found -> - io:format("Error: command ~p not known.~n", [Cmd]); - C -> - print_usage_command(Cmd, C, MaxC, ShCode) - end. - - -print_usage_command(Cmd, C, MaxC, ShCode) -> - #ejabberd_commands{ - tags = TagsAtoms, - desc = Desc, - longdesc = LongDesc, - args = ArgsDef, - result = ResultDef} = C, - - NameFmt = [" ", ?B("Command Name"), ": ", Cmd, "\n"], - - %% Initial indentation of result is 13 = length(" Arguments: ") - Args = [format_usage_ctype(ArgDef, 13) || ArgDef <- ArgsDef], - ArgsMargin = lists:duplicate(13, ?ASCII_SPACE_CHARACTER), - ArgsListFmt = case Args of - [] -> "\n"; - _ -> [ [Arg, "\n", ArgsMargin] || Arg <- Args] - end, - ArgsFmt = [" ", ?B("Arguments"), ": ", ArgsListFmt], - - %% Initial indentation of result is 11 = length(" Returns: ") - ResultFmt = format_usage_ctype(ResultDef, 11), - ReturnsFmt = [" ", ?B("Returns"), ": ", ResultFmt], - - TagsFmt = [" ", ?B("Tags"), ": ", prepare_long_line(8, MaxC, [atom_to_list(TagA) || TagA <- TagsAtoms])], - - DescFmt = [" ", ?B("Description"), ": ", prepare_description(15, MaxC, Desc)], - - LongDescFmt = case LongDesc of - "" -> ""; - _ -> ["", prepare_description(0, MaxC, LongDesc), "\n\n"] - end, - - ?PRINT(["\n", NameFmt, "\n", ArgsFmt, "\n", ReturnsFmt, "\n\n", TagsFmt, "\n\n", DescFmt, "\n\n", LongDescFmt], []). - - -format_usage_ctype(Type, _Indentation) - when (Type==atom) or (Type==integer) or (Type==string) or (Type==rescode) or (Type==restuple) or (Type==binary)-> - io_lib:format("~p", [Type]); -format_usage_ctype({Name, Type}, _Indentation) - when (Type==atom) or (Type==integer) or (Type==string) or (Type==rescode) or (Type==restuple) or (Type==binary)-> - io_lib:format("~p::~p", [Name, Type]); -format_usage_ctype({Name, {list, ElementDef}}, Indentation) -> - NameFmt = atom_to_list(Name), - Indentation2 = Indentation + length(NameFmt) + 4, - ElementFmt = format_usage_ctype(ElementDef, Indentation2), - [NameFmt, "::[ ", ElementFmt, " ]"]; -format_usage_ctype({Name, {tuple, ElementsDef}}, Indentation) -> - NameFmt = atom_to_list(Name), - Indentation2 = Indentation + length(NameFmt) + 4, - ElementsFmt = format_usage_tuple(ElementsDef, Indentation2), - [NameFmt, "::{ " | ElementsFmt]. - - -format_usage_tuple([], _Indentation) -> - []; -format_usage_tuple([ElementDef], Indentation) -> - [format_usage_ctype(ElementDef, Indentation), " }"]; -format_usage_tuple([ElementDef | ElementsDef], Indentation) -> - ElementFmt = format_usage_ctype(ElementDef, Indentation), - MarginString = lists:duplicate(Indentation, ?ASCII_SPACE_CHARACTER), % Put spaces - [ElementFmt, ", \n", MarginString, format_usage_tuple(ElementsDef, Indentation)]. - get_mongoose_status() -> case lists:keyfind(mongooseim, 1, application:which_applications()) of false -> @@ -899,3 +431,30 @@ get_dist_proto() -> {ok, [Proto]} -> Proto; _ -> "inet_tcp" end. + +%%----------------------------- +%% Cluster management commands +%%----------------------------- + +join_cluster(NodeString) -> + handle_cluster_operation(join_cluster, [NodeString]). + +leave_cluster() -> + handle_cluster_operation(leave_cluster, []). + +remove_from_cluster(NodeString) -> + handle_cluster_operation(remove_from_cluster, [NodeString]). + +handle_cluster_operation(Operation, Args) -> + case apply(mongoose_server_api, Operation, Args) of + {ok, Result} -> + ?PRINT("~s\n", [Result]), + ?STATUS_SUCCESS; + {_, Result} -> + ?PRINT("Error: \"~s\"\n", [Result]), + ?STATUS_ERROR + end. + +cluster_command_usage() -> + ?PRINT("This command requires one argument: other node's name\n", []), + ?STATUS_ERROR. diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index 1bc0c324676..9c80372b42a 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -115,7 +115,6 @@ node_cleanup(Acc, #{node := Node}, _) -> init([]) -> internal_database_init(), set_shared_secret(), - ejabberd_commands:register_commands(mongoose_s2s_lib:commands()), gen_hook:add_handlers(hooks()), {ok, #state{}}. @@ -133,7 +132,6 @@ handle_info(Msg, State) -> terminate(_Reason, _State) -> gen_hook:delete_handlers(hooks()), - ejabberd_commands:unregister_commands(mongoose_s2s_lib:commands()), ok. code_change(_OldVsn, State, _Extra) -> diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 181c3f3479f..00446b0a88f 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -88,7 +88,6 @@ -include("mongoose.hrl"). -include("jlib.hrl"). --include("ejabberd_commands.hrl"). -include("session.hrl"). -record(state, {}). @@ -441,7 +440,6 @@ init([]) -> gen_hook:add_handler(node_cleanup, global, fun ?MODULE:node_cleanup/3, #{}, 50), lists:foreach(fun(HostType) -> gen_hook:add_handlers(hooks(HostType)) end, ?ALL_HOST_TYPES), - ejabberd_commands:register_commands(commands()), %% Create metrics after backend has started, otherwise probe could have null value create_metrics(), {ok, #state{}}. @@ -532,13 +530,6 @@ handle_info(_Info, State) -> %%-------------------------------------------------------------------- -spec terminate(_, state()) -> 'ok'. terminate(_Reason, _State) -> - try - ejabberd_commands:unregister_commands(commands()) - catch Class:Reason:Stacktrace -> - ?LOG_ERROR(#{what => sm_terminate_failed, - text => <<"Caught error while terminating SM">>, - class => Class, reason => Reason, stacktrace => Stacktrace}) - end, ok. %%-------------------------------------------------------------------- @@ -927,34 +918,6 @@ process_iq(_, From, To, Acc, Packet) -> {Acc1, Err} = jlib:make_error_reply(Acc, Packet, mongoose_xmpp_errors:bad_request()), ejabberd_router:route(To, From, Acc1, Err). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%%% ejabberd commands - --spec commands() -> [ejabberd_commands:cmd(), ...]. -commands() -> - [ - %% TODO: Implement following API functions with pluggable backends architcture - %% #ejabberd_commands{name = connected_users, - %% tags = [session], - %% desc = "List all established sessions", - %% module = ?MODULE, function = connected_users, - %% args = [], - %% result = {connected_users, {list, {sessions, string}}}}, - %% #ejabberd_commands{name = connected_users_number, - %% tags = [session, stats], - %% desc = "Get the number of established sessions", - %% module = ?MODULE, function = connected_users_number, - %% args = [], - %% result = {num_sessions, integer}}, - #ejabberd_commands{name = user_resources, - tags = [session], - desc = "List user's connected resources", - module = ?MODULE, function = user_resources, - args = [{user, string}, {host, string}], - result = {resources, {list, {resource, binary}}}} - ]. - - -spec user_resources(UserStr :: string(), ServerStr :: string()) -> [binary()]. user_resources(UserStr, ServerStr) -> JID = jid:make_bare(list_to_binary(UserStr), list_to_binary(ServerStr)), diff --git a/src/http_upload/mod_http_upload_api.erl b/src/http_upload/mod_http_upload_api.erl index 88940c39ab3..618ec2727d1 100644 --- a/src/http_upload/mod_http_upload_api.erl +++ b/src/http_upload/mod_http_upload_api.erl @@ -1,23 +1,6 @@ -module(mod_http_upload_api). --export([get_urls/5, get_urls_mongooseimctl/5]). - --ignore_xref([get_urls_mongooseimctl/5]). - --spec get_urls_mongooseimctl(Domain :: jid:lserver(), Filename :: binary(), Size :: pos_integer(), - ContentType :: binary() | undefined, Timeout :: pos_integer()) -> - {ok | error, string()}. -get_urls_mongooseimctl(_Domain, _Filename, Size, _ContentType, _Timeout) when Size =< 0 -> - {error, "size must be positive integer"}; -get_urls_mongooseimctl(_Domain, _Filename, _Size, _ContentType, Timeout) when Timeout =< 0 -> - {error, "timeout must be positive integer"}; -get_urls_mongooseimctl(Domain, Filename, Size, ContentType, Timeout) -> - case get_urls(Domain, Filename, Size, ContentType, Timeout) of - {ok, #{<<"putUrl">> := PutURL, <<"getUrl">> := GetURL, <<"headers">> := Headers}} -> - {ok, generate_output_message(PutURL, GetURL, Headers)}; - {_, Message} -> - {error, Message} - end. +-export([get_urls/5]). -spec get_urls(Domain :: jid:lserver(), Filename :: nonempty_binary(), Size :: pos_integer(), ContentType :: binary() | null | undefined, Timeout :: pos_integer()) -> @@ -53,19 +36,3 @@ check_module_and_get_urls(HostType, Filename, Size, ContentType, Timeout) -> false -> {module_not_loaded_error, "mod_http_upload is not loaded for this host"} end. - --spec generate_output_message(PutURL :: binary(), - GetURL :: binary(), - Headers :: [{ok, map()}]) -> string(). -generate_output_message(PutURL, GetURL, Headers) -> - PutURLOutput = url_output("PutURL:", PutURL), - GetURLOutput = url_output("GetURL:", GetURL), - HeadersOutput = headers_output(Headers), - lists:flatten([PutURLOutput, GetURLOutput, HeadersOutput]). - -url_output(Name, Url) -> - io_lib:format("~s ~s~n", [Name, Url]). - -headers_output(Headers) -> - List = [{Name, Value} || {ok, #{<<"name">> := Name, <<"value">> := Value}} <- Headers], - io_lib:format("Header: ~p~n", [List]). diff --git a/src/mod_auth_token.erl b/src/mod_auth_token.erl index ada75cbc0f9..edf9280c9a1 100644 --- a/src/mod_auth_token.erl +++ b/src/mod_auth_token.erl @@ -4,7 +4,6 @@ -behaviour(mongoose_module_metrics). -include("mongoose.hrl"). --include("ejabberd_commands.hrl"). -include("jlib.hrl"). -include("mod_auth_token.hrl"). -include("mongoose_config_spec.hrl"). @@ -32,9 +31,6 @@ -export([deserialize/1, serialize/1]). -%% Command-line interface --export([revoke_token_command/1]). - %% Test only! -export([datetime_to_seconds/1, seconds_to_datetime/1]). @@ -52,7 +48,7 @@ -ignore_xref([ behaviour_info/1, datetime_to_seconds/1, deserialize/1, expiry_datetime/3, get_key_for_host_type/2, process_iq/5, - revoke/2, revoke_token_command/1, seconds_to_datetime/1, + revoke/2, seconds_to_datetime/1, serialize/1, token/3, token_with_mac/2 ]). @@ -82,7 +78,6 @@ start(HostType, #{iqdisc := IQDisc} = Opts) -> gen_iq_handler:add_iq_handler_for_domain( HostType, ?NS_ESL_TOKEN_AUTH, ejabberd_sm, fun ?MODULE:process_iq/5, #{}, IQDisc), - ejabberd_commands:register_commands(commands()), ok. -spec stop(mongooseim:host_type()) -> ok. @@ -128,13 +123,6 @@ validity_period_spec() -> required = all }. --spec commands() -> [ejabberd_commands:cmd()]. -commands() -> - [#ejabberd_commands{ name = revoke_token, tags = [tokens], - desc = "Revoke REFRESH token", - module = ?MODULE, function = revoke_token_command, - args = [{owner, binary}], result = {res, restuple} }]. - %% %% Other stuff %% @@ -414,17 +402,6 @@ key_name(access) -> token_secret; key_name(refresh) -> token_secret; key_name(provision) -> provision_pre_shared. --spec revoke_token_command(Owner) -> ResTuple when - Owner :: binary(), - ResCode :: ok | not_found | error, - ResTuple :: {ResCode, string()}. -revoke_token_command(Owner) -> - JID = jid:from_binary(Owner), - case mod_auth_token_api:revoke_token_command(JID) of - {ok, _} = Result -> Result; - Error -> Error - end. - -spec clean_tokens(Acc, Params, Extra) -> {ok, Acc} when Acc :: mongoose_acc:t(), Params :: #{jid := jid:jid()}, diff --git a/src/mod_private_api.erl b/src/mod_private_api.erl index d78676455ba..a260966eeaf 100644 --- a/src/mod_private_api.erl +++ b/src/mod_private_api.erl @@ -2,7 +2,6 @@ -module(mod_private_api). -include("mongoose.hrl"). --include("ejabberd_commands.hrl"). -include("jlib.hrl"). -include_lib("exml/include/exml.hrl"). diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 8fb34453013..965bf19cae9 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -48,11 +48,9 @@ process_iq/5, get_roster_entry/4, item_to_map/1, - set_items/3, set_roster_entry/5, remove_from_roster/3, - item_to_xml/1, - broadcast_item/3 + item_to_xml/1 ]). % Hook handlers @@ -68,7 +66,8 @@ get_personal_data/3 ]). --export([transaction/2, +-export([set_items/3, + transaction/2, process_subscription_t/6, get_user_rosters_length/2]). % for testing @@ -77,7 +76,9 @@ -ignore_xref([get_user_rosters_length/2, item_to_xml/1, process_subscription_t/6, - transaction/2]). + set_items/3, + transaction/2 + ]). -include("mongoose.hrl"). -include("jlib.hrl"). diff --git a/src/mongoose_account_api.erl b/src/mongoose_account_api.erl index ec36e2df17f..2af5ac49b57 100644 --- a/src/mongoose_account_api.erl +++ b/src/mongoose_account_api.erl @@ -8,15 +8,11 @@ unregister_user/1, unregister_user/2, ban_account/2, - ban_account/3, change_password/2, change_password/3, check_account/1, - check_account/2, check_password/2, - check_password/3, check_password_hash/3, - check_password_hash/4, import_users/1]). -type register_result() :: {ok | exists | invalid_jid | cannot_register | @@ -126,11 +122,6 @@ change_password(JID, Password) -> Result = ejabberd_auth:set_password(JID, Password), format_change_password(Result). --spec check_account(jid:user(), jid:server()) -> check_account_result(). -check_account(User, Host) -> - JID = jid:make_bare(User, Host), - check_account(JID). - -spec check_account(jid:jid()) -> check_account_result(). check_account(JID) -> case ejabberd_auth:does_user_exist(JID) of @@ -140,11 +131,6 @@ check_account(JID) -> {user_does_not_exist, io_lib:format("User ~s does not exist", [jid:to_binary(JID)])} end. --spec check_password(jid:user(), jid:server(), binary()) -> check_password_result(). -check_password(User, Host, Password) -> - JID = jid:make_bare(User, Host), - check_password(JID, Password). - -spec check_password(jid:jid(), binary()) -> check_password_result(). check_password(JID, Password) -> case ejabberd_auth:does_user_exist(JID) of @@ -163,12 +149,6 @@ check_password(JID, Password) -> " exist", [Password, jid:to_binary(JID)])} end. --spec check_password_hash(jid:user(), jid:server(), string(), string()) -> - check_password_hash_result(). -check_password_hash(User, Host, PasswordHash, HashMethod) -> - JID = jid:make_bare(User, Host), - check_password_hash(JID, PasswordHash, HashMethod). - -spec check_password_hash(jid:jid(), string(), string()) -> check_password_hash_result(). check_password_hash(JID, PasswordHash, HashMethod) -> AccountPass = ejabberd_auth:get_password_s(JID), @@ -214,11 +194,6 @@ from_reason(invalid_jid) -> <<"invalidJID">>; from_reason(null_password) -> <<"emptyPassword">>; from_reason(bad_csv) -> <<"invalidRecord">>. --spec ban_account(jid:user(), jid:server(), binary()) -> change_password_result(). -ban_account(User, Host, ReasonText) -> - JID = jid:make_bare(User, Host), - ban_account(JID, ReasonText). - -spec ban_account(jid:jid(), binary()) -> change_password_result(). ban_account(JID, Reason) -> case ejabberd_auth:does_user_exist(JID) of diff --git a/src/mongoose_bin.erl b/src/mongoose_bin.erl index db832e55659..984d1f0713b 100644 --- a/src/mongoose_bin.erl +++ b/src/mongoose_bin.erl @@ -12,8 +12,7 @@ join/2, encode_crypto/1, gen_from_crypto/0, - gen_from_timestamp/0, - string_to_binary/1]). + gen_from_timestamp/0]). %% --------------------------------------------------- %% API @@ -47,20 +46,6 @@ gen_from_timestamp() -> -spec encode_crypto(iodata()) -> binary(). encode_crypto(Text) -> base16:encode(crypto:hash(sha, Text)). --spec string_to_binary(binary() | list()) -> binary(). -string_to_binary(S) when is_list(S) -> - % If list is in Erlang representation of Unicode, we must use `unicode` module - % If it's not or is already converted, we must use list_to_binary - % since input can be from `file:consult/1` and prior to 17.0 - % this function returned bytes in a list instead of proper unicode string - % so it is already like after a call to `unicode`. - case lists:any(fun(C) -> C > 255 end, S) of - true -> unicode:characters_to_binary(S); - false -> list_to_binary(S) - end; -string_to_binary(B) when is_binary(B) -> - B. - %% --------------------------------------------------- %% Internal functions %% --------------------------------------------------- diff --git a/src/mongoose_cluster.erl b/src/mongoose_cluster.erl index 79e31d343e0..ead93c4b6c2 100644 --- a/src/mongoose_cluster.erl +++ b/src/mongoose_cluster.erl @@ -2,8 +2,6 @@ %% This is a library module for cluster management: joining / leaving a cluster. -%% TODO: it might make sense to expose this stuff as service_admin_extra_cluster - -export([join/1, leave/0, remove_from_cluster/1, is_node_alive/1]). -export([all_cluster_nodes/0, other_cluster_nodes/0]). diff --git a/src/mongoose_server_api.erl b/src/mongoose_server_api.erl index 1d9e1e79183..390ccac5b81 100644 --- a/src/mongoose_server_api.erl +++ b/src/mongoose_server_api.erl @@ -1,20 +1,9 @@ -module(mongoose_server_api). --export([get_loglevel_mongooseimctl/0]). - -export([status/0, get_cookie/0, join_cluster/1, leave_cluster/0, remove_from_cluster/1, stop/0, restart/0, remove_node/1, set_loglevel/1, get_loglevel/0]). --ignore_xref([get_loglevel_mongooseimctl/0]). - --spec get_loglevel_mongooseimctl() -> {ok, iolist()}. -get_loglevel_mongooseimctl() -> - Level = mongoose_logs:get_global_loglevel(), - Number = mongoose_logs:loglevel_keyword_to_number(Level), - String = io_lib:format("global loglevel is ~p, which means '~p'", [Number, Level]), - {ok, String}. - -spec get_loglevel() -> {ok, mongoose_logs:atom_log_level()}. get_loglevel() -> {ok, mongoose_logs:get_global_loglevel()}. diff --git a/src/s2s/mongoose_s2s_lib.erl b/src/s2s/mongoose_s2s_lib.erl index e1df46831e9..74f5a4f5c34 100644 --- a/src/s2s/mongoose_s2s_lib.erl +++ b/src/s2s/mongoose_s2s_lib.erl @@ -13,12 +13,10 @@ choose_pid/2, need_more_connections/2, needed_extra_connections_number_if_allowed/2, - allow_host/1, - commands/0]). + allow_host/1]). -include("mongoose.hrl"). -include("jlib.hrl"). --include("ejabberd_commands.hrl"). -type fromto() :: ejabberd_s2s:fromto(). -type s2s_pids() :: ejabberd_s2s:s2s_pids(). @@ -198,20 +196,3 @@ parent_domains(<<$., Rest/binary>>, Acc) -> parent_domains(Rest, [Rest | Acc]); parent_domains(<<_, Rest/binary>>, Acc) -> parent_domains(Rest, Acc). - --spec commands() -> [ejabberd_commands:cmd()]. -commands() -> - [ - #ejabberd_commands{name = incoming_s2s_number, - tags = [stats, s2s], - desc = "Number of incoming s2s connections on the node", - module = stats_api, function = incoming_s2s_number, - args = [], - result = {s2s_incoming, integer}}, - #ejabberd_commands{name = outgoing_s2s_number, - tags = [stats, s2s], - desc = "Number of outgoing s2s connections on the node", - module = stats_api, function = outgoing_s2s_number, - args = [], - result = {s2s_outgoing, integer}} - ]. diff --git a/src/stats_api.erl b/src/stats_api.erl index 9881e2ecc12..495a85796ab 100644 --- a/src/stats_api.erl +++ b/src/stats_api.erl @@ -1,9 +1,6 @@ -module(stats_api). -export([incoming_s2s_number/0, outgoing_s2s_number/0, stats/1, stats/2]). --export([stats_mongooseimctl/1, stats_mongooseimctl/2]). - --ignore_xref([stats_mongooseimctl/1, stats_mongooseimctl/2]). -include("mongoose.hrl"). @@ -36,17 +33,3 @@ stats(<<"onlineusers">>, Host) -> {ok, ejabberd_sm:get_vh_session_number(Host)}; stats(_Name, _Host) -> {not_found, "Stats not found"}. - --spec stats_mongooseimctl(binary()) -> {ok | not_found, string()}. -stats_mongooseimctl(Name) -> - case stats(Name) of - {ok, Stat} -> {ok, integer_to_list(Stat)}; - Error -> Error - end. - --spec stats_mongooseimctl(binary(), binary()) -> {ok | not_found, string()}. -stats_mongooseimctl(Name, Host) -> - case stats(Name, Host) of - {ok, Stat} -> {ok, integer_to_list(Stat)}; - Error -> Error - end. diff --git a/test/acc_SUITE.erl b/test/acc_SUITE.erl index 7449cdc933f..255e5721bfa 100644 --- a/test/acc_SUITE.erl +++ b/test/acc_SUITE.erl @@ -4,7 +4,6 @@ -include_lib("exml/include/exml.hrl"). -include_lib("eunit/include/eunit.hrl"). --include("ejabberd_commands.hrl"). -include("jlib.hrl"). -include("mongoose.hrl"). diff --git a/test/auth_tokens_SUITE.erl b/test/auth_tokens_SUITE.erl index 15c0d03af61..98ba07a62aa 100644 --- a/test/auth_tokens_SUITE.erl +++ b/test/auth_tokens_SUITE.erl @@ -65,7 +65,6 @@ init_per_testcase(validity_period_test, Config) -> mock_rdbms_backend(), mock_mongoose_metrics(), mock_gen_iq_handler(), - mock_ejabberd_commands(), async_helper:start(Config, [{mongooseim_helper, start_link_loaded_hooks, []}]); init_per_testcase(revoked_token_is_not_valid, Config) -> @@ -91,7 +90,6 @@ end_per_testcase(validity_period_test, C) -> meck:unload(mod_auth_token_backend), meck:unload(mongoose_metrics), meck:unload(gen_iq_handler), - meck:unload(ejabberd_commands), async_helper:stop_all(C), C; @@ -254,10 +252,6 @@ mock_tested_backend() -> receive {valid_seq_no, SeqNo} -> SeqNo end end). -mock_ejabberd_commands() -> - meck:new(ejabberd_commands, []), - meck:expect(ejabberd_commands, register_commands, fun (_) -> ok end). - provision_token_example() -> {token,provision, {{2055,10,27},{10,54,22}}, diff --git a/test/common/config_parser_helper.erl b/test/common/config_parser_helper.erl index bf4a2eacd75..5311da27d49 100644 --- a/test/common/config_parser_helper.erl +++ b/test/common/config_parser_helper.erl @@ -18,7 +18,6 @@ options("host_types") -> {language, <<"en">>}, {listen, []}, {loglevel, warning}, - {mongooseimctl_access_commands, #{}}, {outgoing_pools, []}, {rdbms_server_type, generic}, {registration_timeout, 600}, @@ -80,9 +79,6 @@ options("miscellaneous") -> transport => #{num_acceptors => 10, max_connections => 1024} })]}, {loglevel, warning}, - {mongooseimctl_access_commands, - #{local => #{commands => [join_cluster], - argument_restrictions => #{node => "mongooseim@prime"}}}}, {outgoing_pools, []}, {rdbms_server_type, mssql}, {registration_timeout, 600}, @@ -119,7 +115,6 @@ options("modules") -> {language, <<"en">>}, {listen, []}, {loglevel, warning}, - {mongooseimctl_access_commands, #{}}, {outgoing_pools, []}, {rdbms_server_type, generic}, {registration_timeout, 600}, @@ -240,7 +235,6 @@ options("mongooseim-pgsql") -> ]}, {loglevel, warning}, {max_fsm_queue, 1000}, - {mongooseimctl_access_commands, #{}}, {outgoing_pools, lists:map( fun pool_config/1, @@ -260,10 +254,7 @@ options("mongooseim-pgsql") -> {registration_timeout, infinity}, {routing_modules, mongoose_router:default_routing_modules()}, {services, - #{service_admin_extra => - #{submods => [node, accounts, sessions, vcard, gdpr, upload, - roster, last, private, stanza, stats]}, - service_mongoose_system_metrics => + #{service_mongoose_system_metrics => #{initial_report => 300000, periodic_report => 10800000}}}, {sm_backend, mnesia}, @@ -322,7 +313,6 @@ options("outgoing_pools") -> {language, <<"en">>}, {listen, []}, {loglevel, warning}, - {mongooseimctl_access_commands, #{}}, {outgoing_pools, lists:map( fun pool_config/1, @@ -386,7 +376,6 @@ options("s2s_only") -> {language, <<"en">>}, {listen, []}, {loglevel, warning}, - {mongooseimctl_access_commands, #{}}, {outgoing_pools, []}, {rdbms_server_type, generic}, {registration_timeout, 600}, @@ -1071,8 +1060,6 @@ extra_service_listener_config() -> conflict_behaviour => disconnect, connection_type => component}. -default_config([general, mongooseimctl_access_commands, _Key]) -> - #{commands => all, argument_restrictions => #{}}; default_config([listen, http]) -> (common_listener_config())#{module => ejabberd_cowboy, transport => default_config([listen, http, transport]), @@ -1258,9 +1245,6 @@ default_config([outgoing_pools, _Type, _Tag, conn_opts, tls] = P) -> server_name_indication => default_config(P ++ [server_name_indication])}; default_config([outgoing_pools, _Type, _Tag, conn_opts, tls, server_name_indication]) -> #{enabled => true, protocol => default}; -default_config([services, service_admin_extra]) -> - #{submods => [node, accounts, sessions, vcard, roster, last, - private, stanza, stats, gdpr, upload, domain]}; default_config([services, service_domain_db]) -> #{event_cleaning_interval => 1800, event_max_age => 7200, diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index 9b6935c543f..74240f8a5cd 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -81,7 +81,6 @@ groups() -> http_server_name, rdbms_server_type, route_subdomains, - mongooseimctl_access_commands, routing_modules, replaced_wait_timeout, hide_service_name, @@ -230,8 +229,7 @@ groups() -> mod_version, modules_without_config, incorrect_module]}, - {services, [parallel], [service_admin_extra, - service_domain_db, + {services, [parallel], [service_domain_db, service_mongoose_system_metrics]} ]. @@ -417,19 +415,6 @@ route_subdomains(_Config) -> ?cfgh(route_subdomains, s2s, #{<<"general">> => #{<<"route_subdomains">> => <<"s2s">>}}), ?errh(#{<<"general">> => #{<<"route_subdomains">> => <<"c2s">>}}). -mongooseimctl_access_commands(_Config) -> - ?cfg(mongooseimctl_access_commands, #{}, #{}), % default - P = [mongooseimctl_access_commands, local], - T = fun(Opts) -> - #{<<"general">> => #{<<"mongooseimctl_access_commands">> => #{<<"local">> => Opts}}} - end, - ?cfg(P, default_config([general, mongooseimctl_access_commands, local]), T(#{})), - ?cfg(P ++ [commands], [join_cluster], T(#{<<"commands">> => [<<"join_cluster">>]})), - ?cfg(P ++ [argument_restrictions], #{node => "mim1@host1"}, - T(#{<<"argument_restrictions">> => #{<<"node">> => <<"mim1@host1">>}})), - ?err(T(#{<<"commands">> => [<<>>]})), - ?err(T(#{<<"argument_restrictions">> => #{<<"node">> => 1}})). - routing_modules(_Config) -> ?cfg(routing_modules, mongoose_router:default_routing_modules(), #{}), % default ?cfg(routing_modules, @@ -2857,15 +2842,6 @@ incorrect_module(_Config) -> %% Services -service_admin_extra(_Config) -> - P = [services, service_admin_extra], - T = fun(Opts) -> #{<<"services">> => #{<<"service_admin_extra">> => Opts}} end, - ?cfg(P, default_config(P), T(#{})), - ?cfg(P ++ [submods], [node], T(#{<<"submods">> => [<<"node">>]})), - ?err(T(#{<<"submods">> => 1})), - ?err(T(#{<<"submods">> => [1]})), - ?err(T(#{<<"submods">> => [<<"nodejshaha">>]})). - service_domain_db(_Config) -> P = [services, service_domain_db], T = fun(Opts) -> #{<<"services">> => #{<<"service_domain_db">> => Opts}} end, diff --git a/test/config_parser_SUITE_data/miscellaneous.toml b/test/config_parser_SUITE_data/miscellaneous.toml index e423e6b2d27..6ff3da56a3b 100644 --- a/test/config_parser_SUITE_data/miscellaneous.toml +++ b/test/config_parser_SUITE_data/miscellaneous.toml @@ -14,10 +14,6 @@ replaced_wait_timeout = 2000 hide_service_name = true - [general.mongooseimctl_access_commands.local] - commands = ["join_cluster"] - argument_restrictions.node = "mongooseim@prime" - [[general.domain_certfile]] domain = "example.com" certfile = "priv/cert.pem" diff --git a/test/config_parser_SUITE_data/mongooseim-pgsql.toml b/test/config_parser_SUITE_data/mongooseim-pgsql.toml index 78b5f2e0ed1..b6bc8c6c9d1 100644 --- a/test/config_parser_SUITE_data/mongooseim-pgsql.toml +++ b/test/config_parser_SUITE_data/mongooseim-pgsql.toml @@ -148,10 +148,6 @@ tls.cacertfile = "priv/ca.pem" tls.server_name_indication.enabled = false -[services.service_admin_extra] - submods = ["node", "accounts", "sessions", "vcard", "gdpr", "upload", - "roster", "last", "private", "stanza", "stats"] - [services.service_mongoose_system_metrics] initial_report = 300_000 periodic_report = 10_800_000 diff --git a/test/ejabberd_sm_SUITE.erl b/test/ejabberd_sm_SUITE.erl index 38d4a72caf4..f8f38866074 100644 --- a/test/ejabberd_sm_SUITE.erl +++ b/test/ejabberd_sm_SUITE.erl @@ -400,7 +400,6 @@ unique_count_while_removing_entries(C) -> unload_meck() -> meck:unload(acl), meck:unload(gen_hook), - meck:unload(ejabberd_commands), meck:unload(mongoose_domain_api), catch ets:delete(test_c2s_info), catch meck:unload(mongoose_c2s). @@ -647,7 +646,4 @@ sm_backend(ejabberd_sm_cets) -> cets. set_meck() -> meck:expect(gen_hook, add_handler, fun(_, _, _, _, _) -> ok end), meck:expect(gen_hook, add_handlers, fun(_) -> ok end), - meck:new(ejabberd_commands, []), - meck:expect(ejabberd_commands, register_commands, fun(_) -> ok end), - meck:expect(ejabberd_commands, unregister_commands, fun(_) -> ok end), ok. diff --git a/test/mongoose_cleanup_SUITE.erl b/test/mongoose_cleanup_SUITE.erl index f41a3e83548..72822d01a1f 100644 --- a/test/mongoose_cleanup_SUITE.erl +++ b/test/mongoose_cleanup_SUITE.erl @@ -125,7 +125,7 @@ opts() -> {modules, ?HOST} => #{}}. meck_mods(bosh) -> [exometer, mod_bosh_socket]; -meck_mods(s2s) -> [exometer, ejabberd_commands, mongoose_bin]; +meck_mods(s2s) -> [exometer, mongoose_bin]; meck_mods(component) -> [exometer]; meck_mods(_) -> [exometer, ejabberd_sm, ejabberd_local]. @@ -298,10 +298,6 @@ setup_meck([ejabberd_local | R]) -> meck:expect(ejabberd_local, register_iq_handler, fun(_A1, _A2, _A3) -> ok end), setup_meck(R); -setup_meck([ejabberd_commands | R]) -> - meck:new(ejabberd_commands), - meck:expect(ejabberd_commands, register_commands, fun(_) -> ok end), - setup_meck(R); setup_meck([mongoose_bin | R]) -> meck:new(mongoose_bin, [passthrough]), meck:expect(mongoose_bin, gen_from_crypto, fun() -> <<"123456">> end), diff --git a/test/mongoose_config_SUITE.erl b/test/mongoose_config_SUITE.erl index 50ebd37e482..c2783954df7 100644 --- a/test/mongoose_config_SUITE.erl +++ b/test/mongoose_config_SUITE.erl @@ -175,7 +175,6 @@ minimal_config_opts() -> language => <<"en">>, listen => [], loglevel => warning, - mongooseimctl_access_commands => #{}, outgoing_pools => [], rdbms_server_type => generic, registration_timeout => 600, diff --git a/tools/pkg/scripts/smoke_test.sh b/tools/pkg/scripts/smoke_test.sh index d073dc5354c..fba996dc022 100755 --- a/tools/pkg/scripts/smoke_test.sh +++ b/tools/pkg/scripts/smoke_test.sh @@ -67,14 +67,15 @@ echo "Checking status via 'mongooseimctl status'" mongooseimctl status echo "Trying to register a user with 'mongooseimctl register localhost a_password'" -mongooseimctl register localhost a_password +mongooseimctl account registerUser --domain localhost --password a_password echo "Trying to register a user with 'mongooseimctl register_identified user localhost a_password_2'" -mongooseimctl register_identified user localhost a_password_2 +mongooseimctl account registerUser --username user --domain localhost --password a_password_2 echo "Checking if 2 users are registered on host 'localhost'" expected=2 -registered=$(mongooseimctl registered_users localhost | wc -l) +registered=$(_build/mim1/rel/mongooseim/bin/mongooseimctl account countUsers \ + --domain localhost | grep -o '"countUsers" : [0-9]*' | awk '{print $3}') if [ ${registered} -ne ${expected} ]; then echo "registered value is ${registered} but expected ${expected}" exit 1