aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/ejabberd.exs2
-rwxr-xr-xejabberdctl.template4
-rw-r--r--rebar.config24
-rw-r--r--src/ejabberd_auth.erl28
-rw-r--r--src/ejabberd_http_ws.erl7
-rw-r--r--src/ejabberd_option.erl24
-rw-r--r--src/ejabberd_options.erl9
-rw-r--r--src/ejabberd_options_doc.erl19
-rw-r--r--src/ejabberd_s2s_out.erl20
-rw-r--r--src/ejabberd_sql.erl21
-rw-r--r--src/ejabberd_sql_sup.erl2
-rw-r--r--src/ejabberd_stun.erl4
-rw-r--r--src/ejabberd_web_admin.erl14
-rw-r--r--src/ejabberd_websocket.erl10
-rw-r--r--src/ext_mod.erl2
-rw-r--r--src/mod_configure.erl4
-rw-r--r--src/mod_mam.erl12
-rw-r--r--src/mod_mam_sql.erl2
-rw-r--r--src/mod_muc.erl14
-rw-r--r--src/mod_muc_admin.erl225
-rw-r--r--src/mod_muc_room.erl7
-rw-r--r--src/mod_offline.erl2
-rw-r--r--src/mod_push.erl10
-rw-r--r--src/mod_register.erl24
-rw-r--r--src/mod_roster.erl55
-rw-r--r--src/str.erl10
26 files changed, 379 insertions, 176 deletions
diff --git a/config/ejabberd.exs b/config/ejabberd.exs
index f5f21cd5b..462822183 100644
--- a/config/ejabberd.exs
+++ b/config/ejabberd.exs
@@ -4,9 +4,7 @@ defmodule Ejabberd.ConfigFile do
def start do
[loglevel: 4,
log_rotate_size: 10485760,
- log_rotate_date: "",
log_rotate_count: 1,
- log_rate_limit: 100,
auth_method: :internal,
max_fsm_queue: 1000,
language: "en",
diff --git a/ejabberdctl.template b/ejabberdctl.template
index 68eaefa7b..256a40c7a 100755
--- a/ejabberdctl.template
+++ b/ejabberdctl.template
@@ -87,10 +87,8 @@ ERL_INETRC="$ETC_DIR"/inetrc
# define ejabberd parameters
EJABBERD_OPTS="$EJABBERD_OPTS\
-$(sed '/^log_rate_limit/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\
$(sed '/^log_rotate_size/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\
-$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")\
-$(sed '/^log_rotate_date/!d;s/:[ \t]*\(.[^ ]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")"
+$(sed '/^log_rotate_count/!d;s/:[ \t]*\([0-9]*\).*/ \1/;s/^/ /' "$EJABBERD_CONFIG_PATH")"
[ -n "$EJABBERD_OPTS" ] && EJABBERD_OPTS="-ejabberd $EJABBERD_OPTS"
EJABBERD_OPTS="-mnesia dir \"$SPOOL_DIR\" $MNESIA_OPTIONS $EJABBERD_OPTS -s ejabberd"
diff --git a/rebar.config b/rebar.config
index e0058adef..b40192da0 100644
--- a/rebar.config
+++ b/rebar.config
@@ -20,20 +20,20 @@
{deps, [{base64url, ".*", {git, "https://github.com/dvv/base64url.git", {tag, "1.0.1"}}},
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.25"}}},
- {eimp, ".*", {git, "https://github.com/processone/eimp", {tag, "1.0.17"}}},
+ {eimp, ".*", {git, "https://github.com/processone/eimp", "321c1248d8b5795212cb7f10ae6627ca3a34e793"}},
{if_var_true, elixir,
{elixir, ".*", {git, "https://github.com/elixir-lang/elixir", {tag, "v1.4.4"}}}},
{if_var_true, pam,
- {epam, ".*", {git, "https://github.com/processone/epam", {tag, "1.0.9"}}}},
+ {epam, ".*", {git, "https://github.com/processone/epam", {tag, "1.0.10"}}}},
{if_var_true, redis,
{eredis, ".*", {git, "https://github.com/wooga/eredis", {tag, "v1.0.8"}}}},
{if_var_true, sip,
- {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.37"}}}},
+ {esip, ".*", {git, "https://github.com/processone/esip", {tag, "1.0.38"}}}},
{if_var_true, zlib,
- {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.8"}}}},
- {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.8"}}},
- {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.43"}}},
- {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.27"}}},
+ {ezlib, ".*", {git, "https://github.com/processone/ezlib", {tag, "1.0.9"}}}},
+ {fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.1.9"}}},
+ {fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.44"}}},
+ {fast_yaml, ".*", {git, "https://github.com/processone/fast_yaml", {tag, "1.0.28"}}},
{idna, ".*", {git, "https://github.com/benoitc/erlang-idna", {tag, "6.0.0"}}},
{jiffy, ".*", {git, "https://github.com/davisp/jiffy", {tag, "1.0.5"}}},
{jose, ".*", {git, "https://github.com/potatosalad/erlang-jose", {tag, "1.9.0"}}},
@@ -41,7 +41,7 @@
{if_var_true, tools,
{luerl, ".*", {git, "https://github.com/rvirding/luerl", {tag, "v0.3"}}}},
{mqtree, ".*", {git, "https://github.com/processone/mqtree", {tag, "1.0.10"}}},
- {p1_acme, ".*", {git, "https://github.com/processone/p1_acme.git", {tag, "1.0.8"}}},
+ {p1_acme, ".*", {git, "https://github.com/processone/p1_acme.git", {tag, "1.0.9"}}},
{if_var_true, mysql,
{p1_mysql, ".*", {git, "https://github.com/processone/p1_mysql", {tag, "1.0.16"}}}},
{p1_oauth2, ".*", {git, "https://github.com/processone/p1_oauth2", {tag, "0.6.7"}}},
@@ -54,11 +54,11 @@
{rebar_elixir_plugin, ".*", {git, "https://github.com/processone/rebar_elixir_plugin", "0.1.0"}}}},
{if_var_true, sqlite,
{sqlite3, ".*", {git, "https://github.com/processone/erlang-sqlite3", {tag, "1.1.8"}}}},
- {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.22"}}},
+ {stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.23"}}},
{if_var_true, stun,
- {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.37"}}}},
- {xmpp, ".*", {git, "https://github.com/processone/xmpp", "0fece8d96b5812c43be179c4f232cac7cb98d81c"}},
- {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.7"}}}
+ {stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.39"}}}},
+ {xmpp, ".*", {git, "https://github.com/processone/xmpp", {tag, "1.4.10"}}},
+ {yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.8"}}}
]}.
{gitonly_deps, [elixir, luerl]}.
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().