diff --git a/big_tests/default.spec b/big_tests/default.spec index 219005bfab..8f4adc9819 100644 --- a/big_tests/default.spec +++ b/big_tests/default.spec @@ -40,8 +40,8 @@ {suites, "tests", mod_aws_sns_SUITE}. {suites, "tests", mod_blocking_SUITE}. {suites, "tests", mod_event_pusher_rabbit_SUITE}. +{suites, "tests", mod_event_pusher_http_SUITE}. {suites, "tests", mod_global_distrib_SUITE}. -{suites, "tests", mod_http_notification_SUITE}. {suites, "tests", mod_http_upload_SUITE}. {suites, "tests", mod_ping_SUITE}. {suites, "tests", mod_time_SUITE}. diff --git a/big_tests/test.config b/big_tests/test.config index 442278c41b..e2ad26e173 100644 --- a/big_tests/test.config +++ b/big_tests/test.config @@ -294,7 +294,7 @@ {mod_privacy, "[modules.mod_privacy] backend = \"rdbms\""}, {mod_private, "[modules.mod_private] - backend = \"mysql\""}, + backend = \"rdbms\""}, {mod_offline, "[modules.mod_offline] backend = \"rdbms\""}, {mod_vcard, "[modules.mod_vcard] diff --git a/big_tests/tests/accounts_SUITE.erl b/big_tests/tests/accounts_SUITE.erl index 6b4b92da5e..16dc14454b 100644 --- a/big_tests/tests/accounts_SUITE.erl +++ b/big_tests/tests/accounts_SUITE.erl @@ -167,7 +167,7 @@ admin_notify(Config) -> [Username2, _Server2, _Pass2] = escalus_users:get_usp(Config, UserSpec2), [AdminU, AdminS, AdminP] = escalus_users:get_usp(Config, AdminSpec), - rpc(mim(), ejabberd_auth, try_register, [AdminU, AdminS, AdminP]), + rpc(mim(), ejabberd_auth, try_register, [mongoose_helper:make_jid(AdminU, AdminS), AdminP]), escalus:story(Config, [{admin, 1}], fun(Admin) -> escalus:create_users(Config, escalus:get_users([Name1, Name2])), @@ -198,12 +198,12 @@ null_password(Config) -> {username, Name} = lists:keyfind(username, 1, Details), {server, Server} = lists:keyfind(server, 1, Details), escalus:assert(is_error, [<<"modify">>, <<"not-acceptable">>], Response), - false = rpc(mim(), ejabberd_auth, is_user_exists, [Name, Server]). + false = rpc(mim(), ejabberd_auth, does_user_exist, [mongoose_helper:make_jid(Name, Server)]). check_unregistered(Config) -> [{_, UserSpec}] = escalus_users:get_users([bob]), [Username, Server, _Pass] = escalus_users:get_usp(Config, UserSpec), - false = rpc(mim(), ejabberd_auth, is_user_exists, [Username, Server]). + false = rpc(mim(), ejabberd_auth, does_user_exist, [mongoose_helper:make_jid(Username, Server)]). bad_request_registration_cancelation(Config) -> @@ -373,7 +373,7 @@ bad_cancelation_stanza() -> user_exists(Name, Config) -> {Name, Client} = escalus_users:get_user_by_name(Name), [Username, Server, _Pass] = escalus_users:get_usp(Config, Client), - rpc(mim(), ejabberd_auth, is_user_exists, [Username, Server]). + rpc(mim(), ejabberd_auth, does_user_exist, [mongoose_helper:make_jid(Username, Server)]). reload_mod_register_option(Config, Key, Value) -> Host = domain(), diff --git a/big_tests/tests/ejabberdctl_SUITE.erl b/big_tests/tests/ejabberdctl_SUITE.erl index 79a043e7c9..4f12edade7 100644 --- a/big_tests/tests/ejabberdctl_SUITE.erl +++ b/big_tests/tests/ejabberdctl_SUITE.erl @@ -306,7 +306,8 @@ 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), - rpc(mim(), ejabberd_auth, try_register, [Username, Domain, Pass]) + 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) -> @@ -1310,7 +1311,8 @@ set_last(User, Domain, TStamp) -> delete_users(Config) -> Users = escalus_users:get_users([alice, bob, kate, mike]), lists:foreach(fun({User, Domain}) -> - rpc(mim(), ejabberd_auth, remove_user, [User, Domain]) + JID = mongoose_helper:make_jid(User, Domain), + rpc(mim(), ejabberd_auth, remove_user, [JID]) end, get_registered_users()). %%----------------------------------------------------------------- diff --git a/big_tests/tests/login_SUITE.erl b/big_tests/tests/login_SUITE.erl index 7bf1172bba..dfc88d2b3e 100644 --- a/big_tests/tests/login_SUITE.erl +++ b/big_tests/tests/login_SUITE.erl @@ -430,8 +430,8 @@ verify_format(GroupName, {_User, Props}) -> Username = escalus_utils:jid_to_lower(proplists:get_value(username, Props)), Server = proplists:get_value(server, Props), Password = proplists:get_value(password, Props), - - SPassword = rpc(mim(), ejabberd_auth, get_password, [Username, Server]), + JID = mongoose_helper:make_jid(Username, Server), + SPassword = rpc(mim(), ejabberd_auth, get_password, [JID]), do_verify_format(GroupName, Password, SPassword). diff --git a/big_tests/tests/mam_SUITE.erl b/big_tests/tests/mam_SUITE.erl index f8438ddcfe..166eecd60d 100644 --- a/big_tests/tests/mam_SUITE.erl +++ b/big_tests/tests/mam_SUITE.erl @@ -2990,13 +2990,14 @@ check_user_exist(Config) -> %% when [{_, AdminSpec}] = escalus_users:get_users([admin]), [AdminU, AdminS, AdminP] = escalus_users:get_usp(Config, AdminSpec), - ok = rpc(mim(), ejabberd_auth, try_register, [AdminU, AdminS, AdminP]), + JID = mongoose_helper:make_jid(AdminU, AdminS), + ok = rpc(mim(), ejabberd_auth, try_register, [JID, AdminP]), %% admin user already registered - true = rpc(mim(), ejabberd_users, does_user_exist, [AdminU, AdminS]), - false = rpc(mim(), ejabberd_users, does_user_exist, [<<"fake-user">>, AdminS]), - false = rpc(mim(), ejabberd_users, does_user_exist, [AdminU, <<"fake-domain">>]), + true = rpc(mim(), ejabberd_users, does_user_exist, [JID]), + false = rpc(mim(), ejabberd_users, does_user_exist, [mongoose_helper:make_jid(<<"fake-user">>, AdminS)]), + false = rpc(mim(), ejabberd_users, does_user_exist, [mongoose_helper:make_jid(AdminU, <<"fake-domain">>)]), %% cleanup - ok = rpc(mim(), ejabberd_auth, remove_user, [AdminU, AdminS]). + ok = rpc(mim(), ejabberd_auth, remove_user, [JID]). %% This function supports only one device, one user. %% We don't send initial presence to avoid presence broadcasts between resources diff --git a/big_tests/tests/mod_http_notification_SUITE.erl b/big_tests/tests/mod_event_pusher_http_SUITE.erl similarity index 95% rename from big_tests/tests/mod_http_notification_SUITE.erl rename to big_tests/tests/mod_event_pusher_http_SUITE.erl index 732ac84455..3d969d33d6 100644 --- a/big_tests/tests/mod_http_notification_SUITE.erl +++ b/big_tests/tests/mod_event_pusher_http_SUITE.erl @@ -1,11 +1,11 @@ %%%---------------------------------------------------------------------- -%%% File : mod_http_notification_SUITE +%%% File : mod_event_pusher_http_SUITE %%% Author : Baibossynv Valery %%% Purpose : Testing passing via http %%% Created : 16 Dec 2015 by Baibossynv Valery %%%---------------------------------------------------------------------- --module(mod_http_notification_SUITE). +-module(mod_event_pusher_http_SUITE). -author("baibossynov.valery@gmail.com"). -compile(export_all). @@ -14,7 +14,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). --define(ETS_TABLE, mod_http_notification). +-define(ETS_TABLE, mod_event_pusher_http). -import(distributed_helper, [mim/0, require_rpc_nodes/1, @@ -176,8 +176,11 @@ stop_pool() -> set_modules(Config0, Opts) -> Config = dynamic_modules:save_modules(host(), Config0), - ModOpts = [{worker_timeout, 500}, {host, http_notifications_host()}] ++ Opts, - dynamic_modules:ensure_modules(host(), [{mod_http_notification, ModOpts}]), + ModOpts = [{backends, + [{http, + [{worker_timeout, 500}, + {host, http_notifications_host()}] ++ Opts}]}], + dynamic_modules:ensure_modules(host(), [{mod_event_pusher, ModOpts}]), Config. start_http_listener(simple_message, Prefix) -> diff --git a/big_tests/tests/mongoose_helper.erl b/big_tests/tests/mongoose_helper.erl index 03db170a52..1aa79eb4e5 100644 --- a/big_tests/tests/mongoose_helper.erl +++ b/big_tests/tests/mongoose_helper.erl @@ -26,6 +26,7 @@ -export([wait_until/2, wait_until/3, wait_for_user/3]). -export([inject_module/1, inject_module/2, inject_module/3]). +-export([make_jid/2]). -export([make_jid/3]). -export([make_jid_noprep/3]). -export([get_session_pid/2]). @@ -373,6 +374,9 @@ inject_module(Node, Module, reload) -> make_jid_noprep(User, Server, Resource) -> jid:make_noprep(User, Server, Resource). +make_jid(User, Server) -> + jid:make(User, Server, <<>>). + make_jid(User, Server, Resource) -> jid:make(User, Server, Resource). diff --git a/big_tests/tests/muc_SUITE.erl b/big_tests/tests/muc_SUITE.erl index 7a561270dd..db6b6d2609 100644 --- a/big_tests/tests/muc_SUITE.erl +++ b/big_tests/tests/muc_SUITE.erl @@ -459,20 +459,20 @@ domain() -> ct:get_config({hosts, mim, domain}). init_per_testcase(CaseName = load_already_registered_permanent_rooms, Config) -> - ok = rpc(mim(), meck, new, [mod_muc_room, [no_link, passthrough]]), + meck_room(), meck_room_start(), escalus:init_per_testcase(CaseName, Config); init_per_testcase(CaseName = create_already_registered_room, Config) -> - ok = rpc(mim(), meck, new, [mod_muc_room, [no_link, passthrough]]), + meck_room(), meck_room_start(), escalus:init_per_testcase(CaseName, Config); init_per_testcase(CaseName = check_presence_route_to_offline_room, Config) -> - ok = rpc(mim(), meck, new, [mod_muc_room, [no_link, passthrough]]), + meck_room(), meck_room_start(), meck_room_route(), escalus:init_per_testcase(CaseName, Config); init_per_testcase(CaseName = check_message_route_to_offline_room, Config) -> - ok = rpc(mim(), meck, new, [mod_muc_room, [no_link, passthrough]]), + meck_room(), meck_room_start(), meck_room_route(), escalus:init_per_testcase(CaseName, Config); @@ -534,6 +534,10 @@ init_per_testcase(CaseName, Config) when CaseName =:= disco_features_with_mam; init_per_testcase(CaseName, Config) -> escalus:init_per_testcase(CaseName, Config). +meck_room() -> + RPCSpec = (mim())#{timeout => timer:seconds(10)}, % it takes long to compile this module + ok = rpc(RPCSpec, meck, new, [mod_muc_room, [no_link, passthrough]]). + %% Meck will register a fake room right before a 'real' room is started meck_room_start() -> rpc(mim(), meck, expect, [mod_muc_room, start, diff --git a/big_tests/tests/muc_http_api_SUITE.erl b/big_tests/tests/muc_http_api_SUITE.erl index 5559a1d271..f6e89b8ff6 100644 --- a/big_tests/tests/muc_http_api_SUITE.erl +++ b/big_tests/tests/muc_http_api_SUITE.erl @@ -32,10 +32,12 @@ %%-------------------------------------------------------------------- all() -> - [{group, positive}]. + [{group, positive}, + {group, negative}]. groups() -> - G = [{positive, [parallel], success_response() ++ complex()}], + G = [{positive, [parallel], success_response() ++ complex()}, + {negative, [parallel], failure_response()}], ct_helper:repeat_all_until_all_ok(G). success_response() -> @@ -52,6 +54,9 @@ complex() -> multiparty_multiprotocol ]. +failure_response() -> + [failed_invites, + failed_messages]. %%-------------------------------------------------------------------- %% Init & teardown @@ -93,7 +98,15 @@ create_room(Config) -> Body = #{name => Name, owner => escalus_client:short_jid(Alice), nick => <<"ali">>}, - {{<<"201">>, _}, Name} = rest_helper:post(admin, Path, Body), + Res = rest_helper:make_request(#{role => admin, + method => <<"POST">>, + path => Path, + body => Body, + return_headers => true}), + {{<<"201">>, _}, Headers, Name} = Res, + Exp = <<"/api/mucs/", Host/binary, "/", Name/binary>>, + Uri = uri_string:parse(proplists:get_value(<<"location">>, Headers)), + ?assertEqual(Exp, maps:get(path, Uri)), %% Service acknowledges room creation (10.1.1 Ex. 154), then %% (presumably 7.2.16) sends room subject, finally the IQ %% result of the IQ request (10.1.2) for an instant room. The @@ -113,6 +126,8 @@ invite_online_user_to_room(Config) -> Body = #{sender => escalus_client:short_jid(Alice), recipient => escalus_client:short_jid(Bob), reason => Reason}, + {{<<"404">>, _}, <<"room does not exist">>} = rest_helper:post(admin, Path, Body), + set_up_room(Config, Alice), {{<<"204">>, _}, <<"">>} = rest_helper:post(admin, Path, Body), Stanza = escalus:wait_for_stanza(Bob), is_direct_invitation(Stanza), @@ -136,8 +151,6 @@ send_message_to_room(Config) -> Message = <<"Greetings!">>, Body = #{from => escalus_client:short_jid(Bob), body => Message}, - %% The HTTP call in question. Notice: status 200 because no - %% resource is created. {{<<"204">>, _}, <<"">>} = rest_helper:post(admin, Path, Body), Got = escalus:wait_for_stanza(Bob), escalus:assert(is_message, Got), @@ -265,11 +278,57 @@ multiparty_multiprotocol(Config) -> user_sees_message_from(Alice, Room, 2)) end). +failed_invites(Config) -> + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> + Name = set_up_room(Config, Alice), + BAlice = escalus_client:short_jid(Alice), + BBob = escalus_client:short_jid(Bob), + % non-existing room + {{<<"404">>, _}, <<"room does not exist">>} = send_invite(<<"thisroomdoesnotexist">>, BAlice, BBob), + % invite with bad jid + {{<<"400">>, _}, <<"Invalid jid:", _/binary>>} = send_invite(Name, BAlice, <<"@badjid">>), + {{<<"400">>, _}, <<"Invalid jid:", _/binary>>} = send_invite(Name, <<"@badjid">>, BBob), + ok + end). + +failed_messages(Config) -> + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> + Name = set_up_room(Config, Alice), + % non-existing room + BAlice = escalus_client:short_jid(Alice), + BBob = escalus_client:short_jid(Bob), + {{<<"404">>, _}, <<"room does not exist">>} = send_invite(<<"thisroomdoesnotexist">>, BAlice, BBob), + % invite with bad jid + {{<<"400">>, _}, <<"Invalid jid:", _/binary>>} = send_invite(Name, BAlice, <<"@badjid">>), + {{<<"400">>, _}, <<"Invalid jid:", _/binary>>} = send_invite(Name, <<"@badjid">>, BBob), + ok + end). + %%-------------------------------------------------------------------- %% Ancillary (adapted from the MUC suite) %%-------------------------------------------------------------------- +set_up_room(Config, Alice) -> + % create a room first + Name = ?config(room_name, Config), + Host = <<"localhost">>, + Path = <<"/mucs/", Host/binary>>, + Body = #{name => Name, + owner => escalus_client:short_jid(Alice), + nick => <<"ali">>}, + Res = rest_helper:post(admin, Path, Body), + {{<<"201">>, _}, Name} = Res, + Name. + +send_invite(RoomName, BinFrom, BinTo) -> + Path = <<"/mucs/localhost/", RoomName/binary, "/participants">>, + Reason = <<"I think you'll like this room!">>, + Body = #{sender => BinFrom, + recipient => BinTo, + reason => Reason}, + rest_helper:post(admin, Path, Body). + make_distinct_name(Prefix) -> {_, S, US} = os:timestamp(), L = lists:flatten([integer_to_list(S rem 100), ".", integer_to_list(US)]), diff --git a/big_tests/tests/muc_light_http_api_SUITE.erl b/big_tests/tests/muc_light_http_api_SUITE.erl index 31aa0de108..ec27d91c65 100644 --- a/big_tests/tests/muc_light_http_api_SUITE.erl +++ b/big_tests/tests/muc_light_http_api_SUITE.erl @@ -53,7 +53,8 @@ success_response() -> negative_response() -> [delete_room_by_non_owner, delete_non_existent_room, - delete_room_without_having_a_membership + delete_room_without_having_a_membership, + create_non_unique_room ]. %%-------------------------------------------------------------------- @@ -200,7 +201,7 @@ delete_room_by_non_owner(Config) -> [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate)-> {{<<"403">>, <<"Forbidden">>}, - <<"Command not available for this user">>} = + <<"you can not delete this room">>} = check_delete_room(Config, RoomName, RoomName, Alice, [Bob, Kate], Bob) end). @@ -210,7 +211,7 @@ delete_non_existent_room(Config) -> escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate)-> - {{<<"500">>, _}, _} = + {{<<"404">>, _}, <<"room does not exist">>} = check_delete_room(Config, RoomName, <<"some_non_existent_room">>, Alice, [Bob, Kate], Alice) end). @@ -220,12 +221,29 @@ delete_room_without_having_a_membership(Config) -> escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate)-> - {{<<"500">>, _}, _} = + {{<<"403">>, _}, <<"given user does not occupy this room">>} = check_delete_room(Config, RoomName, RoomName, Alice, [Bob], Kate) end). +create_non_unique_room(Config) -> + escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> + Domain = <<"localhost">>, + Path = <<"/muc-lights", $/, Domain/binary>>, + RandBits = base16:encode(crypto:strong_rand_bytes(5)), + Name = <<"wonderland">>, + RoomID = <<"just_some_id_", RandBits/binary>>, + Body = #{ id => RoomID, + name => Name, + owner => escalus_client:short_jid(Alice), + subject => <<"Lewis Carol">> + }, + {{<<"201">>, _}, _RoomJID} = rest_helper:putt(admin, Path, Body), + {{<<"403">>, _}, <<"Room already exists">>} = rest_helper:putt(admin, Path, Body), + ok + end). + %%-------------------------------------------------------------------- %% Ancillary (borrowed and adapted from the MUC and MUC Light suites) %%-------------------------------------------------------------------- diff --git a/big_tests/tests/oauth_SUITE.erl b/big_tests/tests/oauth_SUITE.erl index c1522b1ea0..140adf3d06 100644 --- a/big_tests/tests/oauth_SUITE.erl +++ b/big_tests/tests/oauth_SUITE.erl @@ -371,7 +371,8 @@ verify_format(GroupName, {_User, Props}) -> Username = escalus_utils:jid_to_lower(proplists:get_value(username, Props)), Server = proplists:get_value(server, Props), Password = proplists:get_value(password, Props), - SPassword = rpc(mim(), ejabberd_auth, get_password, [Username, Server]), + JID = mongoose_helper:make_jid(Username, Server), + SPassword = rpc(mim(), ejabberd_auth, get_password, [JID]), do_verify_format(GroupName, Password, SPassword). do_verify_format(login_scram, _Password, SPassword) -> diff --git a/big_tests/tests/rest_SUITE.erl b/big_tests/tests/rest_SUITE.erl index 756afec555..5c0bfdb046 100644 --- a/big_tests/tests/rest_SUITE.erl +++ b/big_tests/tests/rest_SUITE.erl @@ -1,5 +1,5 @@ %%============================================================================== -%% Copyright 2012 Erlang Solutions Ltd. +%% Copyright 2012-2020 Erlang Solutions Ltd. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -43,6 +43,8 @@ -define(ERROR, {<<"500">>, _}). -define(NOT_FOUND, {<<"404">>, _}). -define(NOT_AUTHORIZED, {<<"401">>, _}). +-define(FORBIDDEN, {<<"403">>, _}). +-define(BAD_REQUEST, {<<"400">>, _}). %%-------------------------------------------------------------------- %% Suite configuration @@ -66,7 +68,8 @@ groups() -> {blank_auth, [parallel], blank_auth_testcases()}, {roster, [parallel], [list_contacts, befriend_and_alienate, - befriend_and_alienate_auto]}, + befriend_and_alienate_auto, + invalid_roster_operations]}, {dynamic_module, [], [stop_start_command_module]}], ct_helper:repeat_all_until_all_ok(G). @@ -84,10 +87,12 @@ test_cases() -> sessions_are_listed, session_can_be_kicked, messages_are_sent_and_received, + messages_error_handling, stanzas_are_sent_and_received, messages_are_archived, messages_can_be_paginated, - password_can_be_changed + password_can_be_changed, + types_are_checked_separately_for_args_and_return ]. suite() -> @@ -124,13 +129,58 @@ end_per_group(auth, _Config) -> end_per_group(_GroupName, Config) -> escalus:delete_users(Config, escalus:get_users([alice, bob, mike])). +init_per_testcase(types_are_checked_separately_for_args_and_return = CaseName, Config) -> + {Mod, Code} = rpc(dynamic_compile, from_string, [custom_module_code()]), + rpc(code, load_binary, [Mod, "mod_commands_test.erl", Code]), + rpc(gen_mod, start_module, [<<"localhost">>, mod_commands_test, []]), + escalus:init_per_testcase(CaseName, Config); init_per_testcase(CaseName, Config) -> MAMTestCases = [messages_are_archived, messages_can_be_paginated], rest_helper:maybe_skip_mam_test_cases(CaseName, MAMTestCases, Config). +end_per_testcase(types_are_checked_separately_for_args_and_return = CaseName, Config) -> + rpc(gen_mod, stop_module, [<<"localhost">>, mod_commands_test]), + escalus:end_per_testcase(CaseName, Config); end_per_testcase(CaseName, Config) -> escalus:end_per_testcase(CaseName, Config). +rpc(M, F, A) -> + distributed_helper:rpc(distributed_helper:mim(), M, F, A). + +custom_module_code() -> + "-module(mod_commands_test). + -export([start/0, stop/0, start/2, stop/1, test_arg/1, test_return/1]). + start() -> mongoose_commands:register(commands()). + stop() -> mongoose_commands:unregister(commands()). + start(_,_) -> start(). + stop(_) -> stop(). + commands() -> + [ + [ + {name, test_arg}, + {category, <<\"test_arg\">>}, + {desc, <<\"List test_arg\">>}, + {module, mod_commands_test}, + {function, test_arg}, + {action, create}, + {args, [{arg, boolean}]}, + {result, [{msg, binary}]} + ], + [ + {name, test_return}, + {category, <<\"test_return\">>}, + {desc, <<\"List test_return\">>}, + {module, mod_commands_test}, + {function, test_return}, + {action, create}, + {args, [{arg, boolean}]}, + {result, {msg, binary}} + ] + ]. + test_arg(_) -> <<\"bleble\">>. + test_return(_) -> ok. + " +. %%-------------------------------------------------------------------- %% Tests @@ -172,11 +222,17 @@ user_can_be_registered_and_removed(_Config) -> {?OK, Lusers1} = gett(admin, <<"/users/localhost">>), assert_inlist(<<"mike@", Domain/binary>>, Lusers1), % try to create the same user - {?ERROR, _} = post(admin, <<"/users/localhost">>, CrUser), + {?FORBIDDEN, _} = post(admin, <<"/users/localhost">>, CrUser), % delete user {?NOCONTENT, _} = delete(admin, <<"/users/localhost/mike">>), {?OK, Lusers2} = gett(admin, <<"/users/localhost">>), assert_notinlist(<<"mike@", Domain/binary>>, Lusers2), + % invalid jid + CrBadUser = #{username => <<"m@ke">>, password => <<"nicniema">>}, + {?BAD_REQUEST, <<"Invalid jid", _/binary>>} = post(admin, <<"/users/localhost">>, CrBadUser), + {?BAD_REQUEST, <<"Invalid jid", _/binary>>} = delete(admin, <<"/users/localhost/@mike">>), +%% {?FORBIDDEN, _} = delete(admin, <<"/users/localhost/mike">>), % he's already gone, but we +%% can't test it because ejabberd_auth_internal:remove_user/2 always returns ok, grrrr ok. sessions_are_listed(_) -> @@ -185,17 +241,22 @@ sessions_are_listed(_) -> true = is_list(Sessions). session_can_be_kicked(Config) -> - escalus:story(Config, [{alice, 1}], fun(Alice) -> + escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> % Alice is connected - Domain = domain(), + AliceJid = jid:nameprep(escalus_client:full_jid(Alice)), + AliceSessionPath = <<"/sessions/", (escalus_client:server(Alice))/binary, + "/", (escalus_client:username(Alice))/binary, + "/", (escalus_client:resource(Alice))/binary>>, {?OK, Sessions1} = gett(admin, "/sessions/localhost"), - assert_inlist(<<"alice@", Domain/binary, "/res1">>, Sessions1), + assert_inlist(AliceJid, Sessions1), % kick alice - {?NOCONTENT, _} = delete(admin, "/sessions/localhost/alice/res1"), + {?NOCONTENT, _} = delete(admin, AliceSessionPath), escalus:wait_for_stanza(Alice), true = escalus_connection:wait_for_close(Alice, timer:seconds(1)), {?OK, Sessions2} = gett(admin, "/sessions/localhost"), - assert_notinlist(<<"alice@", Domain/binary, "/res1">>, Sessions2) + assert_notinlist(AliceJid, Sessions2), + {?NOT_FOUND, <<"no active session">>} = delete(admin, AliceSessionPath), + ok end). messages_are_sent_and_received(Config) -> @@ -207,6 +268,15 @@ messages_are_sent_and_received(Config) -> escalus:assert(is_chat_message, [maps:get(body, M2)], Res1) end). +messages_error_handling(Config) -> + escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> + AliceJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Alice)), + BobJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)), + {{<<"400">>, _}, <<"Invalid jid:", _/binary>>} = send_message_bin(AliceJID, <<"@noway">>), + {{<<"400">>, _}, <<"Invalid jid:", _/binary>>} = send_message_bin(<<"@noway">>, BobJID), + ok + end). + stanzas_are_sent_and_received(Config) -> %% this is to test the API for sending arbitrary stanzas, e.g. message with extra elements escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> @@ -214,8 +284,12 @@ stanzas_are_sent_and_received(Config) -> Res = escalus:wait_for_stanza(Bob), ?assertEqual(<<"attribute">>, exml_query:attr(Res, <<"extra">>)), ?assertEqual(<<"inside the sibling">>, exml_query:path(Res, [{element, <<"sibling">>}, cdata])), + Res1 = send_flawed_stanza(missing_attribute, Alice, Bob), + {?BAD_REQUEST, <<"both from and to are required">>} = Res1, + Res2 = send_flawed_stanza(malformed_xml, Alice, Bob), + {?BAD_REQUEST, <<"Malformed stanza: \"expected >\"">>} = Res2, ok - end). + end). messages_are_archived(Config) -> escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> @@ -307,6 +381,13 @@ password_can_be_changed(Config) -> escalus:story(Config, [{bob, 1}], fun(#client{} = _Bob) -> just_dont_do_anything end), + % test invalid calls + Res1 = putt(admin, "/users/localhost/bob", + #{newpass => <<>>}), + {?BAD_REQUEST, <<"empty password">>} = Res1, + Res2 = putt(admin, "/users/localhost/b@b", + #{newpass => NewPass}), + {?BAD_REQUEST, <<"invalid jid">>} = Res2, ok. list_contacts(Config) -> @@ -431,6 +512,65 @@ befriend_and_alienate_auto(Config) -> ), ok. +invalid_roster_operations(Config) -> + escalus:fresh_story( + Config, [{alice, 1}, {bob, 1}], + fun(Alice, Bob) -> + AliceJID = escalus_utils:jid_to_lower( + escalus_client:short_jid(Alice)), + BobJID = escalus_utils:jid_to_lower(escalus_client:short_jid(Bob)), + AliceS = binary_to_list(AliceJID), + BobS = binary_to_list(BobJID), + AlicePath = lists:flatten(["/contacts/", AliceS]), + % adds them to rosters + {?BAD_REQUEST, <<"Invalid jid", _/binary>>} = post(admin, AlicePath, #{jid => <<"@invalidjid">>}), + {?BAD_REQUEST, <<"Invalid jid", _/binary>>} = post(admin, "/contacts/@invalid_jid", #{jid => BobJID}), + % it is idempotent + {?NOCONTENT, _} = post(admin, AlicePath, #{jid => BobJID}), + {?NOCONTENT, _} = post(admin, AlicePath, #{jid => BobJID}), + PutPathA = lists:flatten([AlicePath, "/@invalid_jid"]), + {?BAD_REQUEST, <<"Invalid jid", _/binary>>} = putt(admin, PutPathA, #{action => <<"subscribe">>}), + PutPathB = lists:flatten(["/contacts/@invalid_jid/", BobS]), + {?BAD_REQUEST, <<"Invalid jid", _/binary>>} = putt(admin, PutPathB, #{action => <<"subscribe">>}), + PutPathC = lists:flatten([AlicePath, "/", BobS]), + {?BAD_REQUEST, <<"invalid action">>} = putt(admin, PutPathC, #{action => <<"something stupid">>}), + ManagePath = lists:flatten(["/contacts/", + AliceS, + "/", + BobS, + "/manage" + ]), + {?BAD_REQUEST, <<"invalid action">>} = putt(admin, ManagePath, #{action => <<"off with his head">>}), + MangePathA = lists:flatten(["/contacts/", + "@invalid", + "/", + BobS, + "/manage" + ]), + {?BAD_REQUEST, <<"Invalid jid", _/binary>>} = putt(admin, MangePathA, #{action => <<"connect">>}), + MangePathB = lists:flatten(["/contacts/", + AliceS, + "/", + "@bzzz", + "/manage" + ]), + {?BAD_REQUEST, <<"Invalid jid", _/binary>>} = putt(admin, MangePathB, #{action => <<"connect">>}), + ok + end + ). + +types_are_checked_separately_for_args_and_return(Config) -> + escalus:story( + Config, [{alice, 1}], + fun(_Alice) -> + % argument doesn't pass typecheck + {?BAD_REQUEST, _} = post(admin, "/test_arg", #{arg => 1}), + % return value doesn't pass typecheck + {?ERROR, _} = post(admin, "/test_return", #{arg => true}), + ok + end + ). + %%-------------------------------------------------------------------- %% Helpers %%-------------------------------------------------------------------- @@ -444,6 +584,11 @@ send_messages(Alice, Bob) -> {?NOCONTENT, _} = post(admin, <<"/messages">>, M1), {M, M1}. +send_message_bin(BFrom, BTo) -> + % this is to trigger invalid jid errors + M = #{caller => BFrom, to => BTo, body => <<"whatever">>}, + post(admin, <<"/messages">>, M). + send_extended_message(From, To) -> M = #xmlel{name = <<"message">>, attrs = [{<<"from">>, escalus_client:full_jid(From)}, @@ -459,6 +604,25 @@ send_extended_message(From, To) -> {?NOCONTENT, _} = post(admin, <<"/stanzas">>, M1), ok. +send_flawed_stanza(missing_attribute, From, _To) -> + M = #xmlel{name = <<"message">>, + attrs = [{<<"from">>, escalus_client:full_jid(From)}, + {<<"extra">>, <<"attribute">>}], + children = [#xmlel{name = <<"body">>, + children = [#xmlcdata{content = <<"the body">>}]}, + #xmlel{name = <<"sibling">>, + children = [#xmlcdata{content = <<"inside the sibling">>}]} + ] + }, + ct:pal("M: ~p", [M]), + M1 = #{stanza => exml:to_binary(M)}, + post(admin, <<"/stanzas">>, M1); +send_flawed_stanza(malformed_xml, _From, _To) -> + % closing > is missing + BadStanza = <<"the body>, + post(admin, <<"/stanzas">>, #{stanza => BadStanza}). + + check_roster(Path, Jid, Subs, Ask) -> {?OK, R} = gett(admin, Path), S = atom_to_binary(Subs, latin1), diff --git a/big_tests/tests/service_mongoose_system_metrics_SUITE.erl b/big_tests/tests/service_mongoose_system_metrics_SUITE.erl index 16f2cab663..99f981ddec 100644 --- a/big_tests/tests/service_mongoose_system_metrics_SUITE.erl +++ b/big_tests/tests/service_mongoose_system_metrics_SUITE.erl @@ -47,7 +47,8 @@ api_are_reported/1, transport_mechanisms_are_reported/1, outgoing_pools_are_reported/1, - xmpp_stanzas_counts_are_reported/1 + xmpp_stanzas_counts_are_reported/1, + config_type_is_reported/1 ]). -export([ @@ -87,6 +88,7 @@ all() -> transport_mechanisms_are_reported, outgoing_pools_are_reported, xmpp_stanzas_counts_are_reported, + config_type_is_reported, {group, log_transparency} ]. @@ -139,14 +141,6 @@ end_per_group(log_transparency, Config) -> end_per_group(_GroupName, Config) -> Config. -init_per_testcase(periodic_report_available, Config) -> - create_events_collection(), - enable_system_metrics(mim()), - Config; -init_per_testcase(system_metrics_are_reported_to_google_analytics_when_mim_starts, Config) -> - create_events_collection(), - enable_system_metrics(mim()), - Config; init_per_testcase(system_metrics_are_not_reported_when_not_allowed, Config) -> create_events_collection(), disable_system_metrics(mim()), @@ -187,17 +181,6 @@ init_per_testcase(_TestcaseName, Config) -> enable_system_metrics(mim()), Config. - -end_per_testcase(periodic_report_available, Config) -> - clear_events_collection(), - disable_system_metrics(mim()), - delete_prev_client_id(mim()), - Config; -end_per_testcase(system_metrics_are_reported_to_google_analytics_when_mim_starts, Config) -> - clear_events_collection(), - delete_prev_client_id(mim()), - disable_system_metrics(mim()), - Config; end_per_testcase(system_metrics_are_not_reported_when_not_allowed, Config) -> clear_events_collection(), delete_prev_client_id(mim()), @@ -222,6 +205,7 @@ end_per_testcase(xmpp_stanzas_counts_are_reported = CN, Config) -> end_per_testcase(_TestcaseName, Config) -> clear_events_collection(), disable_system_metrics(mim()), + delete_prev_client_id(mim()), Config. @@ -244,7 +228,6 @@ all_clustered_mongooses_report_the_same_client_id(_Config) -> mongoose_helper:wait_until(fun hosts_count_is_reported/0, true), all_event_have_the_same_client_id(). - system_metrics_are_reported_to_google_analytics_when_mim_starts(_Config) -> mongoose_helper:wait_until(fun hosts_count_is_reported/0, true), mongoose_helper:wait_until(fun modules_are_reported/0, true), @@ -313,6 +296,9 @@ xmpp_stanzas_counts_are_reported(Config) -> mongoose_helper:wait_until(fun xmpp_messages_count_is_reported/0, true), mongoose_helper:wait_until(fun xmpp_stanzas_counts_are_reported/0, true). +config_type_is_reported(_Config) -> + mongoose_helper:wait_until(fun config_type_is_reported/0, true). + just_removed_from_config_logs_question(_Config) -> disable_system_metrics(mim3()), remove_service_from_config(service_mongoose_system_metrics), @@ -483,6 +469,13 @@ feature_is_reported(EventCategory, EventAction) -> EC == EventCategory andalso EA == EventAction end, Tab). +feature_is_reported(EventCategory, EventAction, EventLabel) -> + Tab = ets:tab2list(?ETS_TABLE), + lists:any( + fun(#event{ec = EC, ea = EA, el = EL}) -> + EC == EventCategory andalso EA == EventAction andalso EL == EventLabel + end, Tab). + mod_vcard_backend_is_reported() -> feature_is_reported(<<"mod_vcard">>, <<"backend">>). @@ -495,6 +488,11 @@ cluster_uptime_is_reported() -> xmpp_components_are_reported() -> feature_is_reported(<<"cluster">>, <<"number_of_components">>). +config_type_is_reported() -> + IsToml = feature_is_reported(<<"cluster">>, <<"config_type">>, <<"toml">>), + IsCfg = feature_is_reported(<<"cluster">>, <<"config_type">>, <<"cfg">>), + IsToml orelse IsCfg. + api_are_reported() -> is_in_table(<<"http_api">>). diff --git a/doc/Advanced-configuration.md b/doc/Advanced-configuration.md index b3f8113ce0..3d709a8bcd 100644 --- a/doc/Advanced-configuration.md +++ b/doc/Advanced-configuration.md @@ -71,4 +71,4 @@ TLS is configured in one of two ways: some modules need a private key and certif In order to create private key & certificate bundle, you may simply concatenate them. -More information about configuring TLS for these endpoints is available in [Listener modules](advanced-configuration/Listener-modules.md) page. +More information about configuring TLS for these endpoints is available in the [listen section configuration](advanced-configuration/listen.md) page. diff --git a/doc/advanced-configuration/Listener-modules.md b/doc/advanced-configuration/Listener-modules.md deleted file mode 100644 index 2eac2a82b5..0000000000 --- a/doc/advanced-configuration/Listener-modules.md +++ /dev/null @@ -1,28 +0,0 @@ -### HTTP module: `mod_cowboy` - -This module provides an additional routing layer on top of HTTP(s) or WS(S) protocols. -It allows other HTTP/WS modules to coexist under the same URL on the single port. -Packets are forwarded to them based on the protocol. -This mechanism is transparent to actual handlers so the path sharing does not require any additional code. - -Example: If you wish, you can use BOSH and WS XMPP handlers (mod_bosh, mod_websockets) on a single port and a URL without any code modifications. - - -Here's an example of its configuration (added to ejabberd_cowboy modules list described above): - -```Erlang -{"_", "/[...]", mod_cowboy, [{http, mod_revproxy, - [{timeout, 5000}, - % time limit for upstream to respond - {body_length, 8000000}, - % maximum body size (may be infinity) - {custom_headers, [{<<"header">>,<<"value">>}]} - % list of extra headers that are send to upstream - ]}, - {ws, xmpp, mod_websockets} - ]}, -``` - -According to this configuration, all HTTP requests will go through the `mod_revproxy` module (see [mod_revproxy](../modules/mod_revproxy.md) for more details). -As for now, all WebSocket connections with the `Sec-WebSocket-Protocol: xmpp` header, will go through the mod_websockets connection. -This is the MongooseIM's regular websocket connection handler. diff --git a/doc/advanced-configuration/Modules.md b/doc/advanced-configuration/Modules.md index 4dee0b2148..b3e787bc31 100644 --- a/doc/advanced-configuration/Modules.md +++ b/doc/advanced-configuration/Modules.md @@ -6,7 +6,7 @@ and `mod_stream_management` is for stanza acknowledgement and stream resumption. This modular architecture provides great flexibility for everyday operations and feature development. A module configuration generally looks like this: -``` +```toml [modules.mod_muc] host = "muc.@HOST@" access = "muc" @@ -161,9 +161,12 @@ Handles push notifications generated by [mod_pubsub](../modules/mod_pubsub.md)'s Implements [XEP-0077: In-Band Registration)](http://xmpp.org/extensions/xep-0077.html), that enables creating an account and changing the password once connected. This does not provide a solution to the forgotten password use case via SMS or email. -### [mod_revproxy](../modules/mod_revproxy.md) +### mod_revproxy With this extension, MongooseIM may serve as a reverse proxy. +**Warning:** This module is deprecated and can only be configured with the older, `.cfg` configuration file. +Please refer to the older versions of the documentation to see how to do this. + ### [mod_roster](../modules/mod_roster.md) Roster support, specified in [RFC 6121](http://xmpp.org/rfcs/rfc6121.html). Includes support for [XEP-0237: Roster Versioning](http://xmpp.org/extensions/xep-0237.html). diff --git a/doc/developers-guide/Hooks-and-handlers.md b/doc/developers-guide/Hooks-and-handlers.md index c864f1c85e..d302839d0e 100644 --- a/doc/developers-guide/Hooks-and-handlers.md +++ b/doc/developers-guide/Hooks-and-handlers.md @@ -260,7 +260,7 @@ The only thing that needs to be done is calling `ejabberd_hooks:run_fold/4` with However, if you want static code analysis, you should put the new hook inside `mongoose_hooks` with a correct type specification. We've added this module to provide some security and type checking in places where the hooks are run. -This is the way all hooks are called in MongooseIM (see the examples in the [hooks description](hooks_description.md)).gen_iq_handler +This is the way all hooks are called in MongooseIM (see the examples in the [hooks description](hooks_description.md)). Of course, as long as no module registers handlers for a hook, running a `run_fold` won't have any effects. diff --git a/doc/migrations/4.0.0_4.X.X.md b/doc/migrations/4.0.0_4.X.X.md new file mode 100644 index 0000000000..06d69545c3 --- /dev/null +++ b/doc/migrations/4.0.0_4.X.X.md @@ -0,0 +1,10 @@ +### `mod_http_notification` module is no longer available + +`mod_http_notification` has been deprecated since MongooseIM 2.1.1 and it is no longer available in this release. +Please use the `http` backend for `mod_event_pusher`, which is the direct equivalent of `mod_http_notification`. + +#### Metrics + +`mod_http_notification` metric was updated and now is available as [`mod_event_pusher_http`](../../modules/mod_event_pusher_http/#metrics). + +For more details on how to configure `mod_event_pusher` with `http` backend, please see [this section](../../modules/mod_event_pusher_http/). diff --git a/doc/modules/mod_adhoc.md b/doc/modules/mod_adhoc.md index 55edeb9a31..9b8c219714 100644 --- a/doc/modules/mod_adhoc.md +++ b/doc/modules/mod_adhoc.md @@ -18,7 +18,7 @@ Strategy to handle incoming stanzas. For details, please refer to Determines whether the Ad-Hoc Commands should be announced upon Service Discovery. ### Example configuration -``` +```toml [modules.mod_adhoc] report_commands_node = true ``` diff --git a/doc/modules/mod_amp.md b/doc/modules/mod_amp.md index af27ee7eae..173f5b7a03 100644 --- a/doc/modules/mod_amp.md +++ b/doc/modules/mod_amp.md @@ -15,7 +15,7 @@ none ### Example Configuration -``` +```toml [modules.mod_amp] ``` diff --git a/doc/modules/mod_auth_token.md b/doc/modules/mod_auth_token.md index f3d7d23d9f..20581c5487 100644 --- a/doc/modules/mod_auth_token.md +++ b/doc/modules/mod_auth_token.md @@ -155,7 +155,7 @@ Access token validity can't be sidestepped right now. ### Example configuration -``` +```toml [modules.mod_auth_token] validity_period = [ {token = "access", value = 13, unit = "minutes"}, diff --git a/doc/modules/mod_blocking.md b/doc/modules/mod_blocking.md index 00b467454f..903768b4de 100644 --- a/doc/modules/mod_blocking.md +++ b/doc/modules/mod_blocking.md @@ -6,7 +6,7 @@ The protocol is much simpler than privacy lists. ### Options ### Example Configuration -``` +```toml [modules.mod_blocking] ``` diff --git a/doc/modules/mod_bosh.md b/doc/modules/mod_bosh.md index 1ce39ba93b..bc8a544652 100644 --- a/doc/modules/mod_bosh.md +++ b/doc/modules/mod_bosh.md @@ -30,12 +30,6 @@ Please note that a long-polling request is not considered to be an inactivity. Enables/disables [acks](http://xmpp.org/extensions/xep-0124.html#ack-request) sent by server. -#### `modules.mod_bosh.backend` - * **Syntax:** `"mnesia"` - * **Default:** `"mnesia"` - * **Example:** `backend = "mnesia"` - -Backend used for storing BOSH session data. `"mnesia"` is the only supported value. #### `modules.mod_bosh.maxpause` * **Syntax:** positive integer * **Default:** `120` @@ -46,7 +40,7 @@ Maximum allowed pause in seconds (e.g. to switch between pages and then resume c ### Example Configuration In the listener section: -``` +```toml [[listen.http]] port = 5280 transport.num_acceptors = 10 @@ -57,11 +51,10 @@ In the listener section: path = "/http-bind" ``` In the module section: -``` +```toml [modules.mod_bosh] inactivity = 20 max_wait = "infinity" server_acks = true - backend = "mnesia" maxpause = 120 ``` diff --git a/doc/modules/mod_caps.md b/doc/modules/mod_caps.md index c061218e5d..747b7a0722 100644 --- a/doc/modules/mod_caps.md +++ b/doc/modules/mod_caps.md @@ -23,7 +23,7 @@ Time (in seconds) after which entries will be removed. ### Example Configuration -``` +```toml [modules.mod_caps] cache_size = 2000 cache_life_time = 86 diff --git a/doc/modules/mod_carboncopy.md b/doc/modules/mod_carboncopy.md index 8b0719bf68..0024fb55f4 100644 --- a/doc/modules/mod_carboncopy.md +++ b/doc/modules/mod_carboncopy.md @@ -65,7 +65,7 @@ Strategy to handle incoming stanzas. For details, please refer to ### Example Configuration -``` +```toml [modules.mod_carboncopy] iqdisc.type = "no_queue" ``` diff --git a/doc/modules/mod_commands.md b/doc/modules/mod_commands.md index 93693d6a96..1199884700 100644 --- a/doc/modules/mod_commands.md +++ b/doc/modules/mod_commands.md @@ -14,7 +14,7 @@ In the future it may replace the current `mongooseimctl` implementation. This module contains command definitions loaded when the module is activated. There are no more configuration parameters, so the following entry in the config file is sufficient: -``` +```toml [modules.mod_commands] ``` @@ -54,7 +54,7 @@ A simple command definition may look like this: Command registry is managed by `mongoose_commands` module. To register a command simply call: -``` +```erlang mongoose_commands:register(list_of_command_definitions) ``` @@ -62,7 +62,9 @@ The registry provides functions for listing commands, retrieving their signature and also calling. To call the above method you should do: ``` mongoose_commands:execute(admin, list_contacts) % if you want superuser privileges +``` or +``` mongoose_commands:execute(<<"alice@wonderland.lit">>, list_contacts) ``` diff --git a/doc/modules/mod_csi.md b/doc/modules/mod_csi.md index 15fa04466d..93dfaea54e 100644 --- a/doc/modules/mod_csi.md +++ b/doc/modules/mod_csi.md @@ -18,7 +18,7 @@ Buffer size for messages queued when session was `inactive`. ### Example Configuration -``` +```toml [modules.mod_csi] buffer_max = 40 ``` diff --git a/doc/modules/mod_disco.md b/doc/modules/mod_disco.md index c6234396a0..bb739ce497 100644 --- a/doc/modules/mod_disco.md +++ b/doc/modules/mod_disco.md @@ -21,7 +21,7 @@ Please note that `mod_disco` doesn't verify these domains, so if no handlers are * **Syntax:** array of tables described below * **Default:** no additional server info * **Example:** -``` +```toml server_info = [ {module = "all", name = "abuse-address", urls = ["admin@example.com"]} ] @@ -46,7 +46,7 @@ Other entities, with empty username part in their JIDs (e.g. `component.example. will still receive full disco results. ### Example Configuration -``` +```toml [modules.mod_disco] iqdisc.type = "one_queue" extra_domains = ["some_domain", "another_domain"] diff --git a/doc/modules/mod_event_pusher.md b/doc/modules/mod_event_pusher.md index cdd580c8ff..79cb630570 100644 --- a/doc/modules/mod_event_pusher.md +++ b/doc/modules/mod_event_pusher.md @@ -23,7 +23,7 @@ Refer to their specific documentation to learn more about their functions and co ### Example configuration -``` +```toml [modules.mod_event_pusher] backend.sns.access_key_id = "AKIAIOSFODNN7EXAMPLE" backend.sns.secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" @@ -45,4 +45,4 @@ Refer to their specific documentation to learn more about their functions and co [sns]: ./mod_event_pusher_sns.md [push]: ./mod_event_pusher_push.md [http_notification]: ./mod_event_pusher_http.md -[rabbit]: ./mod_event_pusher_rabbit.md \ No newline at end of file +[rabbit]: ./mod_event_pusher_rabbit.md diff --git a/doc/modules/mod_event_pusher_http.md b/doc/modules/mod_event_pusher_http.md index dbea550f6e..426634972d 100644 --- a/doc/modules/mod_event_pusher_http.md +++ b/doc/modules/mod_event_pusher_http.md @@ -56,7 +56,7 @@ Name of a module which should be used to check whether a notification should be ## Example configuration -``` +```toml [outgoing_pools.http.http_pool] scope = "global" workers = 50 @@ -73,7 +73,7 @@ Name of a module which should be used to check whether a notification should be Notifications will be POSTed to `http://localhost:8000/webservice/notifications`. -``` +```toml [[modules.mod_event_pusher.backend.http]] pool_name = "http_pool" path = "/notifications" @@ -108,12 +108,11 @@ Below is an example of what the body of an HTTP POST request can look like: If you'd like to learn more about metrics in MongooseIM, please visit [MongooseIM metrics](../operation-and-maintenance/Mongoose-metrics.md) page. -> **Warning:** the metrics' names may change once the deprecated `mod_http_notification` is removed from MongooseIM. | Name | Type | Description (when it gets incremented) | | ---- | ---- | -------------------------------------- | -| `[Host, mod_http_notifications, sent]` | spiral | An HTTP notification is sent successfully. | -| `[Host, mod_http_notifications, failed]` | spiral | An HTTP notification failed. | -| `[Host, mod_http_notifications, response_time]` | histogram | Does not include timings of failed requests. | +| `[Host, mod_event_pusher_http, sent]` | spiral | An HTTP notification is sent successfully. | +| `[Host, mod_event_pusher_http, failed]` | spiral | An HTTP notification failed. | +| `[Host, mod_event_pusher_http, response_time]` | histogram | Does not include timings of failed requests. | [mod_event_pusher]: ./mod_event_pusher.md diff --git a/doc/modules/mod_event_pusher_push.md b/doc/modules/mod_event_pusher_push.md index 84a5d883d9..bfc7cf1deb 100644 --- a/doc/modules/mod_event_pusher_push.md +++ b/doc/modules/mod_event_pusher_push.md @@ -14,7 +14,7 @@ attempts to enable them again. This module is very easy to enable, just paste the following to your MongooseIM configuration file: -``` +```toml [modules.mod_event_pusher] backend.push.wpool.workers = 100 ``` @@ -73,7 +73,7 @@ exists, just for the case of a user attempting to create a node. However, its do for the purpose of sending push notifications. Please note the value of `virtual_pubsub_hosts` option. `"pubsub.@HOSTS@"` is the default domain for `mod_pubsub`. -``` +```toml [modules.mod_pubsub] plugins = ["push"] # mandatory minimal config diff --git a/doc/modules/mod_event_pusher_rabbit.md b/doc/modules/mod_event_pusher_rabbit.md index 28c544f324..cbdb7125f3 100644 --- a/doc/modules/mod_event_pusher_rabbit.md +++ b/doc/modules/mod_event_pusher_rabbit.md @@ -105,7 +105,7 @@ Defines RabbitMQ group chat message received topic name. ### Example configuration -``` +```toml [modules.mod_event_pusher] backend.rabbit.presence_exchange.name ="presence" backend.rabbit.presence_exchange.type = "topic" diff --git a/doc/modules/mod_event_pusher_sns.md b/doc/modules/mod_event_pusher_sns.md index 0f136bb850..fa0af36084 100644 --- a/doc/modules/mod_event_pusher_sns.md +++ b/doc/modules/mod_event_pusher_sns.md @@ -113,7 +113,7 @@ Base exponential backoff time (in ms) for publish errors. ### Example configuration -``` +```toml [modules.mod_event_pusher] backend.sns.access_key_id = "AKIAIOSFODNN7EXAMPLE" backend.sns.secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" diff --git a/doc/modules/mod_global_distrib.md b/doc/modules/mod_global_distrib.md index 539fc24b07..c4252a3f9b 100644 --- a/doc/modules/mod_global_distrib.md +++ b/doc/modules/mod_global_distrib.md @@ -28,7 +28,7 @@ Following structures are stored in Redis: Example: `"user1@example.com/res" -> "dc2.example.com"`. * Domains of components and services registered on the globally distributed host are stored in per-node set structures where the key is `##{domains}`, and the values are the domain names. Example: `"dc1.example.com#mongoose1@dc1.example.com#{domains}" -> {"muc1.example.com", "muc2.example.com"}`. -* Domains of non-hidden components and services (see [`XMPP Components`](../../advanced-configuration/listen/#xmpp-components-listenservice) documentation) are stored in per-node set structures where the key is `##{public_domains}`, and the values are the domain names. +* Domains of non-hidden components and services (see the [`XMPP Components`](../../advanced-configuration/listen/#xmpp-components-listenservice) documentation) are stored in per-node set structures where the key is `##{public_domains}`, and the values are the domain names. * Declared endpoints available on a node are similarly stored in a per-node set structure where the key is `##{endpoints}` and the values represent the TCP endpoints of the node. Example: `"dc1.example.com#mongoose1@dc1.example.com#{endpoints}" -> {"172.16.2.14#8231", "2001:0db8:85a3:0000:0000:8a2e:0370:7334#8882"}`. * Nodes that comprise a host are stored in a set structure with key `#{nodes}` and values being the names of the nodes. @@ -127,13 +127,6 @@ XMPP domain that maps uniquely to the local datacenter; it will be used for inte Number of times a message can be rerouted between datacenters. -#### `modules.mod_global_distrib.bounce` -* **Syntax:** boolean with only `false` being a valid option -* **Default:** not set and `bounce` is enabled. -* **Example:** `bounce = false` - -If this option is present and set to false, message bouncing will be disabled. Refer [here](#message-bouncing-options) for more details. - #### `modules.mod_global_distrib.hosts_refresh_interval` * **Syntax:** non-negative integer, value given in milliseconds * **Default:** `3000` @@ -189,16 +182,17 @@ Endpoint refresh interval, when array of endpoints is empty. An interval between disabled endpoints "garbage collection". It means that disabled endpoints are periodically verified and if Global Distribution detects that connections is no longer alive, the connection pool is closed completely. -#### `modules.mod_global_distrib.connections.tls` -* **Syntax:** boolean with only `false` being a valid option -* **Default:** none, this option is mandatory. Details in the description -* **Example:** `tls = false` +#### TLS options -If this option is present, all data will be sent via standard TCP connections. -To enable TLS support, refer to [TLS](#tls-options) options. +#### `modules.mod_global_distrib.connections.tls.enabled` +* **Syntax:** boolean +* **Default:** `false` +* **Example:** `enabled = true` -#### TLS options -To enable TLS support at least the `cacertfile` and `certfile` options have to be present. These options will be passed to the `fast_tls` driver. +To enable TLS support the `cacertfile` and `certfile` options have to be present. +These options will be passed to the `fast_tls` driver. + +If `tls` is disabled, all data will be sent via standard TCP connections. #### `modules.mod_global_distrib.connections.tls.certfile` * **Syntax:** string, path in the file system @@ -279,7 +273,14 @@ How long should full and bare JID mappings be cached (e.g. `user1@example.com/re The maximum number of JID entries that can be stored in cache at any point in time. #### Message bouncing options -Options for message bouncing. + +#### `modules.mod_global_distrib.bounce.enabled` +* **Syntax:** boolean +* **Default:** `true` +* **Example:** `enabled = false` + +Whether message bouncing should be enabled or not. +Setting this option to `false` makes other `bounce` options have no effect. #### `modules.mod_global_distrib.bounce.resend_after_ms` * **Syntax:** non-negative integer @@ -308,12 +309,13 @@ The endpoints used for connection to a remote datacenter may be overridden by gl #### Configuring mod_global_distrib -``` +```toml [modules.mod_global_distrib] global_host = "example.com" local_host = "datacenter1.example.com" connections.endpoints = [{host = "172.16.0.2", port = 5555}] connections.advertised_endpoints = [{host = "172.16.0.2", port = 5555}] + connections.tls.enabled = true connections.tls.certfile = "priv/dc1.pem" connections.tls.cacertfile = "priv/ca.pem" connections.connections_per_endpoint = 30 @@ -325,7 +327,7 @@ The endpoints used for connection to a remote datacenter may be overridden by gl #### Overriding endpoints to a remote datacenter -``` Erlang +```Erlang { {global_distrib_addr, "datacenter2.example.com"}, [{"124.12.4.3", 5556}, {"182.172.23.55", 5555}] }. ``` @@ -333,7 +335,7 @@ The endpoints used for connection to a remote datacenter may be overridden by gl For more information about Dynomite configuration, consult [Dynomite wiki](https://github.com/Netflix/dynomite/wiki). -``` yaml +```yaml dyn_o_mite: datacenter: dc1 rack: rack1 @@ -350,7 +352,7 @@ dyn_o_mite: stats_listen: 0.0.0.0:22221 ``` -``` yaml +```yaml dyn_o_mite: datacenter: dc2 rack: rack1 diff --git a/doc/modules/mod_http_upload.md b/doc/modules/mod_http_upload.md index f4e2224d17..d6f100137d 100644 --- a/doc/modules/mod_http_upload.md +++ b/doc/modules/mod_http_upload.md @@ -102,7 +102,7 @@ The [AWS region][aws-region] to use for requests. ### Example configuration -``` +```toml [modules.mod_http_upload] host = "upload.@HOST@" backend = "s3" diff --git a/doc/modules/mod_inbox.md b/doc/modules/mod_inbox.md index bd0f292665..7123588bb5 100644 --- a/doc/modules/mod_inbox.md +++ b/doc/modules/mod_inbox.md @@ -5,13 +5,6 @@ To use it, enable mod\_inbox in the config file. ### Options -#### `modules.mod_inbox.backend` -* **Syntax:** string -* **Default:** `"rdbms"` -* **Example:** `backend = "rdbms"` - -Database backend to use. For now, only `rdbms` is supported. - #### `modules.mod_inbox.reset_markers` * **Syntax:** array of strings, out of `"displayed"`, `"received"`, `"acknowledged"` * **Default:** `["displayed"]` @@ -228,7 +221,7 @@ value: ### Example Configuration -``` +```toml [modules.mod_inbox] backend = "rdbms" reset_markers = ["displayed"] diff --git a/doc/modules/mod_jingle_sip.md b/doc/modules/mod_jingle_sip.md index 1d03cd8614..b48d9352f3 100644 --- a/doc/modules/mod_jingle_sip.md +++ b/doc/modules/mod_jingle_sip.md @@ -61,7 +61,7 @@ The stanza has reason `general-error` with the SIP error code in the `sip-error` ##### Non-standard Jingle stanzas used by jingle.js -The following non-standard Jingle stanzas were integrated with https://github.com/softwarehutpl/jingle.js +The following non-standard Jingle stanzas were integrated with [Jingle.js](https://github.com/softwarehutpl/jingle.js): * `source-remove` * `source-add` @@ -75,7 +75,7 @@ Similarly when MongooseIM gets a SIP in-dialog `INVITE` request, it will check if there is a custom attribute and use it as the `action` attribute of the Jingle stanza sent to the user. If there is no such attribute, the action will be set to regular Jingle `transport-info`. -##### Non-stadard Jingle existing-session-initiate stanza +##### Non-standard Jingle existing-session-initiate stanza MongooseIM allows a user to ask for an unanswered `session-initiate` request. This may be useful in web applications when there is a need to handle the call in a new browser window. @@ -137,7 +137,7 @@ The value of the `c=` SDP attribute. The simplest configuration is the following: -``` +```toml [modules.mod_jingle_sip] ``` diff --git a/doc/modules/mod_keystore.md b/doc/modules/mod_keystore.md index 7a1dcb37fe..c0328bc272 100644 --- a/doc/modules/mod_keystore.md +++ b/doc/modules/mod_keystore.md @@ -39,68 +39,40 @@ Names, types, and optional filepaths of the keys. The module public API is hook-based: ```erlang -ejabberd_hooks:run_fold(get_key, Domain, [], [{KeyName, Domain}]). +mongoose_hooks:get_key(Domain, [], KeyName). ``` -An example of usage can be found in [mod_auth_token:get_key_for_user/2](https://github.com/esl/MongooseIM/blob/26a23a260b14176c103339d745037cf4e3c1c188/apps/ejabberd/src/mod_auth_token.erl#L367). + +An example of usage can be found in [mod_auth_token:get_key_for_user/2](https://github.com/esl/MongooseIM/blob/4.0.0/src/mod_auth_token.erl#L393). ### Example Configuration Simple configuration - single tenant (i.e. server hosting just one XMPP domain): -``` +```toml [modules.mod_keystore] - - [[modules.mod_keystore.keys]] - name = "access_secret" - type = "ram" - - [[modules.mod_keystore.keys]] - name = "access_psk" - type = "file" - path = "priv/access_psk" - - [[modules.mod_keystore.keys]] - name = "provision_psk" - type = "file" - path = "priv/provision_psk" + keys = [{name = "access_secret, type = "ram"}, + {name = "access_psk, type = "file", path = "priv/access_psk"}, + {name = "provision_psk, type = "file", path = "priv/provision_psk"}] ``` Multi-tenant setup (`mod_keystore` configured differently for each virtual XMPP domain): -``` +```toml [[host_config]] host = "first.com" - [[[modules.mod_keystore.keys]]] - name = "access_secret" - type = "ram" - - [[[modules.mod_keystore.keys]]] - name = "access_psk" - type = "file" - path = "priv/access_psk" - - [[[modules.mod_keystore.keys]]] - name = "provision_psk" - type = "file" - path = "priv/provision_psk" + [host_config.modules.mod_keystore] + keys = [{name = "access_secret, type = "ram"}, + {name = "access_psk, type = "file", path = "priv/first_access_psk"}, + {name = "provision_psk, type = "file", path = "priv/first_provision_psk"}] [[host_config]] host = "second.com" - [[[modules.mod_keystore.keys]]] - name = "access_secret" - type = "ram" - - [[[modules.mod_keystore.keys]]] - name = "access_psk" - type = "file" - path = "priv/access_psk" - - [[[modules.mod_keystore.keys]]] - name = "provision_psk" - type = "file" - path = "priv/provision_psk" + [host_config.modules.mod_keystore] + keys = [{name = "access_secret, type = "ram"}, + {name = "access_psk, type = "file", path = "priv/second_access_psk"}, + {name = "provision_psk, type = "file", path = "priv/second_provision_psk"}] ``` diff --git a/doc/modules/mod_last.md b/doc/modules/mod_last.md index 3608c8116e..6d24df875f 100644 --- a/doc/modules/mod_last.md +++ b/doc/modules/mod_last.md @@ -31,7 +31,7 @@ Riak bucket type. ### Example Configuration -``` +```toml [modules.mod_last] backend = "rdbms" ``` diff --git a/doc/modules/mod_muc.md b/doc/modules/mod_muc.md index cd3f1cba8d..4d8e89615f 100644 --- a/doc/modules/mod_muc.md +++ b/doc/modules/mod_muc.md @@ -176,7 +176,7 @@ A time after which a hibernated room is stopped (deeply hibernated). * **Syntax:** A TOML table of options described below * **Default:** Default room options * **Example:** -``` +```toml [modules.mod_muc.default_room] password_protected = true description = "An example description." @@ -188,7 +188,7 @@ A time after which a hibernated room is stopped (deeply hibernated). affiliation = "member" ``` or: -``` +```toml default_room.password_protected = true default_room.description = "An example description." @@ -360,7 +360,7 @@ Available room configuration options to be overridden in the initial state: * **Default:** `[]` * **Example:** -``` +```toml [[modules.mod_muc.default_room.affiliations]] user = "alice" server = "localhost" @@ -391,7 +391,7 @@ Available room configuration options to be overridden in the initial state: A nick name of the default subject's author. ### Example Configuration -``` +```toml [modules.mod_muc] host = "muc.example.com" access = "muc" @@ -455,7 +455,7 @@ If the server returns something else, an error presence will be sent back to the **Example:** -``` +```toml [outgoing_pools.http.my_auth_pool] strategy = "available_worker" connection.host = "http://my_server:8000" diff --git a/doc/modules/mod_muc_commands.md b/doc/modules/mod_muc_commands.md index 9082b38d34..6dc5f7d93b 100644 --- a/doc/modules/mod_muc_commands.md +++ b/doc/modules/mod_muc_commands.md @@ -7,7 +7,7 @@ This is a set of commands, providing actions related to multi-user chat features This module contains command definitions which are loaded when the module is activated. There are no options to be provided, therefore the following entry in the config file is sufficient: -``` +```toml [modules.mod_muc_commands] ``` diff --git a/doc/modules/mod_muc_light.md b/doc/modules/mod_muc_light.md index eb04319c74..992554500c 100644 --- a/doc/modules/mod_muc_light.md +++ b/doc/modules/mod_muc_light.md @@ -115,10 +115,10 @@ When enabled, rooms the user occupies are included in their roster. Allowed `config_schema` items are (may be mixed): * Field name and a default value. The value has to be a string. An example: - - field = "field_name" - value = "default_value" - +```toml +field = "field_name" +value = "default_value" +``` * Field name, a default value, an internal key representation string and a type. Valid config field types are: @@ -127,12 +127,12 @@ Valid config field types are: * `float` Useful only for debugging or custom applications. An example: - - field = "display-lines" - value = 30 - internal_key = "display_lines" - type = "integer" - +```toml +field = "display-lines" +value = 30 +internal_key = "display_lines" +type = "integer" +``` **WARNING!** Lack of the `roomname` field will cause room names in Disco results and Roster items be set to the room username. diff --git a/doc/modules/mod_muc_light_commands.md b/doc/modules/mod_muc_light_commands.md index 2861ddc6d8..de506ad0f7 100644 --- a/doc/modules/mod_muc_light_commands.md +++ b/doc/modules/mod_muc_light_commands.md @@ -8,7 +8,7 @@ These commands are used by REST API modules. This module contains command definitions which are loaded when the module is activated. There are no options to be provided, therefore the following entry in the config file is sufficient: -``` +```toml [modules.mod_muc_light_commands] ``` diff --git a/doc/modules/mod_muc_log.md b/doc/modules/mod_muc_log.md index 5e2731dadb..6fed550a72 100644 --- a/doc/modules/mod_muc_log.md +++ b/doc/modules/mod_muc_log.md @@ -88,7 +88,7 @@ When enabled, MongooseIM will enforce `rel="nofollow"` attribute in links sent b ### Example Configuration -``` +```toml [modules.mod_muc_log] outdir = "/tmp/muclogs" access_log = "muc" diff --git a/doc/modules/mod_offline.md b/doc/modules/mod_offline.md index e20fc8327d..c696836a70 100644 --- a/doc/modules/mod_offline.md +++ b/doc/modules/mod_offline.md @@ -29,7 +29,7 @@ Although `mod_offline` may be sufficient in some cases, it is preferable to use Riak bucket type. ### Example Configuration -``` +```toml [modules.mod_offline] access_max_user_messages = "max_user_offline_messages" backend = "riak" diff --git a/doc/modules/mod_offline_stub.md b/doc/modules/mod_offline_stub.md index d9d209cbee..a713b8f2b7 100644 --- a/doc/modules/mod_offline_stub.md +++ b/doc/modules/mod_offline_stub.md @@ -1,7 +1,7 @@ ### Module Description RFC 6121 requires a `` stanza error to be sent to a user messaging an unavailable recipient if the message is not stored for delayed delivery (i.e. as an "offline message"). -If the recipient exists (i.e. auth module returns `true` from `is_user_exists`), `mod_mam` stores the message, but is still returned. This is not compliant with the RFC. +If the recipient exists (i.e. auth module returns `true` from `does_user_exist`), `mod_mam` stores the message, but is still returned. This is not compliant with the RFC. This module prevents returning . Please note that `mod_offline_stub` is not tightly coupled with `mod_mam`. It can be used as a standalone extension, if the specific application requires it. @@ -11,7 +11,7 @@ None. ### Example Configuration -``` +```toml [modules.mod_offline_stub] ``` diff --git a/doc/modules/mod_ping.md b/doc/modules/mod_ping.md index 526ba4792c..4c762c8eba 100644 --- a/doc/modules/mod_ping.md +++ b/doc/modules/mod_ping.md @@ -41,7 +41,7 @@ Strategy to handle incoming stanzas. For details, please refer to ### Example Configuration -``` +```toml [modules.mod_ping] send_pings = true ping_interval = 60 diff --git a/doc/modules/mod_privacy.md b/doc/modules/mod_privacy.md index 2c7c59e8c7..e61b9be7f9 100644 --- a/doc/modules/mod_privacy.md +++ b/doc/modules/mod_privacy.md @@ -33,7 +33,7 @@ Riak bucket type for information about privacy list names. Riak bucket type for privacy lists. ### Example Configuration -``` +```toml [modules.mod_privacy] backend = "riak" riak.defaults_bucket_type = "privacy_defaults" diff --git a/doc/modules/mod_private.md b/doc/modules/mod_private.md index a0fce15d53..a3dbfaf487 100644 --- a/doc/modules/mod_private.md +++ b/doc/modules/mod_private.md @@ -12,12 +12,11 @@ Strategy to handle incoming stanzas. For details, please refer to [IQ processing policies](../../advanced-configuration/Modules/#iq-processing-policies). #### `modules.mod_private.backend` -* **Syntax:** string, one of `"mnesia"`, `"rdbms"`, `"riak"`, `"mysql"`. +* **Syntax:** string, one of `"mnesia"`, `"rdbms"`, `"riak"`. * **Default:** "mnesia" * **Example:** `backend = "mnesia"` Database backend to use. -`mysql` uses MySQL-specific queries so in some cases it is more efficient than generic `rdbms`. **CAUTION:** Riak KV backend doesn't support transactions (rollbacks), so please avoid inserting more than one value in a single set request, otherwise you may end up with partially saved data. @@ -33,7 +32,7 @@ Backend returns the first error. Riak bucket type. ### Example Configuration -``` +```toml [modules.mod_private] backend = "mnesia" ``` diff --git a/doc/modules/mod_pubsub.md b/doc/modules/mod_pubsub.md index da6c5d546f..a09face85b 100644 --- a/doc/modules/mod_pubsub.md +++ b/doc/modules/mod_pubsub.md @@ -125,7 +125,7 @@ It is not coupled with the main DB backend, so it is possible to store the cache ### Example Configuration -``` +```toml [modules.mod_pubsub] access_createnode = "pubsub_createnode" ignore_pep_from_offline = false diff --git a/doc/modules/mod_push_service_mongoosepush.md b/doc/modules/mod_push_service_mongoosepush.md index acc20f87ff..de1795b6e3 100644 --- a/doc/modules/mod_push_service_mongoosepush.md +++ b/doc/modules/mod_push_service_mongoosepush.md @@ -34,7 +34,7 @@ The maximum amount of concurrent HTTP connections. ### Example configuration -``` +```toml [outgoing_pools.http.mongoose_push_http] scope = "global" workers = 50 diff --git a/doc/modules/mod_register.md b/doc/modules/mod_register.md index 8b2358df55..4226bdc958 100644 --- a/doc/modules/mod_register.md +++ b/doc/modules/mod_register.md @@ -57,7 +57,7 @@ Default value allows registration from every IP. ### Example configuration Allow registrations from localhost: -``` +```toml [modules.mod_register] welcome_message = {subject = "Hello from MIM!", body = "Message body."} ip_access = [ @@ -67,7 +67,7 @@ Allow registrations from localhost: ``` Deny registration from network 10.20.0.0 with mask 255.255.0.0. -``` +```toml [modules.mod_register] ip_access = [ {address = "10.20.0.0/16", policy = "deny"} diff --git a/doc/modules/mod_revproxy.md b/doc/modules/mod_revproxy.md deleted file mode 100644 index 39b9498d1d..0000000000 --- a/doc/modules/mod_revproxy.md +++ /dev/null @@ -1,69 +0,0 @@ -### Module Description - -MongooseIM can be used as a reverse proxy thanks to `mod_revproxy` module. -To enable this functionality, configure the appropriate listener and change the -module options in `mongooseim.toml`. - -#### Configuring routes - -To define reverse proxy rules, add entries defining routes to `modules.mod_revproxy.routes`. - -#### `modules.mod_revproxy.routes.host` -* **Syntax:** non-empty string -* **Default:** no default -* **Example:** `host = "www.erlang-solutions.com"` - -#### `modules.mod_revproxy.routes.path` -* **Syntax:** string -* **Default:** no default -* **Example:** `path = "/"` - -#### `modules.mod_revproxy.routes.method` -* **Syntax:** string -* **Default:** `"_"` -* **Example:** `method = "_"` - -#### `modules.mod_revproxy.routes.upstream` -* **Syntax:** non-empty string -* **Default:** no default -* **Example:** `upstream = "https://www.erlang-solutions.com/"` - -Routes are defined in the options of mod_revproxy module using `host`, `path`, -`method` and `upstream` keys. All except `method` are mandatory. -`"_"` can be used as a wildcard for `host`, `path` and `method` and it matches on everything. - -Upstreams can be defined either by host (just `http(s)://host:port`) or URI. -The difference between them is that the host upstreams are concatenated by the -whole request path while the URI upstreams are concatenated only by the remainder -that follows the matched `path`. -This behaviour is similar to the nginx's proxy_pass rules. - -Moreover, bindings may be used to match certain parts of host and/or path. -They will be overlaid with appropriate parts of the upstream URI. - -### Example configuration - -For example, for the shown example configuration, requests for: - -* `Host: www.erlang-solutions.com /admin/resources/case-studies` will be rewritten to `https://www.erlang-solutions.com/resources/case-studies` (rule 1) -* `Host: domain.com /domain/index.html` will be rewritten to `http://localhost:8080/index.html` (rule 2, since binding `:var` matches in both host and path) -* `Host: abc.com /def` will be rewritten to `http://localhost:8080/abc/def` (rule 3) - -``` -[[modules.mod_revproxy.routes]] - host = "www.erlang-solutions.com" - path = "/admin" - method = "_" - upstream = "https://www.erlang-solutions.com/" - -[[modules.mod_revproxy.routes]] - host = ":var.com" - path = "/:var" - upstream = "http://localhost:8080/" - -[[modules.mod_revproxy.routes]] - host = ":domain.com" - path = "/" - method = "_" - upstream = "http://localhost:8080/:domain" -``` diff --git a/doc/modules/mod_roster.md b/doc/modules/mod_roster.md index 037b4bbb97..2e30332485 100644 --- a/doc/modules/mod_roster.md +++ b/doc/modules/mod_roster.md @@ -33,7 +33,7 @@ Improves performance but should be disabled, when shared rosters are used. * **Example:** `backend = "mnesia"` ### Example configuration -``` +```toml [modules.mod_roster] versioning = true store_current_id = true diff --git a/doc/modules/mod_shared_roster_ldap.md b/doc/modules/mod_shared_roster_ldap.md index 40e7f4bc50..53e7b52853 100644 --- a/doc/modules/mod_shared_roster_ldap.md +++ b/doc/modules/mod_shared_roster_ldap.md @@ -133,7 +133,7 @@ Used for retrieving the human-readable name of the roster entries. Filter AND-ed with previous filters. ### Example Configuration -``` +```toml [modules.mod_shared_roster_ldap] ldap_base = "ou=Users,dc=ejd,dc=com" ldap_groupattr = "ou" diff --git a/doc/modules/mod_sic.md b/doc/modules/mod_sic.md index de29299305..7b987ab7d4 100644 --- a/doc/modules/mod_sic.md +++ b/doc/modules/mod_sic.md @@ -12,6 +12,6 @@ Strategy to handle incoming stanzas. For details, please refer to ### Example Configuration -``` +```toml [modules.mod_sic] ``` diff --git a/doc/modules/mod_stream_management.md b/doc/modules/mod_stream_management.md index 0a5f6f2768..8624d249df 100644 --- a/doc/modules/mod_stream_management.md +++ b/doc/modules/mod_stream_management.md @@ -54,7 +54,7 @@ The maximum lifespan of a record in memory. After this, they will be chased for ### Example Configuration -``` +```toml [modules.mod_stream_management] buffer_max = 30 ack_freq = 1 diff --git a/doc/modules/mod_time.md b/doc/modules/mod_time.md index 40d0f8e047..b245416ba4 100644 --- a/doc/modules/mod_time.md +++ b/doc/modules/mod_time.md @@ -15,6 +15,6 @@ Strategy to handle incoming stanzas. For details, please refer to ### Example Configuration -``` +```toml [modules.mod_time] ``` diff --git a/doc/modules/mod_vcard.md b/doc/modules/mod_vcard.md index 77c58b51d7..7dbfc951a4 100644 --- a/doc/modules/mod_vcard.md +++ b/doc/modules/mod_vcard.md @@ -103,7 +103,7 @@ Riak bucket type. Riak index name. ### Example Configuration -``` +```toml [modules.mod_vcard] allow_return_all = true search_all_hosts = true diff --git a/doc/modules/mod_version.md b/doc/modules/mod_version.md index 0f4ab04ab1..ed511782b8 100644 --- a/doc/modules/mod_version.md +++ b/doc/modules/mod_version.md @@ -20,7 +20,7 @@ Determines whether information about the operating system will be included. ### Example configuration -``` +```toml [modules.mod_version] os_info = true ``` diff --git a/include/mod_roster.hrl b/include/mod_roster.hrl index a35666d6d9..a24c7dda16 100644 --- a/include/mod_roster.hrl +++ b/include/mod_roster.hrl @@ -18,12 +18,12 @@ %%% %%%---------------------------------------------------------------------- --record(roster, {usj, - us, - jid, - name = <<>>, +-record(roster, {usj :: {jid:luser(), jid:lserver(), jid:simple_jid()}, + us :: {jid:luser(), jid:lserver()}, + jid :: jid:simple_jid(), + name = <<>> :: binary(), subscription = none :: both | from | to | none | remove, - ask = none, + ask = none :: subscribe | unsubscribe | in | out | both | none, groups = [], askmessage = <<>>, xs = []}). diff --git a/mkdocs.yml b/mkdocs.yml index 0ca0a741e0..0e95885e18 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -30,6 +30,7 @@ pages: - '3.5.0 to 3.6.0': 'migrations/3.5.0_3.6.0.md' - '3.6.0 to 3.7.0': 'migrations/3.6.0_3.7.0.md' - '3.7.0 to 4.0.0': 'migrations/3.7.0_4.0.0.md' + - '4.0.0 to 4.X.X': 'migrations/4.0.0_4.X.X.md' - 'MAM MUC migration helper': 'migrations/jid-from-mam-muc-script.md' - Platform: - 'Contributions to ecosystem': 'Contributions.md' @@ -135,7 +136,6 @@ pages: - 'mod_pubsub': 'modules/mod_pubsub.md' - 'mod_push_service_mongoosepush': 'modules/mod_push_service_mongoosepush.md' - 'mod_register': 'modules/mod_register.md' - - 'mod_revproxy': 'modules/mod_revproxy.md' - 'mod_roster': 'modules/mod_roster.md' - 'mod_shared_roster_ldap': 'modules/mod_shared_roster_ldap.md' - 'mod_sic': 'modules/mod_sic.md' diff --git a/rebar.lock b/rebar.lock index 334aefd236..f241188216 100644 --- a/rebar.lock +++ b/rebar.lock @@ -14,7 +14,7 @@ {<<"cpool">>,{pkg,<<"cpool">>,<<"0.1.0">>},0}, {<<"cqerl">>, {git,"https://github.com/esl/cqerl.git", - {ref,"c6105de4888c61f6de02de28fc9ae1923dcfb999"}}, + {ref,"816a455bcb8ddc1e63d53602e73efe297f4d0a4e"}}, 0}, {<<"credentials_obfuscation">>, {pkg,<<"credentials_obfuscation">>,<<"2.0.0">>}, @@ -136,8 +136,8 @@ {<<"rabbit_common">>,{pkg,<<"rabbit_common">>,<<"3.8.4">>},1}, {<<"ranch">>,{pkg,<<"ranch">>,<<"1.7.1">>},1}, {<<"re2">>, - {git,"https://github.com/tuncer/re2.git", - {ref,"2471e06ed7e548ca1af522e95b16867135afdfdc"}}, + {git,"https://github.com/dukesoferl/re2.git", + {ref,"9d66dff878f229d7f2a33019022f590dede9fef1"}}, 1}, {<<"recon">>,{pkg,<<"recon">>,<<"2.5.1">>},0}, {<<"redbug">>,{pkg,<<"redbug">>,<<"1.2.1">>},1}, @@ -154,7 +154,7 @@ {ref,"c7d509f38298ec6594be4efdcd8a8f2322760039"}}, 1}, {<<"snappy">>, - {git,"https://github.com/fdmanana/snappy-erlang-nif.git", + {git,"https://github.com/skunkwerks/snappy-erlang-nif.git", {ref,"e8907ee8e37cfa07d933a070669a88798082c3d7"}}, 1}, {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},2}, diff --git a/rel/files/mongooseim.toml b/rel/files/mongooseim.toml index 55354140ba..ff03b23bbc 100644 --- a/rel/files/mongooseim.toml +++ b/rel/files/mongooseim.toml @@ -212,8 +212,6 @@ [modules.mod_carboncopy] -{{{mod_http_notification}}} - [shaper.normal] max_rate = 1000 diff --git a/rel/mim3.vars-toml.config b/rel/mim3.vars-toml.config index 68fdb938ef..f5569edb43 100644 --- a/rel/mim3.vars-toml.config +++ b/rel/mim3.vars-toml.config @@ -20,7 +20,6 @@ {s2s_default_policy, "\"allow\""}. {highload_vm_args, ""}. {listen_service, ""}. -{mod_http_notification, "[modules.mod_http_notification]"}. {tls_config, "tls.certfile = \"priv/ssl/fake_server.pem\" tls.mode = \"starttls\" @@ -51,4 +50,3 @@ {c2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. {s2s_dhfile, "tls.dhfile = \"priv/ssl/fake_dh_server.pem\""}. - diff --git a/src/admin_extra/service_admin_extra_accounts.erl b/src/admin_extra/service_admin_extra_accounts.erl index 3db1af99b8..363022efc9 100644 --- a/src/admin_extra/service_admin_extra_accounts.erl +++ b/src/admin_extra/service_admin_extra_accounts.erl @@ -41,6 +41,7 @@ -include("mongoose.hrl"). -include("ejabberd_commands.hrl"). +-include("jlib.hrl"). %%% %%% Register commands @@ -101,9 +102,10 @@ commands() -> -spec set_password(jid:user(), jid:server(), binary()) -> {error, string()} | {ok, string()}. set_password(User, Host, Password) -> - case ejabberd_auth:set_password(User, Host, Password) of + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:set_password(JID, Password) of ok -> - {ok, io_lib:format("Password for user ~s@~s successfully changed", [User, Host])}; + {ok, io_lib:format("Password for user ~s successfully changed", [jid:to_binary(JID)])}; {error, Reason} -> {error, Reason} end. @@ -111,28 +113,30 @@ set_password(User, Host, Password) -> -spec check_password(jid:user(), jid:server(), binary()) -> {Res, string()} when Res :: ok | incorrect | user_does_not_exist. check_password(User, Host, Password) -> - case ejabberd_auth:is_user_exists(User, Host) of + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> - case ejabberd_auth:check_password(User, Host, Password) of + case ejabberd_auth:check_password(JID, Password) of true -> - {ok, io_lib:format("Password '~s' for user ~s@~s is correct", - [Password, User, Host])}; + {ok, io_lib:format("Password '~s' for user ~s is correct", + [Password, jid:to_binary(JID)])}; false -> - {incorrect, io_lib:format("Password '~s' for user ~s@~s is incorrect", - [Password, User, Host])} + {incorrect, io_lib:format("Password '~s' for user ~s is incorrect", + [Password, jid:to_binary(JID)])} end; false -> {user_does_not_exist, - io_lib:format("Password '~s' for user ~s@~s is incorrect because this user does not" + io_lib:format("Password '~s@~s' for user ~s is incorrect because this user does not" " exist", [Password, User, Host])} end. -spec check_account(jid:user(), jid:server()) -> {Res, string()} when Res :: ok | user_does_not_exist. check_account(User, Host) -> - case ejabberd_auth:is_user_exists(User, Host) of + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> - {ok, io_lib:format("User ~s@~s exists", [User, Host])}; + {ok, io_lib:format("User ~s exists", [jid:to_binary(JID)])}; false -> {user_does_not_exist, io_lib:format("User ~s@~s does not exist", [User, Host])} end. @@ -142,7 +146,7 @@ check_account(User, Host) -> Hash :: binary(), Method :: string()) -> {error, string()} | {ok, string()} | {incorrect, string()}. check_password_hash(User, Host, PasswordHash, HashMethod) -> - AccountPass = ejabberd_auth:get_password_s(User, Host), + AccountPass = ejabberd_auth:get_password_s(jid:make(User, Host, <<>>)), AccountPassHash = case HashMethod of "md5" -> get_md5(AccountPass); "sha" -> get_sha(AccountPass); @@ -219,15 +223,15 @@ delete_old_user({LUser, LServer}, TimeStampNow, SecOlder) -> %% Check if the user is logged JID = jid:make(LUser, LServer, <<>>), case ejabberd_sm:get_user_resources(JID) of - [] -> delete_old_user_if_nonactive_long_enough(LUser, LServer, TimeStampNow, SecOlder); + [] -> delete_old_user_if_nonactive_long_enough(JID, TimeStampNow, SecOlder); _ -> false end. --spec delete_old_user_if_nonactive_long_enough(LUser :: jid:luser(), - LServer :: jid:lserver(), +-spec delete_old_user_if_nonactive_long_enough(JID :: jid:jid(), TimeStampNow :: non_neg_integer(), SecOlder :: non_neg_integer()) -> boolean(). -delete_old_user_if_nonactive_long_enough(LUser, LServer, TimeStampNow, SecOlder) -> +delete_old_user_if_nonactive_long_enough(JID, TimeStampNow, SecOlder) -> + {LUser, LServer} = jid:to_lus(JID), case mod_last:get_last_info(LUser, LServer) of {ok, TimeStamp, _Status} -> %% get his age @@ -240,45 +244,45 @@ delete_old_user_if_nonactive_long_enough(LUser, LServer, TimeStampNow, SecOlder) %% older: false -> %% remove the user - ejabberd_auth:remove_user(LUser, LServer), + ejabberd_auth:remove_user(JID), true end; not_found -> - ejabberd_auth:remove_user(LUser, LServer), + ejabberd_auth:remove_user(JID), true end. -spec ban_account(jid:user(), jid:server(), binary() | string()) -> {ok, string()} | {error, string()}. ban_account(User, Host, ReasonText) -> + JID = jid:make(User, Host, <<>>), Reason = service_admin_extra_sessions:prepare_reason(ReasonText), - kick_sessions(User, Host, Reason), - case set_random_password(User, Host, Reason) of + kick_sessions(JID, Reason), + case set_random_password(JID, Reason) of ok -> - {ok, io_lib:format("User ~s@~s successfully banned with reason: ~s", - [User, Host, ReasonText])}; + {ok, io_lib:format("User ~s successfully banned with reason: ~s", + [jid:to_binary(JID), ReasonText])}; {error, ErrorReason} -> {error, ErrorReason} end. --spec kick_sessions(jid:user(), jid:server(), binary()) -> [ok]. -kick_sessions(User, Server, Reason) -> - JID = jid:make(User, Server, <<>>), +-spec kick_sessions(jid:jid(), binary()) -> [ok]. +kick_sessions(JID, Reason) -> lists:map( fun(Resource) -> - service_admin_extra_sessions:kick_session(User, Server, Resource, Reason) + service_admin_extra_sessions:kick_session( + jid:replace_resource(JID, Resource), Reason) end, ejabberd_sm:get_user_resources(JID)). --spec set_random_password(User, Server, Reason) -> Result when - User :: jid:user(), - Server :: jid:server(), +-spec set_random_password(JID, Reason) -> Result when + JID :: jid:jid(), Reason :: binary(), Result :: 'ok' | {error, any()}. -set_random_password(User, Server, Reason) -> +set_random_password(JID, Reason) -> NewPass = build_random_password(Reason), - ejabberd_auth:set_password(User, Server, NewPass). + ejabberd_auth:set_password(JID, NewPass). -spec build_random_password(Reason :: binary()) -> binary(). diff --git a/src/admin_extra/service_admin_extra_gdpr.erl b/src/admin_extra/service_admin_extra_gdpr.erl index ee4bc5b2bf..2a7ed060bc 100644 --- a/src/admin_extra/service_admin_extra_gdpr.erl +++ b/src/admin_extra/service_admin_extra_gdpr.erl @@ -25,9 +25,10 @@ commands() -> [ -spec retrieve_all(jid:user(), jid:server(), Path :: binary()) -> ok | {error, Reason :: any()}. retrieve_all(Username, Domain, ResultFilePath) -> - case user_exists(Username, Domain) of + JID = jid:make(Username, Domain, <<>>), + case user_exists(JID) of true -> - DataFromModules = get_data_from_modules(Username, Domain), + DataFromModules = get_data_from_modules(JID), % The contract is that we create personal data files only when there are any items % returned for the data group. DataToWrite = lists:filter(fun({_, _, Items}) -> Items /= [] end, DataFromModules), @@ -60,6 +61,10 @@ retrieve_all(Username, Domain, ResultFilePath) -> -spec get_data_from_modules(jid:user(), jid:server()) -> gdpr:personal_data(). get_data_from_modules(Username, Domain) -> JID = jid:make(Username, Domain, <<>>), + get_data_from_modules(JID). + +-spec get_data_from_modules(jid:jid()) -> gdpr:personal_data(). +get_data_from_modules(JID) -> mongoose_hooks:get_personal_data(JID#jid.lserver, [], JID). -spec to_csv_file(CsvFilename :: binary(), gdpr:schema(), gdpr:entities(), file:name()) -> ok. @@ -70,9 +75,9 @@ to_csv_file(Filename, DataSchema, DataRows, TmpDir) -> lists:foreach(fun(Row) -> csv_gen:row(File, Row) end, DataRows), file:close(File). --spec user_exists(gdpr:username(), gdpr:domain()) -> boolean(). -user_exists(Username, Domain) -> - ejabberd_auth:is_user_exists(Username, Domain). +-spec user_exists(jid:jid()) -> boolean(). +user_exists(JID) -> + ejabberd_auth:does_user_exist(JID). -spec make_tmp_dir() -> file:name(). make_tmp_dir() -> diff --git a/src/admin_extra/service_admin_extra_last.erl b/src/admin_extra/service_admin_extra_last.erl index 100d438c75..97ce327622 100644 --- a/src/admin_extra/service_admin_extra_last.erl +++ b/src/admin_extra/service_admin_extra_last.erl @@ -61,11 +61,12 @@ commands() -> -spec set_last(jid:user(), jid:server(), _, _) -> {Res, string()} when Res :: ok | user_does_not_exist. set_last(User, Server, Timestamp, Status) -> - case ejabberd_auth:is_user_exists(User, Server) of + JID = jid:make(User, Server, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> - mod_last:store_last_info(jid:nodeprep(User), jid:nameprep(Server), Timestamp, Status), - {ok, io_lib:format("Last activity for user ~s@~s is set as ~B with status ~s", - [User, Server, Timestamp, Status])}; + mod_last:store_last_info(JID#jid.luser, JID#jid.lserver, Timestamp, Status), + {ok, io_lib:format("Last activity for user ~s is set as ~B with status ~s", + [jid:to_binary(JID), Timestamp, Status])}; false -> String = io_lib:format("User ~s@~s does not exist", [User, Server]), {user_does_not_exist, String} diff --git a/src/admin_extra/service_admin_extra_private.erl b/src/admin_extra/service_admin_extra_private.erl index 1dc57d2733..167d88915c 100644 --- a/src/admin_extra/service_admin_extra_private.erl +++ b/src/admin_extra/service_admin_extra_private.erl @@ -68,16 +68,16 @@ commands() -> -spec private_get(jid:user(), jid:server(), binary(), binary()) -> {error, string()} | string(). private_get(Username, Host, Element, Ns) -> - case ejabberd_auth:is_user_exists(Username, Host) of + JID = jid:make(Username, Host, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> - do_private_get(Username, Host, Element, Ns); + do_private_get(JID, Element, Ns); false -> {error, io_lib:format("User ~s@~s does not exist", [Username, Host])} end. -do_private_get(Username, Host, Element, Ns) -> - From = jid:make(Username, Host, <<"">>), - To = jid:make(Username, Host, <<"">>), +do_private_get(JID, Element, Ns) -> + From = To = JID, IQ = {iq, <<"">>, get, ?NS_PRIVATE, <<"">>, #xmlel{ name = <<"query">>, attrs = [{<<"xmlns">>, ?NS_PRIVATE}], @@ -108,18 +108,18 @@ private_set(Username, Host, ElementString) -> private_set2(Username, Host, Xml) -> - case ejabberd_auth:is_user_exists(Username, Host) of + JID = jid:make(Username, Host, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> - do_private_set2(Username, Host, Xml); + do_private_set2(JID, Xml); false -> - {user_does_not_exist, io_lib:format("User ~s@~s does not exist", [Username, Host])} + {user_does_not_exist, io_lib:format("User ~s does not exist", [jid:to_binary(JID)])} end. -do_private_set2(Username, Host, Xml) -> +do_private_set2(#jid{lserver = Host} = JID, Xml) -> case is_private_module_loaded(Host) of true -> - From = jid:make(Username, Host, <<"">>), - To = jid:make(Username, Host, <<"">>), + From = To = JID, IQ = {iq, <<"">>, set, ?NS_PRIVATE, <<"">>, #xmlel{ name = <<"query">>, attrs = [{<<"xmlns">>, ?NS_PRIVATE}], diff --git a/src/admin_extra/service_admin_extra_roster.erl b/src/admin_extra/service_admin_extra_roster.erl index 0d500f71ae..29aae1a86e 100644 --- a/src/admin_extra/service_admin_extra_roster.erl +++ b/src/admin_extra/service_admin_extra_roster.erl @@ -26,16 +26,15 @@ -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 - ]). + 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"). @@ -164,11 +163,13 @@ commands() -> Subs :: subs()) -> {Res, string()} when Res :: user_doest_not_exist | error | bad_subs | ok. add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) -> - case ejabberd_auth:is_user_exists(LocalUser, LocalServer) of + LocalJID = jid:make(LocalUser, LocalServer, <<>>), + case ejabberd_auth:does_user_exist(LocalJID) of true -> - case subscribe(LocalUser, LocalServer, User, Server, Nick, Group, Subs, []) of + RemoteJID = jid:make(User, Server, <<>>), + case subscribe(LocalJID, RemoteJID, Nick, Group, Subs, []) of {atomic, _} -> - do_add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs); + do_add_rosteritem(LocalJID, RemoteJID, Nick, Group, Subs); Other -> {error, io_lib:format("~p", [Other])} end; @@ -178,11 +179,11 @@ add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) -> [LocalUser, LocalServer])} end. -do_add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) -> +do_add_rosteritem(LocalJID, RemoteJID, Nick, Group, Subs) -> case lists:member(Subs, possible_subs_binary()) of true -> - push_roster_item(LocalUser, LocalServer, User, Server, {add, Nick, Subs, Group}), - {ok, io_lib:format("Added the item to the roster of ~s@~s", [LocalUser, LocalServer])}; + 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", @@ -191,20 +192,18 @@ do_add_rosteritem(LocalUser, LocalServer, User, Server, Nick, Group, Subs) -> %% @doc returns result of mnesia or rdbms transaction --spec subscribe(LocalUser :: jid:user(), - LocalServer :: jid:server(), - User :: jid:user(), - Server :: jid:server(), +-spec subscribe(LocalJID :: jid:jid(), + RemoteJID :: jid:jid(), Nick :: binary(), Group :: binary() | string(), Subs :: subs(), _Xattrs :: [jlib:binary_pair()]) -> any(). -subscribe(LU, LS, User, Server, Nick, Group, SubscriptionS, _Xattrs) -> - ItemEl = build_roster_item(User, Server, {add, Nick, SubscriptionS, Group}), +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]}, - mod_roster:set_items(LU, LS, QueryEl). + mod_roster:set_items(LocalJID, QueryEl). -spec delete_rosteritem(LocalUser :: jid:user(), @@ -213,13 +212,15 @@ subscribe(LU, LS, User, Server, Nick, Group, SubscriptionS, _Xattrs) -> Server :: jid:server()) -> {Res, string()} when Res :: ok | error | user_does_not_exist. delete_rosteritem(LocalUser, LocalServer, User, Server) -> - case ejabberd_auth:is_user_exists(LocalUser, LocalServer) of + LocalJID = jid:make(LocalUser, LocalServer, <<>>), + case ejabberd_auth:does_user_exist(LocalJID) of true -> - case unsubscribe(LocalUser, LocalServer, User, Server) of + RemoteJID = jid:make(User, Server, <<>>), + case unsubscribe(LocalJID, RemoteJID) of {atomic, ok} -> - push_roster_item(LocalUser, LocalServer, User, Server, remove), - {ok, io_lib:format("The item removed from roster of ~s@~s", - [LocalUser, LocalServer])}; + push_roster_item(LocalJID, RemoteJID, remove), + {ok, io_lib:format("The item removed from roster of ~s", + [jid:to_binary(LocalJID)])}; Other -> {error, io_lib:format("~p", [Other])} end; @@ -231,16 +232,13 @@ delete_rosteritem(LocalUser, LocalServer, User, Server) -> %% @doc returns result of mnesia or rdbms transaction --spec unsubscribe(LocalUser :: jid:user(), - LocalServer :: jid:server(), - User :: jid:user(), - Server :: jid:server()) -> any(). -unsubscribe(LU, LS, User, Server) -> - ItemEl = build_roster_item(User, Server, remove), +-spec unsubscribe(LocalJID :: jid:jid(), RemoteJID :: jid:jid()) -> any(). +unsubscribe(LocalJID, RemoteJID) -> + ItemEl = build_roster_item(RemoteJID, remove), QueryEl = #xmlel{ name = <<"query">>, attrs = [{<<"xmlns">>, <<"jabber:iq:roster">>}], children = [ItemEl]}, - mod_roster:set_items(LU, LS, QueryEl). + mod_roster:set_items(LocalJID, QueryEl). %% ----------------------------- %% Get Roster @@ -323,7 +321,9 @@ 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(Name1, Server1, Name2, Server2, Nick2, Group2, <<"both">>, []), + subscribe(jid:make(Name1, Server1, <<>>), + jid:make(Name2, Server2, <<>>), + Nick2, Group2, <<"both">>, []), subscribe_roster({Name1, Server1, Group1, Nick1}, Roster). @@ -344,42 +344,35 @@ build_list_users(Group, [{User, Server}|Users], Res) -> build_list_users(Group, Users, [{User, Server, Group, User}|Res]). -%% @spec(LU, LS, U, S, Action) -> ok +%% @spec(LocalJID, RemoteJID, Action) -> ok %% Action = {add, Nick, Subs, Group} | remove %% @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:luser(), jid:lserver(), jid:user(), - jid:server(), Action :: push_action()) -> 'ok'. -push_roster_item(LU, LS, U, S, Action) -> - JID = jid:make(LU, LS, <<>>), +-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), - push_roster_item(RJID, U, S, Action) + BroadcastEl = build_broadcast(U, S, Action), + ejabberd_sm:route(RJID, RJID, BroadcastEl), + 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 push_roster_item(jid:jid(), jid:user(), jid:server(), Action :: push_action()) -> - mongoose_acc:t(). -push_roster_item(JID, U, S, Action) -> - BroadcastEl = build_broadcast(U, S, Action), - ejabberd_sm:route(JID, JID, BroadcastEl), - Item = build_roster_item(U, S, Action), - ResIQ = build_iq_roster_push(Item), - ejabberd_router:route(JID, JID, ResIQ). - --spec build_roster_item(jid:user(), jid:server(), push_action() - ) -> exml:element(). -build_roster_item(U, S, {add, Nick, Subs, Group}) -> +-spec build_roster_item(jid:jid(), push_action()) -> exml:element(). +build_roster_item(#jid{resource = <<>>} = JID, {add, Nick, Subs, Group}) -> #xmlel{ name = <<"item">>, - attrs = [{<<"jid">>, jid:to_binary(jid:make(U, S, <<"">>))}, + attrs = [{<<"jid">>, jid:to_binary(JID)}, {<<"name">>, Nick}, {<<"subscription">>, Subs}], children = [#xmlel{name = <<"group">>, children = [#xmlcdata{content = Group}]}] }; -build_roster_item(U, S, remove) -> +build_roster_item(#jid{resource = <<>>} = JID, remove) -> #xmlel{ name = <<"item">>, - attrs = [{<<"jid">>, jid:to_binary(jid:make(U, S, <<"">>))}, - {<<"subscription">>, <<"remove">>}]}. + attrs = [{<<"jid">>, jid:to_binary(JID)}, + {<<"subscription">>, <<"remove">>}]}; +build_roster_item(#jid{} = JID, Action) -> + build_roster_item(jid:replace_resource(JID, <<>>), Action). -spec build_iq_roster_push(jlib:xmlcdata() | exml:element()) -> exml:element(). diff --git a/src/admin_extra/service_admin_extra_sessions.erl b/src/admin_extra/service_admin_extra_sessions.erl index 048a814629..0c1c325fcb 100644 --- a/src/admin_extra/service_admin_extra_sessions.erl +++ b/src/admin_extra/service_admin_extra_sessions.erl @@ -31,6 +31,7 @@ num_resources/2, resource_num/3, + kick_session/2, kick_session/4, prepare_reason/1, status_num/2, status_num/1, @@ -187,13 +188,15 @@ resource_num(User, Host, Num) -> end. --spec kick_session(jid:user(), jid:server(), jid:resource(), - ReasonText :: binary() | list()) -> 'ok'. -kick_session(User, Server, Resource, ReasonText) -> - ejabberd_c2s:terminate_session(jid:make(User, Server, Resource), - prepare_reason(ReasonText)), +-spec kick_session(jid:jid(), binary() | list()) -> ok. +kick_session(JID, ReasonText) -> + ejabberd_c2s:terminate_session(JID, prepare_reason(ReasonText)), ok. +-spec kick_session(jid:user(), jid:server(), jid:resource(), list() | binary()) -> ok. +kick_session(User, Server, Resource, ReasonText) -> + kick_session(jid:make(User, Server, Resource), prepare_reason(ReasonText)). + -spec prepare_reason(binary() | string()) -> binary(). prepare_reason(<<>>) -> diff --git a/src/admin_extra/service_admin_extra_vcard.erl b/src/admin_extra/service_admin_extra_vcard.erl index 79cb0d544d..755925e498 100644 --- a/src/admin_extra/service_admin_extra_vcard.erl +++ b/src/admin_extra/service_admin_extra_vcard.erl @@ -123,9 +123,10 @@ commands() -> -spec get_vcard(jid:user(), jid:server(), any()) -> {error, string()} | [binary()]. get_vcard(User, Host, Name) -> - case ejabberd_auth:is_user_exists(User, Host) of + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> - get_vcard_content(User, Host, [Name]); + get_vcard_content(JID, [Name]); false -> {error, io_lib:format("User ~s@~s does not exist", [User, Host])} end. @@ -133,9 +134,10 @@ get_vcard(User, Host, Name) -> -spec get_vcard(jid:user(), jid:server(), any(), any()) -> {error, string()} | [binary()]. get_vcard(User, Host, Name, Subname) -> - case ejabberd_auth:is_user_exists(User, Host) of + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> - get_vcard_content(User, Host, [Name, Subname]); + get_vcard_content(JID, [Name, Subname]); false -> {error, io_lib:format("User ~s@~s does not exist", [User, Host])} end. @@ -143,9 +145,10 @@ get_vcard(User, Host, Name, Subname) -> -spec set_vcard(jid:user(), jid:server(), [binary()], binary() | [binary()]) -> {ok, string()} | {user_does_not_exist, string()}. set_vcard(User, Host, Name, SomeContent) -> - case ejabberd_auth:is_user_exists(User, Host) of + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> - set_vcard_content(User, Host, [Name], SomeContent); + set_vcard_content(JID, [Name], SomeContent); false -> {user_does_not_exist, io_lib:format("User ~s@~s does not exist", [User, Host])} end. @@ -153,9 +156,10 @@ set_vcard(User, Host, Name, SomeContent) -> -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) -> - case ejabberd_auth:is_user_exists(User, Host) of + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> - set_vcard_content(User, Host, [Name, Subname], SomeContent); + set_vcard_content(JID, [Name, Subname], SomeContent); false -> {user_does_not_exist, io_lib:format("User ~s@~s does not exist", [User, Host])} end. @@ -172,10 +176,10 @@ get_module_resource(Server) -> end. --spec get_vcard_content(jid:user(), jid:server(), any()) - -> {error, string()} | list(binary()). -get_vcard_content(User, Server, Data) -> - JID = jid:make(User, Server, list_to_binary(get_module_resource(Server))), +-spec get_vcard_content(jid:jid(), any()) -> + {error, string()} | list(binary()). +get_vcard_content(#jid{lserver = LServer} = NoResJID, Data) -> + JID = jid:replace_resource(NoResJID, list_to_binary(get_module_resource(LServer))), IQ = #iq{type = get, xmlns = ?NS_VCARD, sub_el = []}, Acc = mongoose_acc:new(#{ location => ?LOCATION, from_jid => JID, @@ -203,12 +207,11 @@ get_vcard([Data1, Data2], A1) -> get_vcard([Data], A1) -> exml_query:subelements(A1, Data). --spec set_vcard_content(jid:user(), jid:server(), Data :: [binary()], +-spec set_vcard_content(jid:jid(), Data :: [binary()], ContentList :: binary() | [binary()]) -> {ok, string()}. -set_vcard_content(U, S, D, SomeContent) when is_binary(SomeContent) -> - set_vcard_content(U, S, D, [SomeContent]); -set_vcard_content(User, Server, Data, ContentList) -> - JID = jid:make(User, Server, <<>>), +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 = []}, Acc = mongoose_acc:new(#{ location => ?LOCATION, from_jid => JID, diff --git a/src/auth/ejabberd_auth.erl b/src/auth/ejabberd_auth.erl index 5bdcdb9075..4014482bf0 100644 --- a/src/auth/ejabberd_auth.erl +++ b/src/auth/ejabberd_auth.erl @@ -33,22 +33,21 @@ get_opt/3, get_opt/2, authorize/1, - set_password/3, - check_password/3, - check_password/5, - try_register/3, + set_password/2, + check_password/2, + check_password/4, + try_register/2, dirty_get_registered_users/0, get_vh_registered_users/1, get_vh_registered_users/2, get_vh_registered_users_number/1, get_vh_registered_users_number/2, - get_password/2, - get_password_s/2, - get_passterm_with_authmodule/2, + get_password/1, + get_password_s/1, + get_passterm_with_authmodule/1, does_user_exist/1, - is_user_exists/2, - is_user_exists_in_other_modules/3, - remove_user/2, + is_user_exists_in_other_modules/2, + remove_user/1, supports_sasl_module/2, entropy/1 ]). @@ -154,44 +153,24 @@ do_authorize_loop([M | Modules], Creds) -> %% @doc Check if the user and password can login in server. --spec check_password(User :: jid:user(), - Server :: jid:server(), - Password :: binary() ) -> boolean(). -check_password(User, Server, Password) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - do_check_password(LUser, LServer, Password). - --spec do_check_password(jid:luser(), jid:lserver(), binary()) -> boolean(). -do_check_password(LUser, LServer, _) when LUser =:= error; LServer =:= error -> +-spec check_password(JID :: jid:jid() | error, Password :: binary()) -> boolean(). +check_password(error, _Password) -> false; -do_check_password(LUser, LServer, Password) -> - case check_password_with_authmodule(LUser, LServer, Password) of +check_password(#jid{} = JID, Password) -> + case check_password_with_authmodule(JID, Password) of {true, _AuthModule} -> true; false -> false end. %% @doc Check if the user and password can login in server. --spec check_password(User :: jid:user(), - Server :: jid:server(), - Password :: binary(), - Digest :: binary(), - DigestGen :: fun()) -> boolean(). -check_password(User, Server, Password, Digest, DigestGen) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - do_check_password(LUser, LServer, Password, Digest, DigestGen). - --spec do_check_password(User :: jid:luser(), - Server :: jid:lserver(), +-spec check_password(JID :: jid:jid() | error, Password :: binary(), Digest :: binary(), - DigestGen :: fun()) -> boolean(). -do_check_password(LUser, LServer, _, _, _) - when LUser =:= error; LServer =:= error -> + DigestGen :: fun((binary()) -> binary())) -> boolean(). +check_password(error, _, _, _) -> false; -do_check_password(LUser, LServer, Password, Digest, DigestGen) -> - case check_password_with_authmodule(LUser, LServer, Password, Digest, DigestGen) of +check_password(#jid{} = JID, Password, Digest, DigestGen) -> + case check_password_with_authmodule(JID, Password, Digest, DigestGen) of {true, _AuthModule} -> true; false -> false end. @@ -199,46 +178,17 @@ do_check_password(LUser, LServer, Password, Digest, DigestGen) -> %% @doc Check if the user and password can login in server. %% The user can login if at least an authentication method accepts the user %% and the password. --spec check_password_with_authmodule(User :: binary(), - Server :: binary(), - Password :: binary() - ) -> 'false' | {'true', authmodule()}. -check_password_with_authmodule(User, Server, Password) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - do_check_password_with_authmodule(LUser, LServer, Password). - --spec do_check_password_with_authmodule(LUser :: jid:luser(), - LServer :: jid:lserver(), - Password :: binary() - ) -> 'false' | {'true', authmodule()}. -do_check_password_with_authmodule(LUser, LServer, _) - when LUser =:= error; LServer =:= error -> - false; -do_check_password_with_authmodule(LUser, LServer, Password) -> +-spec check_password_with_authmodule(JID :: jid:jid(), Password :: binary()) -> + false | {true, authmodule()}. +check_password_with_authmodule(#jid{luser = LUser, lserver = LServer}, Password) -> check_password_loop(auth_modules(LServer), [LUser, LServer, Password]). --spec check_password_with_authmodule(User :: binary(), - Server :: binary(), +-spec check_password_with_authmodule(JID :: jid:jid(), Password :: binary(), Digest :: binary(), - DigestGen :: fun() - ) -> 'false' | {'true', authmodule()}. -check_password_with_authmodule(User, Server, Password, Digest, DigestGen) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - do_check_password_with_authmodule(LUser, LServer, Password, Digest, DigestGen). - --spec do_check_password_with_authmodule(LUser :: jid:luser(), - LServer :: jid:lserver(), - Password :: binary(), - Digest :: binary(), - DigestGen :: fun() + DigestGen :: fun((binary()) -> binary()) ) -> 'false' | {'true', authmodule()}. -do_check_password_with_authmodule(LUser, LServer, _, _, _) - when LUser =:= error; LServer =:= error -> - false; -do_check_password_with_authmodule(LUser, LServer, Password, Digest, DigestGen) -> +check_password_with_authmodule(#jid{luser = LUser, lserver = LServer}, Password, Digest, DigestGen) -> check_password_loop(auth_modules(LServer), [LUser, LServer, Password, Digest, DigestGen]). @@ -260,26 +210,19 @@ do_check_password_loop([AuthModule | AuthModules], Args) -> do_check_password_loop(AuthModules, Args) end. --spec check_digest(binary(), fun(), binary(), binary()) -> boolean(). +-spec check_digest(binary(), fun((binary()) -> binary()), binary(), binary()) -> boolean(). check_digest(<<>>, _, <<>>, _) -> false; %%empty digest and password check_digest(Digest, DigestGen, _Password, Passwd) -> Digest == DigestGen(Passwd). --spec set_password(User :: jid:user(), - Server :: jid:server(), - Password :: binary() - ) -> ok | {error, empty_password | not_allowed | invalid_jid}. -set_password(_, _, <<"">>) -> +-spec set_password(jid:jid() | error, binary()) -> + ok | {error, empty_password | not_allowed | invalid_jid}. +set_password(_, <<"">>) -> {error, empty_password}; -set_password(User, Server, Password) -> - LUser = jid:nodeprep(User), - LServer = jid:nodeprep(Server), - do_set_password(LUser, LServer, Password). - -do_set_password(LUser, LServer, _) when LUser =:= error; LServer =:= error -> +set_password(error, _) -> {error, invalid_jid}; -do_set_password(LUser, LServer, Password) -> +set_password(#jid{luser = LUser, lserver = LServer}, Password) -> lists:foldl( fun(M, {error, _}) -> M:set_password(LUser, LServer, Password); @@ -288,28 +231,22 @@ do_set_password(LUser, LServer, Password) -> end, {error, not_allowed}, auth_modules(LServer)). --spec try_register(User :: jid:user(), - Server :: jid:server(), - Password :: binary() - ) -> ok | {error, exists | not_allowed | invalid_jid | null_password}. -try_register(_, _, <<"">>) -> +-spec try_register(jid:jid() | error, binary()) -> + ok | {error, exists | not_allowed | invalid_jid | null_password}. +try_register(_, <<"">>) -> {error, null_password}; -try_register(User, Server, Password) -> - LUser = jid:nodeprep(User), - LServer = jid:nodeprep(Server), - do_try_register(LUser, LServer, Password). - --spec do_try_register(jid:luser(), jid:lserver(), binary()) - -> ok | {error, exists | not_allowed | invalid_jid}. -do_try_register(LUser, LServer, _) when LUser =:= error; LServer =:= error -> +try_register(error, _) -> {error, invalid_jid}; -do_try_register(LUser, LServer, Password) -> - Exists = is_user_exists(LUser, LServer), - do_try_register_if_does_not_exist(Exists, LUser, LServer, Password). +try_register(JID, Password) -> + Exists = does_user_exist(JID), + do_try_register_if_does_not_exist(Exists, JID, Password). -do_try_register_if_does_not_exist(true, _, _, _) -> +-spec do_try_register_if_does_not_exist(boolean(), jid:jid(), binary()) -> + ok | {error, exists | not_allowed | invalid_jid | null_password}. +do_try_register_if_does_not_exist(true, _, _) -> {error, exists}; -do_try_register_if_does_not_exist(_, LUser, LServer, Password) -> +do_try_register_if_does_not_exist(_, JID, Password) -> + {LUser, LServer} = jid:to_lus(JID), case lists:member(LServer, ?MYHOSTS) of true -> timed_call(LServer, try_register, @@ -342,8 +279,7 @@ dirty_get_registered_users() -> %% @doc Registered users list do not include anonymous users logged --spec get_vh_registered_users(Server :: jid:server() - ) -> [jid:simple_bare_jid()]. +-spec get_vh_registered_users(Server :: jid:server()) -> [jid:simple_bare_jid()]. get_vh_registered_users(Server) -> LServer = jid:nameprep(Server), do_get_vh_registered_users(LServer). @@ -357,8 +293,8 @@ do_get_vh_registered_users(LServer) -> end, auth_modules(LServer)). --spec get_vh_registered_users(Server :: jid:server(), - Opts :: [any()]) -> [jid:simple_bare_jid()]. +-spec get_vh_registered_users(Server :: jid:server(), Opts :: [any()]) -> + [jid:simple_bare_jid()]. get_vh_registered_users(Server, Opts) -> LServer = jid:nameprep(Server), do_get_vh_registered_users(LServer, Opts). @@ -372,8 +308,7 @@ do_get_vh_registered_users(LServer, Opts) -> end, auth_modules(LServer)). --spec get_vh_registered_users_number(Server :: jid:server() - ) -> integer(). +-spec get_vh_registered_users_number(Server :: jid:server()) -> integer(). get_vh_registered_users_number(Server) -> LServer = jid:nameprep(Server), do_get_vh_registered_users_number(LServer). @@ -388,8 +323,7 @@ do_get_vh_registered_users_number(LServer) -> end, auth_modules(LServer))). --spec get_vh_registered_users_number(Server :: jid:server(), - Opts :: list()) -> integer(). +-spec get_vh_registered_users_number(Server :: jid:server(), Opts :: list()) -> integer(). get_vh_registered_users_number(Server, Opts) -> LServer = jid:nameprep(Server), do_get_vh_registered_users_number(LServer, Opts). @@ -405,146 +339,85 @@ do_get_vh_registered_users_number(LServer, Opts) -> %% @doc Get the password of the user. --spec get_password(User :: jid:user(), - Server :: jid:server()) -> ejabberd_auth:passterm() | false. -get_password(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - do_get_password(LUser, LServer). - -do_get_password(LUser, LServer) when LUser =:= error; LServer =:= error -> - false; -do_get_password(LUser, LServer) -> +-spec get_password(JID :: jid:jid() | error) -> ejabberd_auth:passterm() | false. +get_password(#jid{luser = LUser, lserver = LServer}) -> lists:foldl( fun(M, false) -> M:get_password(LUser, LServer); (_M, Password) -> Password - end, false, auth_modules(LServer)). + end, false, auth_modules(LServer)); +get_password(error) -> + false. --spec get_password_s(User :: jid:user(), - Server :: jid:server()) -> binary(). -get_password_s(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - do_get_password_s(LUser, LServer). - -do_get_password_s(LUser, LServer) when LUser =:= error; LServer =:= error -> - <<"">>; -do_get_password_s(LUser, LServer) -> +-spec get_password_s(JID :: jid:jid() | error) -> binary(). +get_password_s(#jid{luser = LUser, lserver = LServer}) -> lists:foldl( fun(M, <<"">>) -> M:get_password_s(LUser, LServer); (_M, Password) -> Password - end, <<"">>, auth_modules(LServer)). + end, <<"">>, auth_modules(LServer)); +get_password_s(error) -> + <<"">>. %% @doc Get the password(like thing) of the user and the auth module. --spec get_passterm_with_authmodule(jid:user(), jid:server()) -> R when - R :: {passterm(), authmodule()} - | {'false', 'none'}. -get_passterm_with_authmodule(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - do_get_passterm_with_authmodule(LUser, LServer). - -do_get_passterm_with_authmodule(LUser, LServer) - when LUser =:= error; LServer =:= error -> - {false, none}; -do_get_passterm_with_authmodule(LUser, LServer) -> +-spec get_passterm_with_authmodule(error | jid:jid()) -> R when + R :: {passterm(), authmodule()} | {false, none}. +get_passterm_with_authmodule(#jid{luser = LUser, lserver = LServer}) -> lists:foldl( fun(M, {false, _}) -> - {M:get_password(LUser, LServer), M}; - (_M, {Password, AuthModule}) -> + {M:get_password(LUser, LServer), M}; + (_M, {Password, AuthModule}) -> {Password, AuthModule} end, {false, none}, auth_modules(LServer)). %% @doc Returns true if the user exists in the DB or if an anonymous user is %% logged under the given name --spec is_user_exists(User :: jid:user(), - Server :: jid:server()) -> boolean(). -is_user_exists(<<"">>, _) -> - false; -is_user_exists(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - do_does_user_exist(LUser, LServer). - --spec does_user_exist(JID :: jid:jid()) -> boolean(). -does_user_exist(JID) -> - #jid{luser = LUser, lserver = LServer} = JID, - do_does_user_exist(LUser, LServer). - -do_does_user_exist(LUser, LServer) when LUser =:= error; LServer =:= error -> - false; -do_does_user_exist(LUser, LServer) -> - timed_call(LServer, does_user_exist, fun does_user_exist_timed/2, [LUser, LServer]). +-spec does_user_exist(JID :: jid:jid() | error) -> boolean(). +does_user_exist(#jid{luser = LUser, lserver = LServer}) -> + timed_call(LServer, does_user_exist, fun does_user_exist_timed/2, [LUser, LServer]); +does_user_exist(error) -> + false. does_user_exist_timed(LUser, LServer) -> - lists:any( - fun(M) -> - case M:does_user_exist(LUser, LServer) of - {error, Error} -> - ?LOG_ERROR(#{what => does_user_exist_failed, - text => <<"The authentication module returned an error">>, - auth_module => M, reason => Error, - user => LUser, server => LServer}), - false; - Else -> - Else - end - end, auth_modules(LServer)). - -%% Check if the user exists in all authentications module except the module -%% passed as parameter --spec is_user_exists_in_other_modules(Module :: authmodule(), - User :: jid:user(), - Server :: jid:server() - ) -> boolean() | 'maybe'. -is_user_exists_in_other_modules(Module, User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - do_does_user_exist_in_other_modules(Module, LUser, LServer). - -do_does_user_exist_in_other_modules(_, LUser, LServer) - when LUser =:= error; LServer =:= error -> - false; -do_does_user_exist_in_other_modules(Module, LUser, LServer) -> - does_user_exist_in_other_modules_loop( - auth_modules(LServer)--[Module], - LUser, LServer). - - -does_user_exist_in_other_modules_loop([], _User, _Server) -> + Modules = auth_modules(LServer), + does_user_exist_in_given_modules(Modules, LUser, LServer, false). + +%% Check if the user exists in all authentications module +%% except the module passed as parameter +-spec is_user_exists_in_other_modules(Module :: authmodule(), JID :: jid:jid() | error) -> + boolean() | maybe. +is_user_exists_in_other_modules(Module, #jid{luser = LUser, lserver = LServer}) -> + Modules = auth_modules(LServer) -- [Module], + does_user_exist_in_given_modules(Modules, LUser, LServer, maybe); +is_user_exists_in_other_modules(_M, error) -> + false. + +does_user_exist_in_given_modules([], _, _, _) -> false; -does_user_exist_in_other_modules_loop([AuthModule|AuthModules], User, Server) -> - case AuthModule:does_user_exist(User, Server) of +does_user_exist_in_given_modules(_, LUser, LServer, Default) + when LUser =:= error; LServer =:= error -> Default; +does_user_exist_in_given_modules([Mod | Modules], LUser, LServer, Default) -> + case Mod:does_user_exist(LUser, LServer) of true -> true; false -> - does_user_exist_in_other_modules_loop(AuthModules, User, Server); + does_user_exist_in_given_modules(Modules, LUser, LServer, Default); {error, Error} -> - ?LOG_DEBUG(#{what => does_user_exist_failed, + ?LOG_ERROR(#{what => does_user_exist_failed, text => <<"The authentication module returned an error">>, - auth_module => AuthModule, reason => Error, - user => User, server => Server}), - maybe + auth_module => Mod, reason => Error, + user => LUser, server => LServer}), + Default end. - %% @doc Remove user. %% Note: it may return ok even if there was some problem removing the user. --spec remove_user(User :: jid:user(), - Server :: jid:server()) -> ok | error | {error, not_allowed}. -remove_user(User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - do_remove_user(LUser, LServer). - -do_remove_user(LUser, LServer) when LUser =:= error; LServer =:= error -> - error; -do_remove_user(LUser, LServer) -> +-spec remove_user(JID :: jid:jid()) -> ok | {error, not_allowed}; + (error) -> error. +remove_user(#jid{luser = LUser, lserver = LServer}) -> AuthModules = auth_modules(LServer), RemoveResult = [M:remove_user(LUser, LServer) || M <- AuthModules ], case lists:any(fun(El) -> El == ok end, RemoveResult) of @@ -559,7 +432,8 @@ do_remove_user(LUser, LServer) -> user => LUser, server => LServer, auth_modules => AuthModules}), {error, not_allowed} - end. + end; +remove_user(error) -> error. %% @doc Calculate informational entropy. -spec entropy(iolist()) -> float(). diff --git a/src/auth/ejabberd_auth_anonymous.erl b/src/auth/ejabberd_auth_anonymous.erl index 468e909684..3030f19950 100644 --- a/src/auth/ejabberd_auth_anonymous.erl +++ b/src/auth/ejabberd_auth_anonymous.erl @@ -216,11 +216,12 @@ authorize(Creds) -> Password :: binary()) -> boolean(). check_password(LUser, LServer, Password) -> check_password(LUser, LServer, Password, undefined, undefined). + check_password(LUser, LServer, _Password, _Digest, _DigestGen) -> %% We refuse login for registered accounts (They cannot logged but %% they however are "reserved") - case ejabberd_auth:is_user_exists_in_other_modules(?MODULE, - LUser, LServer) of + case ejabberd_auth:is_user_exists_in_other_modules( + ?MODULE, jid:make_noprep(LUser, LServer, <<>>)) of %% If user exists in other module, reject anonymous authentication true -> false; %% If we are not sure whether the user exists in other module, reject anon auth diff --git a/src/auth/ejabberd_auth_external.erl b/src/auth/ejabberd_auth_external.erl index 158d29735d..be4181b672 100644 --- a/src/auth/ejabberd_auth_external.erl +++ b/src/auth/ejabberd_auth_external.erl @@ -193,7 +193,7 @@ get_password_s(LUser, LServer) -> -spec does_user_exist(LUser :: jid:luser(), LServer :: jid:lserver()) -> boolean() | {error, atom()}. does_user_exist(LUser, LServer) -> - try extauth:is_user_exists(LUser, LServer) of + try extauth:does_user_exist(LUser, LServer) of Res -> Res catch _:Error -> {error, Error} diff --git a/src/auth/extauth.erl b/src/auth/extauth.erl index e05a8e94e9..bc4cb5e61e 100644 --- a/src/auth/extauth.erl +++ b/src/auth/extauth.erl @@ -33,7 +33,7 @@ set_password/3, try_register/3, remove_user/2, - is_user_exists/2]). + does_user_exist/2]). -include("mongoose.hrl"). @@ -90,8 +90,8 @@ check_password(User, Server, Password) -> call_port(Server, [<<"auth">>, User, Server, Password]). --spec is_user_exists(jid:user(), jid:server()) -> boolean(). -is_user_exists(User, Server) -> +-spec does_user_exist(jid:user(), jid:server()) -> boolean(). +does_user_exist(User, Server) -> call_port(Server, [<<"isuser">>, User, Server]). diff --git a/src/config/mongoose_config_parser_toml.erl b/src/config/mongoose_config_parser_toml.erl index a217e4b787..50da742644 100644 --- a/src/config/mongoose_config_parser_toml.erl +++ b/src/config/mongoose_config_parser_toml.erl @@ -720,8 +720,6 @@ module_opt([<<"max_wait">>, <<"mod_bosh">>|_], V) -> [{max_wait, int_or_infinity(V)}]; module_opt([<<"server_acks">>, <<"mod_bosh">>|_], V) -> [{server_acks, V}]; -module_opt([<<"backend">>, <<"mod_bosh">>|_], V) -> - [{backend, b2a(V)}]; module_opt([<<"maxpause">>, <<"mod_bosh">>|_], V) -> [{maxpause, V}]; module_opt([<<"cache_size">>, <<"mod_caps">>|_], V) -> @@ -756,8 +754,6 @@ module_opt([<<"max_file_size">>, <<"mod_http_upload">>|_], V) -> module_opt([<<"s3">>, <<"mod_http_upload">>|_] = Path, V) -> S3Opts = parse_section(Path, V), [{s3, S3Opts}]; -module_opt([<<"backend">>, <<"mod_inbox">>|_], V) -> - [{backend, b2a(V)}]; module_opt([<<"reset_markers">>, <<"mod_inbox">>|_] = Path, V) -> Markers = parse_list(Path, V), [{reset_markers, Markers}]; @@ -780,10 +776,8 @@ module_opt([<<"connections">>, <<"mod_global_distrib">>|_] = Path, V) -> module_opt([<<"cache">>, <<"mod_global_distrib">>|_] = Path, V) -> Cache = parse_section(Path, V), [{cache, Cache}]; -module_opt([<<"bounce">>, <<"mod_global_distrib">>|_], false) -> - [{bounce, false}]; module_opt([<<"bounce">>, <<"mod_global_distrib">>|_] = Path, V) -> - Bounce = parse_section(Path, V), + Bounce = parse_section(Path, V, fun format_global_distrib_bounce/1), [{bounce, Bounce}]; module_opt([<<"redis">>, <<"mod_global_distrib">>|_] = Path, V) -> Redis = parse_section(Path, V), @@ -1231,12 +1225,17 @@ mod_global_distrib_connections([<<"endpoint_refresh_interval_when_empty">>|_], V [{endpoint_refresh_interval_when_empty, V}]; mod_global_distrib_connections([<<"disabled_gc_interval">>|_], V) -> [{disabled_gc_interval, V}]; -mod_global_distrib_connections([<<"tls">>|_] = _Path, false) -> - [{tls_opts, false}]; mod_global_distrib_connections([<<"tls">>|_] = Path, V) -> - TLSOpts = parse_section(Path, V), + TLSOpts = parse_section(Path, V, fun format_global_distrib_tls/1), [{tls_opts, TLSOpts}]. +-spec format_global_distrib_tls([option()]) -> option(). +format_global_distrib_tls(Opts) -> + case proplists:lookup(enabled, Opts) of + {enabled, true} -> proplists:delete(enabled, Opts); + _ -> false + end. + -spec mod_global_distrib_cache(path(), toml_value()) -> [option()]. mod_global_distrib_cache([<<"cache_missed">>|_], V) -> [{cache_missed, V}]; @@ -1259,7 +1258,16 @@ mod_global_distrib_redis([<<"refresh_after">>|_], V) -> mod_global_distrib_bounce([<<"resend_after_ms">>|_], V) -> [{resend_after_ms, V}]; mod_global_distrib_bounce([<<"max_retries">>|_], V) -> - [{max_retries, V}]. + [{max_retries, V}]; +mod_global_distrib_bounce([<<"enabled">>|_], V) -> + [{enabled, V}]. + +-spec format_global_distrib_bounce([option()]) -> option(). +format_global_distrib_bounce(Opts) -> + case proplists:lookup(enabled, Opts) of + {enabled, false} -> false; + _ -> proplists:delete(enabled, Opts) + end. -spec mod_global_distrib_connections_endpoints(path(), toml_section()) -> [option()]. mod_global_distrib_connections_endpoints(_, #{<<"host">> := Host, <<"port">> := Port}) -> @@ -1667,6 +1675,11 @@ fast_tls_option([<<"cacertfile">>|_], V) -> [{cafile, b2l(V)}]; fast_tls_option([<<"dhfile">>|_], V) -> [{dhfile, b2l(V)}]; fast_tls_option([<<"ciphers">>|_], V) -> [{ciphers, b2l(V)}]. +mod_global_distrib_tls_option([<<"enabled">>|_], V) -> + [{enabled, V}]; +mod_global_distrib_tls_option(P, V) -> + fast_tls_option(P, V). + -spec verify_peer(boolean()) -> option(). verify_peer(false) -> verify_none; verify_peer(true) -> verify_peer. @@ -1976,7 +1989,7 @@ handler([_,<<"endpoints">>, <<"connections">>, <<"mod_global_distrib">>, <<"modu handler([_,<<"advertised_endpoints">>, <<"connections">>, <<"mod_global_distrib">>, <<"modules">>]) -> fun mod_global_distrib_connections_advertised_endpoints/2; handler([_,<<"tls">>, <<"connections">>, <<"mod_global_distrib">>, <<"modules">>]) -> - fun fast_tls_option/2; + fun mod_global_distrib_tls_option/2; handler([_, <<"keys">>, <<"mod_keystore">>, <<"modules">>]) -> fun mod_keystore_keys/2; handler([_, _, <<"mod_mam_meta">>, <<"modules">>]) -> diff --git a/src/config/mongoose_config_validator_toml.erl b/src/config/mongoose_config_validator_toml.erl index 642359d135..f9d1e6a92c 100644 --- a/src/config/mongoose_config_validator_toml.erl +++ b/src/config/mongoose_config_validator_toml.erl @@ -572,6 +572,13 @@ validate([<<"sns_host">>, <<"sns">>, <<"backend">>, <<"mod_event_pusher">>, <<"modules">>|_], [{sns_host, V}]) -> validate_string(V); +validate([<<"enabled">>, <<"bounce">>, + <<"mod_global_distrib">>, <<"modules">>|_], + [{enabled, true}]) -> + ok; +validate([<<"bounce">>, <<"mod_global_distrib">>, <<"modules">>|_], + [{bounce, false}]) -> + ok; validate([<<"max_retries">>, <<"bounce">>, <<"mod_global_distrib">>, <<"modules">>|_], [{max_retries, V}]) -> @@ -634,7 +641,11 @@ validate([<<"port">>, item, <<"endpoints">>, <<"connections">>, validate_network_port(V); validate([<<"tls">>, <<"connections">>, <<"mod_global_distrib">>, <<"modules">>|_], - [false]) -> + [{tls_opts, false}]) -> + ok; +validate([<<"enabled">>, <<"tls">>, <<"connections">>, + <<"mod_global_distrib">>, <<"modules">>|_], + [{enabled, true}]) -> ok; validate([<<"cacertfile">>, <<"tls">>, <<"connections">>, <<"mod_global_distrib">>, <<"modules">>|_], @@ -1179,9 +1190,6 @@ validate([<<"iqdisc">>, <<"mod_private">>, <<"modules">>|_], validate([<<"bucket_type">>, <<"riak">>, <<"mod_private">>, <<"modules">>|_], [{bucket_type, V}]) -> validate_non_empty_binary(V); -validate([<<"backend">>, <<"mod_bosh">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_bosh, V); validate([<<"inactivity">>, <<"mod_bosh">>, <<"modules">>|_], [{inactivity, V}]) -> validate_non_negative_integer_or_infinity(V); @@ -1194,9 +1202,6 @@ validate([<<"server_acks">>, <<"mod_bosh">>, <<"modules">>|_], validate([<<"aff_changes">>, <<"mod_inbox">>, <<"modules">>|_], [{aff_changes, V}]) -> validate_boolean(V); -validate([<<"backend">>, <<"mod_inbox">>, <<"modules">>|_], - [{backend, V}]) -> - validate_backend(mod_inbox, V); validate([item, <<"groupchat">>, <<"mod_inbox">>, <<"modules">>|_], [V]) -> validate_groupchat_type(V); diff --git a/src/ejabberd_admin.erl b/src/ejabberd_admin.erl index b5474c55c7..0aaa1e052b 100644 --- a/src/ejabberd_admin.erl +++ b/src/ejabberd_admin.erl @@ -331,17 +331,18 @@ register(Host, Password) -> | {'exists', io_lib:chars()} | {'ok', io_lib:chars()}. register(User, Host, Password) -> - case ejabberd_auth:try_register(User, Host, Password) of + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:try_register(JID, Password) of {error, exists} -> - String = io_lib:format("User ~s@~s already registered at node ~p", - [User, Host, node()]), + String = io_lib:format("User ~s already registered at node ~p", + [jid:to_binary(JID), node()]), {exists, String}; {error, Reason} -> - String = io_lib:format("Can't register user ~s@~s at node ~p: ~p", - [User, Host, node(), Reason]), + String = io_lib:format("Can't register user ~s at node ~p: ~p", + [jid:to_binary(JID), node(), Reason]), {cannot_register, String}; _ -> - {ok, io_lib:format("User ~s@~s successfully registered", [User, Host])} + {ok, io_lib:format("User ~s successfully registered", [jid:to_binary(JID)])} end. generate_user() -> @@ -353,7 +354,7 @@ generate_user() -> -spec unregister(User :: jid:user(), Host :: jid:server()) -> {'ok', []}. unregister(User, Host) -> - ejabberd_auth:remove_user(User, Host), + ejabberd_auth:remove_user(jid:make(User, Host, <<>>)), {ok, ""}. -spec registered_users(Host :: jid:server()) -> [jid:user()]. @@ -435,7 +436,7 @@ registrator_proc(Manager, Result) -> {null_password, jid:user()} | {bad_csv, binary()}. do_register([User, Host, Password]) -> - case ejabberd_auth:try_register(User, Host, Password) of + case ejabberd_auth:try_register(jid:make(User, Host, <<>>), Password) of {error, Reason} -> {Reason, User}; _ -> {ok, User} end; diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index edaaba4e6f..1666711580 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -2439,8 +2439,8 @@ process_unauthenticated_stanza(StateData, El) -> ResIQ = IQ#iq{type = error, sub_el = [mongoose_xmpp_errors:service_unavailable()]}, Res1 = jlib:replace_from_to( - jid:make(<<>>, StateData#state.server, <<>>), - jid:make(<<>>, <<>>, <<>>), + jid:make_noprep(<<>>, StateData#state.server, <<>>), + jid:make_noprep(<<>>, <<>>, <<>>), jlib:iq_to_xml(ResIQ)), send_element_from_server_jid(StateData, jlib:remove_attr(<<"to">>, Res1)); _ -> diff --git a/src/ejabberd_commands.erl b/src/ejabberd_commands.erl index 792de1875c..ea5f9ee350 100644 --- a/src/ejabberd_commands.erl +++ b/src/ejabberd_commands.erl @@ -417,14 +417,15 @@ check_access_commands(AccessCommands, Auth, Method, Command, Arguments) -> %% @private %% May throw {error, invalid_account_data} --spec check_auth(auth()) -> {ok, User :: binary(), Server :: binary()} | no_return(). +-spec check_auth(auth()) -> {ok, jid:jid()} | no_return(). check_auth({User, Server, Password}) -> %% Check the account exists and password is valid - AccountPass = ejabberd_auth:get_password_s(User, Server), + JID = jid:make(User, Server, <<>>), + AccountPass = ejabberd_auth:get_password_s(JID), AccountPassMD5 = get_md5(AccountPass), case Password of - AccountPass -> {ok, User, Server}; - AccountPassMD5 -> {ok, User, Server}; + AccountPass -> {ok, JID}; + AccountPassMD5 -> {ok, JID}; _ -> throw({error, invalid_account_data}) end. @@ -441,9 +442,10 @@ check_access(all, _) -> check_access(_, noauth) -> false; check_access(Access, Auth) -> - {ok, User, Server} = check_auth(Auth), + {ok, JID} = check_auth(Auth), %% Check this user has access permission - case acl:match_rule(Server, Access, jid:make(User, Server, <<"">>)) of + {_, LServer} = jid:to_lus(JID), + case acl:match_rule(LServer, Access, JID) of allow -> true; deny -> false end. diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index 6d56bf402b..dddf401ace 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -46,7 +46,8 @@ -export([print_flat_config/0]). -export([get_local_config/0, - get_host_local_config/0]). + get_host_local_config/0, + get_config_path/0]). %% Introspection -export([config_info/0]). diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 638535a065..5f7a4b9731 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -323,7 +323,8 @@ remove_info(JID, Key) -> Type :: any(), Reason :: any(). check_in_subscription(Acc, User, Server, _JID, _Type, _Reason) -> - case ejabberd_auth:is_user_exists(User, Server) of + JID = jid:make(User, Server, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> Acc; false -> diff --git a/src/ejabberd_users.erl b/src/ejabberd_users.erl index 058b4781b5..1ed438afb9 100644 --- a/src/ejabberd_users.erl +++ b/src/ejabberd_users.erl @@ -4,7 +4,7 @@ -export([start/1, stop/1, start_link/2, - does_user_exist/2]). + does_user_exist/1]). %% Hooks. -export([remove_user/3]). @@ -70,13 +70,12 @@ start_link(ProcName, Host) -> gen_server:start_link({local, ProcName}, ?MODULE, [Host], []). --spec does_user_exist(LUser :: jid:luser(), - LServer :: jid:lserver() | string()) -> boolean(). -does_user_exist(LUser, LServer) -> +-spec does_user_exist(JID :: jid:jid()) -> boolean(). +does_user_exist(#jid{luser = LUser, lserver = LServer} = JID) -> case does_cached_user_exist(LUser, LServer) of true -> true; false -> - case does_stored_user_exist(LUser, LServer) of + case does_stored_user_exist(JID) of true -> put_user_into_cache(LUser, LServer), true; @@ -172,14 +171,13 @@ code_change(_OldVsn, State, _Extra) -> %% Helpers %%==================================================================== --spec does_stored_user_exist(jid:luser(), jid:lserver()) -> boolean(). -does_stored_user_exist(LUser, LServer) -> - ejabberd_auth:is_user_exists(LUser, LServer) - andalso not is_anonymous_user(LUser, LServer). +-spec does_stored_user_exist(jid:jid()) -> boolean(). +does_stored_user_exist(JID) -> + ejabberd_auth:does_user_exist(JID) + andalso not is_anonymous_user(JID). - --spec is_anonymous_user(jid:luser(), jid:lserver()) -> boolean(). -is_anonymous_user(LUser, LServer) -> +-spec is_anonymous_user(jid:jid()) -> boolean(). +is_anonymous_user(#jid{luser = LUser, lserver = LServer} = _JID) -> case lists:member(ejabberd_auth_anonymous, ejabberd_auth:auth_modules(LServer)) of true -> ejabberd_auth_anonymous:does_user_exist(LUser, LServer); diff --git a/src/event_pusher/mod_event_pusher_http.erl b/src/event_pusher/mod_event_pusher_http.erl index d296053a23..691748707b 100644 --- a/src/event_pusher/mod_event_pusher_http.erl +++ b/src/event_pusher/mod_event_pusher_http.erl @@ -45,9 +45,9 @@ -define(DEFAULT_POOL_NAME, http_pool). -define(DEFAULT_PATH, ""). --define(SENT_METRIC, [mod_http_notifications, sent]). --define(FAILED_METRIC, [mod_http_notifications, failed]). --define(RESPONSE_METRIC, [mod_http_notifications, response_time]). +-define(SENT_METRIC, [mod_event_pusher_http, sent]). +-define(FAILED_METRIC, [mod_event_pusher_http, failed]). +-define(RESPONSE_METRIC, [mod_event_pusher_http, response_time]). start(Host, _Opts) -> ensure_metrics(Host), diff --git a/src/event_pusher/mod_event_pusher_push_plugin_defaults.erl b/src/event_pusher/mod_event_pusher_push_plugin_defaults.erl index 7130541c5b..590cd5683b 100644 --- a/src/event_pusher/mod_event_pusher_push_plugin_defaults.erl +++ b/src/event_pusher/mod_event_pusher_push_plugin_defaults.erl @@ -78,8 +78,8 @@ publish_notification(Acc, _, Payload, Services) -> %%-------------------------------------------------------------------- -spec should_publish(To :: jid:jid()) -> boolean(). -should_publish(#jid{luser = LUser, lserver = LServer} = To) -> - try ejabberd_users:does_user_exist(LUser, LServer) of +should_publish(#jid{} = To) -> + try ejabberd_users:does_user_exist(To) of false -> false; true -> @@ -146,10 +146,10 @@ publish_via_pubsub(Host, To, {PubsubJID, Node, Form}, PushPayload) -> to_jid => PubsubJID }), ResponseHandler = - fun(_From, _To, Acc, Response) -> + fun(_From, _To, FAcc, Response) -> mod_event_pusher_push:cast(Host, fun handle_publish_response/4, [To, PubsubJID, Node, Response]), - Acc + FAcc end, %% The IQ is routed from the recipient's server JID to pubsub JID %% This is recommended in the XEP and also helps process replies to this IQ diff --git a/src/global_distrib/mod_global_distrib_bounce.erl b/src/global_distrib/mod_global_distrib_bounce.erl index 59e698a6f2..f22de05445 100644 --- a/src/global_distrib/mod_global_distrib_bounce.erl +++ b/src/global_distrib/mod_global_distrib_bounce.erl @@ -91,8 +91,6 @@ maybe_store_message({From, To, Acc0, Packet} = FPacket) -> {ok, ID} = mod_global_distrib:find_metadata(Acc0, id), case mod_global_distrib:get_metadata(Acc0, {bounce_ttl, LocalHost}, opt(max_retries)) of 0 -> - FromBin = jid:to_binary(From), - ToBin = jid:to_binary(To), ?LOG_DEBUG(#{what => gd_skip_store_message, text => <<"Not storing global message">>, gd_id => ID, acc => Acc0, bounce_ttl => 0}), diff --git a/src/inbox/mod_inbox.erl b/src/inbox/mod_inbox.erl index 172cbb329f..ad34221aa8 100644 --- a/src/inbox/mod_inbox.erl +++ b/src/inbox/mod_inbox.erl @@ -275,9 +275,9 @@ maybe_process_message(Host, From, To, Msg, Dir) -> To :: jid:jid(), Dir :: outgoing | incoming) -> boolean(). inbox_owner_exists(From, _To, outgoing) -> - ejabberd_users:does_user_exist(From#jid.luser, From#jid.lserver); + ejabberd_users:does_user_exist(From); inbox_owner_exists(_From, To, incoming) -> - ejabberd_users:does_user_exist(To#jid.luser, To#jid.lserver). + ejabberd_users:does_user_exist(To). maybe_process_acceptable_message(Host, From, To, Msg, Dir, one2one) -> process_message(Host, From, To, Msg, Dir, one2one); diff --git a/src/mam/mod_mam.erl b/src/mam/mod_mam.erl index 446b61d9c4..bbb3355779 100644 --- a/src/mam/mod_mam.erl +++ b/src/mam/mod_mam.erl @@ -300,10 +300,10 @@ user_send_packet(Acc, From, To, Packet) -> -spec filter_packet(Value :: fpacket() | drop) -> fpacket() | drop. filter_packet(drop) -> drop; -filter_packet({From, To=#jid{luser=LUser, lserver=LServer}, Acc, Packet}) -> +filter_packet({From, To = #jid{lserver = LServer}, Acc, Packet}) -> ?LOG_DEBUG(#{what => mam_user_receive_packet, acc => Acc}), {AmpEvent, PacketAfterArchive} = - case ejabberd_users:does_user_exist(LUser, LServer) of + case ejabberd_users:does_user_exist(To) of false -> {mam_failed, Packet}; true -> @@ -465,10 +465,10 @@ handle_get_message_form(_From=#jid{lserver = Host}, _ArcJID=#jid{}, IQ=#iq{}) -> determine_amp_strategy(Strategy = #amp_strategy{deliver = Deliver}, FromJID, ToJID, Packet, initial_check) -> - #jid{luser = LUser, lserver = LServer} = ToJID, + #jid{lserver = LServer} = ToJID, ShouldBeStored = is_archivable_message(LServer, incoming, Packet) andalso is_interesting(ToJID, FromJID) - andalso ejabberd_auth:is_user_exists(LUser, LServer), + andalso ejabberd_auth:does_user_exist(ToJID), case ShouldBeStored of true -> Strategy#amp_strategy{deliver = amp_deliver_strategy(Deliver)}; false -> Strategy diff --git a/src/mod_commands.erl b/src/mod_commands.erl index 647da7af88..1e07167d88 100644 --- a/src/mod_commands.erl +++ b/src/mod_commands.erl @@ -27,6 +27,8 @@ send_stanza/1 ]). +-export([parse_from_to/2]). + -include("mongoose.hrl"). -include("jlib.hrl"). -include("mongoose_rsm.hrl"). @@ -238,7 +240,7 @@ commands() -> kick_session(Host, User, Resource) -> J = jid:make(User, Host, Resource), case ejabberd_c2s:terminate_session(J, <<"kicked">>) of - no_session -> <<"no active session">>; + no_session -> {error, not_found, <<"no active session">> }; {exit, <<"kicked">>} -> <<"kicked">> end. @@ -252,45 +254,39 @@ registered_users(Host) -> [jid:to_binary(US) || US <- SUsers]. register(Host, User, Password) -> - case ejabberd_auth:try_register(User, Host, Password) of + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:try_register(JID, Password) of {error, exists} -> - String = io_lib:format("User ~s@~s already registered at node ~p", - [User, Host, node()]), - throw({exists, String}); + String = io_lib:format("User ~s already registered at node ~p", + [jid:to_binary(JID), node()]), + {error, denied, String}; + {error, invalid_jid} -> + String = io_lib:format("Invalid jid: ~s@~s", + [User, Host]), + {error, bad_request, String}; {error, Reason} -> String = io_lib:format("Can't register user ~s@~s at node ~p: ~p", [User, Host, node(), Reason]), - throw({error, String}); + {error, internal, String}; _ -> - list_to_binary(io_lib:format("User ~s@~s successfully registered", - [User, Host])) + <<"User ", (jid:to_binary(JID))/binary, "successfully registered">> end. unregister(Host, User) -> - ejabberd_auth:remove_user(User, Host), - <<"">>. + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:remove_user(JID) of + ok -> + <<"ok">>; + error -> + {error, bad_request, io_lib:format("Invalid jid: ~p@~p", [User, Host])}; + {error, not_allowed} -> + {error, forbidden, "User does not exist or you are not authorised properly"} + end. send_message(From, To, Body) -> - F = jid:from_binary(From), - T = jid:from_binary(To), - Packet = build_message(From, To, Body), - Acc0 = mongoose_acc:new(#{location => ?LOCATION, - lserver => F#jid.lserver, - element => Packet}), - - mongoose_hooks:user_send_packet(F#jid.lserver, - Acc0, - F, T, Packet), - - %% privacy check is missing, but is it needed? - ejabberd_router:route(F, T, Packet), - ok. - -send_stanza(BinStanza) -> - case exml:parse(BinStanza) of - {ok, Packet} -> - F = jid:from_binary(exml_query:attr(Packet, <<"from">>)), - T = jid:from_binary(exml_query:attr(Packet, <<"to">>)), + case parse_from_to(From, To) of + {ok, F, T} -> + Packet = build_message(From, To, Body), Acc0 = mongoose_acc:new(#{location => ?LOCATION, lserver => F#jid.lserver, element => Packet}), @@ -299,10 +295,45 @@ send_stanza(BinStanza) -> Acc0, F, T, Packet), + %% privacy check is missing, but is it needed? ejabberd_router:route(F, T, Packet), ok; + E -> + E + end. + +send_stanza(BinStanza) -> + case exml:parse(BinStanza) of + {ok, Packet} -> + F = exml_query:attr(Packet, <<"from">>), + T = exml_query:attr(Packet, <<"to">>), + case parse_from_to(F, T) of + {ok, From, To} -> + Acc0 = mongoose_acc:new(#{location => ?LOCATION, + lserver => From#jid.lserver, + element => Packet}), + mongoose_hooks:user_send_packet(From#jid.lserver, + Acc0, + From, To, Packet), + ejabberd_router:route(From, To, Packet), + ok; + {error, missing} -> + {error, bad_request, "both from and to are required"}; + {error, type_error, E} -> + {error, type_error, E} + end; {error, Reason} -> - throw({badarg, io_lib:format("Malformed stanza: ~p", [Reason])}) + {error, bad_request, io_lib:format("Malformed stanza: ~p", [Reason])} + end. + +-spec parse_from_to(jid:jid() | binary() | undefined, jid:jid() | binary() | undefined) -> + {ok, jid:jid(), jid:jid()} | {error, missing} | {error, type_error, string()}. +parse_from_to(F, T) when F == undefined; T == undefined -> + {error, missing}; +parse_from_to(F, T) -> + case parse_jid_list([F, T]) of + {ok, [Fjid, Tjid]} -> {ok, Fjid, Tjid}; + E -> E end. list_contacts(Caller) -> @@ -327,9 +358,13 @@ add_contact(Caller, JabberID) -> add_contact(Caller, JabberID, Name) -> add_contact(Caller, JabberID, Name, []). -add_contact(Caller, JabberID, Name, Groups) -> - CJid = jid:from_binary(Caller), - mod_roster:set_roster_entry(CJid, JabberID, Name, Groups). +add_contact(Caller, Other, Name, Groups) -> + case parse_from_to(Caller, Other) of + {ok, CallerJid, OtherJid} -> + mod_roster:set_roster_entry(CallerJid, OtherJid, Name, Groups); + E -> + E + end. delete_contacts(Caller, ToDelete) -> maybe_delete_contacts(Caller, ToDelete, []). @@ -343,18 +378,21 @@ maybe_delete_contacts(Caller, [H | T], NotDeleted) -> maybe_delete_contacts(Caller, T, NotDeleted ++ [H]) end. -delete_contact(Caller, JabberID) -> - CJid = jid:from_binary(Caller), - case jid_exists(Caller, JabberID) of - false -> error; - true -> - mod_roster:remove_from_roster(CJid, JabberID) +delete_contact(Caller, Other) -> + case parse_from_to(Caller, Other) of + {ok, CallerJid, OtherJid} -> + case jid_exists(CallerJid, OtherJid) of + false -> error; + true -> + mod_roster:remove_from_roster(CallerJid, OtherJid) + end; + E -> + E end. --spec jid_exists(binary(), binary()) -> boolean(). -jid_exists(CJid, Jid) -> - FJid = jid:from_binary(CJid), - Res = mod_roster:get_roster_entry(FJid#jid.luser, FJid#jid.lserver, Jid), +-spec jid_exists(jid:jid(), jid:jid()) -> boolean(). +jid_exists(CallerJid, OtherJid) -> + Res = mod_roster:get_roster_entry(CallerJid, OtherJid), Res =/= does_not_exist. registered_commands() -> @@ -378,8 +416,16 @@ get_recent_messages(Caller, With, Before, Limit) -> lists:map(fun record_to_map/1, Res). change_user_password(Host, User, Password) -> - ejabberd_auth:set_password(User, Host, Password), - ok. + JID = jid:make(User, Host, <<>>), + case ejabberd_auth:set_password(JID, Password) of + ok -> ok; + {error, empty_password} -> + {error, bad_request, "empty password"}; + {error, not_allowed} -> + {error, denied, "password change not allowed"}; + {error, invalid_jid} -> + {error, bad_request, "invalid jid"} + end. record_to_map({Id, From, Msg}) -> Jbin = jid:to_binary(From), @@ -429,11 +475,21 @@ lookup_recent_messages(ArcJID, WithJID, Before, Limit) -> L. subscription(Caller, Other, Action) -> - Act = decode_action(Action), - run_subscription(Act, jid:from_binary(Caller), jid:from_binary(Other)). + case decode_action(Action) of + error -> + {error, bad_request, <<"invalid action">>}; + Act -> + case parse_from_to(Caller, Other) of + {ok, CallerJid, OtherJid} -> + run_subscription(Act, CallerJid, OtherJid); + E -> + E + end + end. decode_action(<<"subscribe">>) -> subscribe; -decode_action(<<"subscribed">>) -> subscribed. +decode_action(<<"subscribed">>) -> subscribed; +decode_action(_) -> error. -spec run_subscription(subscribe | subscribed, jid:jid(), jid:jid()) -> ok. run_subscription(Type, CallerJid, OtherJid) -> @@ -453,7 +509,21 @@ run_subscription(Type, CallerJid, OtherJid) -> ejabberd_router:route(CallerJid, OtherJid, Acc2), ok. -set_subscription(Caller, Other, <<"connect">>) -> + +set_subscription(Caller, Other, Action) -> + case parse_from_to(Caller, Other) of + {ok, CallerJid, OtherJid} -> + case Action of + A when A == <<"connect">>; A == <<"disconnect">> -> + do_set_subscription(CallerJid, OtherJid, Action); + _ -> + {error, bad_request, <<"invalid action">>} + end; + E -> + E + end. + +do_set_subscription(Caller, Other, <<"connect">>) -> add_contact(Caller, Other), add_contact(Other, Caller), subscription(Caller, Other, <<"subscribe">>), @@ -461,7 +531,24 @@ set_subscription(Caller, Other, <<"connect">>) -> subscription(Other, Caller, <<"subscribed">>), subscription(Caller, Other, <<"subscribed">>), ok; -set_subscription(Caller, Other, <<"disconnect">>) -> +do_set_subscription(Caller, Other, <<"disconnect">>) -> delete_contact(Caller, Other), delete_contact(Other, Caller), ok. + +-spec parse_jid_list(BinJids :: [binary()]) -> {ok, [jid:jid()]} | {error, type_error, string()}. +parse_jid_list([_ | _] = BinJids) -> + Jids = lists:map(fun parse_jid/1, BinJids), + case [Msg || {error, Msg} <- Jids] of + [] -> {ok, Jids}; + Errors -> {error, type_error, lists:join("; ", Errors)} + end. + +-spec parse_jid(binary() | jid:jid()) -> jid:jid() | {error, string()}. +parse_jid(#jid{} = Jid) -> + Jid; +parse_jid(Jid) when is_binary(Jid) -> + case jid:from_binary(Jid) of + error -> {error, io_lib:format("Invalid jid: ~p", [Jid])}; + B -> B + end. diff --git a/src/mod_disco.erl b/src/mod_disco.erl index 6429a7370d..119eed1f01 100644 --- a/src/mod_disco.erl +++ b/src/mod_disco.erl @@ -380,19 +380,19 @@ get_sm_items(empty, From, To, _Node, _Lang) -> -spec is_presence_subscribed(jid:jid(), jid:jid()) -> boolean(). -is_presence_subscribed(#jid{luser=User, lserver=Server} = From, - #jid{luser=LUser, lserver=LServer} = _To) -> +is_presence_subscribed(#jid{luser = LFromUser, lserver = LFromServer} = _From, + #jid{luser = LToUser, lserver = LToServer} = _To) -> A = mongoose_acc:new(#{ location => ?LOCATION, - lserver => From#jid.lserver, + lserver => LFromServer, element => undefined }), - A2 = mongoose_hooks:roster_get(Server, A, User, Server), + A2 = mongoose_hooks:roster_get(LFromServer, A, LFromUser, LFromServer), Roster = mongoose_acc:get(roster, items, [], A2), lists:any(fun({roster, _, _, JID, _, S, _, _, _, _}) -> {TUser, TServer} = jid:to_lus(JID), - LUser == TUser andalso LServer == TServer andalso S /= none + LToUser == TUser andalso LToServer == TServer andalso S /= none end, Roster) - orelse User == LUser andalso Server == LServer. + orelse LFromUser == LToUser andalso LFromServer == LToServer. -spec process_sm_iq_info(jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) -> @@ -430,8 +430,8 @@ process_sm_iq_info(From, To, Acc, #iq{type = get, lang = Lang, sub_el = SubEl} = To :: jid:jid(), Node :: binary(), Lang :: ejabberd:lang()) -> [exml:element()]. -get_sm_identity(Acc, _From, #jid{luser = LUser, lserver = LServer}, _Node, _Lang) -> - Acc ++ case ejabberd_auth:is_user_exists(LUser, LServer) of +get_sm_identity(Acc, _From, JID = #jid{}, _Node, _Lang) -> + Acc ++ case ejabberd_auth:does_user_exist(JID) of true -> [#xmlel{name = <<"identity">>, attrs = [{<<"category">>, <<"account">>}, diff --git a/src/mod_http_notification.erl b/src/mod_http_notification.erl deleted file mode 100644 index 60a0636bf4..0000000000 --- a/src/mod_http_notification.erl +++ /dev/null @@ -1,20 +0,0 @@ --module(mod_http_notification). - --include("mongoose.hrl"). - --behaviour(gen_mod). --behaviour(mongoose_module_metrics). - --export([deps/2, start/2, stop/1]). - -deps(_Host, Opts) -> - [{mod_event_pusher, [{backends, [{http, Opts}]}], hard}]. - -start(_Host, _Opts) -> - Text = <<"mod_http_notification is deprecated and will be removed in the future.~n" - "Please use mod_event_pusher with http backend.~n" - "Refer to mod_event_pusher documentation for more information.">>, - ?LOG_WARNING(#{what => module_deprecated, text => Text}). - -stop(_Host) -> - ok. diff --git a/src/mod_muc_commands.erl b/src/mod_muc_commands.erl index 608023b6ef..0d45c66f89 100644 --- a/src/mod_muc_commands.erl +++ b/src/mod_muc_commands.erl @@ -122,35 +122,44 @@ create_instant_room(Host, Name, Owner, Nick) -> %% Send IQ set to unlock the room. ejabberd_router:route(OwnerJID, BareRoomJID, declination(OwnerJID, BareRoomJID)), - case mod_muc_room:can_access_room(BareRoomJID, OwnerJID) of - {ok, true} -> + case verify_room(BareRoomJID, OwnerJID) of + ok -> Name; - {ok, false} -> - {error, room_remains_locked}; - {error, not_found} = E -> + E -> E end. invite_to_room(Host, Name, Sender, Recipient, Reason) -> - S = jid:binary_to_bare(Sender), - R = jid:binary_to_bare(Recipient), - %% Direct invitation: i.e. not mediated by MUC room. See XEP 0249. - X = #xmlel{name = <<"x">>, - attrs = [{<<"xmlns">>, ?NS_CONFERENCE}, - {<<"jid">>, room_address(Name, Host)}, - {<<"reason">>, Reason}] - }, - Invite = message(S, R, <<>>, [ X ]), - ejabberd_router:route(S, R, Invite). + case mod_commands:parse_from_to(Sender, Recipient) of + {ok, S, R} -> + case verify_room(Host, Name, Sender) of + ok -> + %% Direct invitation: i.e. not mediated by MUC room. See XEP 0249. + X = #xmlel{name = <<"x">>, + attrs = [{<<"xmlns">>, ?NS_CONFERENCE}, + {<<"jid">>, room_address(Name, Host)}, + {<<"reason">>, Reason}] + }, + Invite = message(S, R, <<>>, [ X ]), + ejabberd_router:route(S, R, Invite); + E -> + E + end; + E -> + E + end. send_message_to_room(Host, Name, Sender, Message) -> - S = jid:binary_to_bare(Sender), - Room = jid:from_binary(room_address(Name, Host)), - B = #xmlel{name = <<"body">>, - children = [ #xmlcdata{ content = Message } ] - }, - Stanza = message(S, Room, <<"groupchat">>, [ B ]), - ejabberd_router:route(S, Room, Stanza). + case mod_commands:parse_from_to(Sender, room_address(Name, Host)) of + {ok, S, Room} -> + B = #xmlel{name = <<"body">>, + children = [ #xmlcdata{ content = Message } ] + }, + Stanza = message(S, Room, <<"groupchat">>, [ B ]), + ejabberd_router:route(S, Room, Stanza); + E -> + E + end. kick_user_from_room(Host, Name, Nick) -> %% All the machinery which is already deeply embedden in the MUC @@ -173,6 +182,26 @@ kick_user_from_room(Host, Name, Nick) -> %% Ancillary %%-------------------------------------------------------------------- +-spec verify_room(jid:server(), mod_muc:room(), jid:literal_jid()) -> + ok | {error, internal | not_found, term()}. +verify_room(Host, RoomName, User) -> + MUCHost = gen_mod:get_module_opt_subhost(Host, mod_muc, mod_muc:default_host()), + BareRoomJID = jid:make(RoomName, MUCHost, <<"">>), + UserJID = jid:binary_to_bare(User), + verify_room(BareRoomJID, UserJID). + +-spec verify_room(jid:jid(), jid:jid()) -> + ok | {error, internal | not_found, term()}. +verify_room(BareRoomJID, OwnerJID) -> + case mod_muc_room:can_access_room(BareRoomJID, OwnerJID) of + {ok, true} -> + ok; + {ok, false} -> + {error, internal, "room is locked"}; + {error, not_found} -> + {error, not_found, "room does not exist"} + end. + room_address(Name, Host) -> MUCHost = gen_mod:get_module_opt_subhost(Host, mod_muc, mod_muc:default_host()), <>. diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index f4de649e87..41c8c4637b 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -238,8 +238,8 @@ can_access_room(RoomJID, UserJID) -> case mod_muc:room_jid_to_pid(RoomJID) of {ok, Pid} -> gen_fsm_compat:sync_send_all_state_event(Pid, {can_access_room, UserJID}); - {error, Reason} -> - {error, Reason} + Error -> + Error end. %% @doc Return true if UserJID can read real user JIDs diff --git a/src/mod_private_mysql.erl b/src/mod_private_mysql.erl deleted file mode 100644 index 59239af449..0000000000 --- a/src/mod_private_mysql.erl +++ /dev/null @@ -1,62 +0,0 @@ -%%% NS is namespace or key. -%%% XML is #xmlel{} or value. --module(mod_private_mysql). --author('arcusfelis@gmail.com'). --behaviour(mod_private). - --export([init/2, - multi_set_data/3, - multi_get_data/3, - remove_user/2]). - --export([get_all_nss/2]). - --include("mongoose.hrl"). --include("jlib.hrl"). - -init(_Host, _Opts) -> - ok. - -multi_set_data(LUser, LServer, NS2XML) -> - SLUser = mongoose_rdbms:escape_string(LUser), - Rows = [sql_row(NS, XML) || {NS, XML} <- NS2XML], - replace_like_insert_result( - rdbms_queries:multi_set_private_data(LServer, SLUser, Rows)). - -replace_like_insert_result({updated, _}) -> ok; -replace_like_insert_result({error, Reason}) -> {error, Reason}. - -sql_row(NS, XML) -> - SNS = mongoose_rdbms:escape_string(NS), - SData = mongoose_rdbms:escape_string(exml:to_binary(XML)), - {SNS, SData}. - -multi_get_data(LUser, LServer, NS2Def) -> - SLUser = mongoose_rdbms:escape_string(LUser), - SNSs = [mongoose_rdbms:escape_string(NS) || {NS, _Def} <- NS2Def], - case rdbms_queries:multi_get_private_data(LServer, SLUser, SNSs) of - {selected, Rows} -> - RowsDict = dict:from_list(Rows), - [select_value(NSDef, RowsDict) || NSDef <- NS2Def]; - _ -> - [Def || {_NS, Def} <- NS2Def] - end. - -select_value({NS, Def}, RowsDict) -> - case dict:find(NS, RowsDict) of - {ok, SData} -> - {ok, Elem} = exml:parse(SData), - Elem; - error -> - Def - end. - -remove_user(LUser, LServer) -> - SLUser = mongoose_rdbms:escape_string(LUser), - rdbms_queries:del_user_private_storage(LServer, SLUser). - -get_all_nss(LUser, LServer) -> - EscLUser = mongoose_rdbms:escape_string(LUser), - {selected, Res} = rdbms_queries:get_all_private_namespaces(LServer, EscLUser), - lists:map(fun({R}) -> R end, Res). - diff --git a/src/mod_private_rdbms.erl b/src/mod_private_rdbms.erl index 20fcc2c2e7..aff1d9f4fe 100644 --- a/src/mod_private_rdbms.erl +++ b/src/mod_private_rdbms.erl @@ -40,47 +40,63 @@ -include("mongoose.hrl"). -include("jlib.hrl"). -init(_Host, _Opts) -> +init(Host, _Opts) -> + mongoose_rdbms:prepare(private_select_data, private_storage, + [username, namespace], + <<"SELECT data FROM private_storage WHERE username=? AND namespace=?">>), + mongoose_rdbms:prepare(private_select_namespaces, private_storage, + [username], + <<"SELECT namespace FROM private_storage WHERE username=?">>), + mongoose_rdbms:prepare(private_remove_user, private_storage, + [username], + <<"DELETE FROM private_storage WHERE username=?">>), + rdbms_queries:prepare_upsert(Host, private_upsert, private_storage, + [<<"username">>, <<"namespace">>, <<"data">>], + [<<"data">>], + [<<"username">>, <<"namespace">>]), ok. multi_set_data(LUser, LServer, NS2XML) -> - F = fun() -> multi_set_data_t(LUser, LServer, NS2XML) end, + NS2BinXML = make_xml_binary(NS2XML), + F = fun() -> multi_set_data_t(LUser, LServer, NS2BinXML) end, case rdbms_queries:sql_transaction(LServer, F) of {atomic, ok} -> ok; {aborted, Reason} -> {aborted, Reason}; {error, Reason} -> {error, Reason} end. + multi_set_data_t(LUser, LServer, NS2XML) -> - SLUser = mongoose_rdbms:escape_string(LUser), - [set_data_t(SLUser, LServer, NS, XML) || {NS, XML} <- NS2XML], + [upsert_data_t(LUser, LServer, NS, XML) || {NS, XML} <- NS2XML], ok. -set_data_t(SLUser, LServer, NS, XML) -> - SNS = mongoose_rdbms:escape_string(NS), - SData = mongoose_rdbms:escape_string(exml:to_binary(XML)), - rdbms_queries:set_private_data(LServer, SLUser, SNS, SData). +upsert_data_t(LUser, Host, NS, XML) -> + InsertParams = [LUser, NS, XML], + UpdateParams = [XML], + UniqueKeyValues = [LUser, NS], + rdbms_queries:execute_upsert(Host, private_upsert, InsertParams, UpdateParams, UniqueKeyValues). + + +make_xml_binary(NS2XML) -> + [{NS, exml:to_binary(XML)} || {NS, XML} <- NS2XML]. multi_get_data(LUser, LServer, NS2Def) -> [get_data(LUser, LServer, NS, Default) || {NS, Default} <- NS2Def]. %% @doc Return stored value or default. get_data(LUser, LServer, NS, Default) -> - SLUser = mongoose_rdbms:escape_string(LUser), - SNS = mongoose_rdbms:escape_string(NS), - case catch rdbms_queries:get_private_data(LServer, SLUser, SNS) of - {selected, [{SData}]} -> - {ok, Elem} = exml:parse(SData), + Res = mongoose_rdbms:execute(LServer, private_select_data, [LUser, NS]), + case Res of + {selected, [{BinData}]} -> + {ok, Elem} = exml:parse(BinData), Elem; _ -> Default end. get_all_nss(LUser, LServer) -> - EscLUser = mongoose_rdbms:escape_string(LUser), - {selected, Res} = rdbms_queries:get_all_private_namespaces(LServer, EscLUser), + {selected, Res} = mongoose_rdbms:execute(LServer, private_select_namespaces, [LUser]), lists:map(fun({R}) -> R end, Res). remove_user(LUser, LServer) -> - SLUser = mongoose_rdbms:escape_string(LUser), - rdbms_queries:del_user_private_storage(LServer, SLUser). + mongoose_rdbms:execute(LServer, private_remove_user, [LUser]). diff --git a/src/mod_register.erl b/src/mod_register.erl index d371c6adb9..456a74dea2 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -168,22 +168,21 @@ register_or_change_password(Credentials, ClientJID, #jid{lserver = ServerDomain} error_response(IQ, mongoose_xmpp_errors:forbidden()) end. -attempt_cancelation(ClientJID, #jid{lserver = ServerDomain}, #iq{} = IQ) -> - #jid{user = Username, lserver = UserDomain} = ClientJID, +attempt_cancelation(#jid{} = ClientJID, #jid{lserver = ServerDomain}, #iq{} = IQ) -> case inband_registration_and_cancelation_allowed(ServerDomain, ClientJID) of true -> %% The response must be sent *before* the %% XML stream is closed (the call to - %% `ejabberd_auth:remove_user/2' does + %% `ejabberd_auth:remove_user/1' does %% this): as it is, when canceling a %% registration, there is no way to deal %% with failure. ResIQ = IQ#iq{type = result, sub_el = []}, ejabberd_router:route( - jid:make(<<>>, <<>>, <<>>), + jid:make_noprep(<<>>, <<>>, <<>>), ClientJID, jlib:iq_to_xml(ResIQ)), - ejabberd_auth:remove_user(Username, UserDomain), + ejabberd_auth:remove_user(ClientJID), ignore; false -> error_response(IQ, mongoose_xmpp_errors:not_allowed()) @@ -199,8 +198,8 @@ process_iq_get(From, _To, #iq{lang = Lang, sub_el = Child} = IQ, _Source) -> true = is_query_element(Child), {_IsRegistered, UsernameSubels, QuerySubels} = case From of - #jid{user = User, lserver = Server} -> - case ejabberd_auth:is_user_exists(User, Server) of + JID = #jid{user = User} -> + case ejabberd_auth:does_user_exist(JID) of true -> {true, [#xmlcdata{content = User}], [#xmlel{name = <<"registered">>}]}; @@ -222,9 +221,9 @@ process_iq_get(From, _To, #iq{lang = Lang, sub_el = Child} = IQ, _Source) -> #xmlel{name = <<"password">>} | QuerySubels]}]}. -try_register_or_set_password(User, Server, Password, #jid{ user = User, lserver = Server }, +try_register_or_set_password(User, Server, Password, #jid{user = User, lserver = Server} = UserJID, IQ, SubEl, _Source, Lang) -> - try_set_password(User, Server, Password, IQ, SubEl, Lang); + try_set_password(UserJID, Password, IQ, SubEl, Lang); try_register_or_set_password(User, Server, Password, _From, IQ, SubEl, Source, Lang) -> case check_timeout(Source) of true -> @@ -240,10 +239,10 @@ try_register_or_set_password(User, Server, Password, _From, IQ, SubEl, Source, L end. %% @doc Try to change password and return IQ response -try_set_password(User, Server, Password, IQ, SubEl, Lang) -> - case is_strong_password(Server, Password) of +try_set_password(#jid{lserver = LServer} = UserJID, Password, IQ, SubEl, Lang) -> + case is_strong_password(LServer, Password) of true -> - case ejabberd_auth:set_password(User, Server, Password) of + case ejabberd_auth:set_password(UserJID, Password) of ok -> IQ#iq{type = result, sub_el = [SubEl]}; {error, empty_password} -> @@ -277,11 +276,10 @@ try_register(User, Server, Password, SourceRaw, Lang) -> end end. -verify_password_and_register(#jid{ user = User, server = Server } = JID, - Password, SourceRaw, Lang) -> - case is_strong_password(Server, Password) of +verify_password_and_register(#jid{lserver = LServer} = JID, Password, SourceRaw, Lang) -> + case is_strong_password(LServer, Password) of true -> - case ejabberd_auth:try_register(User, Server, Password) of + case ejabberd_auth:try_register(JID, Password) of {error, exists} -> {error, mongoose_xmpp_errors:conflict()}; {error, invalid_jid} -> @@ -300,14 +298,13 @@ verify_password_and_register(#jid{ user = User, server = Server } = JID, {error, mongoose_xmpp_errors:not_acceptable(Lang, ErrText)} end. -send_welcome_message(JID) -> - Host = JID#jid.lserver, +send_welcome_message(#jid{lserver = Host} = JID) -> case gen_mod:get_module_opt(Host, ?MODULE, welcome_message, {"", ""}) of {"", ""} -> ok; {Subj, Body} -> ejabberd_router:route( - jid:make(<<>>, Host, <<>>), + jid:make_noprep(<<>>, Host, <<>>), JID, #xmlel{name = <<"message">>, attrs = [{<<"type">>, <<"normal">>}], children = [#xmlel{name = <<"subject">>, @@ -318,8 +315,7 @@ send_welcome_message(JID) -> ok end. -send_registration_notifications(UJID, Source) -> - Host = UJID#jid.lserver, +send_registration_notifications(#jid{lserver = Host} = UJID, Source) -> case gen_mod:get_module_opt(Host, ?MODULE, registration_watchers, []) of [] -> ok; JIDs when is_list(JIDs) -> @@ -342,7 +338,7 @@ send_registration_notification(JIDBin, Host, Body) -> attrs = [{<<"type">>, <<"chat">>}], children = [#xmlel{name = <<"body">>, children = [#xmlcdata{content = Body}]}]}, - ejabberd_router:route(jid:make(<<>>, Host, <<>>), JID, Message) + ejabberd_router:route(jid:make_noprep(<<>>, Host, <<>>), JID, Message) end. check_timeout(undefined) -> @@ -410,8 +406,7 @@ write_time({{Y, Mo, D}, {H, Mi, S}}) -> io_lib:format("~w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w", [Y, Mo, D, H, Mi, S]). -is_strong_password(Server, Password) -> - LServer = jid:nameprep(Server), +is_strong_password(LServer, Password) -> case gen_mod:get_module_opt(LServer, ?MODULE, password_strength, 0) of Entropy when is_number(Entropy), Entropy == 0 -> true; @@ -419,7 +414,7 @@ is_strong_password(Server, Password) -> ejabberd_auth:entropy(Password) >= Entropy; Wrong -> ?LOG_WARNING(#{what => reg_wrong_password_strength, - host => Server, value => Wrong}), + host => LServer, value => Wrong}), true end. diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 8bcb7fe949..ff8a9f9f91 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -46,15 +46,13 @@ process_local_iq/4, get_user_roster/2, get_subscription_lists/3, + get_roster_entry/2, get_roster_entry/3, - get_roster_entry/4, - get_roster_entry_t/3, - get_roster_entry_t/4, get_roster/2, item_to_map/1, in_subscription/6, out_subscription/5, - set_items/3, + set_items/2, set_roster_entry/4, remove_user/2, % for tests remove_user/3, @@ -62,13 +60,12 @@ get_jid_info/4, item_to_xml/1, get_versioning_feature/2, - roster_versioning_enabled/1, roster_version/2 - ]). + ]). -export([remove_test_user/2, transaction/2, - process_subscription_transaction/6, + process_subscription_transaction/5, get_user_rosters_length/2]). % for testing -export([get_personal_data/2]). @@ -226,18 +223,14 @@ hooks(Host) -> {roster_get_versioning_feature, Host, ?MODULE, get_versioning_feature, 50}, {get_personal_data, Host, ?MODULE, get_personal_data, 50}]. -get_roster_entry(LUser, LServer, Jid) -> - mod_roster_backend:get_roster_entry(jid:nameprep(LUser), LServer, jid_arg_to_lower(Jid)). +get_roster_entry(#jid{luser = LUser, lserver = LServer}, Jid) -> + mod_roster_backend:get_roster_entry(LUser, LServer, jid_arg_to_lower(Jid)). -get_roster_entry(LUser, LServer, Jid, full) -> - mod_roster_backend:get_roster_entry(jid:nameprep(LUser), LServer, jid_arg_to_lower(Jid), full). +get_roster_entry(#jid{luser = LUser, lserver = LServer}, Jid, full) -> + mod_roster_backend:get_roster_entry(LUser, LServer, jid_arg_to_lower(Jid), full). -get_roster_entry_t(LUser, LServer, Jid) -> - mod_roster_backend:get_roster_entry_t(jid:nameprep(LUser), LServer, jid_arg_to_lower(Jid)). - -get_roster_entry_t(LUser, LServer, Jid, full) -> - mod_roster_backend:get_roster_entry_t(jid:nameprep(LUser), LServer, - jid_arg_to_lower(Jid), full). +get_roster_entry_t(#jid{luser = LUser, lserver = LServer}, Jid, full) -> + mod_roster_backend:get_roster_entry_t(LUser, LServer, jid_arg_to_lower(Jid), full). -spec jid_arg_to_lower(JID :: jid:simple_jid() | jid:jid() | binary()) -> error | jid:simple_jid(). @@ -247,6 +240,8 @@ jid_arg_to_lower(Jid) when is_binary(Jid) -> jid_arg_to_lower(Jid) -> jid:to_lower(Jid). +-spec process_iq(jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) -> + {mongoose_acc:t(), jlib:iq()}. process_iq(From, To, Acc, IQ) -> #iq{sub_el = SubEl} = IQ, #jid{lserver = LServer} = From, @@ -257,6 +252,8 @@ process_iq(From, To, Acc, IQ) -> {Acc, IQ#iq{type = error, sub_el = [SubEl, mongoose_xmpp_errors:item_not_found()]}} end. +-spec process_local_iq(jid:jid(), jid:jid(), mongoose_acc:t(), jlib:iq()) -> + {mongoose_acc:t(), jlib:iq()}. process_local_iq(From, To, Acc, #iq{type = Type} = IQ) -> case Type of set -> @@ -270,9 +267,11 @@ roster_hash(Items) -> R = #roster{groups = Grs} <- Items], sha:sha1_hex(term_to_binary(lists:sort(L))). +-spec roster_versioning_enabled(jid:lserver()) -> boolean(). roster_versioning_enabled(Host) -> gen_mod:get_module_opt(Host, ?MODULE, versioning, false). +-spec roster_version_on_db(jid:lserver()) -> boolean(). roster_version_on_db(Host) -> gen_mod:get_module_opt(Host, ?MODULE, store_current_id, false). @@ -319,21 +318,19 @@ write_roster_version(LUser, LServer, InTransaction) -> %% - roster versioning is used by server and client, %% BUT the server isn't storing versions on db OR %% - the roster version from client don't match current version. +-spec process_iq_get(jid:jid(), jid:jid(), jlib:iq()) -> jlib:iq(). process_iq_get(From, To, IQ) -> mongoose_iq:try_to_handle_iq(From, To, IQ, fun do_process_iq_get/3). -do_process_iq_get(From, To, #iq{sub_el = SubEl} = IQ) -> - LServer = From#jid.lserver, +-spec do_process_iq_get(jid:jid(), jid:jid(), jlib:iq()) -> jlib:iq(). +do_process_iq_get(#jid{lserver = LServer} = From, To, #iq{sub_el = SubEl} = IQ) -> AttrVer = exml_query:attr(SubEl, <<"ver">>), %% type binary() | undefined VersioningRequested = is_binary(AttrVer), VersioningEnabled = roster_versioning_enabled(LServer), VersionOnDb = roster_version_on_db(LServer), - Strategy = choose_get_user_roster_strategy( - VersioningRequested, VersioningEnabled, VersionOnDb), - {ItemsToSend, VersionToSend} = - get_user_roster_based_on_version(Strategy, AttrVer, From, To), - IQ#iq{type = result, - sub_el = create_sub_el(ItemsToSend, VersionToSend)}. + Strategy = choose_get_user_roster_strategy(VersioningRequested, VersioningEnabled, VersionOnDb), + {ItemsToSend, VersionToSend} = get_user_roster_based_on_version(Strategy, AttrVer, From, To), + IQ#iq{type = result, sub_el = create_sub_el(ItemsToSend, VersionToSend)}. -spec choose_get_user_roster_strategy(VersioningRequested :: boolean(), VersioningEnabled :: boolean(), @@ -370,8 +367,7 @@ get_user_roster_db_versioning(RequestedVersion, From, To) get_user_roster_hash_versioning(RequestedVersion, From, To) when is_binary(RequestedVersion) -> - RosterItems = get_roster_old(To#jid.lserver, From#jid.luser, - From#jid.lserver), + RosterItems = get_roster_old(To#jid.lserver, From#jid.luser, From#jid.lserver), case roster_hash(RosterItems) of RequestedVersion -> {false, false}; @@ -397,9 +393,7 @@ create_sub_el(Items, Version) -> {<<"ver">>, Version}], children = Items}]. --spec get_user_roster(mongoose_acc:t(), - {jid:luser(), jid:lserver()}) -> - mongoose_acc:t(). +-spec get_user_roster(mongoose_acc:t(), {jid:luser(), jid:lserver()}) -> mongoose_acc:t(). get_user_roster(Acc, {LUser, LServer}) -> case mongoose_acc:get(roster, show_full_roster, false, Acc) of true -> @@ -445,41 +439,39 @@ item_to_xml(Item) -> #xmlel{name = <<"item">>, attrs = Attrs4, children = SubEls}. +-spec process_iq_set(jid:jid(), jid:jid(), jlib:iq()) -> jlib:iq(). process_iq_set(#jid{lserver = LServer} = From, To, #iq{sub_el = SubEl} = IQ) -> #xmlel{children = Els} = SubEl, mongoose_hooks:roster_set(LServer, ok, From, To, SubEl), lists:foreach(fun(El) -> process_item_set(From, To, El) end, Els), IQ#iq{type = result, sub_el = []}. +-spec process_item_set(jid:jid(), jid:jid(), exml:element()) -> ok. process_item_set(From, To, #xmlel{attrs = Attrs} = El) -> JID1 = jid:from_binary(xml:get_attr_s(<<"jid">>, Attrs)), do_process_item_set(JID1, From, To, El); process_item_set(_From, _To, _) -> ok. +-spec do_process_item_set(error | jid:jid(), jid:jid(), jid:jid(), exml:element()) -> ok. do_process_item_set(error, _, _, _) -> ok; -do_process_item_set(JID1, - #jid{user = User, luser = LUser, lserver = LServer} = From, - To, +do_process_item_set(#jid{} = JID1, #jid{} = From, #jid{} = To, #xmlel{attrs = Attrs, children = Els}) -> - LJID = jid:to_lower(JID1), MakeItem2 = fun(Item) -> Item1 = process_item_attrs(Item, Attrs), process_item_els(Item1, Els) end, - set_roster_item(User, LUser, LServer, LJID, From, To, MakeItem2). + set_roster_item(JID1, From, To, MakeItem2). %% @doc this is run when a roster item is to be added, updated or removed %% the interface of this func could probably be a bit simpler --spec set_roster_item(User :: binary(), - LUser :: binary(), - LServer :: binary(), - LJID :: jid:simple_jid() | error, - From ::jid:jid(), - To ::jid:jid(), +-spec set_roster_item(JID1 :: jid:jid(), + From :: jid:jid(), + To :: jid:jid(), MakeItem2 :: fun( (roster()) -> roster())) -> ok. -set_roster_item(User, LUser, LServer, LJID, From, To, MakeItem2) -> - F = fun () -> - Item = case get_roster_entry(LUser, LServer, LJID) of +set_roster_item(JID1, #jid{luser = LUser, lserver = LServer} = From, To, MakeItem2) -> + LJID = jid:to_lower(JID1), + F = fun() -> + Item = case get_roster_entry(From, LJID) of does_not_exist -> #roster{usj = {LUser, LServer, LJID}, us = {LUser, LServer}, @@ -501,7 +493,7 @@ set_roster_item(User, LUser, LServer, LJID, From, To, MakeItem2) -> end, case transaction(LServer, F) of {atomic, {OldItem, NewItem}} -> - push_item(User, LServer, To, NewItem), + push_item(From, To, NewItem), case NewItem#roster.subscription of remove -> send_unsubscribing_presence(From, OldItem), ok; @@ -516,7 +508,7 @@ process_item_attrs(Item, [{<<"jid">>, Val} | Attrs]) -> error -> process_item_attrs(Item, Attrs); JID1 -> - JID = {JID1#jid.luser, JID1#jid.lserver, JID1#jid.lresource}, + JID = jid:to_lower(JID1), process_item_attrs(Item#roster{jid = JID}, Attrs) end; process_item_attrs(Item, [{<<"name">>, Val} | Attrs]) -> @@ -549,26 +541,29 @@ process_item_els(Item, [{xmlcdata, _} | Els]) -> process_item_els(Item, Els); process_item_els(Item, []) -> Item. -push_item(User, Server, From, Item) -> - #jid{luser = LUser} = JID = jid:make(User, Server, <<"">>), - ejabberd_sm:route(jid:make(<<"">>, <<"">>, <<"">>), JID, +push_item(#jid{luser = LUser, lserver = LServer} = JID, From, Item) -> + ejabberd_sm:route(jid:make_noprep(<<>>, <<>>, <<>>), JID, {broadcast, {item, Item#roster.jid, Item#roster.subscription}}), - case roster_versioning_enabled(Server) of + case roster_versioning_enabled(LServer) of true -> - push_item_version(JID, Server, User, From, Item, - roster_version(Server, LUser)); + push_item_version(JID, From, Item, roster_version(LServer, LUser)); false -> - lists:foreach(fun (Resource) -> - push_item(User, Server, Resource, From, Item) + lists:foreach(fun(Resource) -> + push_item_without_version(JID, Resource, From, Item) end, ejabberd_sm:get_user_resources(JID)) end. -push_item(User, Server, Resource, From, Item) -> +push_item_without_version(#jid{lserver = Server} = JID, Resource, From, Item) -> mongoose_hooks:roster_push(Server, ok, From, Item), - push_item(User, Server, Resource, From, Item, not_found). + push_item_final(jid:replace_resource(JID, Resource), From, Item, not_found). + +push_item_version(JID, From, Item, RosterVersion) -> + lists:foreach(fun(Resource) -> + push_item_final(jid:replace_resource(JID, Resource), From, Item, RosterVersion) + end, ejabberd_sm:get_user_resources(JID)). -push_item(User, Server, Resource, From, Item, RosterVersion) -> +push_item_final(JID, From, Item, RosterVersion) -> ExtraAttrs = case RosterVersion of not_found -> []; _ -> [{<<"ver">>, RosterVersion}] @@ -581,26 +576,14 @@ push_item(User, Server, Resource, From, Item, RosterVersion) -> [#xmlel{name = <<"query">>, attrs = [{<<"xmlns">>, ?NS_ROSTER} | ExtraAttrs], children = [item_to_xml(Item)]}]}, - ejabberd_router:route(From, - jid:make(User, Server, Resource), - jlib:iq_to_xml(ResIQ)). - -push_item_version(JID, Server, User, From, Item, - RosterVersion) -> - lists:foreach(fun (Resource) -> - push_item(User, Server, Resource, From, Item, - RosterVersion) - end, - ejabberd_sm:get_user_resources(JID)). + ejabberd_router:route(From, JID, jlib:iq_to_xml(ResIQ)). -spec get_subscription_lists(Acc :: mongoose_acc:t(), User :: binary(), Server :: binary()) -> mongoose_acc:t(). get_subscription_lists(Acc, User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), + #jid{luser = LUser, lserver = LServer} = JID = jid:make(User, Server, <<>>), Items = mod_roster_backend:get_subscription_lists(Acc, LUser, LServer), - JID = jid:make(User, Server, <<>>), SubLists = fill_subscription_lists(JID, LServer, Items, [], [], []), mongoose_acc:set(roster, subscription_lists, SubLists, Acc). @@ -660,31 +643,32 @@ transaction(LServer, F) -> -spec in_subscription(Acc:: mongoose_acc:t(), User :: binary(), Server :: binary(), - JID ::jid:jid(), + JID :: jid:jid(), Type :: sub_presence(), Reason :: any()) -> mongoose_acc:t(). in_subscription(Acc, User, Server, JID, Type, Reason) -> - Res = process_subscription(in, User, Server, JID, Type, - Reason), + Res = process_subscription(in, User, Server, JID, Type, Reason), mongoose_acc:set(hook, result, Res, Acc). -spec out_subscription(Acc:: mongoose_acc:t(), User :: binary(), Server :: binary(), - JID ::jid:jid(), + JID :: jid:jid(), Type :: sub_presence()) -> mongoose_acc:t(). out_subscription(Acc, User, Server, JID, Type) -> - Res = process_subscription(out, User, Server, JID, Type, <<"">>), + Res = process_subscription(out, User, Server, JID, Type, <<>>), mongoose_acc:set(hook, result, Res, Acc). process_subscription(Direction, User, Server, JID1, Type, Reason) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - LJID = jid:to_lower(JID1), - TransactionFun = fun() -> process_subscription_transaction(Direction, LUser, LServer, - LJID, Type, Reason) end, + JID = jid:make(User, Server, <<>>), + LServer = case JID of + #jid{lserver = LS} -> LS; + error -> error + end, + TransactionFun = + fun() -> process_subscription_transaction(Direction, JID, JID1, Type, Reason) end, case transaction(LServer, TransactionFun) of {atomic, {Push, AutoReply}} -> case AutoReply of @@ -693,13 +677,13 @@ process_subscription(Direction, User, Server, JID1, Type, Reason) -> PresenceStanza = #xmlel{name = <<"presence">>, attrs = [{<<"type">>, autoreply_to_type(AutoReply)}], children = []}, - ejabberd_router:route(jid:make(User, Server, <<"">>), JID1, PresenceStanza) + ejabberd_router:route(JID, JID1, PresenceStanza) end, case Push of {push, #roster{ subscription = none, ask = in }} -> true; {push, Item} -> - push_item(User, Server, jid:make(User, Server, <<"">>), Item), + push_item(JID, JID, Item), true; none -> false end; @@ -709,11 +693,14 @@ process_subscription(Direction, User, Server, JID1, Type, Reason) -> autoreply_to_type(subscribed) -> <<"subscribed">>; autoreply_to_type(unsubscribed) -> <<"unsubscribed">>. -process_subscription_transaction(Direction, LUser, LServer, LJID, Type, Reason) -> - Item = case mod_roster_backend:get_roster_entry_t(LUser, LServer, LJID, full) of +process_subscription_transaction(Direction, JID, JID1, Type, Reason) -> + #jid{luser = LUser, lserver = LServer} = JID, + LJID = jid:to_lower(JID1), + Item = case get_roster_entry_t(JID, JID1, full) of does_not_exist -> #roster{usj = {LUser, LServer, LJID}, - us = {LUser, LServer}, jid = LJID}; + us = {LUser, LServer}, + jid = LJID}; R -> R end, NewState = case Direction of @@ -864,21 +851,24 @@ get_user_rosters_length(User, Server) -> %% Used only by tests remove_user(User, Server) -> - Acc = mongoose_acc:new(#{ location => ?LOCATION, - lserver => <<_/binary>> = jid:nameprep(Server), - element => undefined }), + Acc = mongoose_acc:new(#{location => ?LOCATION, + lserver => <<_/binary>> = jid:nameprep(Server), + element => undefined}), remove_user(Acc, User, Server). remove_user(Acc, User, Server) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - Acc1 = try_send_unsubscription_to_rosteritems(Acc, LUser, LServer), - mod_roster_backend:remove_user(LUser, LServer), - Acc1. + case jid:make(User, Server, <<>>) of + #jid{luser = LUser, lserver = LServer} = JID -> + Acc1 = try_send_unsubscription_to_rosteritems(Acc, JID), + mod_roster_backend:remove_user(LUser, LServer), + Acc1; + error -> + Acc + end. -try_send_unsubscription_to_rosteritems(Acc, LUser, LServer) -> +try_send_unsubscription_to_rosteritems(Acc, JID) -> try - send_unsubscription_to_rosteritems(Acc, LUser, LServer) + send_unsubscription_to_rosteritems(Acc, JID) catch E:R:S -> ?LOG_WARNING(#{what => roster_unsubcribe_failed, @@ -889,14 +879,13 @@ try_send_unsubscription_to_rosteritems(Acc, LUser, LServer) -> %% For each contact with Subscription: %% Both or From, send a "unsubscribed" presence stanza; %% Both or To, send a "unsubscribe" presence stanza. -send_unsubscription_to_rosteritems(Acc, LUser, LServer) -> +send_unsubscription_to_rosteritems(Acc, JID) -> + #jid{luser = LUser, lserver = LServer} = JID, Acc1 = get_user_roster(Acc, {LUser, LServer}), RosterItems = mongoose_acc:get(roster, items, [], Acc1), - From = jid:make({LUser, LServer, <<"">>}), - lists:foreach(fun (RosterItem) -> - send_unsubscribing_presence(From, RosterItem) - end, - RosterItems), + lists:foreach(fun(RosterItem) -> + send_unsubscribing_presence(JID, RosterItem) + end, RosterItems), Acc1. %% @spec (From::jid(), Item::roster()) -> any() @@ -921,49 +910,30 @@ send_presence_type(From, To, Type) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -set_items(User, Server, SubEl) -> +set_items(#jid{luser = LUser, lserver = LServer}, SubEl) -> #xmlel{children = Els} = SubEl, - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), F = fun () -> - lists:foreach(fun (El) -> + lists:foreach(fun(El) -> process_item_set_t(LUser, LServer, El) - end, - Els) + end, Els) end, transaction(LServer, F). %% @doc add a contact to roster, or update --spec set_roster_entry(jid:jid(), binary(), binary(), [binary()]) -> ok|error. -set_roster_entry(UserJid, ContactBin, Name, Groups) -> - set_roster_entry(UserJid, ContactBin, Name, Groups, unchanged). +-spec set_roster_entry(jid:jid(), jid:jid() | error, binary(), [binary()]) -> ok | error. +set_roster_entry(UserJid, ContactJid, Name, Groups) -> + set_roster_entry(UserJid, ContactJid, Name, Groups, unchanged). -spec set_roster_entry(UserJid ::jid:jid(), - ContactBin :: binary(), + ContactJid :: jid:jid(), Name :: binary() | unchanged, Groups :: [binary()] | unchanged, - NewSubscription :: remove | unchanged) -> ok|error. -set_roster_entry(UserJid, ContactBin, Name, Groups, NewSubscription) -> - LUser = UserJid#jid.luser, - LServer = UserJid#jid.lserver, - JID1 = jid:from_binary(ContactBin), - case JID1 of - error -> error; - _ -> - LJID = jid:to_lower(JID1), - MakeItem = fun(Item) -> - modify_roster_item(Item, Name, Groups, NewSubscription) - end, - set_roster_item( - LUser, % User - LUser, % LUser - LServer, % LServer - LJID, % LJID - UserJid, % From - UserJid, % To - MakeItem - ) - end. + NewSubscription :: remove | unchanged) -> ok | error. +set_roster_entry(UserJid, ContactJid, Name, Groups, NewSubscription) -> + MakeItem = fun(Item) -> + modify_roster_item(Item, Name, Groups, NewSubscription) + end, + set_roster_item(ContactJid, UserJid, UserJid, MakeItem). modify_roster_item(Item, Name, Groups, NewSubscription) -> Item1 = case Name of @@ -981,10 +951,9 @@ modify_roster_item(Item, Name, Groups, NewSubscription) -> %% @doc remove from roster - in practice it means changing %% subscription state to 'remove' --spec remove_from_roster(UserJid ::jid:jid(), - ContactBin :: binary()) -> ok|error. -remove_from_roster(UserJid, ContactBin) -> - set_roster_entry(UserJid, ContactBin, unchanged, unchanged, remove). +-spec remove_from_roster(UserJid ::jid:jid(), ContactJid :: jid:jid()) -> ok | error. +remove_from_roster(UserJid, ContactJid) -> + set_roster_entry(UserJid, ContactJid, unchanged, unchanged, remove). update_roster_t(LUser, LServer, LJID, Item) -> mod_roster_backend:update_roster_t(LUser, LServer, LJID, Item). @@ -1045,11 +1014,12 @@ process_item_attrs_ws(Item, []) -> Server :: jid:lserver(), JID ::jid:jid() | jid:ljid()) -> {subscription_state(), [binary()]}. get_jid_info(_, User, Server, JID) -> - case get_roster_entry(User, Server, JID, full) of + ToJID = jid:make(User, Server, <<>>), + case get_roster_entry(ToJID, JID, full) of error -> {none, []}; does_not_exist -> LRJID = jid:to_bare(jid:to_lower(JID)), - case get_roster_entry(User, Server, LRJID, full) of + case get_roster_entry(ToJID, LRJID, full) of error -> {none, []}; does_not_exist -> {none, []}; R -> {R#roster.subscription, R#roster.groups} diff --git a/src/mod_shared_roster_ldap.erl b/src/mod_shared_roster_ldap.erl index 1d6db004b5..60a3fa2936 100644 --- a/src/mod_shared_roster_ldap.erl +++ b/src/mod_shared_roster_ldap.erl @@ -411,7 +411,7 @@ search_group_info(State, Group) -> end end, AuthChecker = case State#state.auth_check of - true -> fun ejabberd_auth:is_user_exists/2; + true -> fun ejabberd_auth:does_user_exist/1; _ -> fun (_U, _S) -> true end end, Host = State#state.host, @@ -458,7 +458,8 @@ check_and_accumulate_member({ok, UID}, AuthChecker, Host, JIDsAcc) -> error -> JIDsAcc; _ -> - case AuthChecker(PUID, Host) of + JID = jid:make(PUID, Host, <<>>), + case AuthChecker(JID) of true -> [{PUID, Host} | JIDsAcc]; _ -> diff --git a/src/mod_vcard.erl b/src/mod_vcard.erl index cd620a4ad2..33bd0c015f 100644 --- a/src/mod_vcard.erl +++ b/src/mod_vcard.erl @@ -129,7 +129,7 @@ %%-------------------------------------------------------------------- -spec get_personal_data(gdpr:personal_data(), jid:jid()) -> gdpr:personal_data(). -get_personal_data(Acc, #jid{ luser = LUser, lserver = LServer }) -> +get_personal_data(Acc, #jid{luser = LUser, lserver = LServer}) -> Jid = jid:to_binary({LUser, LServer}), Schema = ["jid", "vcard"], Entries = case mod_vcard_backend:get_vcard(LUser, LServer) of diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index 6d02a9ed3b..fe713804fc 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -148,7 +148,8 @@ get_vcard(LUser, LServer) -> Proc = gen_mod:get_module_proc(LServer, ?PROCNAME), {ok, State} = gen_server:call(Proc, get_state), LServer = State#state.serverhost, - case ejabberd_auth:is_user_exists(LUser, LServer) of + JID = jid:make(LUser, LServer, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> VCardMap = State#state.vcard_map, case find_ldap_user(LUser, State) of @@ -408,7 +409,8 @@ attrs_to_item_xml(Attrs, #state{uids = UIDs} = State) -> make_user_item_if_exists(Username, Attrs, #state{serverhost = LServer, search_reported = SearchReported, vcard_map = VCardMap, binary_search_fields = BinFields}) -> - case ejabberd_auth:is_user_exists(Username, LServer) of + JID = jid:make(Username, LServer, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> RFields = lists:map(fun ({_, VCardName}) -> {VCardName, map_vcard_attr(VCardName, Attrs, VCardMap, diff --git a/src/mongoose_api_client.erl b/src/mongoose_api_client.erl index 93d0d1c4bf..33af78040e 100644 --- a/src/mongoose_api_client.erl +++ b/src/mongoose_api_client.erl @@ -30,7 +30,6 @@ -import(mongoose_api_common, [action_to_method/1, method_to_action/1, - error_code/1, process_request/4, error_response/4, parse_request_body/1]). @@ -154,9 +153,8 @@ do_authorize({basic, User, Password}, Req, State) -> do_authorize(_, Req, State) -> make_unauthorized_response(Req, State). -do_check_password(#jid{luser = User, lserver = Server} = JID, - Password, Req, State) -> - case ejabberd_auth:check_password(User, Server, Password) of +do_check_password(#jid{} = JID, Password, Req, State) -> + case ejabberd_auth:check_password(JID, Password) of true -> {true, Req, State#http_api_state{entity = jid:to_binary(JID)}}; _ -> diff --git a/src/mongoose_api_common.erl b/src/mongoose_api_common.erl index 5df09f534b..fdf25f2a14 100644 --- a/src/mongoose_api_common.erl +++ b/src/mongoose_api_common.erl @@ -67,8 +67,6 @@ %% API -export([create_admin_url_path/1, create_user_url_path/1, - error_response/3, - error_response/4, action_to_method/1, method_to_action/1, parse_request_body/1, @@ -77,6 +75,7 @@ reload_dispatches/1, get_auth_details/1, is_known_auth_method/1, + error_response/4, make_unauthorized_response/2]). @@ -102,8 +101,11 @@ create_admin_url_path(Command) -> create_user_url_path(Command) -> ["/", mongoose_commands:category(Command), maybe_add_bindings(Command, user)]. --spec process_request(method(), mongoose_commands:t(), any(), http_api_state()) -> - {any(), any(), http_api_state()}. +-spec process_request(Method :: method(), + Command :: mongoose_commands:t(), + Req :: cowboy_req:req() | {list(), cowboy_req:req()}, + State :: http_api_state()) -> + {any(), cowboy_req:req(), http_api_state()}. process_request(Method, Command, Req, #http_api_state{bindings = Binds, entity = Entity} = State) when ((Method == <<"POST">>) or (Method == <<"PUT">>)) -> {Params, Req2} = Req, @@ -118,22 +120,29 @@ process_request(Method, Command, Req, #http_api_state{bindings = Binds, entity = BindsAndVars = Binds ++ QV ++ maybe_add_caller(Entity), handle_request(Method, Command, BindsAndVars, Req, State). --spec handle_request(method(), mongoose_commands:t(), args_applied(), term(), http_api_state()) -> - {any(), any(), http_api_state()}. +-spec handle_request(Method :: method(), + Command :: mongoose_commands:t(), + Args :: args_applied(), + Req :: cowboy_req:req(), + State :: http_api_state()) -> + {any(), cowboy_req:req(), http_api_state()}. handle_request(Method, Command, Args, Req, #http_api_state{entity = Entity} = State) -> - ConvertedArgs = check_and_extract_args(mongoose_commands:args(Command), - mongoose_commands:optargs(Command), Args), - Result = execute_command(ConvertedArgs, Command, Entity), - handle_result(Method, Result, Req, State). + case check_and_extract_args(mongoose_commands:args(Command), + mongoose_commands:optargs(Command), Args) of + {error, Type, Reason} -> + handle_result(Method, {error, Type, Reason}, Req, State); + ConvertedArgs -> + handle_result(Method, + execute_command(ConvertedArgs, Command, Entity), + Req, State) + end. --type correct_result() :: ok | {ok, any()}. --type error_result() :: mongoose_commands:failure() | - {error, bad_request, any()}. --type expected_result() :: correct_result() | error_result(). +-type correct_result() :: mongoose_commands:success(). +-type error_result() :: mongoose_commands:failure(). -spec handle_result(Method, Result, Req, State) -> Return when - Method :: method(), - Result :: expected_result(), + Method :: method() | no_call, + Result :: correct_result() | error_result(), Req :: cowboy_req:req(), State :: http_api_state(), Return :: {any(), cowboy_req:req(), http_api_state()}. @@ -142,8 +151,7 @@ handle_result(Verb, ok, Req, State) -> handle_result(<<"GET">>, {ok, Result}, Req, State) -> {jiffy:encode(Result), Req, State}; handle_result(<<"POST">>, {ok, nocontent}, Req, State) -> - Path = iolist_to_binary(cowboy_req:uri(Req)), - Req2 = maybe_add_location_header(nocontent, binary_to_list(Path), Req), + Req2 = cowboy_req:reply(204, Req), {stop, Req2, State}; handle_result(<<"POST">>, {ok, Res}, Req, State) -> Path = iolist_to_binary(cowboy_req:uri(Req)), @@ -161,16 +169,10 @@ handle_result(<<"PUT">>, {ok, Res}, Req, State) -> Req2 = cowboy_req:set_resp_body(Res, Req), Req3 = cowboy_req:reply(201, Req2), {stop, Req3, State}; -handle_result(_, {error, Error, Reason}, Req, State) when is_binary(Reason) -> +handle_result(_, {error, Error, Reason}, Req, State) -> error_response(Error, Reason, Req, State); -handle_result(_, {error, Error, Reason}, Req, State) when is_list(Reason) -> - error_response(Error, list_to_binary(Reason), Req, State); -handle_result(_, {error, Error, _R}, Req, State) -> - error_response(Error, Req, State); -% handle_result(_, {error, Error}, Req, State) -> -% error_response(Error, Req, State); handle_result(no_call, _, Req, State) -> - error_response(not_implemented, Req, State). + error_response(not_implemented, <<>>, Req, State). -spec parse_request_body(any()) -> {args_applied(), cowboy_req:req()} | {error, any()}. @@ -204,24 +206,11 @@ check_and_extract_args(ReqArgs, OptArgs, RequestArgList) -> {error, bad_request, Reason} end. - --spec execute_command(list({atom(), any()}) | map() | {error, atom(), any()}, - mongoose_commands:t(), admin | binary()) -> - correct_result() | error_result(). -execute_command({error, _Type, _Reason} = Err, _, _) -> - Err; +-spec execute_command(mongoose_commands:args(), + mongoose_commands:t(), + mongoose_commands:caller()) -> + correct_result() | error_result(). execute_command(ArgMap, Command, Entity) -> - try - do_execute_command(ArgMap, Command, Entity) - catch - Class:Reason:StackTrace -> - ?LOG_ERROR(#{what => execute_command_failed, class => Class, - reason => Reason, stacktrace => StackTrace}), - {error, bad_request, Reason} - end. - --spec do_execute_command(map(), mongoose_commands:t(), admin|binary()) -> ok | {ok, any()}. -do_execute_command(ArgMap, Command, Entity) -> mongoose_commands:execute(Entity, mongoose_commands:name(Command), ArgMap). -spec maybe_add_caller(admin | binary()) -> list() | list({caller, binary()}). @@ -230,7 +219,7 @@ maybe_add_caller(admin) -> maybe_add_caller(JID) -> [{caller, JID}]. --spec maybe_add_location_header(binary() | list() | nocontent, list(), any()) +-spec maybe_add_location_header(binary() | list(), list(), any()) -> cowboy_req:req(). maybe_add_location_header(Result, ResourcePath, Req) when is_binary(Result) -> add_location_header(binary_to_list(Result), ResourcePath, Req); @@ -325,22 +314,21 @@ to_atom(Atom) when is_atom(Atom) -> %%-------------------------------------------------------------------- %% HTTP utils %%-------------------------------------------------------------------- --spec error_response(integer() | atom(), any(), http_api_state()) -> - {stop, any(), http_api_state()}. -error_response(Code, Req, State) when is_integer(Code) -> - Req1 = cowboy_req:reply(Code, Req), - {stop, Req1, State}; -error_response(ErrorType, Req, State) -> - error_response(error_code(ErrorType), Req, State). - --spec error_response(any(), any(), any(), http_api_state()) -> - {stop, any(), http_api_state()}. -error_response(Code, Reason, Req, State) when is_integer(Code) -> - Req1 = cowboy_req:reply(Code, #{}, Reason, Req), - {stop, Req1, State}; -error_response(ErrorType, Reason, Req, State) -> - error_response(error_code(ErrorType), Reason, Req, State). +%%-spec error_response(mongoose_commands:errortype(), any(), http_api_state()) -> +%% {stop, any(), http_api_state()}. +%%error_response(ErrorType, Req, State) -> +%% error_response(ErrorType, <<>>, Req, State). +-spec error_response(mongoose_commands:errortype(), mongoose_commands:errorreason(), cowboy_req:req(), http_api_state()) -> + {stop, cowboy_req:req(), http_api_state()}. +error_response(ErrorType, Reason, Req, State) -> + BinReason = case Reason of + B when is_binary(B) -> B; + L when is_list(L) -> list_to_binary(L); + Other -> list_to_binary(io_lib:format("~p", [Other])) + end, + Req1 = cowboy_req:reply(error_code(ErrorType), #{}, BinReason, Req), + {stop, Req1, State}. %% HTTP status codes error_code(denied) -> 403; @@ -348,7 +336,10 @@ error_code(not_implemented) -> 501; error_code(bad_request) -> 400; error_code(type_error) -> 400; error_code(not_found) -> 404; -error_code(internal) -> 500. +error_code(internal) -> 500; +error_code(Other) -> + ?WARNING_MSG("Unknown error identifier \"~p\". See mongoose_commands:errortype() for allowed values.", [Other]), + 500. action_to_method(read) -> <<"GET">>; action_to_method(update) -> <<"PUT">>; diff --git a/src/mongoose_api_users.erl b/src/mongoose_api_users.erl index 27d14106a2..0203d6e95d 100644 --- a/src/mongoose_api_users.erl +++ b/src/mongoose_api_users.erl @@ -82,9 +82,10 @@ put_user(Data, Bindings) -> delete_user(Bindings) -> Host = gen_mod:get_opt(host, Bindings), Username = gen_mod:get_opt(username, Bindings), - case ejabberd_auth:is_user_exists(Username, Host) of + JID = jid:make(Username, Host, <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> - maybe_delete_user(Username, Host); + maybe_delete_user(JID); false -> {error, not_found} end. @@ -93,25 +94,26 @@ delete_user(Bindings) -> %% internal functions %%-------------------------------------------------------------------- maybe_register_user(Username, Host, Password) -> - case ejabberd_auth:try_register(Username, Host, Password) of + JID = jid:make(Username, Host, <<>>), + case ejabberd_auth:try_register(JID, Password) of {error, not_allowed} -> ?ERROR; {error, exists} -> - maybe_change_password(Username, Host, Password); + maybe_change_password(JID, Password); _ -> ok end. -maybe_change_password(Username, Host, Password) -> - case ejabberd_auth:set_password(Username, Host, Password) of +maybe_change_password(JID, Password) -> + case ejabberd_auth:set_password(JID, Password) of {error, _} -> ?ERROR; ok -> ok end. -maybe_delete_user(Username, Host) -> - case ejabberd_auth:remove_user(Username, Host) of +maybe_delete_user(JID) -> + case ejabberd_auth:remove_user(JID) of ok -> ok; _ -> diff --git a/src/mongoose_client_api/mongoose_client_api.erl b/src/mongoose_client_api/mongoose_client_api.erl index 578a75ce6b..58e71bd5ea 100644 --- a/src/mongoose_client_api/mongoose_client_api.erl +++ b/src/mongoose_client_api/mongoose_client_api.erl @@ -88,35 +88,37 @@ is_authorized(Req, State) -> case AuthDetails of undefined -> mongoose_api_common:make_unauthorized_response(Req, State); - _ -> - authorize(AuthDetails, HTTPMethod, Req, State) + {AuthMethod, User, Password} -> + authorize(AuthMethod, User, Password, HTTPMethod, Req, State) end. -authorize({_AuthMethod, User, _Pass} = AuthDetails, HTTPMethod, Req, State) -> - case do_authorize(AuthDetails, HTTPMethod) of +authorize(AuthMethod, User, Password, HTTPMethod, Req, State) -> + MaybeJID = jid:from_binary(User), + case do_authorize(AuthMethod, MaybeJID, Password, HTTPMethod) of noauth -> {true, Req, State}; true -> - {true, Req, State#{user => User, jid => jid:from_binary(User)}}; + {true, Req, State#{user => User, jid => MaybeJID}}; false -> mongoose_api_common:make_unauthorized_response(Req, State) end. -do_authorize({AuthMethod, User, Password}, HTTPMethod) -> +do_authorize(AuthMethod, MaybeJID, Password, HTTPMethod) -> case is_noauth_http_method(HTTPMethod) of true -> noauth; false -> - check_password(User, Password) andalso + check_password(MaybeJID, Password) andalso mongoose_api_common:is_known_auth_method(AuthMethod) end. -check_password(<<>>, _) -> +-spec check_password(jid:jid() | error, binary()) -> boolean(). +check_password(error, _) -> false; -check_password(User, Password) -> - #jid{luser = RawUser, lserver = Server} = jid:from_binary(User), - Creds0 = mongoose_credentials:new(Server), - Creds1 = mongoose_credentials:set(Creds0, username, RawUser), +check_password(JID, Password) -> + {LUser, LServer} = jid:to_lus(JID), + Creds0 = mongoose_credentials:new(LServer), + Creds1 = mongoose_credentials:set(Creds0, username, LUser), Creds2 = mongoose_credentials:set(Creds1, password, Password), case ejabberd_auth:authorize(Creds2) of {ok, _} -> true; diff --git a/src/mongoose_client_api/mongoose_client_api_contacts.erl b/src/mongoose_client_api/mongoose_client_api_contacts.erl index fc657b569e..81f899e33b 100644 --- a/src/mongoose_client_api/mongoose_client_api_contacts.erl +++ b/src/mongoose_client_api/mongoose_client_api_contacts.erl @@ -193,6 +193,6 @@ to_binary(S) -> -spec jid_exists(binary(), binary()) -> boolean(). jid_exists(CJid, Jid) -> - FJid = jid:from_binary(CJid), - Res = mod_roster:get_roster_entry(FJid#jid.luser, FJid#jid.lserver, Jid), + #jid{} = FJid = jid:from_binary(CJid), + Res = mod_roster:get_roster_entry(FJid, Jid), Res =/= does_not_exist. diff --git a/src/mongoose_client_api/mongoose_client_api_rooms.erl b/src/mongoose_client_api/mongoose_client_api_rooms.erl index 7a57299e5e..ebab2528a9 100644 --- a/src/mongoose_client_api/mongoose_client_api_rooms.erl +++ b/src/mongoose_client_api/mongoose_client_api_rooms.erl @@ -116,7 +116,7 @@ from_json(Req, State) -> handle_request(Method, JSONData, Req, State) -> case handle_request_by_method(Method, JSONData, Req, State) of - {error, _} -> + {error, _, _} -> {false, Req, State}; Room -> RoomJID = jid:from_binary(Room), diff --git a/src/mongoose_client_api/mongoose_client_api_rooms_config.erl b/src/mongoose_client_api/mongoose_client_api_rooms_config.erl index 2f645254ea..7b3ac51e08 100644 --- a/src/mongoose_client_api/mongoose_client_api_rooms_config.erl +++ b/src/mongoose_client_api/mongoose_client_api_rooms_config.erl @@ -54,9 +54,9 @@ handle_request(Method, JSONData, Req, State) -> case handle_request_by_method(Method, JSONData, Req, State) of ok -> {true, Req, State}; - {error,not_allowed} -> + {error, internal, not_allowed} -> mongoose_client_api:forbidden_request(Req, State); - {error, _} -> + {error, internal, _} -> {false, Req, State} end. diff --git a/src/mongoose_commands.erl b/src/mongoose_commands.erl index 045e18e11c..ce53f27a75 100644 --- a/src/mongoose_commands.erl +++ b/src/mongoose_commands.erl @@ -159,23 +159,27 @@ -type security() :: [admin | user]. %% later acl option will be added --type errortype() :: denied | not_implemented | type_error | internal. -%% we should agree on a set of atoms so that the frontend can map it to http codes +-type errortype() :: denied | not_implemented | bad_request | type_error | not_found | internal. +-type errorreason() :: term(). --type failure() :: {error, errortype(), binary()}. +-type args() :: [{atom(), term()}] | map(). +-type failure() :: {error, errortype(), errorreason()}. +-type success() :: ok | {ok, term()}. -export_type([t/0]). +-export_type([caller/0]). -export_type([action/0]). -export_type([argspec/0]). -export_type([optargspec/0]). -export_type([errortype/0]). +-export_type([errorreason/0]). -export_type([failure/0]). -type command_properties() :: [{atom(), term()}]. %%%% API --export([check_type/2]). +-export([check_type/3]). -export([init/0]). -export([register/1, @@ -309,12 +313,12 @@ func_arity(Cmd) -> length(Cmd#mongoose_command.args) + length(Cmd#mongoose_command.optargs). %% @doc Command execution. --spec execute(caller(), atom() | t(), [term()] | map()) -> - {ok, term()} | ok | failure(). +-spec execute(caller(), atom() | t(), args()) -> + success() | failure(). execute(Caller, Name, Args) when is_atom(Name) -> case ets:lookup(mongoose_commands, Name) of [Command] -> execute_command(Caller, Command, Args); - [] -> {error, not_implemented, <<"This command is not supported">>} + [] -> {error, not_implemented, {command_not_supported, Name, sizeof(Args)}} end; execute(Caller, #mongoose_command{name = Name}, Args) -> execute(Caller, Name, Args). @@ -344,21 +348,23 @@ unregister_commands(Commands) -> end, Commands). +-spec execute_command(caller(), atom() | t(), args()) -> + success() | failure(). execute_command(Caller, Command, Args) -> try check_and_execute(Caller, Command, Args) of ignore_result -> ok; - Res -> + {error, Type, Reason} -> + {error, Type, Reason}; + {ok, Res} -> {ok, Res} catch - {badarg, E} -> - {error, type_error, E}; - {type_error, E} -> - {error, type_error, E}; - permission_denied -> - {error, denied, <<"Command not available for this user">>}; - caller_jid_mismatch -> - {error, denied, <<"Caller ids do not match">>}; + % admittedly, not the best style of coding, in Erlang at least. But we have to do plenty + % of various checks, and in absence of something like Elixir's "with" construct we are + % facing a choice between throwing stuff or using some more or less tortured syntax + % to chain these checks. + throw:{Type, Reason} -> + {error, Type, Reason}; Class:Reason:Stacktrace -> ?LOG_ERROR(#{what => command_failed, command_name => Command#mongoose_command.name, @@ -375,7 +381,7 @@ add_defaults(Args, Opts) when is_map(Args) -> % @doc This performs many checks - types, permissions etc, may throw one of many exceptions %% returns what the func returned or just ok if command spec tells so --spec check_and_execute(caller(), t(), [term()]|map()) -> term(). +-spec check_and_execute(caller(), t(), args()) -> success() | failure() | ignore_result. check_and_execute(Caller, Command, Args) when is_map(Args) -> Args1 = add_defaults(Args, Command#mongoose_command.optargs), ArgList = maps_to_list(Args1, Command#mongoose_command.args, Command#mongoose_command.optargs), @@ -386,8 +392,8 @@ check_and_execute(Caller, Command, Args) -> true -> ok; false -> - throw(permission_denied) - end, + throw({denied, "Command not available for this user"}) + end, % check caller (if it is given in args, and the engine is called by a 'real' user, then it % must match check_caller(Caller, Command, Args), @@ -399,78 +405,79 @@ check_and_execute(Caller, Command, Args) -> ALen = length(Args), case SpecLen =/= ALen of true -> - type_error("Invalid number of arguments: should be ~p, got ~p", [SpecLen, ALen]); + type_error(argument, "Invalid number of arguments: should be ~p, got ~p", [SpecLen, ALen]); _ -> ok end, - [check_type(S, A) || {S, A} <- lists:zip(FullSpec, Args)], + [check_type(argument, S, A) || {S, A} <- lists:zip(FullSpec, Args)], % run command Res = apply(Command#mongoose_command.module, Command#mongoose_command.function, Args), case Res of - {error, not_allowed} -> - throw(permission_denied); - {error, E} -> - throw({func_returned_error, E}); + {error, Type, Reason} -> + {error, Type, Reason}; _ -> - % transitional - ResSpec = case Command#mongoose_command.result of - {ok, R} -> R; - R -> R - end, - check_type(ResSpec, Res), - maybe_ignore_result(ResSpec, Res) + case Command#mongoose_command.result of + ok -> + ignore_result; + ResSpec -> + check_type(return, ResSpec, Res), + {ok, Res} + end end. -maybe_ignore_result(ok, _) -> - ignore_result; -maybe_ignore_result(_, Res) -> - Res. - -check_type(ok, _) -> +check_type(_, ok, _) -> ok; -check_type(A, A) -> +check_type(_, A, A) -> true; -check_type({_Name, boolean}, Value) when is_boolean(Value) -> +check_type(_, {_Name, boolean}, Value) when is_boolean(Value) -> true; -check_type({_Name, binary}, Value) when is_binary(Value) -> +check_type(Mode, {Name, boolean}, Value) -> + type_error(Mode, "For ~p expected boolean, got ~p", [Name, Value]); +check_type(_, {_Name, binary}, Value) when is_binary(Value) -> true; -check_type({_Name, integer}, Value) when is_integer(Value) -> +check_type(Mode, {Name, binary}, Value) -> + type_error(Mode, "For ~p expected binary, got ~p", [Name, Value]); +check_type(_, {_Name, integer}, Value) when is_integer(Value) -> true; -check_type({_Name, [_] = LSpec}, Value) when is_list(Value) -> - check_type(LSpec, Value); -check_type(Spec, Value) when is_tuple(Spec) and not is_tuple(Value) -> - type_error("~p is not a tuple", [Value]); -check_type(Spec, Value) when is_tuple(Spec) -> - compare_tuples(Spec, Value); -check_type([_Spec], []) -> +check_type(Mode, {Name, integer}, Value) -> + type_error(Mode, "For ~p expected integer, got ~p", [Name, Value]); +check_type(Mode, {_Name, [_] = LSpec}, Value) when is_list(Value) -> + check_type(Mode, LSpec, Value); +check_type(Mode, Spec, Value) when is_tuple(Spec) and not is_tuple(Value) -> + type_error(Mode, "~p is not a tuple", [Value]); +check_type(Mode, Spec, Value) when is_tuple(Spec) -> + compare_tuples(Mode, Spec, Value); +check_type(_, [_Spec], []) -> true; -check_type([Spec], [H|T]) -> - check_type({none, Spec}, H), - check_type([Spec], T); -check_type([], [_|_]) -> +check_type(Mode, [Spec], [H|T]) -> + check_type(Mode, {none, Spec}, H), + check_type(Mode, [Spec], T); +check_type(_, [], [_|_]) -> true; -check_type([], []) -> +check_type(_, [], []) -> true; -check_type(Spec, Value) -> - type_error("Catch-all: ~p vs ~p", [Spec, Value]). +check_type(Mode, Spec, Value) -> + type_error(Mode, "Catch-all: ~p vs ~p", [Spec, Value]). -compare_tuples(Spec, Val) -> +compare_tuples(Mode, Spec, Val) -> Ssize = tuple_size(Spec), Vsize = tuple_size(Val), case Ssize of Vsize -> - compare_lists(tuple_to_list(Spec), tuple_to_list(Val)); + compare_lists(Mode, tuple_to_list(Spec), tuple_to_list(Val)); _ -> - type_error("Tuples of different size: ~p and ~p", [Spec, Val]) + type_error(Mode, "Tuples of different size: ~p and ~p", [Spec, Val]) end. -compare_lists([], []) -> +compare_lists(_, [], []) -> true; -compare_lists([S|Sp], [V|Val]) -> - check_type(S, V), - compare_lists(Sp, Val). +compare_lists(Mode, [S|Sp], [V|Val]) -> + check_type(Mode, S, V), + compare_lists(Mode, Sp, Val). -type_error(Fmt, V) -> - throw({type_error, io_lib:format(Fmt, V)}). +type_error(argument, Fmt, V) -> + throw({type_error, io_lib:format(Fmt, V)}); +type_error(return, Fmt, V) -> + throw({internal, io_lib:format(Fmt, V)}). check_identifiers(update, [], _) -> baddef(identifiers, empty); @@ -612,9 +619,9 @@ mapget(K, Map) -> V -> V catch error:{badkey, K} -> - type_error("Missing argument: ~p", [K]); + type_error(argument, "Missing argument: ~p", [K]); error:bad_key -> - type_error("Missing argument: ~p", [K]) + type_error(argument, "Missing argument: ~p", [K]) end. maps_to_list(Map, Args, Optargs) -> @@ -622,7 +629,7 @@ maps_to_list(Map, Args, Optargs) -> ALen = maps:size(Map), case SpecLen of ALen -> ok; - _ -> type_error("Invalid number of arguments: should be ~p, got ~p", [SpecLen, ALen]) + _ -> type_error(argument, "Invalid number of arguments: should be ~p, got ~p", [SpecLen, ALen]) end, [mapget(K, Map) || {K, _} <- Args] ++ [mapget(K, Map) || {K, _, _} <- Optargs]. @@ -676,6 +683,8 @@ check_caller(Caller, #mongoose_command{caller_pos = CallerPos}, Args) -> ACal -> ok; _ -> - throw(caller_jid_mismatch) + throw({denied, "Caller ids do not match"}) end. +sizeof(#{} = M) -> maps:size(M); +sizeof([_|_] = L) -> length(L). diff --git a/src/mongoose_credentials.erl b/src/mongoose_credentials.erl index 6a9f24662f..f9255743e3 100644 --- a/src/mongoose_credentials.erl +++ b/src/mongoose_credentials.erl @@ -22,13 +22,9 @@ %% Each backend may require different values to be present. extra :: [proplists:property()] }. --spec new(jid:server()) -> mongoose_credentials:t(). -new(Server) -> - case jid:nameprep(Server) of - error -> error(nameprep_failed, [Server]); - LServer when is_binary(LServer) -> - #mongoose_credentials{lserver = LServer} - end. +-spec new(jid:lserver()) -> mongoose_credentials:t(). +new(LServer) when is_binary(LServer) -> + #mongoose_credentials{lserver = LServer}. -spec lserver(t()) -> jid:lserver(). lserver(#mongoose_credentials{lserver = S}) -> S. diff --git a/src/muc_light/mod_muc_light.erl b/src/muc_light/mod_muc_light.erl index 2ac9d752e0..59a9fdd23c 100644 --- a/src/muc_light/mod_muc_light.erl +++ b/src/muc_light/mod_muc_light.erl @@ -319,8 +319,11 @@ add_rooms_to_roster(Acc, UserUS) -> Info = get_rooms_info(lists:sort(RoomList)), NewItems = lists:foldl( fun({{RoomU, RoomS}, RoomName, RoomVersion}, Items0) -> + JID = jid:make_noprep(RoomU, RoomS, <<>>), Item = #roster{ - jid = jid:make_noprep(RoomU, RoomS, <<>>), + usj = {RoomU, RoomS, jid:to_lower(JID)}, + us = {RoomU, RoomS}, + jid = jid:to_lower(JID), name = RoomName, subscription = to, groups = [?NS_MUC_LIGHT], diff --git a/src/muc_light/mod_muc_light_commands.erl b/src/muc_light/mod_muc_light_commands.erl index e1d3f75445..f6be9eed57 100644 --- a/src/muc_light/mod_muc_light_commands.erl +++ b/src/muc_light/mod_muc_light_commands.erl @@ -168,12 +168,18 @@ create_identifiable_room(Domain, Identifier, RoomName, Creator, Subject) -> invite_to_room(Domain, RoomName, Sender, Recipient0) -> Recipient1 = jid:binary_to_bare(Recipient0), - {ok, R, _Aff} = muc_light_room_name_to_jid_and_aff(jid:from_binary(Sender), RoomName, Domain), - S = jid:binary_to_bare(Sender), - Changes = query(?NS_MUC_LIGHT_AFFILIATIONS, - [affiliate(jid:to_binary(Recipient1), <<"member">>)]), - ejabberd_router:route(S, R, iq(jid:to_binary(S), jid:to_binary(R), - <<"set">>, [Changes])). + case muc_light_room_name_to_jid_and_aff(jid:from_binary(Sender), RoomName, Domain) of + {ok, R, _Aff} -> + S = jid:binary_to_bare(Sender), + Changes = query(?NS_MUC_LIGHT_AFFILIATIONS, + [affiliate(jid:to_binary(Recipient1), <<"member">>)]), + ejabberd_router:route(S, R, iq(jid:to_binary(S), jid:to_binary(R), + <<"set">>, [Changes])); + {error, given_user_does_not_occupy_any_room} -> + {error, forbidden, "given user does not occupy any room"}; + {error, not_found} -> + {error, not_found, "room does not exist"} + end. change_affiliation(Domain, RoomID, Sender, Recipient0, Affiliation) -> Recipient1 = jid:binary_to_bare(Recipient0), @@ -195,8 +201,8 @@ change_room_config(Domain, RoomID, RoomName, User, Subject) -> case mod_muc_light:change_room_config(UserUS, RoomID, MUCLightDomain, ConfigReq) of {ok, _RoomJID, _} -> ok; - {error, _Reason} = E -> - E + {error, Reason} -> + {error, internal, Reason} end. send_message(Domain, RoomName, Sender, Message) -> @@ -210,7 +216,7 @@ send_message(Domain, RoomName, Sender, Message) -> S = jid:binary_to_bare(Sender), case get_user_rooms(jid:to_lus(S), Domain) of [] -> - {error, given_user_does_not_occupy_any_room}; + {error, denied, "given user does not occupy any room"}; RoomJIDs when is_list(RoomJIDs) -> FindFun = find_room_and_user_aff_by_room_name(RoomName, jid:to_lus(S)), {ok, {RU, RS}, _Aff} = lists:foldl(FindFun, none, RoomJIDs), @@ -221,13 +227,14 @@ send_message(Domain, RoomName, Sender, Message) -> -spec delete_room(DomainName :: binary(), RoomName :: binary(), Owner :: binary()) -> - ok | {error, not_exists} | {error, not_allowed}. + ok | {error, atom(), term()}. delete_room(DomainName, RoomName, Owner) -> OwnerJID = jid:binary_to_bare(Owner), case muc_light_room_name_to_jid_and_aff(OwnerJID, RoomName, DomainName) of - {error, _} = Error -> Error; {ok, RoomJID, owner} -> mod_muc_light:delete_room(jid:to_lus(RoomJID)); - {ok, _, _} -> {error, not_allowed} + {ok, _, _} -> {error, denied, "you can not delete this room"}; + {error, given_user_does_not_occupy_any_room} -> {error, denied, "given user does not occupy this room"}; + {error, not_found} -> {error, not_found, "room does not exist"} end. %%-------------------------------------------------------------------- @@ -243,8 +250,10 @@ create_room(Domain, Identifier, RoomName, Creator, Subject) -> case mod_muc_light:try_to_create_room(C, MUCService, Config) of {ok, RoomUS, _} -> jid:to_binary(RoomUS); - {error, _Reason} = E -> - E + {error, exists} -> + {error, denied, "Room already exists"}; + {error, Reason} -> + {error, internal, Reason} end. make_room_config(Name, Subject) -> @@ -255,7 +264,7 @@ make_room_config(Name, Subject) -> -spec muc_light_room_name_to_jid_and_aff(UserJID :: jid:jid(), RoomName :: binary(), Domain :: jid:lserver()) -> - {ok, jid:jid(), aff()} | {error, given_user_does_not_occupy_any_room}. + {ok, jid:jid(), aff()} | {error, given_user_does_not_occupy_any_room} | {error, not_found}. muc_light_room_name_to_jid_and_aff(UserJID, RoomName, Domain) -> UserUS = jid:to_lus(UserJID), case get_user_rooms(UserUS, Domain) of @@ -263,9 +272,13 @@ muc_light_room_name_to_jid_and_aff(UserJID, RoomName, Domain) -> {error, given_user_does_not_occupy_any_room}; RoomUSs when is_list(RoomUSs) -> FindFun = find_room_and_user_aff_by_room_name(RoomName, UserUS), - {ok, {RU, RS}, UserAff} = lists:foldl(FindFun, none, RoomUSs), - true = is_subdomain(RS, Domain), - {ok, jid:make(RU, RS, <<>>), UserAff} + case lists:foldl(FindFun, none, RoomUSs) of + {ok, {RU, RS}, UserAff} -> + true = is_subdomain(RS, Domain), + {ok, jid:make(RU, RS, <<>>), UserAff}; + none -> + {error, not_found} + end end. -spec get_user_rooms(UserUS :: jid:simple_bare_jid(), Domain :: jid:lserver()) -> diff --git a/src/offline/mod_offline.erl b/src/offline/mod_offline.erl index 2e54308b6f..ef2b49d3d5 100644 --- a/src/offline/mod_offline.erl +++ b/src/offline/mod_offline.erl @@ -252,9 +252,7 @@ srv_name(Host) -> determine_amp_strategy(Strategy = #amp_strategy{deliver = [none]}, _FromJID, ToJID, _Packet, initial_check) -> - #jid{luser = LUser, lserver = LServer} = ToJID, - ShouldBeStored = ejabberd_auth:is_user_exists(LUser, LServer), - case ShouldBeStored of + case ejabberd_auth:does_user_exist(ToJID) of true -> Strategy#amp_strategy{deliver = [stored, none]}; false -> Strategy end; diff --git a/src/offline/mod_offline_stub.erl b/src/offline/mod_offline_stub.erl index 769146f8f5..6a9adade06 100644 --- a/src/offline/mod_offline_stub.erl +++ b/src/offline/mod_offline_stub.erl @@ -10,7 +10,7 @@ %%% if the message is not stored for delayed delivery %%% (i.e. as an "offline message"). %%% If the recipient exists (i.e. auth module returns `true` -%%% from `is_user_exists`) mod_mam stores the message, +%%% from `does_user_exist`) mod_mam stores the message, %%% but is still returned, %%% what is not compliant with the RFC. %%% diff --git a/src/rdbms/mongoose_rdbms_pgsql.erl b/src/rdbms/mongoose_rdbms_pgsql.erl index bf7624c968..a41f4ced85 100644 --- a/src/rdbms/mongoose_rdbms_pgsql.erl +++ b/src/rdbms/mongoose_rdbms_pgsql.erl @@ -65,7 +65,7 @@ prepare(Connection, Name, _Table, _Fields, Statement) -> BinName = [atom_to_binary(Name, latin1)], ReplacedStatement = replace_question_marks(Statement), case epgsql:parse(Connection, BinName, ReplacedStatement, []) of - {ok, _} -> {ok, BinName}; + {ok, _} -> epgsql:describe(Connection, statement, BinName); Error -> Error end. diff --git a/src/rdbms/rdbms_queries.erl b/src/rdbms/rdbms_queries.erl index 75f7cf6b99..7b230a259d 100644 --- a/src/rdbms/rdbms_queries.erl +++ b/src/rdbms/rdbms_queries.erl @@ -64,13 +64,6 @@ roster_subscribe/4, get_subscription/3, get_subscription_t/3, - set_private_data/4, - set_private_data_sql/3, - get_all_private_namespaces/2, - get_private_data/3, - multi_get_private_data/3, - multi_set_private_data/3, - del_user_private_storage/2, get_default_privacy_list/2, get_default_privacy_list_t/1, count_privacy_lists/1, @@ -626,61 +619,6 @@ get_subscription(LServer, Username, SJID) -> get_subscription_t(_LServer, Username, SJID) -> mongoose_rdbms:sql_query_t(q_get_subscription(Username, SJID)). -set_private_data(_LServer, Username, LXMLNS, SData) -> - update_t(<<"private_storage">>, - [<<"username">>, <<"namespace">>, <<"data">>], - [Username, LXMLNS, SData], - [<<"username=">>, mongoose_rdbms:use_escaped_string(Username), - <<" and namespace=">>, mongoose_rdbms:use_escaped_string(LXMLNS)]). - -set_private_data_sql(Username, LXMLNS, SData) -> - [[<<"delete from private_storage " - "where username=">>, mongoose_rdbms:use_escaped_string(Username), <<" and " - "namespace=">>, mongoose_rdbms:use_escaped_string(LXMLNS), ";"], - [<<"insert into private_storage(username, namespace, data) " - "values (">>, mongoose_rdbms:use_escaped_string(Username), ", ", - mongoose_rdbms:use_escaped_string(LXMLNS), ", ", - mongoose_rdbms:use_escaped_string(SData), ");"]]. - -get_all_private_namespaces(LServer, Username) -> - mongoose_rdbms:sql_query( - LServer, - [<<"select namespace from private_storage where username=">>, - mongoose_rdbms:use_escaped_string(Username), " ;"]). - -get_private_data(LServer, Username, LXMLNS) -> - mongoose_rdbms:sql_query( - LServer, - [<<"select data from private_storage " - "where username=">>, mongoose_rdbms:use_escaped_string(Username), <<" and " - "namespace=">>, mongoose_rdbms:use_escaped_string(LXMLNS)]). - -multi_get_private_data(LServer, Username, LXMLNSs) when length(LXMLNSs) > 0 -> - mongoose_rdbms:sql_query( - LServer, - [<<"select namespace, data from private_storage " - "where username=">>, mongoose_rdbms:use_escaped_string(Username), <<" and " - "namespace IN (">>, join_escaped(LXMLNSs), ");"]). - -%% set_private_data for multiple queries using MySQL's specific syntax. -multi_set_private_data(LServer, Username, SNS2XML) when length(SNS2XML) > 0 -> - Rows = [private_data_row(Username, NS, Data) || {NS, Data} <- SNS2XML], - mongoose_rdbms:sql_query( - LServer, - [<<"replace into private_storage (username, namespace, data) " - "values ">>, join(Rows, ", ")]). - -private_data_row(Username, NS, Data) -> - [<<"(">>, mongoose_rdbms:use_escaped_string(Username), - <<", ">>, mongoose_rdbms:use_escaped_string(NS), - <<", ">>, mongoose_rdbms:use_escaped_string(Data), <<")">>]. - -del_user_private_storage(LServer, Username) -> - mongoose_rdbms:sql_query( - LServer, - [<<"delete from private_storage where username=">>, - mongoose_rdbms:use_escaped_string(Username)]). - set_vcard(LServer, SLServer, SLUsername, SBDay, SCTRY, SEMail, SFN, SFamily, SGiven, diff --git a/src/sasl/cyrsasl_anonymous.erl b/src/sasl/cyrsasl_anonymous.erl index cbe10fad46..594391c6c9 100644 --- a/src/sasl/cyrsasl_anonymous.erl +++ b/src/sasl/cyrsasl_anonymous.erl @@ -49,7 +49,8 @@ mech_step(#state{creds = Creds}, _ClientIn) -> User = <<(mongoose_bin:gen_from_crypto())/binary, (integer_to_binary(erlang:unique_integer([positive])))/binary>>, %% Checks that the username is available - case ejabberd_auth:is_user_exists(User, mongoose_credentials:lserver(Creds)) of + JID = jid:make(User, mongoose_credentials:lserver(Creds), <<>>), + case ejabberd_auth:does_user_exist(JID) of true -> {error, <<"not-authorized">>}; false -> {ok, mongoose_credentials:extend(Creds, [{username, User}, {auth_module, ?MODULE}])} diff --git a/src/sasl/cyrsasl_digest.erl b/src/sasl/cyrsasl_digest.erl index d027d92bad..084c4944f1 100644 --- a/src/sasl/cyrsasl_digest.erl +++ b/src/sasl/cyrsasl_digest.erl @@ -109,7 +109,8 @@ authorize_if_uri_valid(State, KeyVals, Nonce) -> maybe_authorize(UserName, KeyVals, Nonce, State) -> AuthzId = xml:get_attr_s(<<"authzid">>, KeyVals), LServer = mongoose_credentials:lserver(State#state.creds), - case ejabberd_auth:get_passterm_with_authmodule(UserName, LServer) of + JID = jid:make(UserName, LServer, <<>>), + case ejabberd_auth:get_passterm_with_authmodule(JID) of {false, _} -> {error, <<"not-authorized">>, UserName}; {Passwd, AuthModule} -> diff --git a/src/sasl/cyrsasl_scram.erl b/src/sasl/cyrsasl_scram.erl index 368bbd666d..553a785806 100644 --- a/src/sasl/cyrsasl_scram.erl +++ b/src/sasl/cyrsasl_scram.erl @@ -91,7 +91,8 @@ mech_step(#state{step = 2} = State, ClientIn) -> Creds = State#state.creds, LServer = mongoose_credentials:lserver(Creds), Sha = State#state.sha, - case get_scram_attributes(UserName, LServer, Sha) of + JID = jid:make(UserName, LServer, <<>>), + case get_scram_attributes(JID, Sha) of {AuthModule, {StoredKey, ServerKey, Salt, IterationCount}} -> {NStart, _} = binary:match(ClientIn, <<"n=">>), ClientFirstMessageBare = @@ -186,10 +187,11 @@ unescape_username_attribute({EscapedUserName, ClientNonce}) -> UserName -> {ok, {UserName, ClientNonce}} end. --spec get_scram_attributes(jid:username(), jid:lserver(), sha()) -> scram_att() | error(). -get_scram_attributes(UserName, LServer, Sha) -> - case ejabberd_auth:get_passterm_with_authmodule(UserName, LServer) of +-spec get_scram_attributes(jid:jid(), sha()) -> scram_att() | error(). +get_scram_attributes(JID, Sha) -> + case ejabberd_auth:get_passterm_with_authmodule(JID) of {false, _} -> + {UserName, _} = jid:to_lus(JID), {error, <<"not-authorized">>, UserName}; {Params, AuthModule} -> {AuthModule, do_get_scram_attributes(Params, Sha)} diff --git a/src/system_metrics/mongoose_system_metrics_collector.erl b/src/system_metrics/mongoose_system_metrics_collector.erl index b4cd7bea1e..b827121dfd 100644 --- a/src/system_metrics/mongoose_system_metrics_collector.erl +++ b/src/system_metrics/mongoose_system_metrics_collector.erl @@ -33,7 +33,8 @@ report_getters() -> fun get_api/0, fun get_transport_mechanisms/0, fun get_tls_options/0, - fun get_outgoing_pools/0 + fun get_outgoing_pools/0, + fun get_config_type/0 ]. get_hosts_count() -> @@ -215,3 +216,12 @@ calculate_stanza_rate(PrevReport, NewCount) -> undefined -> Count; Total -> Count-Total end} || {Type, Count} <- NewCount]. + +get_config_type() -> + ConfigPath = ejabberd_config:get_config_path(), + ConfigType = case filename:extension(ConfigPath) of + ".toml" -> toml; + ".cfg" -> cfg; + _ -> unknown_config_type + end, + [#{report_name => cluster, key => config_type, value => ConfigType}]. diff --git a/test/auth_http_SUITE.erl b/test/auth_http_SUITE.erl index 3dee9ecd66..7635bef87d 100644 --- a/test/auth_http_SUITE.erl +++ b/test/auth_http_SUITE.erl @@ -45,7 +45,7 @@ all_tests() -> set_password, try_register, get_password, - is_user_exists, + does_user_exist, remove_user, supported_sasl_mechanisms ]. @@ -196,7 +196,7 @@ get_password(_Config) -> false = ejabberd_auth_http:get_password(<<"anakin">>, ?DOMAIN1), <<>> = ejabberd_auth_http:get_password_s(<<"anakin">>, ?DOMAIN1). -is_user_exists(_Config) -> +does_user_exist(_Config) -> true = ejabberd_auth_http:does_user_exist(<<"alice">>, ?DOMAIN1), false = ejabberd_auth_http:does_user_exist(<<"madhatter">>, ?DOMAIN1). diff --git a/test/auth_jwt_SUITE.erl b/test/auth_jwt_SUITE.erl index a3ab3af750..f63ace4ace 100644 --- a/test/auth_jwt_SUITE.erl +++ b/test/auth_jwt_SUITE.erl @@ -29,7 +29,7 @@ generic_tests() -> set_password, try_register, get_password, - is_user_exists, + does_user_exist, remove_user, get_vh_registered_users_number, get_vh_registered_users, @@ -122,7 +122,7 @@ get_password(_Config) -> false = ejabberd_auth_jwt:get_password(<<"anaking">>, ?DOMAIN1), <<>> = ejabberd_auth_jwt:get_password_s(<<"anakin">>, ?DOMAIN1). -is_user_exists(_Config) -> +does_user_exist(_Config) -> true = ejabberd_auth_jwt:does_user_exist(<<"madhatter">>, ?DOMAIN1). % remove_user/2,3 diff --git a/test/commands_SUITE.erl b/test/commands_SUITE.erl index a40c82859d..3e742ee975 100644 --- a/test/commands_SUITE.erl +++ b/test/commands_SUITE.erl @@ -177,7 +177,7 @@ new_type_checker(_C) -> ok. t_check_type(Spec, Value) -> - R = try mongoose_commands:check_type(Spec, Value) of + R = try mongoose_commands:check_type(argument, Spec, Value) of true -> true catch E -> @@ -254,8 +254,7 @@ new_execute(_C) -> %% backend func throws exception {error, internal, _} = mongoose_commands:execute(admin, command_one, [<<"throw">>]), %% backend func returns error - ExpError = term_to_binary({func_returned_error, byleco}), - {error, internal, ExpError} = mongoose_commands:execute(admin, command_one, [<<"error">>]), + {error, internal, <<"byleco">>} = mongoose_commands:execute(admin, command_one, [<<"error">>]), % user executes his command {ok, <<"bzzzz">>} = mongoose_commands:execute(ujid(), command_foruser, #{msg => <<"bzzzz">>}), % a caller arg @@ -546,7 +545,7 @@ cmd_one(<<"throw">>) -> C = 12, <<"A", C/binary>>; cmd_one(<<"error">>) -> - {error, byleco}; + {error, internal, <<"byleco">>}; cmd_one(M) -> M. diff --git a/test/commands_backend_SUITE.erl b/test/commands_backend_SUITE.erl index 1b0e870425..9102a48d25 100644 --- a/test/commands_backend_SUITE.erl +++ b/test/commands_backend_SUITE.erl @@ -671,7 +671,7 @@ do_request(Path, Method, Body, {headers, Headers}) -> request(Path, Method, BodyData, {{_User, _Pass} = Auth, Authorized}) -> setup(client_module()), - meck:expect(ejabberd_auth, check_password, fun(_, _, _) -> Authorized end), + meck:expect(ejabberd_auth, check_password, fun(_, _) -> Authorized end), Body = maybe_add_body(BodyData), AuthHeader = maybe_add_auth_header(Auth), AcceptHeader = maybe_add_accepted_headers(Method), diff --git a/test/config_parser_SUITE.erl b/test/config_parser_SUITE.erl index e44ca136b5..69176b2b90 100644 --- a/test/config_parser_SUITE.erl +++ b/test/config_parser_SUITE.erl @@ -1387,14 +1387,12 @@ mod_bosh_cases() -> ?_eqf(M(max_wait, infinity), T(<<"max_wait">>, <<"infinity">>)), ?_eqf(M(server_acks, true), T(<<"server_acks">>, true)), ?_eqf(M(server_acks, false), T(<<"server_acks">>, false)), - ?_eqf(M(backend, mnesia), T(<<"backend">>, <<"mnesia">>)), ?errf(T(<<"inactivity">>, -1)), ?errf(T(<<"inactivity">>, <<"10">>)), ?errf(T(<<"inactivity">>, <<"inactivity">>)), ?errf(T(<<"max_wait">>, <<"10">>)), ?errf(T(<<"max_wait">>, -1)), - ?errf(T(<<"server_acks">>, -1)), - ?errf(T(<<"backend">>, <<"devnull">>))]. + ?errf(T(<<"server_acks">>, -1))]. mod_caps(_Config) -> run_multi(mod_caps_cases()). @@ -1528,8 +1526,6 @@ mod_inbox(_Config) -> T(<<"remove_on_kicked">>, true)), ?eqf(modopts(mod_inbox, [{remove_on_kicked, false}]), T(<<"remove_on_kicked">>, false)), - ?eqf(modopts(mod_inbox, [{backend, rdbms}]), - T(<<"backend">>, <<"rdbms">>)), ?errf(T(<<"reset_markers">>, 1)), ?errf(T(<<"reset_markers">>, <<"test">>)), ?errf(T(<<"reset_markers">>, [<<"test">>])), @@ -1540,7 +1536,6 @@ mod_inbox(_Config) -> ?errf(T(<<"aff_changes">>, <<"true">>)), ?errf(T(<<"remove_on_kicked">>, 1)), ?errf(T(<<"remove_on_kicked">>, <<"true">>)), - ?errf(T(<<"backend">>, <<"devnull">>)), check_iqdisc(mod_inbox). mod_global_distrib(_Config) -> @@ -1563,6 +1558,7 @@ mod_global_distrib(_Config) -> BounceOpts = [ {max_retries, 3}, {resend_after_ms, 300} ], RedisOpts = [ {expire_after, 120}, {pool, global_distrib}, {refresh_after, 60} ], TTOpts = #{ + <<"enabled">> => true, <<"certfile">> => <<"/dev/null">>, <<"cacertfile">> => <<"/dev/null">>, <<"dhfile">> => <<"/dev/null">>, @@ -1631,12 +1627,12 @@ mod_global_distrib(_Config) -> set_pl(connections, set_pl(tls_opts, false, ConnOpts), MBase)), - T(Base#{<<"connections">> => TConnOpts#{<<"tls">> => false}})), + T(Base#{<<"connections">> => TConnOpts#{<<"tls">> => #{<<"enabled">> => false}}})), ?eqf(modopts(mod_global_distrib, set_pl(connections, set_pl(tls_opts, false, ConnOpts), MBase)), - T(Base#{<<"connections">> => TConnOpts#{<<"tls">> => false}})), + T(Base#{<<"connections">> => TConnOpts#{<<"tls">> => #{<<"enabled">> => false}}})), %% Connection opts ?errf(T(Base#{<<"connections">> => TConnOpts#{ <<"tls">> =>TTOpts#{<<"certfile">> => <<"/this/does/not/exist">>}}})), diff --git a/test/config_parser_SUITE_data/modules.options b/test/config_parser_SUITE_data/modules.options index 56ab1e5402..e53c5f9292 100644 --- a/test/config_parser_SUITE_data/modules.options +++ b/test/config_parser_SUITE_data/modules.options @@ -122,7 +122,6 @@ [{inactivity,20}, {max_wait,infinity}, {server_acks,true}, - {backend,mnesia}, {maxpause,120}]}, {mod_push_service_mongoosepush, [{pool_name,mongoose_push_http}, @@ -217,8 +216,7 @@ {top_link,{"/","Home"}}, {cssfile,<<"path/to/css/file">>}]}, {mod_inbox, - [{backend,rdbms}, - {reset_markers,[displayed]}, + [{reset_markers,[displayed]}, {aff_changes,true}, {remove_on_kicked,true}, {groupchat,[muclight]}]}, @@ -395,7 +393,6 @@ [{inactivity,20}, {max_wait,infinity}, {server_acks,true}, - {backend,mnesia}, {maxpause,120}]}, {mod_push_service_mongoosepush, [{pool_name,mongoose_push_http}, @@ -490,8 +487,7 @@ {top_link,{"/","Home"}}, {cssfile,<<"path/to/css/file">>}]}, {mod_inbox, - [{backend,rdbms}, - {reset_markers,[displayed]}, + [{reset_markers,[displayed]}, {aff_changes,true}, {remove_on_kicked,true}, {groupchat,[muclight]}]}, diff --git a/test/config_parser_SUITE_data/modules.toml b/test/config_parser_SUITE_data/modules.toml index 735c7440f2..49d4bbec87 100644 --- a/test/config_parser_SUITE_data/modules.toml +++ b/test/config_parser_SUITE_data/modules.toml @@ -18,7 +18,6 @@ inactivity = 20 max_wait = "infinity" server_acks = true - backend = "mnesia" maxpause = 120 [modules.mod_caps] @@ -103,7 +102,6 @@ s3.secret_access_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" [modules.mod_inbox] - backend = "rdbms" reset_markers = ["displayed"] aff_changes = true remove_on_kicked = true @@ -114,6 +112,7 @@ local_host = "datacenter1.example.com" connections.endpoints = [{host = "172.16.0.2", port = 5555}] connections.advertised_endpoints = [{host = "172.16.0.2", port = 5555}] + connections.tls.enabled = true connections.tls.certfile = "priv/dc1.pem" connections.tls.cacertfile = "priv/ca.pem" connections.connections_per_endpoint = 30 diff --git a/test/ejabberd_admin_SUITE.erl b/test/ejabberd_admin_SUITE.erl index 5f75825549..a50b5cd82b 100644 --- a/test/ejabberd_admin_SUITE.erl +++ b/test/ejabberd_admin_SUITE.erl @@ -3,6 +3,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). +-include("jlib.hrl"). -import(ejabberd_helper, [data/2]). @@ -24,21 +25,17 @@ end_per_suite(_Config) -> init_per_group(_, Config) -> meck:new(ejabberd_auth, [no_link]), meck:expect(ejabberd_auth, try_register, - fun(<<"existing_user">>, _, _) -> {error, exists}; - (<<"null_password_user">>, _, _) -> {error, null_password}; - (_, <<"not_allowed_domain">>, _) -> {error, not_allowed}; - (<<"invalid_jid_user">>, _, _) -> {error, invalid_jid}; - (_, _, _) -> ok + fun(#jid{user = <<"existing_user">>}, _) -> {error, exists}; + (#jid{user = <<"null_password_user">>}, _) -> {error, null_password}; + (#jid{server = <<"not_allowed_domain">>}, _) -> {error, not_allowed}; + (#jid{user = <<"invalid_jid_user">>}, _) -> {error, invalid_jid}; + (_, _) -> ok end), - Config; - -init_per_group(_, Config) -> Config. + Config. end_per_group(_, _Config) -> meck:unload(ejabberd_auth), - ok; - -end_per_group(_, _) -> ok. + ok. init_per_testcase(_TestCase, Config) -> Config. @@ -73,7 +70,7 @@ import_users_from_valid_csv_with_quoted_fields(Config) -> ?assertEqual([{ok, <<"username,with,commas">>}], Result). -import_from_invalid_csv(Config) -> +import_from_invalid_csv(_Config) -> % given NonExistingPath = "", % then diff --git a/test/roster_SUITE.erl b/test/roster_SUITE.erl index 6fe3bb99e5..28e8e6b451 100644 --- a/test/roster_SUITE.erl +++ b/test/roster_SUITE.erl @@ -67,7 +67,7 @@ end_per_testcase(_TC, C) -> roster_old(_C) -> R1 = get_roster_old(), ?assertEqual(length(R1), 0), - mod_roster:set_items(a(), host(), addbob_stanza()), + mod_roster:set_items(alice_jid(), addbob_stanza()), assert_state_old(none, none), subscription(out, subscribe), assert_state_old(none, out), @@ -76,7 +76,7 @@ roster_old(_C) -> roster_old_with_filter(_C) -> R1 = get_roster_old(), ?assertEqual(0, length(R1)), - mod_roster:set_items(a(), host(), addbob_stanza()), + mod_roster:set_items(alice_jid(), addbob_stanza()), assert_state_old(none, none), subscription(in, subscribe), R2 = get_roster_old(), @@ -86,29 +86,29 @@ roster_old_with_filter(_C) -> ok. roster_new(_C) -> - R1 = mod_roster:get_roster_entry(a(), host(), bob()), + R1 = mod_roster:get_roster_entry(alice_jid(), bob()), ?assertEqual(does_not_exist, R1), - mod_roster:set_items(a(), host(), addbob_stanza()), + mod_roster:set_items(alice_jid(), addbob_stanza()), assert_state_old(none, none), ct:pal("get_roster_old(): ~p", [get_roster_old()]), - R2 = mod_roster:get_roster_entry(a(), host(), bob()), + R2 = mod_roster:get_roster_entry(alice_jid(), bob()), ?assertMatch(#roster{}, R2), % is not guaranteed to contain full info - R3 = mod_roster:get_roster_entry(a(), host(), bob(), full), + R3 = mod_roster:get_roster_entry(alice_jid(), bob(), full), assert_state(R3, none, none, [<<"friends">>]), subscription(out, subscribe), - R4 = mod_roster:get_roster_entry(a(), host(), bob(), full), + R4 = mod_roster:get_roster_entry(alice_jid(), bob(), full), assert_state(R4, none, out, [<<"friends">>]). roster_case_insensitive(_C) -> - mod_roster:set_items(a(), host(), addbob_stanza()), + mod_roster:set_items(alice_jid(), addbob_stanza()), R1 = get_roster_old(), ?assertEqual(1, length(R1)), R2 = get_roster_old(ae()), ?assertEqual(1, length(R2)), - R3 = mod_roster:get_roster_entry(a(), host(), bob(), full), + R3 = mod_roster:get_roster_entry(alice_jid(), bob(), full), assert_state(R3, none, none, [<<"friends">>]), - R3 = mod_roster:get_roster_entry(ae(), host(), bob(), full), + R3 = mod_roster:get_roster_entry(alicE_jid(), bob(), full), assert_state(R3, none, none, [<<"friends">>]), ok. @@ -123,11 +123,10 @@ assert_state(Rentry, Subscription, Ask, Groups) -> ?assertEqual(Groups, Rentry#roster.groups). subscription(Direction, Type) -> - LBob = jid:to_lower(jid:from_binary(bob())), + BobJID = jid:from_binary(bob()), TFun = fun() -> mod_roster:process_subscription_transaction(Direction, - a(), - host(), - LBob, + alice_jid(), + BobJID, Type, <<"">>) end, @@ -166,6 +165,12 @@ delete_ets() -> catch ets:delete(mongoose_services), ok. +alice_jid() -> + jid:make(a(), host(), <<>>). + +alicE_jid() -> + jid:make(ae(), host(), <<>>). + a() -> <<"alice">>. ae() -> <<"alicE">>. diff --git a/tools/circle-build-and-push-docker.sh b/tools/circle-build-and-push-docker.sh index 73ff218ba4..0d33d803f0 100755 --- a/tools/circle-build-and-push-docker.sh +++ b/tools/circle-build-and-push-docker.sh @@ -33,7 +33,7 @@ IMAGE_TAG=${DOCKERHUB_REPO}/mongooseim:${DOCKERHUB_TAG} git clone https://github.com/esl/mongooseim-docker.git cd mongooseim-docker -git checkout aeeeef12ea64a00aa72da88472aff8c4f0e049a6 +git checkout 318e1ee6582e7473303a2cd0b4baca0c9c09a1be cp ../${MONGOOSE_TGZ} member diff --git a/tools/pkg/scripts/deb/build_package.sh b/tools/pkg/scripts/deb/build_package.sh index 23745cf178..c82adf82d8 100755 --- a/tools/pkg/scripts/deb/build_package.sh +++ b/tools/pkg/scripts/deb/build_package.sh @@ -15,11 +15,13 @@ apt-get update rm -rf /usr/lib/erlang/man/man3/cerff.3.gz /usr/lib/erlang/man/man3/cerfl.3.gz /usr/lib/erlang/man/man3/cerfcl.3.gz /usr/lib/erlang/man/man3/cerfcf.3.gz /usr/lib/erlang/man/man3/cerfcf.3.gz /usr/lib/erlang/man/man1/x86_64-linux-gnu-gcov-tool.1.gz /usr/lib/erlang/man/man1/ocamlbuild.native.1.gz /usr/lib/erlang/man/man1/gcov-tool.1.gz /usr/lib/erlang/man/man1/ocamlbuild.byte.1.gz +make clean sed -i '1 s/^.*$/\#\!\/bin\/bash/' tools/install ./tools/configure with-all user=mongooseim prefix="" system=yes sed -i 's#PREFIX=""#PREFIX="mongooseim"#' configure.out source configure.out export GIT_SSL_NO_VERIFY=1 + make install cp -r ../deb/debian mongooseim/DEBIAN mkdir -p mongooseim/etc/systemd/system/ diff --git a/tools/pkg/scripts/rpm/mongooseim.spec b/tools/pkg/scripts/rpm/mongooseim.spec index 1a087a5448..a4c4f4db46 100644 --- a/tools/pkg/scripts/rpm/mongooseim.spec +++ b/tools/pkg/scripts/rpm/mongooseim.spec @@ -36,6 +36,7 @@ scale in need of more capacity (by just adding a box/VM). cp %{SOURCE0} . %install +make clean ./tools/configure with-all user=root prefix=/ system=yes sed -i 's#PREFIX=\"/\"#PREFIX=\"%{buildroot}\"#' configure.out make install