diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ejabberd_auth.erl | 28 | ||||
-rw-r--r-- | src/ejabberd_http_ws.erl | 7 | ||||
-rw-r--r-- | src/ejabberd_option.erl | 24 | ||||
-rw-r--r-- | src/ejabberd_options.erl | 9 | ||||
-rw-r--r-- | src/ejabberd_options_doc.erl | 19 | ||||
-rw-r--r-- | src/ejabberd_s2s_out.erl | 20 | ||||
-rw-r--r-- | src/ejabberd_sql.erl | 21 | ||||
-rw-r--r-- | src/ejabberd_sql_sup.erl | 2 | ||||
-rw-r--r-- | src/ejabberd_stun.erl | 4 | ||||
-rw-r--r-- | src/ejabberd_web_admin.erl | 14 | ||||
-rw-r--r-- | src/ejabberd_websocket.erl | 10 | ||||
-rw-r--r-- | src/ext_mod.erl | 2 | ||||
-rw-r--r-- | src/mod_configure.erl | 4 | ||||
-rw-r--r-- | src/mod_mam.erl | 12 | ||||
-rw-r--r-- | src/mod_mam_sql.erl | 2 | ||||
-rw-r--r-- | src/mod_muc.erl | 14 | ||||
-rw-r--r-- | src/mod_muc_admin.erl | 225 | ||||
-rw-r--r-- | src/mod_muc_room.erl | 7 | ||||
-rw-r--r-- | src/mod_offline.erl | 2 | ||||
-rw-r--r-- | src/mod_push.erl | 10 | ||||
-rw-r--r-- | src/mod_register.erl | 24 | ||||
-rw-r--r-- | src/mod_roster.erl | 55 | ||||
-rw-r--r-- | src/str.erl | 10 |
23 files changed, 366 insertions, 159 deletions
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index 9623dc96e..112d677be 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -605,6 +605,7 @@ db_get_password(User, Server, Mod) -> false when UseCache -> case ets_cache:lookup(cache_tab(Mod), {User, Server}) of {ok, exists} -> error; + not_found -> error; Other -> Other end; false -> @@ -621,20 +622,29 @@ db_user_exists(User, Server, Mod) -> case db_get_password(User, Server, Mod) of {ok, _} -> true; + not_found -> + false; error -> case {Mod:store_type(Server), use_cache(Mod, Server)} of {external, true} -> - case ets_cache:lookup( - cache_tab(Mod), {User, Server}, - fun() -> - case Mod:user_exists(User, Server) of - {CacheTag, true} -> {CacheTag, {ok, exists}}; - {CacheTag, false} -> {CacheTag, error}; - {_, {error, _}} = Err -> Err - end - end) of + Val = case ets_cache:lookup(cache_tab(Mod), {User, Server}, error) of + error -> + ets_cache:update(cache_tab(Mod), {User, Server}, {ok, exists}, + fun() -> + case Mod:user_exists(User, Server) of + {CacheTag, true} -> {CacheTag, {ok, exists}}; + {CacheTag, false} -> {CacheTag, not_found}; + {_, {error, _}} = Err -> Err + end + end); + Other -> + Other + end, + case Val of {ok, _} -> true; + not_found -> + false; error -> false; {error, _} = Err -> diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl index d8c58618e..35b38e3e9 100644 --- a/src/ejabberd_http_ws.erl +++ b/src/ejabberd_http_ws.erl @@ -364,5 +364,8 @@ parsed_items(List) -> -spec route_text(pid(), binary()) -> ok. route_text(Pid, Data) -> - Pid ! {text, Data}, - ok. + Pid ! {text_with_reply, Data, self()}, + receive + {text_reply, Pid} -> + ok + end. diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 030001ed8..e5b47f01f 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -90,6 +90,8 @@ -export([oom_killer/0]). -export([oom_queue/0]). -export([oom_watermark/0]). +-export([outgoing_s2s_ipv4_address/0, outgoing_s2s_ipv4_address/1]). +-export([outgoing_s2s_ipv6_address/0, outgoing_s2s_ipv6_address/1]). -export([outgoing_s2s_families/0, outgoing_s2s_families/1]). -export([outgoing_s2s_port/0, outgoing_s2s_port/1]). -export([outgoing_s2s_timeout/0, outgoing_s2s_timeout/1]). @@ -137,6 +139,7 @@ -export([sql_database/0, sql_database/1]). -export([sql_keepalive_interval/0, sql_keepalive_interval/1]). -export([sql_password/0, sql_password/1]). +-export([sql_odbc_driver/0, sql_odbc_driver/1]). -export([sql_pool_size/0, sql_pool_size/1]). -export([sql_port/0, sql_port/1]). -export([sql_prepared_statements/0, sql_prepared_statements/1]). @@ -666,6 +669,20 @@ outgoing_s2s_families() -> outgoing_s2s_families(Host) -> ejabberd_config:get_option({outgoing_s2s_families, Host}). +-spec outgoing_s2s_ipv4_address() -> inet:ip4_address(). +outgoing_s2s_ipv4_address() -> + outgoing_s2s_ipv4_address(global). +-spec outgoing_s2s_ipv4_address(global | binary()) -> inet:ip4_address(). +outgoing_s2s_ipv4_address(Host) -> + ejabberd_config:get_option({outgoing_s2s_ipv4_address, Host}). + +-spec outgoing_s2s_ipv6_address() -> inet:ip6_address(). +outgoing_s2s_ipv6_address() -> + outgoing_s2s_ipv6_address(global). +-spec outgoing_s2s_ipv6_address(global | binary()) -> inet:ip6_address(). +outgoing_s2s_ipv6_address(Host) -> + ejabberd_config:get_option({outgoing_s2s_ipv6_address, Host}). + -spec outgoing_s2s_port() -> 1..1114111. outgoing_s2s_port() -> outgoing_s2s_port(global). @@ -928,6 +945,13 @@ sql_password() -> sql_password(Host) -> ejabberd_config:get_option({sql_password, Host}). +-spec sql_odbc_driver() -> binary(). +sql_odbc_driver() -> + sql_odbc_driver(global). +-spec sql_odbc_driver(global | binary()) -> binary(). +sql_odbc_driver(Host) -> + ejabberd_config:get_option({sql_odbc_driver, Host}). + -spec sql_pool_size() -> pos_integer(). sql_pool_size() -> sql_pool_size(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index e8b8cb890..a03071dc1 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -271,6 +271,10 @@ opt_type(outgoing_s2s_families) -> (ipv6) -> inet6 end, L) end); +opt_type(outgoing_s2s_ipv4_address) -> + econf:ipv4(); +opt_type(outgoing_s2s_ipv6_address) -> + econf:ipv6(); opt_type(outgoing_s2s_port) -> econf:port(); opt_type(outgoing_s2s_timeout) -> @@ -371,6 +375,8 @@ opt_type(sql_keepalive_interval) -> econf:timeout(second); opt_type(sql_password) -> econf:binary(); +opt_type(sql_odbc_driver) -> + econf:binary(); opt_type(sql_pool_size) -> econf:pos_int(); opt_type(sql_port) -> @@ -587,6 +593,8 @@ options() -> {oom_queue, 10000}, {oom_watermark, 80}, {outgoing_s2s_families, [inet, inet6]}, + {outgoing_s2s_ipv4_address, undefined}, + {outgoing_s2s_ipv6_address, undefined}, {outgoing_s2s_port, 5269}, {outgoing_s2s_timeout, timer:seconds(10)}, {pam_service, <<"ejabberd">>}, @@ -645,6 +653,7 @@ options() -> {sql_database, undefined}, {sql_keepalive_interval, undefined}, {sql_password, <<"">>}, + {sql_odbc_driver, <<"libtdsodbc.so">>}, % default is FreeTDS driver {sql_pool_size, fun(Host) -> case ejabberd_config:get_option({sql_type, Host}) of diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index 99f02a9d4..b48a4b415 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -920,6 +920,18 @@ doc() -> ?T("Specify which address families to try, in what order. " "The default is '[ipv4, ipv6]' which means it first tries " "connecting with IPv4, if that fails it tries using IPv6.")}}, + {outgoing_s2s_ipv4_address, + #{value => "Address", + desc => + ?T("Specify the IPv4 address that will be used when establishing " + "an outgoing S2S IPv4 connection, for example \"127.0.0.1\". " + "The default value is 'undefined'.")}}, + {outgoing_s2s_ipv6_address, + #{value => "Address", + desc => + ?T("Specify the IPv6 address that will be used when establishing " + "an outgoing S2S IPv6 connection, for example " + "\"::FFFF:127.0.0.1\". The default value is 'undefined'.")}}, {outgoing_s2s_port, #{value => "1..65535", desc => @@ -1219,6 +1231,13 @@ doc() -> ?T("An interval to make a dummy SQL request to keep alive the " "connections to the database. There is no default value, so no " "keepalive requests are made.")}}, + {sql_odbc_driver, + #{value => "Path", + desc => + ?T("Path to the ODBC driver to use to connect to a Microsoft SQL " + "Server database. This option is only valid if the 'sql_type' " + "option is set to 'mssql'. " + "The default value is: 'libtdsodbc.so'")}}, {sql_password, #{value => ?T("Password"), desc => diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index 3904c7fad..ae298b51c 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -24,7 +24,7 @@ %% xmpp_stream_out callbacks -export([tls_options/1, tls_required/1, tls_verify/1, tls_enabled/1, - connect_timeout/1, address_families/1, default_port/1, + connect_options/3, connect_timeout/1, address_families/1, default_port/1, dns_retries/1, dns_timeout/1, handle_auth_success/2, handle_auth_failure/3, handle_packet/2, handle_stream_end/2, handle_stream_downgraded/2, @@ -187,6 +187,20 @@ tls_verify(#{server_host := ServerHost} = State) -> tls_enabled(#{server_host := ServerHost}) -> ejabberd_s2s:tls_enabled(ServerHost). +connect_options(Addr, Opts, #{server_host := ServerHost}) -> + BindAddr = case get_addr_type(Addr) of + inet -> + ejabberd_option:outgoing_s2s_ipv4_address(ServerHost); + inet6 -> + ejabberd_option:outgoing_s2s_ipv6_address(ServerHost) + end, + case BindAddr of + undefined -> + Opts; + _ -> + [{ip, BindAddr} | Opts] + end. + connect_timeout(#{server_host := ServerHost}) -> ejabberd_option:outgoing_s2s_timeout(ServerHost). @@ -318,6 +332,10 @@ code_change(_OldVsn, State, _Extra) -> %%%=================================================================== %%% Internal functions %%%=================================================================== +-spec get_addr_type(inet:ip_address()) -> inet:address_family(). +get_addr_type({_, _, _, _}) -> inet; +get_addr_type({_, _, _, _, _, _, _, _}) -> inet6. + -spec resend_queue(state()) -> state(). resend_queue(State) -> queue_fold( diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl index 8b952f4e0..0fd338222 100644 --- a/src/ejabberd_sql.erl +++ b/src/ejabberd_sql.erl @@ -52,7 +52,7 @@ encode_term/1, decode_term/1, odbcinst_config/0, - init_mssql/0, + init_mssql/1, keep_alive/2, to_list/2, to_array/2]). @@ -349,11 +349,11 @@ init([Host]) -> connecting(connect, #state{host = Host} = State) -> ConnectRes = case db_opts(Host) of - [mysql | Args] -> apply(fun mysql_connect/8, Args); - [pgsql | Args] -> apply(fun pgsql_connect/8, Args); - [sqlite | Args] -> apply(fun sqlite_connect/1, Args); - [mssql | Args] -> apply(fun odbc_connect/2, Args); - [odbc | Args] -> apply(fun odbc_connect/2, Args) + [mysql | Args] -> apply(fun mysql_connect/8, Args); + [pgsql | Args] -> apply(fun pgsql_connect/8, Args); + [sqlite | Args] -> apply(fun sqlite_connect/1, Args); + [mssql | Args] -> apply(fun odbc_connect/2, Args); + [odbc | Args] -> apply(fun odbc_connect/2, Args) end, case ConnectRes of {ok, Ref} -> @@ -1107,7 +1107,7 @@ db_opts(Host) -> SSLOpts = get_ssl_opts(Transport, Host), case Type of mssql -> - [mssql, <<"DRIVER=FreeTDS;SERVER=", Server/binary, ";UID=", User/binary, + [mssql, <<"DRIVER=ODBC;SERVER=", Server/binary, ";UID=", User/binary, ";DATABASE=", DB/binary ,";PWD=", Pass/binary, ";PORT=", (integer_to_binary(Port))/binary ,";CLIENT_CHARSET=UTF-8;">>, Timeout]; _ -> @@ -1151,9 +1151,10 @@ get_ssl_opts(ssl, Host) -> get_ssl_opts(tcp, _) -> []. -init_mssql() -> - ODBCINST = io_lib:fwrite("[FreeTDS]~n" - "Driver = libtdsodbc.so~n", []), +init_mssql(Host) -> + Driver = ejabberd_option:sql_odbc_driver(Host), + ODBCINST = io_lib:fwrite("[ODBC]~n" + "Driver = ~s~n", [Driver]), ?DEBUG("~ts:~n~ts", [odbcinst_config(), ODBCINST]), case filelib:ensure_dir(odbcinst_config()) of ok -> diff --git a/src/ejabberd_sql_sup.erl b/src/ejabberd_sql_sup.erl index ee37c7e61..6a3e979de 100644 --- a/src/ejabberd_sql_sup.erl +++ b/src/ejabberd_sql_sup.erl @@ -96,7 +96,7 @@ init([Host]) -> sqlite -> check_sqlite_db(Host); mssql -> - ejabberd_sql:init_mssql(); + ejabberd_sql:init_mssql(Host); _ -> ok end, diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl index bda3e581e..415ab1048 100644 --- a/src/ejabberd_stun.erl +++ b/src/ejabberd_stun.erl @@ -212,8 +212,8 @@ init_logger() -> ok. -else. init_logger() -> - case logger:add_primary_filter(stun, {fun ?MODULE:stun_filter/2, - ?STUN_MAX_LOG_LEVEL}) of + case logger:add_primary_filter(ejabberd_stun, {fun ?MODULE:stun_filter/2, + ?STUN_MAX_LOG_LEVEL}) of ok -> ok; {error, {already_exist, _}} -> diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl index 3511b32df..271a5a37c 100644 --- a/src/ejabberd_web_admin.erl +++ b/src/ejabberd_web_admin.erl @@ -930,7 +930,7 @@ user_info(User, Server, Query, Lang) -> end; _ -> translate:translate(Lang, ?T("Online")) end, - [?XC(<<"h1">>, (str:format(translate:translate(Lang, ?T("User ~ts")), + [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("User ~ts"), [us_to_list(US)])))] ++ case Res of @@ -1087,7 +1087,7 @@ get_node(global, Node, [], Query, Lang) -> Base = get_base_path(global, Node, 2), MenuItems2 = make_menu_items(global, Node, Base, Lang), [?XC(<<"h1">>, - (str:format(translate:translate(Lang, ?T("Node ~p")), [Node])))] + (str:translate_and_format(Lang, ?T("Node ~p"), [Node])))] ++ case Res of ok -> [?XREST(?T("Submitted"))]; @@ -1109,7 +1109,7 @@ get_node(global, Node, [], Query, Lang) -> get_node(Host, Node, [], _Query, Lang) -> Base = get_base_path(Host, Node, 4), MenuItems2 = make_menu_items(Host, Node, Base, Lang), - [?XC(<<"h1">>, (str:format(translate:translate(Lang, ?T("Node ~p")), [Node]))), + [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("Node ~p"), [Node]))), ?XE(<<"ul">>, MenuItems2)]; get_node(global, Node, [<<"db">>], Query, Lang) -> case ejabberd_cluster:call(Node, mnesia, system_info, [tables]) of @@ -1165,7 +1165,7 @@ get_node(global, Node, [<<"db">>], Query, Lang) -> end, STables), [?XC(<<"h1">>, - (str:format(translate:translate(Lang, ?T("Database Tables at ~p")), + (str:translate_and_format(Lang, ?T("Database Tables at ~p"), [Node])) )] ++ @@ -1203,7 +1203,7 @@ get_node(global, Node, [<<"backup">>], Query, Lang) -> [?XRES(<<(translate:translate(Lang, ?T("Error")))/binary, ": ", ((str:format("~p", [Error])))/binary>>)] end, - [?XC(<<"h1">>, (str:format(translate:translate(Lang, ?T("Backup of ~p")), [Node])))] + [?XC(<<"h1">>, (str:translate_and_format(Lang, ?T("Backup of ~p"), [Node])))] ++ ResS ++ [?XCT(<<"p">>, @@ -1357,7 +1357,7 @@ get_node(global, Node, [<<"stats">>], _Query, Lang) -> TransactionsLogged = ejabberd_cluster:call(Node, mnesia, system_info, [transaction_log_writes]), [?XC(<<"h1">>, - (str:format(translate:translate(Lang, ?T("Statistics of ~p")), [Node]))), + (str:translate_and_format(Lang, ?T("Statistics of ~p"), [Node]))), ?XAE(<<"table">>, [], [?XE(<<"tbody">>, [?XE(<<"tr">>, @@ -1425,7 +1425,7 @@ get_node(global, Node, [<<"update">>], Query, Lang) -> FmtLowLevelScript = (?XC(<<"pre">>, (str:format("~p", [LowLevelScript])))), [?XC(<<"h1">>, - (str:format(translate:translate(Lang, ?T("Update ~p")), [Node])))] + (str:translate_and_format(Lang, ?T("Update ~p"), [Node])))] ++ case Res of ok -> [?XREST(?T("Submitted"))]; diff --git a/src/ejabberd_websocket.erl b/src/ejabberd_websocket.erl index 3b4f03bdf..01a7aa6a1 100644 --- a/src/ejabberd_websocket.erl +++ b/src/ejabberd_websocket.erl @@ -225,6 +225,16 @@ ws_loop(FrameInfo, Socket, WsHandleLoopPid, SocketMode, Shaper) -> end, erlang:demonitor(Ref), websocket_close(Socket, WsHandleLoopPid, SocketMode, Code); + {text_with_reply, Data, Sender} -> + SocketMode:send(Socket, encode_frame(Data, 1)), + Sender ! {text_reply, self()}, + ws_loop(FrameInfo, Socket, WsHandleLoopPid, + SocketMode, Shaper); + {data_with_reply, Data, Sender} -> + SocketMode:send(Socket, encode_frame(Data, 2)), + Sender ! {data_reply, self()}, + ws_loop(FrameInfo, Socket, WsHandleLoopPid, + SocketMode, Shaper); {text, Data} -> SocketMode:send(Socket, encode_frame(Data, 1)), ws_loop(FrameInfo, Socket, WsHandleLoopPid, diff --git a/src/ext_mod.erl b/src/ext_mod.erl index e4feeaee6..4f0773ceb 100644 --- a/src/ext_mod.erl +++ b/src/ext_mod.erl @@ -326,7 +326,7 @@ geturl(Url) -> _ -> ok end, - User = case getenv("PROXY_USER", "", [4]) of + User = case getenv("PROXY_USER", "", ":") of [U, Pass] -> [{proxy_auth, {U, Pass}}]; _ -> [] end, diff --git a/src/mod_configure.erl b/src/mod_configure.erl index 71c411979..adc97f114 100644 --- a/src/mod_configure.erl +++ b/src/mod_configure.erl @@ -663,7 +663,7 @@ get_outgoing_s2s(Host, Lang) -> Host == FH orelse str:suffix(DotHost, FH)], lists:map( fun (T) -> - Name = str:format(tr(Lang, ?T("To ~ts")),[T]), + Name = str:translate_and_format(Lang, ?T("To ~ts"),[T]), #disco_item{jid = jid:make(Host), node = <<"outgoing s2s/", T/binary>>, name = Name} @@ -675,7 +675,7 @@ get_outgoing_s2s(Host, Lang, To) -> lists:map( fun ({F, _T}) -> Node = <<"outgoing s2s/", To/binary, "/", F/binary>>, - Name = str:format(tr(Lang, ?T("From ~ts")), [F]), + Name = str:translate_and_format(Lang, ?T("From ~ts"), [F]), #disco_item{jid = jid:make(Host), node = Node, name = Name} end, lists:keysort( diff --git a/src/mod_mam.erl b/src/mod_mam.erl index a2ff3d4ff..9ea04217f 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -687,11 +687,11 @@ process_iq(LServer, #iq{from = #jid{luser = LUser}, lang = Lang, #mam_query{rsm = #rsm_set{index = I}} when is_integer(I) -> Txt = ?T("Unsupported <index/> element"), xmpp:make_error(IQ, xmpp:err_feature_not_implemented(Txt, Lang)); - #mam_query{rsm = RSM, xmlns = NS} -> + #mam_query{rsm = RSM, flippage = FlipPage, xmlns = NS} -> case parse_query(SubEl, Lang) of {ok, Query} -> NewRSM = limit_max(RSM, NS), - select_and_send(LServer, Query, NewRSM, IQ, MsgType); + select_and_send(LServer, Query, NewRSM, FlipPage, IQ, MsgType); {error, Err} -> xmpp:make_error(IQ, Err) end @@ -1017,7 +1017,7 @@ maybe_activate_mam(LUser, LServer) -> ok end. -select_and_send(LServer, Query, RSM, #iq{from = From, to = To} = IQ, MsgType) -> +select_and_send(LServer, Query, RSM, FlipPage, #iq{from = From, to = To} = IQ, MsgType) -> Ret = case MsgType of chat -> select(LServer, From, From, Query, RSM, MsgType); @@ -1027,7 +1027,11 @@ select_and_send(LServer, Query, RSM, #iq{from = From, to = To} = IQ, MsgType) -> case Ret of {Msgs, IsComplete, Count} -> SortedMsgs = lists:keysort(2, Msgs), - send(SortedMsgs, Count, IsComplete, IQ); + SortedMsgs2 = case FlipPage of + true -> lists:reverse(SortedMsgs); + false -> SortedMsgs + end, + send(SortedMsgs2, Count, IsComplete, IQ); {error, _} -> Txt = ?T("Database failure"), Err = xmpp:err_internal_server_error(Txt, IQ#iq.lang), diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl index dd2c6a8b2..90b2b8330 100644 --- a/src/mod_mam_sql.erl +++ b/src/mod_mam_sql.erl @@ -256,7 +256,7 @@ do_select_query(LServer, JidRequestor, #jid{luser = LUser} = JidArchive, RSM, {Res, true} end, MucState = #state{config = #config{anonymous = true}}, - JidArchiveS = jid:encode(JidArchive), + JidArchiveS = jid:encode(jid:remove_resource(JidArchive)), {lists:flatmap( fun([TS, XML, PeerBin, Kind, Nick]) -> case make_archive_el(JidArchiveS, TS, XML, PeerBin, Kind, Nick, diff --git a/src/mod_muc.erl b/src/mod_muc.erl index c30d3a190..28a0f505d 100644 --- a/src/mod_muc.erl +++ b/src/mod_muc.erl @@ -68,7 +68,7 @@ can_use_nick/4, get_subscribed_rooms/2, procname/2, - route/1]). + route/1, unhibernate_room/3]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, @@ -526,6 +526,18 @@ extract_password(#iq{} = IQ) -> false end. +-spec unhibernate_room(binary(), binary(), binary()) -> {ok, pid()} | error. +unhibernate_room(ServerHost, Host, Room) -> + RMod = gen_mod:ram_db_mod(ServerHost, ?MODULE), + case RMod:find_online_room(ServerHost, Room, Host) of + error -> + case load_room(RMod, Host, ServerHost, Room) of + {ok, _} = R -> R; + _ -> error + end; + {ok, _} = R2 -> R2 + end. + -spec route_to_room(stanza(), binary()) -> ok. route_to_room(Packet, ServerHost) -> From = xmpp:get_from(Packet), diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl index 2d37d3fb6..eee2ada0e 100644 --- a/src/mod_muc_admin.erl +++ b/src/mod_muc_admin.erl @@ -264,7 +264,11 @@ get_commands_spec() -> #ejabberd_commands{name = send_direct_invitation, tags = [muc_room], desc = "Send a direct invitation to several destinations", - longdesc = "Password and Message can also be: none. Users JIDs are separated with : ", + longdesc = "Since ejabberd 20.10, this command is " + "asynchronous: the API call may return before the " + "server has send all the invitations.\n\n" + "Password and Message can also be: none. " + "Users JIDs are separated with : ", module = ?MODULE, function = send_direct_invitation, args_desc = ["Room name", "MUC service", "Password, or none", "Reason text, or none", "Users JIDs separated with : characters"], @@ -401,12 +405,25 @@ build_summary_room(Name, Host, Pid) -> }. muc_register_nick(Nick, FromBinary, Service) -> - ServerHost = get_room_serverhost(Service), - From = jid:decode(FromBinary), - Lang = <<"en">>, - case mod_muc:iq_set_register_info(ServerHost, Service, From, Nick, Lang) of - {result, undefined} -> ok; - E -> E + try {get_room_serverhost(Service), jid:decode(FromBinary)} of + {ServerHost, From} -> + Lang = <<"en">>, + case mod_muc:iq_set_register_info(ServerHost, Service, From, Nick, Lang) of + {result, undefined} -> ok; + {error, #stanza_error{reason = 'conflict'}} -> + throw({error, "Nick already registered"}); + {error, _} -> + throw({error, "Database error"}) + end + catch + error:{invalid_domain, _} -> + throw({error, "Invalid 'service'"}); + error:{unregistered_route, _} -> + throw({error, "Invalid 'service'"}); + error:{bad_jid, _} -> + throw({error, "Invalid 'jid'"}); + _ -> + throw({error, "Internal error"}) end. muc_unregister_nick(FromBinary, Service) -> @@ -628,53 +645,58 @@ create_room(Name1, Host1, ServerHost) -> create_room_with_opts(Name1, Host1, ServerHost, []). create_room_with_opts(Name1, Host1, ServerHost1, CustomRoomOpts) -> - true = (error /= (Name = jid:nodeprep(Name1))), - true = (error /= (Host = jid:nodeprep(Host1))), - true = (error /= (ServerHost = jid:nodeprep(ServerHost1))), - - %% Get the default room options from the muc configuration - DefRoomOpts = mod_muc_opt:default_room_options(ServerHost), - %% Change default room options as required - FormattedRoomOpts = [format_room_option(Opt, Val) || {Opt, Val}<-CustomRoomOpts], - RoomOpts = lists:ukeymerge(1, - lists:keysort(1, FormattedRoomOpts), - lists:keysort(1, DefRoomOpts)), - - %% Store the room on the server, it is not started yet though at this point - case lists:keyfind(persistent, 1, RoomOpts) of - {persistent, true} -> - mod_muc:store_room(ServerHost, Host, Name, RoomOpts); - _ -> - ok - end, - - %% Get all remaining mod_muc parameters that might be utilized - Access = mod_muc_opt:access(ServerHost), - AcCreate = mod_muc_opt:access_create(ServerHost), - AcAdmin = mod_muc_opt:access_admin(ServerHost), - AcPer = mod_muc_opt:access_persistent(ServerHost), - AcMam = mod_muc_opt:access_mam(ServerHost), - HistorySize = mod_muc_opt:history_size(ServerHost), - RoomShaper = mod_muc_opt:room_shaper(ServerHost), - QueueType = mod_muc_opt:queue_type(ServerHost), - - %% If the room does not exist yet in the muc_online_room - case mod_muc:find_online_room(Name, Host) of - error -> - %% Start the room - {ok, Pid} = mod_muc_room:start( - Host, - ServerHost, - {Access, AcCreate, AcAdmin, AcPer, AcMam}, - Name, - HistorySize, - RoomShaper, - RoomOpts, - QueueType), - mod_muc:register_online_room(Name, Host, Pid), - ok; - {ok, _} -> - error + case {jid:nodeprep(Name1), jid:nodeprep(Host1), jid:nodeprep(ServerHost1)} of + {error, _, _} -> + throw({error, "Invalid 'name'"}); + {_, error, _} -> + throw({error, "Invalid 'host'"}); + {_, _, error} -> + throw({error, "Invalid 'serverhost'"}); + {Name, Host, ServerHost} -> + %% Get the default room options from the muc configuration + DefRoomOpts = mod_muc_opt:default_room_options(ServerHost), + %% Change default room options as required + FormattedRoomOpts = [format_room_option(Opt, Val) || {Opt, Val}<-CustomRoomOpts], + RoomOpts = lists:ukeymerge(1, + lists:keysort(1, FormattedRoomOpts), + lists:keysort(1, DefRoomOpts)), + + + %% Get all remaining mod_muc parameters that might be utilized + Access = mod_muc_opt:access(ServerHost), + AcCreate = mod_muc_opt:access_create(ServerHost), + AcAdmin = mod_muc_opt:access_admin(ServerHost), + AcPer = mod_muc_opt:access_persistent(ServerHost), + AcMam = mod_muc_opt:access_mam(ServerHost), + HistorySize = mod_muc_opt:history_size(ServerHost), + RoomShaper = mod_muc_opt:room_shaper(ServerHost), + QueueType = mod_muc_opt:queue_type(ServerHost), + + %% If the room does not exist yet in the muc_online_room + case get_room_pid(Name, Host) of + room_not_found -> + %% Store the room on the server, it is not started yet though at this point + case lists:keyfind(persistent, 1, RoomOpts) of + {persistent, true} -> + mod_muc:store_room(ServerHost, Host, Name, RoomOpts); + _ -> + ok + end, + %% Start the room + {ok, Pid} = mod_muc_room:start( + Host, + ServerHost, + {Access, AcCreate, AcAdmin, AcPer, AcMam}, + Name, + HistorySize, + RoomShaper, + RoomOpts, + QueueType), + mod_muc:register_online_room(Name, Host, Pid), + ok; + _ -> + throw({error, "Room already exists"}) + end end. %% Create the room only in the database. @@ -689,11 +711,14 @@ muc_create_room(ServerHost, {Name, Host, _}, DefRoomOpts) -> %% If the room has participants, they are not notified that the room was destroyed; %% they will notice when they try to chat and receive an error that the room doesn't exist. destroy_room(Name, Service) -> - case mod_muc:find_online_room(Name, Service) of - {ok, Pid} -> - mod_muc_room:destroy(Pid); - error -> - error + case get_room_pid(Name, Service) of + room_not_found -> + throw({error, "Room doesn't exists"}); + invalid_service -> + throw({error, "Invalid 'service'"}); + Pid -> + mod_muc_room:destroy(Pid), + ok end. destroy_room({N, H, SH}) -> @@ -714,7 +739,7 @@ destroy_rooms_file(Filename) -> Rooms = read_rooms(F, RJID, []), file:close(F), [destroy_room(A) || A <- Rooms], - ok. + ok. read_rooms(_F, eof, L) -> L; @@ -758,7 +783,7 @@ create_rooms_file(Filename) -> %% Read the default room options defined for the first virtual host DefRoomOpts = mod_muc_opt:default_room_options(ejabberd_config:get_myname()), [muc_create_room(ejabberd_config:get_myname(), A, DefRoomOpts) || A <- Rooms], - ok. + ok. %%--------------------------------- @@ -904,8 +929,8 @@ act_on_room(_Method, list, _) -> get_room_occupants(Room, Host) -> case get_room_pid(Room, Host) of - room_not_found -> throw({error, room_not_found}); - Pid -> get_room_occupants(Pid) + Pid when is_pid(Pid) -> get_room_occupants(Pid); + _ -> throw({error, room_not_found}) end. get_room_occupants(Pid) -> @@ -920,11 +945,11 @@ get_room_occupants(Pid) -> get_room_occupants_number(Room, Host) -> case get_room_pid(Room, Host) of - room_not_found -> - throw({error, room_not_found}); - Pid -> + Pid when is_pid(Pid )-> S = get_room_state(Pid), - maps:size(S#state.users) + maps:size(S#state.users); + _ -> + throw({error, room_not_found}) end. %%---------------------------- @@ -933,13 +958,16 @@ get_room_occupants_number(Room, Host) -> %% http://xmpp.org/extensions/xep-0249.html send_direct_invitation(RoomName, RoomService, Password, Reason, UsersString) -> - RoomJid = jid:make(RoomName, RoomService), - XmlEl = build_invitation(Password, Reason, RoomJid), - Users = get_users_to_invite(RoomJid, UsersString), - [send_direct_invitation(RoomJid, UserJid, XmlEl) - || UserJid <- Users], - timer:sleep(1000), - ok. + case jid:make(RoomName, RoomService) of + error -> + throw({error, "Invalid 'roomname' or 'service'"}); + RoomJid -> + XmlEl = build_invitation(Password, Reason, RoomJid), + Users = get_users_to_invite(RoomJid, UsersString), + [send_direct_invitation(RoomJid, UserJid, XmlEl) + || UserJid <- Users], + ok + end. get_users_to_invite(RoomJid, UsersString) -> UsersStrings = binary:split(UsersString, <<":">>, [global]), @@ -993,7 +1021,9 @@ send_direct_invitation(FromJid, UserJid, Msg) -> change_room_option(Name, Service, OptionString, ValueString) -> case get_room_pid(Name, Service) of room_not_found -> - room_not_found; + throw({error, "Room not found"}); + invalid_service -> + throw({error, "Invalid 'service'"}); Pid -> {Option, Value} = format_room_option(OptionString, ValueString), change_room_option(Pid, Option, Value) @@ -1033,12 +1063,21 @@ format_room_option(OptionString, ValueString) -> {Option, Value}. %% @doc Get the Pid of an existing MUC room, or 'room_not_found'. +-spec get_room_pid(binary(), binary()) -> {ok, pid()} | room_not_found | invalid_service. get_room_pid(Name, Service) -> - case mod_muc:find_online_room(Name, Service) of - error -> - room_not_found; - {ok, Pid} -> - Pid + try get_room_serverhost(Service) of + ServerHost -> + case mod_muc:unhibernate_room(ServerHost, Service, Name) of + error -> + room_not_found; + {ok, Pid} -> + Pid + end + catch + error:{invalid_domain, _} -> + invalid_service; + error:{unregistered_route, _} -> + invalid_service end. %% It is required to put explicitly all the options because @@ -1081,8 +1120,8 @@ change_option(Option, Value, Config) -> get_room_options(Name, Service) -> case get_room_pid(Name, Service) of - room_not_found -> []; - Pid -> get_room_options(Pid) + Pid when is_pid(Pid) -> get_room_options(Pid); + _ -> [] end. get_room_options(Pid) -> @@ -1106,8 +1145,8 @@ get_options(Config) -> %% [{JID::string(), Domain::string(), Role::string(), Reason::string()}] %% @doc Get the affiliations of the room Name@Service. get_room_affiliations(Name, Service) -> - case mod_muc:find_online_room(Name, Service) of - {ok, Pid} -> + case get_room_pid(Name, Service) of + Pid when is_pid(Pid) -> %% Get the PID of the online room, then request its state {ok, StateData} = mod_muc_room:get_state(Pid), Affiliations = maps:to_list(StateData#state.affiliations), @@ -1117,7 +1156,7 @@ get_room_affiliations(Name, Service) -> ({{Uname, Domain, _Res}, Aff}) when is_atom(Aff)-> {Uname, Domain, Aff, <<>>} end, Affiliations); - error -> + _ -> throw({error, "The room does not exist."}) end. @@ -1130,13 +1169,13 @@ get_room_affiliations(Name, Service) -> %% @doc Get affiliation of a user in the room Name@Service. get_room_affiliation(Name, Service, JID) -> - case mod_muc:find_online_room(Name, Service) of - {ok, Pid} -> + case get_room_pid(Name, Service) of + Pid when is_pid(Pid) -> %% Get the PID of the online room, then request its state {ok, StateData} = mod_muc_room:get_state(Pid), UserJID = jid:decode(JID), mod_muc_room:get_affiliation(UserJID, StateData); - error -> + _ -> throw({error, "The room does not exist."}) end. @@ -1154,14 +1193,16 @@ get_room_affiliation(Name, Service, JID) -> %% In any other case the action will be to create the affiliation. set_room_affiliation(Name, Service, JID, AffiliationString) -> Affiliation = misc:binary_to_atom(AffiliationString), - case mod_muc:find_online_room(Name, Service) of - {ok, Pid} -> + case get_room_pid(Name, Service) of + Pid when is_pid(Pid) -> %% Get the PID for the online room so we can get the state of the room {ok, StateData} = mod_muc_room:change_item(Pid, jid:decode(JID), affiliation, Affiliation, <<"">>), mod_muc:store_room(StateData#state.server_host, StateData#state.host, StateData#state.room, make_opts(StateData)), ok; - error -> - error + room_not_found -> + throw({error, "Room doesn't exists"}); + invalid_service -> + throw({error, "Invalid 'service'"}) end. %%% diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl index 2a6bc4838..dbabbc0d4 100644 --- a/src/mod_muc_room.erl +++ b/src/mod_muc_room.erl @@ -1444,7 +1444,8 @@ get_error_text(#stanza_error{text = Txt}) -> make_reason(Packet, From, StateData, Reason1) -> #user{nick = FromNick} = maps:get(jid:tolower(From), StateData#state.users), Condition = get_error_condition(xmpp:get_error(Packet)), - str:format(Reason1, [FromNick, Condition]). + Reason2 = unicode:characters_to_list(Reason1), + str:format(Reason2, [FromNick, Condition]). -spec expulse_participant(stanza(), jid(), state(), binary()) -> state(). @@ -3493,8 +3494,8 @@ get_config(Lang, StateData, From) -> DefaultRoomMaxUsers = get_default_room_maxusers(StateData), Config = StateData#state.config, MaxUsersRoom = get_max_users(StateData), - Title = str:format( - translate:translate(Lang, ?T("Configuration of room ~s")), + Title = str:translate_and_format( + Lang, ?T("Configuration of room ~s"), [jid:encode(StateData#state.jid)]), Fs = [{roomname, Config#config.title}, {roomdesc, Config#config.description}, diff --git a/src/mod_offline.erl b/src/mod_offline.erl index 1cc790de0..9746036e5 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -988,7 +988,7 @@ user_queue(User, Server, Query, Lang) -> end, Hdrs = get_messages_subset(User, Server, HdrsAll), FMsgs = format_user_queue(Hdrs), - PageTitle = str:format(translate:translate(Lang, ?T("~ts's Offline Messages Queue")), [us_to_list(US)]), + PageTitle = str:translate_and_format(Lang, ?T("~ts's Offline Messages Queue"), [us_to_list(US)]), (?H1GL(PageTitle, <<"modules/#mod-offline">>, <<"mod_offline">>)) ++ [?XREST(?T("Submitted"))] ++ [?XAE(<<"form">>, diff --git a/src/mod_push.erl b/src/mod_push.erl index 42f0b7a43..1b35b9e0f 100644 --- a/src/mod_push.erl +++ b/src/mod_push.erl @@ -119,7 +119,9 @@ reload(Host, NewOpts, OldOpts) -> NewMod:init(Host, NewOpts); true -> ok - end. + end, + init_cache(NewMod, Host, NewOpts), + ok. -spec depends(binary(), gen_mod:opts()) -> [{module(), hard | soft}]. depends(_Host, _Opts) -> @@ -241,9 +243,9 @@ delete_old_sessions(Days) -> [] -> ?INFO_MSG("Deleted push sessions older than ~B days", [Days]), ok; - [NotOk | _] -> - ?ERROR_MSG("Error while deleting old push sessions: ~p", [NotOk]), - NotOk + [{error, Reason} | _] -> + ?ERROR_MSG("Error while deleting old push sessions: ~p", [Reason]), + Reason end. %%-------------------------------------------------------------------- diff --git a/src/mod_register.erl b/src/mod_register.erl index 32dd2fc1c..cf125db78 100644 --- a/src/mod_register.erl +++ b/src/mod_register.erl @@ -114,13 +114,12 @@ process_iq(#iq{from = From, to = To} = IQ, Source) -> end, Server = To#jid.lserver, Access = mod_register_opt:access_remove(Server), - Remove = case acl:match_rule(Server, Access, From) of - deny -> deny; - allow when From#jid.lserver /= Server -> - deny; - allow -> - check_access(From#jid.luser, Server, Source) - end, + Remove = case {acl:match_rule(Server, Access, From), From#jid.lserver} of + {allow, Server} -> + allow; + {_, _} -> + deny + end, process_iq(IQ, Source, IsCaptchaEnabled, Remove == allow). process_iq(#iq{type = set, lang = Lang, @@ -223,8 +222,7 @@ process_iq(#iq{type = get, from = From, to = To, id = ID, lang = Lang} = IQ, "with this server")), URL = mod_register_opt:redirect_url(Server), if (URL /= undefined) and not IsRegistered -> - Txt = translate:translate(Lang, ?T("To register, visit ~s")), - Desc = str:format(Txt, [URL]), + Desc = str:translate_and_format(Lang, ?T("To register, visit ~s"), [URL]), xmpp:make_iq_result( IQ, #register{instructions = Desc, sub_els = [#oob_x{url = URL}]}); @@ -636,10 +634,10 @@ mod_doc() -> [{access, #{value => ?T("AccessName"), desc => - ?T("Specify rules to restrict what usernames can be registered and " - "unregistered. If a rule returns 'deny' on the requested username, " - "registration and unregistration of that user name is denied. " - "There are no restrictions by default.")}}, + ?T("Specify rules to restrict what usernames can be registered. " + "If a rule returns 'deny' on the requested username, " + "registration of that user name is denied. There are no " + "restrictions by default.")}}, {access_from, #{value => ?T("AccessName"), desc => diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 6d7ae46e2..438e6d3e0 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -708,6 +708,30 @@ in_state_change(both, none, subscribe) -> none; in_state_change(both, none, subscribed) -> none; in_state_change(both, none, unsubscribe) -> {to, none}; in_state_change(both, none, unsubscribed) -> + {from, none}; +% Invalid states that can occurs from roster modification from API +in_state_change(to, out, subscribe) -> {to, in}; +in_state_change(to, out, subscribed) -> none; +in_state_change(to, out, unsubscribe) -> none; +in_state_change(to, out, unsubscribed) -> {none, none}; +in_state_change(to, both, subscribe) -> none; +in_state_change(to, both, subscribed) -> none; +in_state_change(to, both, unsubscribe) -> {to, none}; +in_state_change(to, both, unsubscribed) -> {none, in}; +in_state_change(from, in, subscribe) -> none; +in_state_change(from, in, subscribed) -> {both, none}; +in_state_change(from, in, unsubscribe) -> + {none, none}; +in_state_change(from, in, unsubscribed) -> none; +in_state_change(from, both, subscribe) -> none; +in_state_change(from, both, subscribed) -> {both, none}; +in_state_change(from, both, unsubscribe) -> {none, out}; +in_state_change(from, both, unsubscribed) -> + {from, none}; +in_state_change(both, _, subscribe) -> none; +in_state_change(both, _, subscribed) -> none; +in_state_change(both, _, unsubscribe) -> {to, none}; +in_state_change(both, _, unsubscribed) -> {from, none}. out_state_change(none, none, subscribe) -> {none, out}; @@ -715,8 +739,7 @@ out_state_change(none, none, subscribed) -> none; out_state_change(none, none, unsubscribe) -> none; out_state_change(none, none, unsubscribed) -> none; out_state_change(none, out, subscribe) -> - {none, - out}; %% We need to resend query (RFC3921, section 9.2) + {none, out}; %% We need to resend query (RFC3921, section 9.2) out_state_change(none, out, subscribed) -> none; out_state_change(none, out, unsubscribe) -> {none, none}; @@ -755,6 +778,32 @@ out_state_change(both, none, subscribed) -> none; out_state_change(both, none, unsubscribe) -> {from, none}; out_state_change(both, none, unsubscribed) -> + {to, none}; +% Invalid states that can occurs from roster modification from API +out_state_change(to, out, subscribe) -> none; +out_state_change(to, out, subscribed) -> {both, none}; +out_state_change(to, out, unsubscribe) -> {none, none}; +out_state_change(to, out, unsubscribed) -> none; +out_state_change(to, both, subscribe) -> none; +out_state_change(to, both, subscribed) -> {both, none}; +out_state_change(to, both, unsubscribe) -> {none, in}; +out_state_change(to, both, unsubscribed) -> {to, none}; +out_state_change(from, in, subscribe) -> {from, out}; +out_state_change(from, in, subscribed) -> none; +out_state_change(from, in, unsubscribe) -> none; +out_state_change(from, in, unsubscribed) -> + {none, none}; +out_state_change(from, both, subscribe) -> none; +out_state_change(from, both, subscribed) -> none; +out_state_change(from, both, unsubscribe) -> + {from, none}; +out_state_change(from, both, unsubscribed) -> + {none, out}; +out_state_change(both, _, subscribe) -> none; +out_state_change(both, _, subscribed) -> none; +out_state_change(both, _, unsubscribe) -> + {from, none}; +out_state_change(both, _, unsubscribed) -> {to, none}. in_auto_reply(from, none, subscribe) -> subscribed; @@ -1004,7 +1053,7 @@ user_roster(User, Server, Query, Lang) -> end, SItems)))])] end, - PageTitle = str:format(translate:translate(Lang, ?T("Roster of ~ts")), [us_to_list(US)]), + PageTitle = str:translate_and_format(Lang, ?T("Roster of ~ts"), [us_to_list(US)]), (?H1GL(PageTitle, <<"modules/#mod-roster">>, <<"mod_roster">>)) ++ case Res of diff --git a/src/str.erl b/src/str.erl index 16e167664..c3206a261 100644 --- a/src/str.erl +++ b/src/str.erl @@ -64,10 +64,11 @@ to_float/1, prefix/2, suffix/2, - format/2, + format/2, to_integer/1, sha/1, - to_hexlist/1]). + to_hexlist/1, + translate_and_format/3]). %%%=================================================================== %%% API @@ -288,6 +289,11 @@ suffix(B1, B2) -> format(Format, Args) -> unicode:characters_to_binary(io_lib:format(Format, Args)). +-spec translate_and_format(binary(), binary(), list()) -> binary(). + +translate_and_format(Lang, Format, Args) -> + format(unicode:characters_to_list(translate:translate(Lang, Format)), Args). + -spec sha(iodata()) -> binary(). |