1: %%============================================================================== 2: %% Copyright 2013 Erlang Solutions Ltd. 3: %% 4: %% Licensed under the Apache License, Version 2.0 (the "License"); 5: %% you may not use this file except in compliance with the License. 6: %% You may obtain a copy of the License at 7: %% 8: %% http://www.apache.org/licenses/LICENSE-2.0 9: %% 10: %% Unless required by applicable law or agreed to in writing, software 11: %% distributed under the License is distributed on an "AS IS" BASIS, 12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13: %% See the License for the specific language governing permissions and 14: %% limitations under the License. 15: %%============================================================================== 16: -module(mongooseimctl_SUITE). 17: -compile([export_all, nowarn_export_all, nowarn_shadow_vars]). 18: 19: -include_lib("escalus/include/escalus.hrl"). 20: -include_lib("common_test/include/ct.hrl"). 21: -include_lib("exml/include/exml.hrl"). 22: -include_lib("eunit/include/eunit.hrl"). 23: 24: -import(mongooseimctl_helper, [mongooseimctl/3, rpc_call/3]). 25: -import(mongoose_helper, [auth_modules/0]). 26: -import(distributed_helper, [mim/0, 27: require_rpc_nodes/1, 28: rpc/4]). 29: -import(domain_helper, [host_type/0, domain/0]). 30: 31: -define(HTTP_UPLOAD_FILENAME, "tmp.txt"). 32: -define(HTTP_UPLOAD_FILESIZE, "5"). 33: -define(HTTP_UPLOAD_TIMEOUT, "666"). 34: -define(HTTP_UPLOAD_PARAMS(ContentType), ?HTTP_UPLOAD_PARAMS(?HTTP_UPLOAD_FILENAME, 35: ?HTTP_UPLOAD_FILESIZE, 36: ContentType, 37: ?HTTP_UPLOAD_TIMEOUT)). 38: -define(HTTP_UPLOAD_PARAMS_WITH_FILESIZE(X), ?HTTP_UPLOAD_PARAMS(?HTTP_UPLOAD_FILENAME, X, 39: "", ?HTTP_UPLOAD_TIMEOUT)). 40: -define(HTTP_UPLOAD_PARAMS_WITH_TIMEOUT(X), ?HTTP_UPLOAD_PARAMS(?HTTP_UPLOAD_FILENAME, 41: ?HTTP_UPLOAD_FILESIZE, "", X)). 42: -define(HTTP_UPLOAD_PARAMS(FileName, FileSize, ContentType, Timeout), 43: [domain(), FileName, FileSize, ContentType, Timeout]). 44: 45: -define(CTL_ERROR(Messsage), "Error: \"" ++ Messsage ++ "\"\n"). 46: -define(HTTP_UPLOAD_NOT_ENABLED_ERROR, ?CTL_ERROR("mod_http_upload is not loaded for this host")). 47: -define(HTTP_UPLOAD_FILESIZE_ERROR, ?CTL_ERROR("size must be positive integer")). 48: -define(HTTP_UPLOAD_TIMEOUT_ERROR, ?CTL_ERROR("timeout must be positive integer")). 49: 50: -define(S3_BUCKET_URL, "http://localhost:9000/mybucket/"). 51: -define(S3_REGION, "eu-east-25"). 52: -define(S3_ACCESS_KEY_ID, "AKIAIAOAONIULXQGMOUA"). 53: -define(MINIO_OPTS(AddAcl), 54: [ 55: {max_file_size, 1234}, 56: {s3, [ 57: {bucket_url, ?S3_BUCKET_URL}, 58: {add_acl, AddAcl}, 59: {region, ?S3_REGION}, 60: {access_key_id, ?S3_ACCESS_KEY_ID}, 61: {secret_access_key, "CG5fGqG0/n6NCPJ10FylpdgRnuV52j8IZvU7BSj8"} 62: ]} 63: ]). 64: 65: %%Prefix MUST be a constant string, otherwise it results in compilation error 66: -define(GET_URL(Prefix, Sting), fun() -> Prefix ++ URL = Sting, URL end()). 67: 68: %% The following is an example presigned URL: 69: %% 70: %% https://s3.amazonaws.com/examplebucket/test.txt 71: %% ?X-Amz-Algorithm=AWS4-HMAC-SHA256 72: %% &X-Amz-Credential=<your-access-key-id>/20130721/us-east-1/s3/aws4_request 73: %% &X-Amz-Date=20130721T201207Z 74: %% &X-Amz-Expires=86400 75: %% &X-Amz-SignedHeaders=host 76: %% &X-Amz-Signature=<signature-value> 77: %% 78: %% for more details see 79: %% https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html 80: -define(S3_BASE_URL_REGEX, "^"?S3_BUCKET_URL".+/"?HTTP_UPLOAD_FILENAME). 81: -define(S3_ALGORITHM_REGEX, "[?&]X-Amz-Algorithm=AWS4-HMAC-SHA256(&|$)"). 82: -define(S3_CREDENTIAL_REGEX, 83: % X-Amz-Credential=<your-access-key-id>/<date>/<AWS-region>/<AWS-service>/aws4_request 84: "[?&]X-Amz-Credential="?S3_ACCESS_KEY_ID"%2F[0-9]{8}%2F"?S3_REGION"%2Fs3%2Faws4_request(&|$)"). 85: -define(S3_DATE_REGEX, "X-Amz-Date=[0-9]{8}T[0-9]{6}Z(&|$)"). 86: -define(S3_EXPIRATION_REGEX, "[?&]X-Amz-Expires="?HTTP_UPLOAD_TIMEOUT"(&|$)"). 87: -define(S3_SIGNED_HEADERS, "[?&]X-Amz-SignedHeaders=content-length%3Bhost(&|$)"). 88: -define(S3_SIGNED_HEADERS_WITH_ACL, 89: "[?&]X-Amz-SignedHeaders=content-length%3Bhost%3Bx-amz-acl(&|$)"). 90: -define(S3_SIGNED_HEADERS_WITH_CONTENT_TYPE, 91: "[?&]X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost(&|$)"). 92: -define(S3_SIGNED_HEADERS_WITH_CONTENT_TYPE_AND_ACL, 93: "[?&]X-Amz-SignedHeaders=content-length%3Bcontent-type%3Bhost%3Bx-amz-acl(&|$)"). 94: -define(S3_SIGNATURE_REGEX, "[?&]X-Amz-Signature=[^&]+(&|$)"). 95: 96: 97: -record(offline_msg, {us, timestamp, expire, from, to, packet, permanent_fields = []}). 98: 99: %%-------------------------------------------------------------------- 100: %% Suite configuration 101: %%-------------------------------------------------------------------- 102: 103: all() -> 104: [ 105: {group, accounts}, 106: {group, sessions}, 107: {group, vcard}, 108: {group, roster}, 109: {group, roster_advanced}, 110: {group, last}, 111: {group, private}, 112: {group, stanza}, 113: {group, stats}, 114: {group, basic}, 115: {group, upload}, 116: {group, graphql} 117: ]. 118: 119: groups() -> 120: [{accounts, [sequence], accounts()}, 121: {sessions, [sequence], sessions()}, 122: {vcard, [sequence], vcard()}, 123: {roster, [sequence], roster()}, 124: {last, [sequence], last()}, 125: {private, [sequence], private()}, 126: {stanza, [sequence], stanza()}, 127: {roster_advanced, [sequence], roster_advanced()}, 128: {basic, [sequence], basic()}, 129: {stats, [sequence], stats()}, 130: {upload, [], upload()}, 131: {upload_with_acl, [], upload_enabled()}, 132: {upload_without_acl, [], upload_enabled()}, 133: {graphql, [], graphql()}]. 134: 135: basic() -> 136: [simple_register, 137: simple_unregister, 138: register_twice, 139: backup_restore_mnesia, 140: restore_mnesia_wrong, 141: dump_and_load, 142: load_mnesia_wrong, 143: dump_table, 144: get_loglevel, 145: remove_old_messages_test, 146: remove_expired_messages_test]. 147: 148: accounts() -> [change_password, check_password_hash, check_password, 149: check_account, ban_account, num_active_users, delete_old_users, 150: delete_old_users_vhost]. 151: 152: sessions() -> [num_resources_num, kick_session, status, 153: sessions_info, set_presence]. 154: 155: vcard() -> [vcard_rw, vcard2_rw, vcard2_multi_rw]. 156: 157: roster() -> [rosteritem_rw, 158: presence_after_add_rosteritem, 159: push_roster, 160: push_roster_all, 161: push_roster_alltoall]. 162: 163: roster_advanced() -> [process_rosteritems_list_simple, 164: process_rosteritems_list_nomatch, 165: process_rosteritems_list_advanced1, 166: process_rosteritems_list_advanced2, 167: process_rosteritems_delete_advanced, 168: process_rosteritems_delete_advanced2]. 169: 170: last() -> [set_last]. 171: 172: private() -> [private_rw]. 173: 174: stanza() -> [send_message, send_message_wrong_jid, send_stanza, send_stanzac2s_wrong]. 175: 176: stats() -> [stats_global, stats_host]. 177: 178: upload() -> 179: [upload_not_enabled, upload_wrong_filesize, upload_wrong_timeout, 180: {group, upload_with_acl}, {group, upload_without_acl}]. 181: 182: upload_enabled() -> 183: [upload_returns_correct_urls_without_content_type, 184: upload_returns_correct_urls_with_content_type, 185: real_upload_without_content_type, 186: real_upload_with_content_type]. 187: 188: graphql() -> 189: [graphql_wrong_arguments_number, 190: can_execute_admin_queries_with_permissions, 191: can_handle_execution_error]. 192: 193: suite() -> 194: require_rpc_nodes([mim]) ++ escalus:suite(). 195: 196: init_per_suite(Config) -> 197: TemplatePath = filename:join(?config(mim_data_dir, Config), "roster.template"), 198: AuthMods = auth_modules(), 199: Node = mim(), 200: Config1 = ejabberd_node_utils:init(Node, Config), 201: Config2 = escalus:init_per_suite([{ctl_auth_mods, AuthMods}, 202: {roster_template, TemplatePath} | Config1]), 203: dynamic_modules:ensure_modules(domain_helper:host_type(), [{mod_last, 204: config_parser_helper:default_mod_config(mod_last)}]), 205: dynamic_modules:ensure_modules(domain_helper:secondary_host_type(), 206: [{mod_last, config_parser_helper:default_mod_config(mod_last)}]), 207: prepare_roster_template(TemplatePath, domain()), 208: %% dump_and_load requires at least one mnesia table 209: %% ensure, that passwd table is available 210: catch rpc_call(ejabberd_auth_internal, start, [host_type()]), 211: escalus:create_users(Config2, escalus:get_users([alice, mike, bob, kate])). 212: 213: prepare_roster_template(TemplatePath, Domain) -> 214: {ok, [RosterIn]} = file:consult(TemplatePath ++ ".in"), 215: DomainStr = binary_to_list(Domain), 216: Roster = [{User, DomainStr, Group, Name} || {User, Group, Name} <- RosterIn], 217: FormattedRoster = io_lib:format("~tp.~n", [Roster]), 218: file:write_file(TemplatePath, FormattedRoster). 219: 220: end_per_suite(Config) -> 221: Config1 = lists:keydelete(ctl_auth_mods, 1, Config), 222: delete_users(Config1), 223: dynamic_modules:stop(domain_helper:host_type(), mod_last), 224: dynamic_modules:stop(domain_helper:secondary_host_type(), mod_last), 225: escalus:end_per_suite(Config1). 226: 227: init_per_group(basic, Config) -> 228: dynamic_modules:ensure_modules(domain_helper:host_type(), [{mod_offline, []}]), 229: Config; 230: init_per_group(private, Config) -> 231: dynamic_modules:ensure_modules(domain_helper:host_type(), 232: [{mod_private, #{iqdisc => one_queue}}] 233: ), 234: Config; 235: init_per_group(vcard, Config) -> 236: case rpc(mim(), gen_mod, get_module_opt, [host_type(), mod_vcard, backend, mnesia]) of 237: ldap -> 238: {skip, vcard_set_not_supported_with_ldap}; 239: _ -> 240: Config 241: end; 242: init_per_group(roster_advanced, Config) -> 243: case rpc(mim(), gen_mod, get_module_opt, [host_type(), mod_roster, backend, mnesia]) of 244: mnesia -> 245: Config; 246: _ -> 247: {skip, command_process_rosteritems_supports_only_mnesia} 248: end; 249: init_per_group(upload_without_acl, Config) -> 250: dynamic_modules:start(host_type(), mod_http_upload, ?MINIO_OPTS(false)), 251: [{with_acl, false} | Config]; 252: init_per_group(upload_with_acl, Config) -> 253: dynamic_modules:start(host_type(), mod_http_upload, ?MINIO_OPTS(true)), 254: [{with_acl, true} | Config]; 255: init_per_group(_GroupName, Config) -> 256: Config. 257: 258: end_per_group(basic, Config) -> 259: dynamic_modules:stop(domain_helper:host_type(), mod_offline), 260: Config; 261: end_per_group(private, Config) -> 262: dynamic_modules:stop(domain_helper:host_type(), mod_private), 263: Config; 264: end_per_group(Rosters, Config) when (Rosters == roster) or (Rosters == roster_advanced) -> 265: TemplatePath = escalus_config:get_config(roster_template, Config), 266: RegUsers = [atom_to_list(U) || {U, _} <- escalus_config:get_config(escalus_users, Config)], 267: {ok, [Roster]} = file:consult(TemplatePath), 268: C = fun({U, S, _, _}) -> 269: case lists:member(U, RegUsers) of 270: true -> 271: SB = string_to_binary(S), 272: UB = string_to_binary(U), 273: Acc = mongoose_helper:new_mongoose_acc(SB), 274: rpc(mim(), mongoose_hooks, remove_user, [Acc, SB, UB]); 275: _ -> 276: ok 277: end 278: end, 279: lists:foreach(C, Roster), 280: Config; 281: end_per_group(UploadGroup, Config) when UploadGroup =:= upload_without_acl; 282: UploadGroup =:= upload_with_acl -> 283: dynamic_modules:stop(host_type(), mod_http_upload), 284: Config; 285: end_per_group(_GroupName, Config) -> 286: Config. 287: 288: get_registered_users() -> 289: rpc(mim(), ejabberd_auth, get_vh_registered_users, [domain()]). 290: 291: init_per_testcase(CaseName, Config) 292: when CaseName == delete_old_users_vhost 293: orelse CaseName == stats_global 294: orelse CaseName == stats_host -> 295: {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), 296: case lists:member(ejabberd_auth_ldap, AuthMods) of 297: true -> {skip, "not supported for LDAP"}; 298: false -> escalus:init_per_testcase(CaseName, Config) 299: end; 300: init_per_testcase(check_password_hash, Config) -> 301: {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), 302: case lists:member(ejabberd_auth_ldap, AuthMods) of 303: true -> 304: {skip, not_fully_supported_with_ldap}; 305: false -> 306: AuthOpts = mongoose_helper:auth_opts_with_password_format(plain), 307: Config1 = mongoose_helper:backup_and_set_config_option(Config, {auth, host_type()}, 308: AuthOpts), 309: Config2 = escalus:create_users(Config1, escalus:get_users([carol])), 310: escalus:init_per_testcase(check_password_hash, Config2) 311: end; 312: init_per_testcase(delete_old_users, Config) -> 313: {_, AuthMods} = lists:keyfind(ctl_auth_mods, 1, Config), 314: case lists:member(ejabberd_auth_ldap, AuthMods) of 315: true -> {skip, not_fully_supported_with_ldap}; 316: false -> escalus:init_per_testcase(delete_old_users, Config) 317: end; 318: init_per_testcase(CaseName, Config) when CaseName == real_upload_without_content_type; 319: CaseName == real_upload_with_content_type -> 320: case mongoose_helper:should_minio_be_running(Config) of 321: true -> escalus:init_per_testcase(CaseName, Config); 322: false -> {skip, "minio is not running"} 323: end; 324: init_per_testcase(CaseName, Config) -> 325: escalus:init_per_testcase(CaseName, Config). 326: 327: end_per_testcase(delete_old_users, Config) -> 328: Users = escalus_users:get_users([alice, bob, kate, mike]), 329: lists:foreach(fun({_User, UserSpec}) -> 330: {Username, Domain, Pass} = get_user_data(UserSpec, Config), 331: JID = mongoose_helper:make_jid(Username, Domain), 332: rpc(mim(), ejabberd_auth, try_register, [JID, Pass]) 333: end, Users), 334: escalus:end_per_testcase(delete_old_users, Config); 335: end_per_testcase(check_password_hash, Config) -> 336: mongoose_helper:restore_config(Config), 337: escalus:delete_users(Config, escalus:get_users([carol])); 338: end_per_testcase(CaseName, Config) -> 339: %% Because kick_session fails with unexpected stanza received: 340: %% <presence from="alicE@localhost/res3" 341: %% to="alice@localhost/res1" type="unavailable" /> 342: %% TODO: Remove when escalus learns how to automatically deal 343: %% with 'unavailable' stanzas on client stop. 344: mongoose_helper:kick_everyone(), 345: escalus:end_per_testcase(CaseName, Config). 346: 347: %%-------------------------------------------------------------------- 348: %% http upload tests 349: %%-------------------------------------------------------------------- 350: upload_not_enabled(Config) -> 351: Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS("text/plain"), Config), 352: ?assertEqual({?HTTP_UPLOAD_NOT_ENABLED_ERROR, 1}, Ret). 353: 354: upload_wrong_filesize(Config) -> 355: Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_FILESIZE("0"), Config), 356: ?assertEqual({?HTTP_UPLOAD_FILESIZE_ERROR, 1}, Ret), 357: Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_FILESIZE("-1"), Config), 358: ?assertEqual({?HTTP_UPLOAD_FILESIZE_ERROR, 1}, Ret). 359: 360: upload_wrong_timeout(Config) -> 361: Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_TIMEOUT("0"), Config), 362: ?assertEqual({?HTTP_UPLOAD_TIMEOUT_ERROR, 1}, Ret), 363: Ret = mongooseimctl("http_upload", ?HTTP_UPLOAD_PARAMS_WITH_TIMEOUT("-1"), Config), 364: ?assertEqual({?HTTP_UPLOAD_TIMEOUT_ERROR, 1}, Ret). 365: 366: upload_returns_correct_urls_with_content_type(Config) -> 367: upload_returns_correct_urls(Config, "text/plain"). 368: 369: upload_returns_correct_urls_without_content_type(Config) -> 370: upload_returns_correct_urls(Config, ""). 371: 372: real_upload_with_content_type(Config) -> 373: real_upload(Config, "text/plain"). 374: 375: real_upload_without_content_type(Config) -> 376: real_upload(Config, ""). 377: 378: upload_returns_correct_urls(Config, ContentType) -> 379: HttpUploadParams = ?HTTP_UPLOAD_PARAMS(ContentType), 380: {Output, 0} = mongooseimctl("http_upload", HttpUploadParams, Config), 381: {PutURL, GetURL} = get_urls(Output), 382: WithACL = proplists:get_value(with_acl, Config), 383: check_urls(PutURL, GetURL, WithACL, ContentType). 384: 385: get_urls(Output) -> 386: [PutStr, GetStr | _] = string:split(Output, "\n", all), 387: PutURL = ?GET_URL("PutURL: ", PutStr), 388: GetURL = ?GET_URL("GetURL: ", GetStr), 389: {PutURL, GetURL}. 390: 391: check_urls(PutURL, GetURL, WithACL, ContentType) -> 392: check_bucket_url_and_filename(put, PutURL), 393: check_bucket_url_and_filename(get, GetURL), 394: check_substring(?S3_ALGORITHM_REGEX, PutURL), 395: check_substring(?S3_CREDENTIAL_REGEX, PutURL), 396: check_substring(?S3_DATE_REGEX, PutURL), 397: check_substring(?S3_EXPIRATION_REGEX, PutURL), 398: SignedHeadersRegex = signed_headers_regex(WithACL, ContentType), 399: check_substring(SignedHeadersRegex, PutURL), 400: check_substring(?S3_SIGNATURE_REGEX, PutURL). 401: 402: check_bucket_url_and_filename(Type, Url) -> 403: UrlRegex = case Type of 404: get -> ?S3_BASE_URL_REGEX"$"; 405: put -> ?S3_BASE_URL_REGEX"\?.*" 406: end, 407: ?assertMatch({match, [{0, _}]}, re:run(Url, UrlRegex)). 408: 409: check_substring(SubString, String) -> 410: ?assertMatch({match, [_]}, re:run(String, SubString, [global])). 411: 412: signed_headers_regex(false, "") -> ?S3_SIGNED_HEADERS; 413: signed_headers_regex(false, _) -> ?S3_SIGNED_HEADERS_WITH_CONTENT_TYPE; 414: signed_headers_regex(true, "") -> ?S3_SIGNED_HEADERS_WITH_ACL; 415: signed_headers_regex(true, _) -> ?S3_SIGNED_HEADERS_WITH_CONTENT_TYPE_AND_ACL. 416: 417: real_upload(Config, ContentType) -> 418: #{node := Node} = mim(), 419: BinPath = distributed_helper:bin_path(Node, Config), 420: UploadScript = filename:join(?config(mim_data_dir, Config), "test_file_upload.sh"), 421: Ret = mongooseimctl_helper:run(UploadScript, [ContentType], [{cd, BinPath}]), 422: ?assertMatch({_, 0}, Ret), 423: ok. 424: %%-------------------------------------------------------------------- 425: %% mod_admin_extra_accounts tests 426: %%-------------------------------------------------------------------- 427: 428: change_password(Config) -> 429: {User, Domain, OldPassword} = get_user_data(alice, Config), 430: mongooseimctl("change_password", [User, Domain, <<OldPassword/binary, $2>>], Config), 431: {error, {connection_step_failed, _, _}} = escalus_client:start_for(Config, alice, <<"newres">>), 432: mongooseimctl("change_password", [User, Domain, OldPassword], Config), 433: {ok, Alice2} = escalus_client:start_for(Config, alice, <<"newres2">>), 434: escalus_client:stop(Config, Alice2). 435: 436: check_password_hash(Config) -> 437: {User, Domain, Pass} = get_user_data(carol, Config), 438: MD5Hash = get_md5(Pass), 439: MD5HashBad = get_md5(<<Pass/binary, "bad">>), 440: SHAHash = get_sha(Pass), 441: 442: {_, 0} = mongooseimctl("check_password_hash", [User, Domain, MD5Hash, "md5"], Config), 443: {_, ErrCode} = mongooseimctl("check_password_hash", [User, Domain, MD5HashBad, "md5"], Config), 444: true = (ErrCode =/= 0), %% Must return code other than 0 445: {_, 0} = mongooseimctl("check_password_hash", [User, Domain, SHAHash, "sha"], Config). 446: 447: check_password(Config) -> 448: {User, Domain, Pass} = get_user_data(alice, Config), 449: MetricName = [backends, auth, check_password], 450: OldValue = get_metric(MetricName), 451: {_, 0} = mongooseimctl("check_password", [User, Domain, Pass], Config), 452: {_, ErrCode} = mongooseimctl("check_password", [User, Domain, <<Pass/binary, "Bad">>], Config), 453: mongoose_helper:wait_until( 454: fun() -> get_metric(MetricName) end, true, 455: #{validator => fun(NewValue) -> OldValue =/= NewValue end, name => ?FUNCTION_NAME}), 456: true = (ErrCode =/= 0). %% Must return code other than 0 457: 458: get_metric(MetricName) -> 459: HostType = domain_helper:host_type(mim), 460: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 461: {ok, Value} = rpc(mim(), mongoose_metrics, get_metric_value, [[HostTypePrefix | MetricName]]), 462: Value. 463: 464: check_account(Config) -> 465: {User, Domain, _Pass} = get_user_data(alice, Config), 466: 467: {_, 0} = mongooseimctl("check_account", [User, Domain], Config), 468: {_, ErrCode} = mongooseimctl("check_account", [<<User/binary, "Bad">>, Domain], Config), 469: true = (ErrCode =/= 0). %% Must return code other than 0 470: 471: ban_account(Config) -> 472: {User, Domain, Pass} = get_user_data(mike, Config), 473: 474: {ok, Mike} = escalus_client:start_for(Config, mike, <<"newres">>), 475: {_, 0} = mongooseimctl("ban_account", [User, Domain, "SomeReason"], Config), 476: escalus:assert(is_stream_error, [<<"conflict">>, <<"SomeReason">>], 477: escalus:wait_for_stanza(Mike)), 478: {error, {connection_step_failed, _, _}} = escalus_client:start_for(Config, mike, <<"newres2">>), 479: mongooseimctl("change_password", [User, Domain, Pass], Config), 480: escalus_connection:wait_for_close(Mike, 1000), 481: escalus_cleaner:remove_client(Config, Mike). 482: 483: num_active_users(Config) -> 484: %% Given some users with recorded last activity timestamps 485: {AliceName, Domain, _} = get_user_data(alice, Config), 486: {MikeName, Domain, _} = get_user_data(mike, Config), 487: {Mega, Secs, _} = os:timestamp(), 488: Now = Mega * 1000000 + Secs, 489: set_last(AliceName, Domain, Now), 490: set_last(MikeName, Domain, Now), 491: {SLastActiveBefore, _} = mongooseimctl("num_active_users", [Domain, "5"], Config), 492: %% When we artificially remove a user's last activity timestamp in the given period 493: TenDaysAgo = Now - 864000, 494: set_last(MikeName, Domain, TenDaysAgo), 495: %% Then we expect that the number of active users in the last 5 days is one less 496: %% than before the change above 497: {SLastActiveAfter, _} = mongooseimctl("num_active_users", [Domain, "5"], Config), 498: NLastActiveBefore = list_to_integer(string:strip(SLastActiveBefore, both, $\n)), 499: NLastActiveAfter = list_to_integer(string:strip(SLastActiveAfter, both, $\n)), 500: NLastActiveAfter = NLastActiveBefore - 1. 501: 502: delete_old_users(Config) -> 503: {AliceName, Domain, _} = get_user_data(alice, Config), 504: {BobName, Domain, _} = get_user_data(bob, Config), 505: {KateName, Domain, _} = get_user_data(kate, Config), 506: {MikeName, Domain, _} = get_user_data(mike, Config), 507: 508: {Mega, Secs, _} = os:timestamp(), 509: Now = Mega*1000000+Secs, 510: set_last(AliceName, Domain, Now), 511: set_last(BobName, Domain, Now), 512: set_last(MikeName, Domain, Now), 513: set_last(KateName, Domain, 0), 514: 515: {_, 0} = mongooseimctl("delete_old_users", ["10"], Config), 516: {_, 0} = mongooseimctl("check_account", [AliceName, Domain], Config), 517: {_, ErrCode} = mongooseimctl("check_account", [KateName, Domain], Config), 518: true = (ErrCode =/= 0). %% Must return code other than 0 519: 520: delete_old_users_vhost(Config) -> 521: {AliceName, Domain, _} = get_user_data(alice, Config), 522: {KateName, Domain, KatePass} = get_user_data(kate, Config), 523: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 524: 525: {Mega, Secs, _} = os:timestamp(), 526: Now = Mega*1000000+Secs, 527: set_last(AliceName, Domain, Now-86400*30), 528: 529: {_, 0} = mongooseimctl("register_identified", [KateName, SecDomain, KatePass], Config), 530: {_, 0} = mongooseimctl("check_account", [KateName, SecDomain], Config), 531: {_, 0} = mongooseimctl("delete_old_users_vhost", [SecDomain, "10"], Config), 532: {_, 0} = mongooseimctl("check_account", [AliceName, Domain], Config), 533: {_, ErrCode} = mongooseimctl("check_account", [KateName, SecDomain], Config), 534: true = (ErrCode =/= 0). %% Must return code other than 0 535: 536: %%-------------------------------------------------------------------- 537: %% mod_admin_extra_accounts tests 538: %%-------------------------------------------------------------------- 539: 540: %% Checks both num_resources and resource_num 541: num_resources_num(Config) -> 542: escalus:story(Config, [{alice, 3}, {bob, 1}], fun(_, Alice2, _, _) -> 543: {Username, Domain, _} = get_user_data(alice, Config), 544: ResName = binary_to_list(escalus_client:resource(Alice2)) ++ "\n", 545: 546: {"3\n", _} = mongooseimctl("num_resources", [Username, Domain], Config), 547: {ResName, _} = mongooseimctl("resource_num", [Username, Domain, "2"], Config) 548: end). 549: 550: kick_session(Config) -> 551: escalus:story(Config, [{alice, 1}], fun(Alice) -> 552: Username = escalus_client:username(Alice), 553: Domain = escalus_client:server(Alice), 554: Resource = escalus_client:resource(Alice), 555: Args = [Username, Domain, Resource, "Because I can!"], 556: 557: {_, 0} = mongooseimctl("kick_session", Args, Config), 558: Stanza = escalus:wait_for_stanza(Alice), 559: escalus:assert(is_stream_error, [<<"conflict">>, <<"Because I can!">>], Stanza) 560: end). 561: 562: status(Config) -> 563: escalus:story(Config, [{alice, 1}, {mike, 1}, {bob, 1}], fun(User1, User2, User3) -> 564: PriDomain = escalus_client:server(User1), 565: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 566: AwayPresence = escalus_stanza:presence_show(<<"away">>), 567: escalus_client:send(User2, AwayPresence), 568: 569: {"2\n", _} = mongooseimctl("status_num", ["available"], Config), 570: 571: {"2\n", _} = mongooseimctl("status_num_host", [PriDomain, "available"], Config), 572: {"0\n", _} = mongooseimctl("status_num_host", [SecDomain, "available"], Config), 573: 574: {StatusList, _} = mongooseimctl("status_list", ["available"], Config), 575: match_user_status([User1, User3], StatusList), 576: 577: {StatusList2, _} = mongooseimctl("status_list_host", 578: [PriDomain, "available"], Config), 579: match_user_status([User1, User3], StatusList2), 580: {[], _} = mongooseimctl("status_list_host", [SecDomain, "available"], Config) 581: end). 582: 583: sessions_info(Config) -> 584: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(User1, User2, User3) -> 585: Username1 = escalus_client:username(User1), 586: PriDomain = escalus_client:server(User1), 587: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 588: AwayPresence = escalus_stanza:presence_show(<<"away">>), 589: escalus_client:send(User2, AwayPresence), 590: 591: {UserList, _} = mongooseimctl("connected_users_info", [], Config), 592: match_user_info([User1, User2, User3], UserList), 593: 594: {UserList2, _} = mongooseimctl("connected_users_vhost", [PriDomain], Config), 595: match_user_info([User1, User2, User3], UserList2), 596: {[], _} = mongooseimctl("connected_users_vhost", [SecDomain], Config), 597: 598: {UserList3, _} = mongooseimctl("user_sessions_info", 599: [Username1, PriDomain], Config), 600: match_user_info([User1], UserList3) 601: end). 602: 603: set_presence(Config) -> 604: escalus:story(Config, [{alice, 1}], fun(Alice) -> 605: Username = escalus_client:username(Alice), 606: Domain = escalus_client:server(Alice), 607: Resource = escalus_client:resource(Alice), 608: 609: {_, 0} = mongooseimctl("set_presence", 610: [Username, Domain, Resource, 611: "available", "away", "mystatus", "10"], 612: Config), 613: Presence = escalus:wait_for_stanza(Alice), 614: escalus:assert(is_presence_with_show, [<<"away">>], Presence), 615: escalus:assert(is_presence_with_status, [<<"mystatus">>], Presence), 616: escalus:assert(is_presence_with_priority, [<<"10">>], Presence) 617: end). 618: 619: %%-------------------------------------------------------------------- 620: %% mod_admin_extra_vcard tests 621: %%-------------------------------------------------------------------- 622: 623: vcard_rw(Config) -> 624: {Username, Domain, _} = get_user_data(alice, Config), 625: 626: {_, ExitCode} = mongooseimctl("get_vcard", [Username, Domain, "NICKNAME"], Config), 627: true = (ExitCode /= 0), 628: 629: {_, 0} = mongooseimctl("set_vcard", [Username, Domain, "NICKNAME", "SomeNickname"], Config), 630: {"SomeNickname\n", 0} = mongooseimctl("get_vcard", [Username, Domain, "NICKNAME"], Config). 631: 632: vcard2_rw(Config) -> 633: {Username, Domain, _} = get_user_data(alice, Config), 634: 635: {_, ExitCode} = mongooseimctl("get_vcard2", [Username, Domain, "ORG", "ORGNAME"], Config), 636: true = (ExitCode /= 0), 637: 638: {_, 0} = mongooseimctl("set_vcard2", [Username, Domain, "ORG", "ORGNAME", "ESL"], Config), 639: {"ESL\n", 0} = mongooseimctl("get_vcard2", [Username, Domain, "ORG", "ORGNAME"], Config). 640: 641: vcard2_multi_rw(Config) -> 642: {Username, Domain, _} = get_user_data(alice, Config), 643: 644: {_, ExitCode} = mongooseimctl("get_vcard2_multi", [Username, Domain, "ORG", "ORGUNIT"], Config), 645: true = (ExitCode /= 0), 646: 647: Args = [Username, Domain, "ORG", "ORGUNIT", "sales;marketing"], 648: {_, 0} = mongooseimctl("set_vcard2_multi", Args, Config), 649: {OrgUnits0, 0} = mongooseimctl("get_vcard2_multi", 650: [Username, Domain, "ORG", "ORGUNIT"], Config), 651: OrgUnits = string:tokens(OrgUnits0, "\n"), 652: 2 = length(OrgUnits), 653: true = (lists:member("sales", OrgUnits) andalso lists:member("marketing", OrgUnits)). 654: 655: %%-------------------------------------------------------------------- 656: %% mod_admin_extra_vcard tests 657: %%-------------------------------------------------------------------- 658: 659: rosteritem_rw(Config) -> 660: escalus:story(Config, [{alice, 1}], fun(Alice) -> 661: BobJid = escalus_users:get_jid(Config, bob), 662: MikeJid = escalus_users:get_jid(Config, mike), 663: 664: {AliceName, Domain, _} = get_user_data(alice, Config), 665: {BobName, Domain, _} = get_user_data(bob, Config), 666: {MikeName, Domain, _} = get_user_data(mike, Config), 667: 668: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 669: {_, 0} = mongooseimctl("add_rosteritem", 670: [AliceName, Domain, MikeName, 671: Domain, "My Mike", 672: "My Group", "both"], Config), 673: 674: [Push1, Push2] = escalus:wait_for_stanzas(Alice, 2), % Check roster broadcasts 675: escalus:assert(is_roster_set, Push1), 676: escalus:assert(roster_contains, [BobJid], Push1), 677: escalus:assert(is_roster_set, Push2), 678: escalus:assert(roster_contains, [MikeJid], Push2), 679: 680: {Items1, 0} = mongooseimctl("get_roster", [AliceName, Domain], Config), 681: match_roster([{BobName, Domain, "MyBob", "MyGroup", "both"}, 682: {MikeName, Domain, "MyMike", "MyGroup", "both"}], Items1), 683: 684: escalus:send(Alice, escalus_stanza:roster_get()), 685: Roster1 = escalus:wait_for_stanza(Alice), 686: escalus:assert(is_roster_result, Roster1), 687: escalus:assert(roster_contains, [BobJid], Roster1), 688: escalus:assert(roster_contains, [MikeJid], Roster1), 689: 690: {_, 0} = mongooseimctl("delete_rosteritem", 691: [AliceName, Domain, BobName, Domain], 692: Config), 693: 694: Push3 = escalus:wait_for_stanza(Alice), 695: escalus:assert(is_roster_set, Push3), 696: escalus:assert(roster_contains, [BobJid], Push3), 697: 698: {Items2, 0} = mongooseimctl("get_roster", [AliceName, Domain], Config), 699: match_roster([{MikeName, Domain, "MyMike", "MyGroup", "both"}], Items2), 700: 701: escalus:send(Alice, escalus_stanza:roster_remove_contact(MikeJid)), % cleanup 702: escalus:wait_for_stanzas(Alice, 2, 5000) 703: end). 704: 705: presence_after_add_rosteritem(Config) -> 706: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 707: BobJid = escalus_users:get_jid(Config, bob), 708: {AliceName, Domain, _} = get_user_data(alice, Config), 709: {BobName, Domain, _} = get_user_data(bob, Config), 710: 711: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 712: 713: escalus:send(Alice, escalus_stanza:presence(<<"available">>)), 714: escalus:assert(is_presence, escalus:wait_for_stanza(Bob)), 715: 716: escalus:send(Alice, escalus_stanza:roster_remove_contact(BobJid)), % cleanup 717: %% Wait for stanzas, so they would not end up in the next story 718: escalus:wait_for_stanzas(Alice, 3, 5000) 719: end). 720: 721: push_roster(Config) -> 722: escalus:story(Config, [{alice, 1}], fun(Alice) -> 723: BobJid = escalus_users:get_jid(Config, bob), 724: {AliceName, Domain, _} = get_user_data(alice, Config), 725: TemplatePath = escalus_config:get_config(roster_template, Config), 726: 727: {_, 0} = mongooseimctl("push_roster", [TemplatePath, AliceName, Domain], Config), 728: escalus:send(Alice, escalus_stanza:roster_get()), 729: Roster1 = escalus:wait_for_stanza(Alice), 730: escalus:assert(is_roster_result, Roster1), 731: escalus:assert(roster_contains, [BobJid], Roster1), 732: 733: escalus:send(Alice, escalus_stanza:roster_remove_contact(BobJid)), % cleanup 734: escalus:wait_for_stanzas(Alice, 2, 5000) 735: end). 736: 737: process_rosteritems_list_simple(Config) -> 738: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 739: %% given 740: Action = "list", 741: Subs = "any", 742: Asks = "any", 743: User = escalus_client:short_jid(Alice), 744: Contact = string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), 745: {AliceName, Domain, _} = get_user_data(alice, Config), 746: {BobName, Domain, _} = get_user_data(bob, Config), 747: %% when 748: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 749: _S = escalus:wait_for_stanzas(Alice, 2), 750: {R, 0} = mongooseimctl("process_rosteritems", [Action, Subs, Asks, User, Contact], Config), 751: %% then 752: {match, _} = re:run(R, ".*Matches:.*" ++ Contact ++ ".*"), 753: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, BobName, Domain], Config) 754: end). 755: 756: process_rosteritems_list_nomatch(Config) -> 757: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 758: %% given 759: Action = "list", 760: Subs = "from:both", 761: Asks = "any", 762: User = escalus_client:short_jid(Alice), 763: Contact =string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), 764: {AliceName, Domain, _} = get_user_data(alice, Config), 765: {BobName, Domain, _} = get_user_data(bob, Config), 766: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, BobName, 767: Domain, "MyBob", "MyGroup", "to"], Config), 768: escalus:wait_for_stanzas(Alice, 2), 769: %% when 770: {R, 0} = mongooseimctl("process_rosteritems", [Action, Subs, Asks, User, Contact], Config), 771: %% then 772: nomatch = re:run(R, ".*Matches:.*" ++ Contact ++ ".*"), 773: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, BobName, Domain], Config) 774: end). 775: 776: process_rosteritems_list_advanced1(Config) -> 777: escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> 778: %% given 779: Action = "list", 780: Subs = "from:both", 781: Asks = "any", 782: User = escalus_client:short_jid(Alice), 783: {AliceName, Domain, _} = get_user_data(alice, Config), 784: {MikeName, Domain, _} = get_user_data(mike, Config), 785: {KateName, Domain, _} = get_user_data(kate, Config), 786: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 787: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 788: ContactsRegexp = ContactMike ++ ":" ++ 789: string:substr(binary_to_list(KateName), 1, 2) ++ 790: ".*@.*", 791: 792: {_, 0} = add_rosteritem2(AliceName, Domain, MikeName, Domain, Config), 793: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, 794: Domain, "BestFriend", "MyGroup", "both"], Config), 795: escalus:wait_for_stanzas(Alice, 4), 796: %% when 797: {R, 0} = mongooseimctl("process_rosteritems", 798: [Action, Subs, Asks, User, ContactsRegexp], 799: Config), 800: %% then 801: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 802: {match, _} = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 803: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 804: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) 805: end). 806: 807: process_rosteritems_delete_advanced(Config) -> 808: escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> 809: %% given 810: Action = "delete", 811: Subs = "from", 812: Asks = "any", 813: User = escalus_client:short_jid(Alice), 814: {AliceName, Domain, _} = get_user_data(alice, Config), 815: {MikeName, Domain, _} = get_user_data(mike, Config), 816: {KateName, Domain, _} = get_user_data(kate, Config), 817: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 818: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 819: ContactsRegexp = ".*" ++ string:substr(ContactMike, 3) ++ 820: ":" ++ string:substr(ContactKate, 1, 2) ++ 821: "@" ++ binary_to_list(Domain), 822: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, MikeName, 823: Domain, "DearMike", "MyGroup", "from"], Config), 824: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, 825: Domain, "Friend", "MyGroup", "from"], Config), 826: escalus:wait_for_stanzas(Alice, 4), 827: %% when 828: {R, 0} = mongooseimctl("process_rosteritems", 829: [Action, Subs, Asks, User, ContactsRegexp], 830: Config), 831: %% then 832: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 833: nomatch = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 834: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 835: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) 836: end). 837: 838: process_rosteritems_list_advanced2(Config) -> 839: escalus:story(Config, [{alice, 1}, {mike, 1}, {kate, 1}], fun(Alice, Mike, Kate) -> 840: %% given 841: Action = "list", 842: Subs = "any", 843: Asks = "any", 844: User = escalus_client:short_jid(Alice), 845: {AliceName, Domain, _} = get_user_data(alice, Config), 846: {MikeName, Domain, _} = get_user_data(mike, Config), 847: {KateName, Domain, _} = get_user_data(kate, Config), 848: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 849: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 850: ContactsRegexp = ".*e@lo.*", 851: {_, 0} = add_rosteritem2(AliceName, Domain, MikeName, Domain, Config), 852: {_, 0} = mongooseimctl("add_rosteritem", [AliceName, Domain, KateName, 853: Domain, "KateFromSchool", 854: "MyGroup", "from"], Config), 855: escalus:wait_for_stanzas(Alice, 4), 856: %% when 857: {R, 0} = mongooseimctl("process_rosteritems", 858: [Action, Subs, Asks, User, ContactsRegexp], 859: Config), 860: %% then 861: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 862: {match, _} = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 863: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 864: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config) 865: end). 866: 867: process_rosteritems_delete_advanced2(Config) -> 868: escalus:story(Config, [{alice, 1}, {bob, 1}, {mike, 1}, {kate, 1}], 869: fun(Alice, Bob, Mike, Kate) -> 870: %% given 871: Action = "delete", 872: Subs = "to:from", 873: Asks = "any", 874: User = "al.c[e]@.*host:((b[o]b)|(mike))@loc.*t2", 875: {AliceName, Domain, _} = get_user_data(alice, Config), 876: {BobName, Domain, _} = get_user_data(bob, Config), 877: {MikeName, Domain, _} = get_user_data(mike, Config), 878: {KateName, Domain, _} = get_user_data(kate, Config), 879: ContactMike = string:to_lower(binary_to_list(escalus_client:short_jid(Mike))), 880: ContactKate= string:to_lower(binary_to_list(escalus_client:short_jid(Kate))), 881: ContactBob= string:to_lower(binary_to_list(escalus_client:short_jid(Bob))), 882: ContactsReg = ".ik[ea]@localho+.*:k@loc.*st:(alice)+@.*:no", 883: {_, 0} = mongooseimctl("add_rosteritem", 884: [AliceName, Domain, MikeName, 885: Domain, "DearMike", "MyGroup", "to"], 886: Config), 887: {_, 0} = mongooseimctl("add_rosteritem", 888: [AliceName, Domain, KateName, 889: Domain, "HateHerSheHasSoNiceLegs", 890: "MyGroup", "to"], Config), 891: {_, 0} = mongooseimctl("add_rosteritem", [BobName, Domain, AliceName, 892: Domain, "Girlfriend", "MyGroup", "from"], Config), 893: escalus:wait_for_stanzas(Alice, 4), 894: escalus:wait_for_stanzas(Bob, 2), 895: %% when 896: {R, 0} = mongooseimctl("process_rosteritems", 897: [Action, Subs, Asks, User, ContactsReg], 898: Config), 899: %% then 900: {match, _} = re:run(R, ".*Matches:.*" ++ ContactMike ++ ".*"), 901: nomatch = re:run(R, ".*Matches:.*" ++ ContactKate ++ ".*"), 902: nomatch = re:run(R, ".*Matches:.*" ++ ContactBob ++ ".*"), 903: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, MikeName, Domain], Config), 904: {_, 0} = mongooseimctl("delete_rosteritem", [AliceName, Domain, KateName, Domain], Config), 905: {_, 0} = mongooseimctl("delete_rosteritem", [BobName, Domain, AliceName, Domain], Config) 906: end). 907: 908: push_roster_all(Config) -> 909: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 910: TemplatePath = escalus_config:get_config(roster_template, Config), 911: 912: {_, 0} = mongooseimctl("push_roster_all", [TemplatePath], Config), 913: 914: escalus:send(Alice, escalus_stanza:roster_get()), 915: Roster1 = escalus:wait_for_stanza(Alice), 916: escalus:assert(is_roster_result, Roster1), 917: BobJid = escalus_client:short_jid(Bob), 918: escalus:assert(roster_contains, [BobJid], Roster1), 919: 920: escalus:send(Bob, escalus_stanza:roster_get()), 921: Roster2 = escalus:wait_for_stanza(Bob), 922: escalus:assert(is_roster_result, Roster2), 923: AliceJid = escalus_client:short_jid(Alice), 924: escalus:assert(roster_contains, [AliceJid], Roster2), 925: 926: escalus:send_and_wait(Alice, escalus_stanza:roster_remove_contact(bob)), % cleanup 927: escalus:send_and_wait(Bob, escalus_stanza:roster_remove_contact(alice)) % cleanup 928: end). 929: 930: push_roster_alltoall(Config) -> 931: escalus:story(Config, [{alice, 1}], fun(Alice) -> 932: BobJid = escalus_users:get_jid(Config, bob), 933: MikeJid = escalus_users:get_jid(Config, mike), 934: KateJid = escalus_users:get_jid(Config, kate), 935: {_, Domain, _} = get_user_data(alice, Config), 936: 937: {_, 0} = mongooseimctl("push_roster_alltoall", [Domain, "MyGroup"], Config), 938: 939: escalus:send(Alice, escalus_stanza:roster_get()), 940: Roster = escalus:wait_for_stanza(Alice), 941: 942: escalus:assert(is_roster_result, Roster), 943: escalus:assert(roster_contains, [BobJid], Roster), 944: escalus:assert(roster_contains, [MikeJid], Roster), 945: escalus:assert(roster_contains, [KateJid], Roster) 946: end). 947: 948: %%-------------------------------------------------------------------- 949: %% mod_admin_extra_last tests 950: %%-------------------------------------------------------------------- 951: 952: set_last(Config) -> 953: escalus:story(Config, [{alice, 1}], fun(Alice) -> 954: BobJid = escalus_users:get_jid(Config, bob), 955: {AliceName, Domain, _} = get_user_data(alice, Config), 956: {BobName, Domain, _} = get_user_data(bob, Config), 957: 958: {_, 0} = add_rosteritem1(AliceName, Domain, BobName, Config), 959: {_, 0} = mongooseimctl("add_rosteritem", 960: [BobName, Domain, AliceName, 961: Domain, "MyAlice", "MyGroup", "both"], 962: Config), 963: 964: escalus:wait_for_stanza(Alice), % ignore push 965: 966: Now = os:system_time(second), 967: TS = integer_to_list(Now - 7200), 968: {_, 0} = mongooseimctl("set_last", [BobName, Domain, TS, "Status"], Config), 969: escalus:send(Alice, escalus_stanza:last_activity(BobJid)), 970: LastAct = escalus:wait_for_stanza(Alice), 971: escalus:assert(is_last_result, LastAct), 972: Seconds = list_to_integer(binary_to_list( 973: exml_query:path(LastAct, [{element, <<"query">>}, 974: {attr, <<"seconds">>}]))), 975: true = (( (Seconds > 7100) andalso (Seconds < 7300) ) orelse Seconds), 976: 977: {_, 0} = mongooseimctl("delete_rosteritem", 978: [AliceName, Domain, BobName, Domain], 979: Config), % cleanup 980: {_, 0} = mongooseimctl("delete_rosteritem", 981: [BobName, Domain, AliceName, Domain], 982: Config) 983: end). 984: 985: %%-------------------------------------------------------------------- 986: %% mod_admin_extra_private tests 987: %%-------------------------------------------------------------------- 988: 989: private_rw(Config) -> 990: {AliceName, Domain, _} = get_user_data(alice, Config), 991: XmlEl1 = "<secretinfo xmlns=\"nejmspejs\">1</secretinfo>", 992: XmlEl2 = "<secretinfo xmlns=\"inny\">2</secretinfo>", 993: 994: {_, 0} = mongooseimctl("private_set", [AliceName, Domain, XmlEl1], Config), 995: {_, 0} = mongooseimctl("private_set", [AliceName, Domain, XmlEl2], Config), 996: 997: {Result, 0} = mongooseimctl("private_get", 998: [AliceName, Domain, "secretinfo", "nejmspejs"], 999: Config), 1000: {ok, #xmlel{ name = <<"secretinfo">>, attrs = [{<<"xmlns">>, <<"nejmspejs">>}], 1001: children = [#xmlcdata{ content = <<"1">> }]}} = exml:parse(list_to_binary(Result)). 1002: 1003: %%-------------------------------------------------------------------- 1004: %% mod_admin_extra_stanza tests 1005: %%-------------------------------------------------------------------- 1006: 1007: send_message(Config) -> 1008: escalus:story(Config, [{alice, 1}, {bob, 2}], fun(Alice, Bob1, Bob2) -> 1009: {_, 0} = mongooseimctl("send_message_chat", [escalus_client:full_jid(Alice), 1010: escalus_client:full_jid(Bob1), 1011: "Hi Bob!"], Config), 1012: Stanza1 = escalus:wait_for_stanza(Bob1), 1013: escalus:assert(is_chat_message, [<<"Hi Bob!">>], Stanza1), 1014: 1015: {_, 0} = mongooseimctl("send_message_headline", 1016: [escalus_client:full_jid(Alice), 1017: escalus_client:short_jid(Bob1), 1018: "Subj", "Hi Bob!!"], Config), 1019: Stanza2 = escalus:wait_for_stanza(Bob1), 1020: Stanza3 = escalus:wait_for_stanza(Bob2), 1021: escalus:assert(is_headline_message, [<<"Subj">>, <<"Hi Bob!!">>], Stanza2), 1022: escalus:assert(is_headline_message, [<<"Subj">>, <<"Hi Bob!!">>], Stanza3) 1023: end). 1024: 1025: send_message_wrong_jid(Config) -> 1026: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1027: {_, Err1} = mongooseimctl("send_message_chat", ["@@#$%!!.§§£", 1028: escalus_client:full_jid(Bob), 1029: "Hello bobby!"], Config), 1030: {_, Err2} = mongooseimctl("send_message_headline", ["%%@&@&@==//\///", 1031: escalus_client:short_jid(Bob), 1032: "Subj", "Are 1033: you there?"], 1034: Config), 1035: true = Err1 =/= 0, 1036: true = Err2 =/= 0, 1037: escalus_assert:has_no_stanzas(Alice), 1038: escalus_assert:has_no_stanzas(Bob) 1039: end). 1040: 1041: send_stanza(Config) -> 1042: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1043: Domain = escalus_client:server(Alice), 1044: Resource = escalus_client:resource(Alice), 1045: {BobName, _, _} = get_user_data(bob, Config), 1046: BobJID = <<BobName/binary, $@, Domain/binary, $/, 1047: (escalus_client:resource(Bob))/binary>>, 1048: 1049: Stanza = Stanza = create_stanza(Alice, BobJID), 1050: {_, 0} = mongooseimctl("send_stanza_c2s", 1051: [BobName, Domain, Resource, Stanza], 1052: Config), 1053: 1054: Message = escalus:wait_for_stanza(Alice), 1055: escalus:assert(is_chat_message, [<<"Hi">>], Message), 1056: escalus:assert(is_stanza_from, [Bob], Message) 1057: end). 1058: 1059: send_stanzac2s_wrong(Config) -> 1060: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 1061: Domain = escalus_client:server(Alice), 1062: Resource = escalus_client:resource(Alice), 1063: WrongBobName = "bobby_the_great", 1064: {BobName, _, _} = get_user_data(bob, Config), 1065: BobJID = <<BobName/binary, $@, Domain/binary, $/, (escalus_client:resource(Bob))/binary>>, 1066: Stanza = create_stanza(Alice, BobJID), 1067: StanzaWrong = <<"<iq type='get' id='234234'><xmlns='wrongwrong'>">>, 1068: {_, Err} = mongooseimctl("send_stanza_c2s", 1069: [WrongBobName, Domain, Resource, Stanza], 1070: Config), 1071: {_, Err2} = mongooseimctl("send_stanza_c2s", 1072: [BobName, Domain, Resource, StanzaWrong], 1073: Config), 1074: 1075: true = Err =/= 0, 1076: true = Err2 =/= 0, 1077: escalus_assert:has_no_stanzas(Alice) 1078: end). 1079: 1080: create_stanza(Name1, JID2) -> 1081: exml:to_binary(escalus_stanza:from(escalus_stanza:chat_to(Name1, "Hi"), JID2)). 1082: 1083: %%-------------------------------------------------------------------- 1084: %% mod_admin_extra_stats tests 1085: %%-------------------------------------------------------------------- 1086: 1087: stats_global(Config) -> 1088: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(_Alice, _Bob) -> 1089: RegisteredCount = length(escalus_config:get_config(escalus_users, Config, [])), 1090: Registered = integer_to_list(RegisteredCount) ++ "\n", 1091: 1092: {UpTime, 0} = mongooseimctl("stats", ["uptimeseconds"], Config), 1093: _ = list_to_integer(string:strip(UpTime, both, $\n)), 1094: {Registered, 0} = mongooseimctl("stats", ["registeredusers"], Config), 1095: 1096: {"2\n", 0} = mongooseimctl("stats", ["onlineusersnode"], Config), 1097: 1098: {"2\n", 0} = mongooseimctl("stats", ["onlineusers"], Config) 1099: end). 1100: 1101: stats_host(Config) -> 1102: escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, _Bob) -> 1103: RegisteredCount = length(escalus_config:get_config(escalus_users, Config, [])), 1104: Registered = integer_to_list(RegisteredCount) ++ "\n", 1105: 1106: PriDomain = escalus_client:server(Alice), 1107: SecDomain = ct:get_config({hosts, mim, secondary_domain}), 1108: 1109: {Registered, 0} = mongooseimctl("stats_host", 1110: ["registeredusers", PriDomain], Config), 1111: {"0\n", 0} = mongooseimctl("stats_host", ["registeredusers", SecDomain], Config), 1112: 1113: {"2\n", 0} = mongooseimctl("stats_host", ["onlineusers", PriDomain], Config), 1114: {"0\n", 0} = mongooseimctl("stats_host", ["onlineusers", SecDomain], Config) 1115: end). 1116: 1117: %%-------------------------------------------------------------------- 1118: %% mongoose_graphql tests 1119: %%-------------------------------------------------------------------- 1120: 1121: can_execute_admin_queries_with_permissions(Config) -> 1122: Query = "query { checkAuth }", 1123: Res = mongooseimctl("graphql", [Query], Config), 1124: ?assertMatch({_, 0}, Res), 1125: Data = element(1, Res), 1126: ?assertNotEqual(nomatch, string:find(Data, "AUTHORIZED")). 1127: 1128: can_handle_execution_error(Config) -> 1129: Query = "{}", 1130: Res = mongooseimctl("graphql", [Query], Config), 1131: ?assertMatch({_, 0}, Res), 1132: Data = element(1, Res), 1133: ?assertNotEqual(nomatch, string:find(Data, "parser_error")). 1134: 1135: graphql_wrong_arguments_number(Config) -> 1136: ExpectedFragment = "This command requires", 1137: ResNoArgs = mongooseimctl("graphql", [], Config), 1138: ?assertMatch({_, 1}, ResNoArgs), 1139: Data1 = element(1, ResNoArgs), 1140: ?assertNotEqual(nomatch, string:find(Data1, ExpectedFragment)), 1141: 1142: ResTooManyArgs = mongooseimctl("graphql", ["{}", "{}"], Config), 1143: ?assertMatch({_, 1}, ResTooManyArgs), 1144: Data2 = element(1, ResTooManyArgs), 1145: ?assertNotEqual(nomatch, string:find(Data2, ExpectedFragment)). 1146: 1147: %%----------------------------------------------------------------- 1148: %% Improve coverage 1149: %%----------------------------------------------------------------- 1150: 1151: simple_register(Config) -> 1152: %% given 1153: Domain = domain(), 1154: {Name, Password} = {<<"tyler">>, <<"durden">>}, 1155: %% when 1156: {R1, 0} = mongooseimctl("registered_users", [Domain], Config), 1157: Before = length(string:tokens(R1, "\n")), 1158: {_, 0} = mongooseimctl("register", [Domain, Password], Config), 1159: {_, 0} = mongooseimctl("register_identified", [Name, Domain, Password], Config), 1160: 1161: {R2, 0} = mongooseimctl("registered_users", [Domain], Config), 1162: After = length(string:tokens(R2, "\n")), 1163: %% then 1164: 2 = After - Before. 1165: 1166: simple_unregister(Config) -> 1167: %% given 1168: Domain = domain(), 1169: {Name, _} = {<<"tyler">>, <<"durden">>}, 1170: %% when 1171: {_, 0} = mongooseimctl("unregister", [Name, Domain], Config), 1172: {R2, 0} = mongooseimctl("registered_users", [Domain], Config), 1173: %% then 1174: nomatch = re:run(R2, ".*(" ++ binary_to_list(Name) ++ ").*"). 1175: 1176: register_twice(Config) -> 1177: %% given 1178: Domain = domain(), 1179: {Name, Password} = {<<"tyler">>, <<"durden">>}, 1180: %% when 1181: {_, 0} = mongooseimctl("register_identified", [Name, Domain, Password], Config), 1182: {R, Code} = mongooseimctl("register_identified", [Name, Domain, Password], Config), 1183: %% then 1184: {match, _} = re:run(R, ".*(already registered).*"), 1185: true = (Code =/= 0), 1186: {_, 0} = mongooseimctl("unregister", [Name, Domain], Config). 1187: 1188: backup_restore_mnesia(Config) -> 1189: %% given 1190: TableName = passwd, 1191: TableSize = rpc_call(mnesia, table_info, [TableName, size]), 1192: %% Table passwd should not be empty 1193: FileName = "backup_mnesia.bup", 1194: %% when 1195: {R, 0} = mongooseimctl("backup", [FileName], Config), 1196: nomatch = re:run(R, ".+"), 1197: rpc_call(mnesia, clear_table, [TableName]), 1198: 0 = rpc_call(mnesia, table_info, [TableName, size]), 1199: {R2, 0} = mongooseimctl("restore", [FileName], Config), 1200: %% then 1201: nomatch = re:run(R2, ".+"), 1202: TableSize = rpc_call(mnesia, table_info, [TableName, size]). 1203: 1204: restore_mnesia_wrong(Config) -> 1205: FileName = "file that doesnt exist13123.bup", 1206: {R2, _} = mongooseimctl("restore", [FileName], Config), 1207: {match, Code} = re:run(R2, ".+"), 1208: true = (Code =/= 0). 1209: 1210: dump_and_load(Config) -> 1211: FileName = "dump.bup", 1212: TableName = passwd, 1213: %% Table passwd should not be empty 1214: TableSize = rpc_call(mnesia, table_info, [TableName, size]), 1215: {DumpReturns, 0} = mongooseimctl("dump", [FileName], Config), 1216: ct:log("DumpReturns ~p", [DumpReturns]), 1217: {ok, DumpData} = rpc_call(file, consult, [FileName]), 1218: ct:log("DumpData ~p", [DumpData]), 1219: rpc_call(mnesia, clear_table, [TableName]), 1220: 0 = rpc_call(mnesia, table_info, [TableName, size]), 1221: {R, 0} = mongooseimctl("load", [FileName], Config), 1222: ct:log("LoadReturns ~p", [R]), 1223: {match, _} = re:run(R, ".+"), 1224: TableSize = rpc_call(mnesia, table_info, [TableName, size]). 1225: 1226: load_mnesia_wrong(Config) -> 1227: FileName = "file that doesnt existRHCP.bup", 1228: {R2, Code} = mongooseimctl("restore", [FileName], Config), 1229: {match, _} = re:run(R2, ".+"), 1230: true = (Code =/= 0). 1231: 1232: dump_table(Config) -> 1233: FileName = "dump.mn", 1234: TableName = passwd, 1235: %% Table passwd should not be empty 1236: TableSize = rpc_call(mnesia, table_info, [TableName, size]), 1237: {_, 0} = mongooseimctl("dump_table", [FileName, atom_to_list(TableName)], Config), 1238: rpc_call(mnesia, clear_table, [TableName]), 1239: 0 = rpc_call(mnesia, table_info, [TableName, size]), 1240: {R, 0} = mongooseimctl("load", [FileName], Config), 1241: {match, _} = re:run(R, ".+"), 1242: TableSize = rpc_call(mnesia, table_info, [TableName, size]). 1243: 1244: get_loglevel(Config) -> 1245: {R, 0} = mongooseimctl("get_loglevel", [], Config), 1246: LogLevel = rpc_call(mongoose_logs, get_global_loglevel, []), 1247: Regexp = io_lib:format("global loglevel is \(.\)\{1,2\}, which means '~p'", [LogLevel]), 1248: {match, _} = re:run(R, Regexp, [{capture, first}]). 1249: 1250: remove_old_messages_test(Config) -> 1251: escalus:story(Config, [{alice, 1}], fun(_) -> 1252: %% given 1253: JidA = nick_to_jid(alice, Config), 1254: JidB = nick_to_jid(bob, Config), 1255: JidRecordAlice = jid:from_binary(JidA), 1256: JidRecordBob = jid:from_binary(JidB), 1257: Domain = domain(), 1258: Msg1 = escalus_stanza:chat_to(<<"bob@", Domain/binary>>, 1259: "Hi, how are you? Its old message!"), 1260: Msg2 = escalus_stanza:chat_to(<<"bob@", Domain/binary>>, 1261: "Hello its new message!"), 1262: OldTimestamp = fallback_timestamp(10, os:system_time(microsecond)), 1263: OfflineOld = generate_offline_message(JidRecordAlice, JidRecordBob, Msg1, OldTimestamp), 1264: OfflineNew = generate_offline_message(JidRecordAlice, JidRecordBob, Msg2, os:system_time(microsecond)), 1265: {jid, _, _, _, LUser, LServer, _} = JidRecordBob, 1266: HostType = host_type(), 1267: rpc_call(mod_offline_backend, write_messages, [host_type(), LUser, LServer, [OfflineOld, OfflineNew]]), 1268: %% when 1269: {_, 0} = mongooseimctl("delete_old_messages", [LServer, "1"], Config), 1270: {ok, SecondList} = rpc_call(mod_offline_backend, pop_messages, [HostType, JidRecordBob]), 1271: %% then 1272: 1 = length(SecondList) 1273: end). 1274: 1275: remove_expired_messages_test(Config) -> 1276: escalus:story(Config, [{mike, 1}], fun(_) -> 1277: %% given 1278: JidA = nick_to_jid(mike, Config), 1279: JidB = nick_to_jid(kate, Config), 1280: JidRecordMike = jid:from_binary(JidA), 1281: JidRecordKate = jid:from_binary(JidB), 1282: Domain = domain(), 1283: Msg1 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Rolling stones"), 1284: Msg2 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "Arctic monkeys!"), 1285: Msg3 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "More wine..."), 1286: Msg4 = escalus_stanza:chat_to(<<"kate@", Domain/binary>>, "kings of leon"), 1287: OldTimestamp = fallback_timestamp(10, os:system_time(microsecond)), 1288: ExpirationTime = fallback_timestamp(2, os:system_time(microsecond)), 1289: ExpirationTimeFuture= fallback_timestamp(-5, os:system_time(microsecond)), 1290: OfflineOld = generate_offline_expired_message(JidRecordMike, 1291: JidRecordKate, Msg1, 1292: OldTimestamp, 1293: ExpirationTime), 1294: OfflineNow = generate_offline_expired_message(JidRecordMike, 1295: JidRecordKate, Msg2, os:system_time(microsecond), ExpirationTime), 1296: OfflineFuture = generate_offline_expired_message(JidRecordMike, 1297: JidRecordKate, Msg3, os:system_time(microsecond), ExpirationTimeFuture), 1298: OfflineFuture2 = generate_offline_expired_message(JidRecordMike, 1299: JidRecordKate, Msg4, 1300: OldTimestamp, 1301: ExpirationTimeFuture), 1302: {jid, _, _, _, LUser, LServer, _} = JidRecordKate, 1303: Args = [OfflineOld, OfflineNow, OfflineFuture, OfflineFuture2], 1304: HostType = host_type(), 1305: rpc_call(mod_offline_backend, write_messages, [HostType, LUser, LServer, Args]), 1306: %% when 1307: {_, 0} = mongooseimctl("delete_expired_messages", [LServer], Config), 1308: {ok, SecondList} = rpc_call(mod_offline_backend, pop_messages, [HostType, JidRecordKate]), 1309: %% then 1310: 2 = length(SecondList) 1311: end). 1312: 1313: %%----------------------------------------------------------------- 1314: %% Helpers 1315: %%----------------------------------------------------------------- 1316: 1317: 1318: nick_to_jid(UserName, Config) when is_atom(UserName) -> 1319: UserSpec = escalus_users:get_userspec(Config, UserName), 1320: escalus_utils:jid_to_lower(escalus_users:get_jid(Config, UserSpec)). 1321: 1322: generate_offline_message(From, To, Msg, TimeStamp) -> 1323: {jid, _, _, _, LUser, LServer, _} = To, 1324: #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, expire = never, 1325: from = From, to = To, packet = Msg}. 1326: 1327: generate_offline_expired_message(From, To, Msg, TimeStamp, ExpirationTime) -> 1328: {jid, _, _, _, LUser, LServer, _} = To, 1329: #offline_msg{us = {LUser, LServer}, timestamp = TimeStamp, 1330: expire = ExpirationTime, from = From, to = To, packet = Msg}. 1331: 1332: 1333: fallback_timestamp(HowManyDays, TS_MicroSeconds) -> 1334: HowManySeconds = HowManyDays * 86400, 1335: HowManyMicroSeconds = erlang:convert_time_unit(HowManySeconds, second, microsecond), 1336: TS_MicroSeconds - HowManyMicroSeconds. 1337: 1338: get_user_data(User, Config) when is_atom(User) -> 1339: get_user_data(escalus_users:get_options(Config, User, <<"newres">>), Config); 1340: get_user_data(User, _Config) -> 1341: {_, Password} = lists:keyfind(password, 1, User), 1342: {_, Username} = lists:keyfind(username, 1, User), 1343: {_, Domain} = lists:keyfind(server, 1, User), 1344: {Username, Domain, Password}. 1345: 1346: get_md5(AccountPass) -> 1347: lists:flatten([io_lib:format("~.16B", [X]) 1348: || X <- binary_to_list(crypto:hash(md5, AccountPass))]). 1349: get_sha(AccountPass) -> 1350: lists:flatten([io_lib:format("~.16B", [X]) 1351: || X <- binary_to_list(crypto:hash(sha, AccountPass))]). 1352: 1353: set_last(User, Domain, TStamp) -> 1354: rpc(mim(), mod_last, store_last_info, 1355: [host_type(), escalus_utils:jid_to_lower(User), Domain, TStamp, <<>>]). 1356: 1357: delete_users(_Config) -> 1358: lists:foreach(fun({User, Domain}) -> 1359: JID = mongoose_helper:make_jid(User, Domain), 1360: rpc(mim(), ejabberd_auth, remove_user, [JID]) 1361: end, get_registered_users()). 1362: 1363: %%----------------------------------------------------------------- 1364: %% Predicates 1365: %%----------------------------------------------------------------- 1366: 1367: match_user_status(Users, StatusTxt) -> 1368: Statuses = string:tokens(StatusTxt, "\n"), 1369: 1370: true = (length(Users) == length(Statuses)), 1371: match_user_status2(Users, Statuses). 1372: 1373: match_user_status2([], _) -> 1374: true; 1375: match_user_status2([User | UserR], Statuses) -> 1376: Username = binary_to_list(escalus_client:username(User)), 1377: Domain = binary_to_list(escalus_client:server(User)), 1378: Resource = binary_to_list(escalus_client:resource(User)), 1379: 1380: true = lists:any(fun(Status) -> 1381: [Username, Domain, Resource] 1382: =:= 1383: lists:sublist(string:tokens(Status, "\t"), 1, 3) 1384: end, Statuses), 1385: match_user_status2(UserR, Statuses). 1386: 1387: match_user_info(Users, UsersTxt) -> 1388: UsersInfo = string:tokens(UsersTxt, "\n"), 1389: case length(Users) == length(UsersInfo) of 1390: true -> 1391: ok; 1392: false -> 1393: ct:fail(#{what => match_user_info_failed, 1394: users => Users, user_info => UsersInfo}) 1395: end, 1396: match_user_info2(Users, UsersInfo). 1397: 1398: match_user_info2([], _) -> 1399: true; 1400: match_user_info2([User | UserR], UsersInfo) -> 1401: Username = binary_to_list(escalus_client:username(User)), 1402: Domain = binary_to_list(escalus_client:server(User)), 1403: Resource = binary_to_list(escalus_client:resource(User)), 1404: FullJID = Username ++ "@" ++ Domain ++ "/" ++ Resource, 1405: 1406: true = lists:any(fun(UserInfo) -> 1407: string:str(UserInfo, string:to_lower(FullJID)) =:= 1 1408: end, UsersInfo), 1409: match_user_info2(UserR, UsersInfo). 1410: 1411: match_roster(ItemsValid, Items) -> 1412: ItemsTokens = [ string:tokens(ItemToken, "\t") || ItemToken <- string:tokens(Items, "\n") ], 1413: 1414: true = (length(ItemsValid) == length(ItemsTokens)), 1415: true = lists:all(fun({Username, Domain, _Nick, _Group, _Sub}) -> 1416: JID = escalus_utils:jid_to_lower(<<Username/binary, "@", Domain/binary >>), 1417: lists:any(fun 1418: ([RosterJID, _Nick, _Sub, "none", _Group]) -> 1419: JID =:= escalus_utils:jid_to_lower(list_to_binary(RosterJID)); 1420: (_) -> 1421: false 1422: end, ItemsTokens) 1423: end, ItemsValid). 1424: 1425: string_to_binary(List) -> 1426: case erlang:system_info(otp_release) of 1427: [$R|_] -> 1428: list_to_binary(List); 1429: _ -> 1430: unicode:characters_to_binary(List) 1431: end. 1432: 1433: add_rosteritem1(UserName1, Domain, UserName2, Config) -> 1434: mongooseimctl("add_rosteritem", 1435: [UserName1, Domain, UserName2, 1436: Domain, "MyBob", "MyGroup", "both"], Config). 1437: 1438: add_rosteritem2(Name1, Domain1, Name2, Domain2, Config) -> 1439: mongooseimctl("add_rosteritem", 1440: [Name1, Domain1, Name2, 1441: Domain2, "DearMike", "MyGroup", "both"], Config).