aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cyrsasl_digest.erl4
-rw-r--r--src/cyrsasl_plain.erl11
-rw-r--r--src/ejabberd_auth.erl22
-rw-r--r--src/ejabberd_c2s.erl14
-rw-r--r--src/ejabberd_commands_doc.erl18
-rw-r--r--src/ejabberd_config.erl10
-rw-r--r--src/ejabberd_http.erl4
-rw-r--r--src/ejabberd_iq.erl4
-rw-r--r--src/ejabberd_s2s_in.erl13
-rw-r--r--src/ejabberd_s2s_out.erl4
-rw-r--r--src/ejabberd_service.erl83
-rw-r--r--src/ejabberd_stun.erl4
-rw-r--r--src/ejabberd_sup.erl192
-rw-r--r--src/ejabberd_xmlrpc.erl6
-rw-r--r--src/gen_mod.erl13
-rw-r--r--src/mod_admin_extra.erl10
-rw-r--r--src/mod_block_strangers.erl14
-rw-r--r--src/mod_blocking.erl3
-rw-r--r--src/mod_carboncopy.erl17
-rw-r--r--src/mod_http_upload.erl23
-rw-r--r--src/mod_last.erl6
-rw-r--r--src/mod_mam.erl69
-rw-r--r--src/mod_muc.erl5
-rw-r--r--src/mod_muc_admin.erl40
-rw-r--r--src/mod_muc_room.erl55
-rw-r--r--src/mod_s2s_dialback.erl31
-rw-r--r--src/mod_stream_mgmt.erl78
-rw-r--r--src/prosody2ejabberd.erl6
-rw-r--r--src/randoms.erl20
-rw-r--r--src/xmpp_stream_in.erl28
30 files changed, 463 insertions, 344 deletions
diff --git a/src/cyrsasl_digest.erl b/src/cyrsasl_digest.erl
index 68fa82ea5..08edf3020 100644
--- a/src/cyrsasl_digest.erl
+++ b/src/cyrsasl_digest.erl
@@ -59,8 +59,8 @@
start(_Opts) ->
Fqdn = get_local_fqdn(),
- ?INFO_MSG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
- [Fqdn]),
+ ?DEBUG("FQDN used to check DIGEST-MD5 SASL authentication: ~s",
+ [Fqdn]),
cyrsasl:register_mechanism(<<"DIGEST-MD5">>, ?MODULE,
digest).
diff --git a/src/cyrsasl_plain.erl b/src/cyrsasl_plain.erl
index ec52b00a8..3bdcf8476 100644
--- a/src/cyrsasl_plain.erl
+++ b/src/cyrsasl_plain.erl
@@ -81,15 +81,8 @@ prepare(ClientIn) ->
_ -> error
end.
-parse(S) -> parse1(binary_to_list(S), "", []).
-
-parse1([0 | Cs], S, T) ->
- parse1(Cs, "", [list_to_binary(lists:reverse(S)) | T]);
-parse1([C | Cs], S, T) -> parse1(Cs, [C | S], T);
-%parse1([], [], T) ->
-% lists:reverse(T);
-parse1([], S, T) ->
- lists:reverse([list_to_binary(lists:reverse(S)) | T]).
+parse(S) ->
+ binary:split(S, <<0>>, [global]).
parse_domain(S) -> parse_domain1(binary_to_list(S), "", []).
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl
index df75be9eb..861d8f37b 100644
--- a/src/ejabberd_auth.erl
+++ b/src/ejabberd_auth.erl
@@ -526,7 +526,10 @@ db_get_password(User, Server, Mod) ->
UseCache = use_cache(Mod, Server),
case erlang:function_exported(Mod, get_password, 2) of
false when UseCache ->
- ets_cache:lookup(?AUTH_CACHE, {User, Server});
+ case ets_cache:lookup(?AUTH_CACHE, {User, Server}) of
+ {ok, exists} -> error;
+ Other -> Other
+ end;
false ->
error;
true when UseCache ->
@@ -544,7 +547,20 @@ db_user_exists(User, Server, Mod) ->
error ->
case Mod:store_type(Server) of
external ->
- Mod:user_exists(User, Server);
+ case ets_cache:lookup(
+ ?AUTH_CACHE, {User, Server},
+ fun() ->
+ case Mod:user_exists(User, Server) of
+ true -> {ok, exists};
+ false -> error;
+ {error, _} = Err -> Err
+ end
+ end) of
+ {ok, _} ->
+ true;
+ error ->
+ false
+ end;
_ ->
false
end
@@ -568,7 +584,7 @@ db_check_password(User, AuthzId, Server, ProvidedPassword,
false ->
error
end
- end, cache_nodes(Mod, Server)) of
+ end) of
{ok, _} ->
true;
error ->
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 1e81f4d1a..4efdafef2 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -519,6 +519,7 @@ init([State, Opts]) ->
TLSRequired = proplists:get_bool(starttls_required, Opts),
TLSVerify = proplists:get_bool(tls_verify, Opts),
Zlib = proplists:get_bool(zlib, Opts),
+ Timeout = ejabberd_config:negotiation_timeout(),
State1 = State#{tls_options => TLSOpts2,
tls_required => TLSRequired,
tls_enabled => TLSEnabled,
@@ -530,7 +531,8 @@ init([State, Opts]) ->
lserver => ?MYNAME,
access => Access,
shaper => Shaper},
- ejabberd_hooks:run_fold(c2s_init, {ok, State1}, [Opts]).
+ State2 = xmpp_stream_in:set_timeout(State1, Timeout),
+ ejabberd_hooks:run_fold(c2s_init, {ok, State2}, [Opts]).
handle_call(get_presence, From, #{jid := JID} = State) ->
Pres = case maps:get(pres_last, State, error) of
@@ -997,6 +999,9 @@ opt_type(_) ->
(max_stanza_size) -> fun((timeout()) -> timeout());
(max_fsm_queue) -> fun((timeout()) -> timeout());
(stream_management) -> fun((boolean()) -> boolean());
+ (inet) -> fun((boolean()) -> boolean());
+ (inet6) -> fun((boolean()) -> boolean());
+ (backlog) -> fun((timeout()) -> timeout());
(atom()) -> [atom()].
listen_opt_type(access) -> fun acl:access_rules_validator/1;
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
@@ -1029,13 +1034,18 @@ listen_opt_type(stream_management) ->
?ERROR_MSG("listening option 'stream_management' is ignored: "
"use mod_stream_mgmt module", []),
fun(B) when is_boolean(B) -> B end;
+listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(backlog) ->
+ fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(O) ->
StreamOpts = mod_stream_mgmt:mod_options(?MYNAME),
case lists:keyfind(O, 1, StreamOpts) of
false ->
[access, shaper, certfile, ciphers, dhfile, cafile,
protocol_options, tls, tls_compression, starttls,
- starttls_required, tls_verify, zlib, max_fsm_queue];
+ starttls_required, tls_verify, zlib, max_fsm_queue,
+ backlog, inet, inet6];
_ ->
?ERROR_MSG("Listening option '~s' is ignored: use '~s' "
"option from mod_stream_mgmt module", [O, O]),
diff --git a/src/ejabberd_commands_doc.erl b/src/ejabberd_commands_doc.erl
index b66015bac..a15bfd157 100644
--- a/src/ejabberd_commands_doc.erl
+++ b/src/ejabberd_commands_doc.erl
@@ -363,10 +363,6 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc,
args=Args, args_desc=ArgsDesc,
result=Result, result_desc=ResultDesc}=Cmd, HTMLOutput, Langs) ->
try
- LDesc = case LongDesc of
- "" -> Desc;
- _ -> LongDesc
- end,
ArgsText = case ArgsDesc of
none ->
[?TAG(ul, "args-list", [gen_param(AName, Type, undefined, HTMLOutput)
@@ -393,11 +389,15 @@ gen_doc(#ejabberd_commands{name=Name, tags=_Tags, desc=Desc, longdesc=LongDesc,
end
end,
- [?TAG(h1, [?TAG(strong, atom_to_list(Name)), <<" - ">>, ?RAW(Desc)]),
- ?TAG(p, ?RAW(LDesc)),
- ?TAG(h2, <<"Arguments:">>), ArgsText,
- ?TAG(h2, <<"Result:">>), ResultText,
- ?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)]
+ [?TAG(h1, atom_to_list(Name)),
+ ?TAG(p, ?RAW(Desc)),
+ case LongDesc of
+ "" -> [];
+ _ -> ?TAG(p, ?RAW(LongDesc))
+ end,
+ ?TAG(h2, <<"Arguments:">>), ArgsText,
+ ?TAG(h2, <<"Result:">>), ResultText,
+ ?TAG(h2, <<"Examples:">>), gen_calls(Cmd, HTMLOutput, Langs)]
catch
_:Ex ->
throw(iolist_to_binary(io_lib:format(
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index 5ec2556f6..cf3d099cc 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -37,7 +37,7 @@
default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
- codec_options/1]).
+ codec_options/1, get_plain_terms_file/2, negotiation_timeout/0]).
-export([start/2]).
@@ -1415,6 +1415,8 @@ opt_type(cache_life_time) ->
(infinity) -> infinity;
(unlimited) -> infinity
end;
+opt_type(negotiation_timeout) ->
+ fun(T) when T > 0 -> T end;
opt_type(shared_key) ->
fun iolist_to_binary/1;
opt_type(node_start) ->
@@ -1425,7 +1427,7 @@ opt_type(_) ->
[hide_sensitive_log_data, hosts, language, max_fsm_queue,
default_db, default_ram_db, queue_type, queue_dir, loglevel,
use_cache, cache_size, cache_missed, cache_life_time,
- shared_key, node_start, validate_stream].
+ shared_key, node_start, validate_stream, negotiation_timeout].
-spec may_hide_data(any()) -> any().
may_hide_data(Data) ->
@@ -1479,3 +1481,7 @@ codec_options(Host) ->
true -> [];
false -> [ignore_els]
end.
+
+-spec negotiation_timeout() -> pos_integer().
+negotiation_timeout() ->
+ timer:seconds(get_option(negotiation_timeout, 30)).
diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl
index 05bdc4495..474304a5d 100644
--- a/src/ejabberd_http.erl
+++ b/src/ejabberd_http.erl
@@ -994,6 +994,10 @@ listen_opt_type(default_host) ->
fun(A) -> A end;
listen_opt_type(custom_headers) ->
fun expand_custom_headers/1;
+listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(backlog) ->
+ fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
%% TODO
fun(A) -> A end.
diff --git a/src/ejabberd_iq.erl b/src/ejabberd_iq.erl
index 282ff7e15..2ac07e72a 100644
--- a/src/ejabberd_iq.erl
+++ b/src/ejabberd_iq.erl
@@ -144,7 +144,7 @@ noreply(#state{expire = Expire} = State) ->
-spec encode_id(non_neg_integer(), binary()) -> binary().
encode_id(Expire, Rnd) ->
ExpireBin = integer_to_binary(Expire),
- Node = atom_to_binary(node(), utf8),
+ Node = ejabberd_cluster:node_id(),
CheckSum = calc_checksum(<<ExpireBin/binary, Rnd/binary, Node/binary>>),
<<"rr-", ExpireBin/binary, $-, Rnd/binary, $-, CheckSum/binary, $-, Node/binary>>.
@@ -155,7 +155,7 @@ decode_id(<<"rr-", ID/binary>>) ->
[Rnd, Rest] = binary:split(Tail, <<"-">>),
[CheckSum, NodeBin] = binary:split(Rest, <<"-">>),
CheckSum = calc_checksum(<<ExpireBin/binary, Rnd/binary, NodeBin/binary>>),
- Node = erlang:binary_to_existing_atom(NodeBin, utf8),
+ Node = ejabberd_cluster:get_node_by_id(NodeBin),
Expire = binary_to_integer(ExpireBin),
{ok, Expire, Rnd, Node}
catch _:{badmatch, _} ->
diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl
index 5345727a2..a02834c78 100644
--- a/src/ejabberd_s2s_in.erl
+++ b/src/ejabberd_s2s_in.erl
@@ -259,6 +259,7 @@ init([State, Opts]) ->
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
+ Timeout = ejabberd_config:negotiation_timeout(),
State1 = State#{tls_options => TLSOpts2,
auth_domains => sets:new(),
xmlns => ?NS_SERVER,
@@ -268,7 +269,8 @@ init([State, Opts]) ->
server_host => ?MYNAME,
established => false,
shaper => Shaper},
- ejabberd_hooks:run_fold(s2s_in_init, {ok, State1}, [Opts]).
+ State2 = xmpp_stream_in:set_timeout(State1, Timeout),
+ ejabberd_hooks:run_fold(s2s_in_init, {ok, State2}, [Opts]).
handle_call(Request, From, #{server_host := LServer} = State) ->
ejabberd_hooks:run_fold(s2s_in_handle_call, LServer, State, [Request, From]).
@@ -356,6 +358,9 @@ change_shaper(#{shaper := ShaperName, server_host := ServerHost} = State,
(supervisor) -> fun((boolean()) -> boolean());
(max_stanza_type) -> fun((timeout()) -> timeout());
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
+ (inet) -> fun((boolean()) -> boolean());
+ (inet6) -> fun((boolean()) -> boolean());
+ (backlog) -> fun((timeout()) -> timeout());
(atom()) -> [atom()].
listen_opt_type(shaper) -> fun acl:shaper_rules_validator/1;
listen_opt_type(certfile = Opt) ->
@@ -379,6 +384,10 @@ listen_opt_type(max_stanza_size) ->
end;
listen_opt_type(max_fsm_queue) ->
fun(I) when is_integer(I), I>0 -> I end;
+listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(backlog) ->
+ fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
[shaper, certfile, ciphers, dhfile, cafile, protocol_options,
- tls_compression, tls, max_fsm_queue].
+ tls_compression, tls, max_fsm_queue, backlog, inet, inet6].
diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl
index 9abc0d017..f82d017ea 100644
--- a/src/ejabberd_s2s_out.erl
+++ b/src/ejabberd_s2s_out.erl
@@ -270,15 +270,17 @@ init([#{server := LServer, remote_server := RServer} = State, Opts]) ->
{_, N} -> N;
false -> unlimited
end,
+ Timeout = ejabberd_config:negotiation_timeout(),
State1 = State#{on_route => queue,
queue => p1_queue:new(QueueType, QueueLimit),
xmlns => ?NS_SERVER,
lang => ?MYLANG,
server_host => ServerHost,
shaper => none},
+ State2 = xmpp_stream_out:set_timeout(State1, Timeout),
?INFO_MSG("Outbound s2s connection started: ~s -> ~s",
[LServer, RServer]),
- ejabberd_hooks:run_fold(s2s_out_init, ServerHost, {ok, State1}, [Opts]).
+ ejabberd_hooks:run_fold(s2s_out_init, ServerHost, {ok, State2}, [Opts]).
handle_call(Request, From, #{server_host := ServerHost} = State) ->
ejabberd_hooks:run_fold(s2s_out_handle_call, ServerHost, State, [Request, From]).
diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl
index 7016cd77d..03b768bdf 100644
--- a/src/ejabberd_service.erl
+++ b/src/ejabberd_service.erl
@@ -100,16 +100,20 @@ init([State, Opts]) ->
false -> [compression_none | TLSOpts1];
true -> TLSOpts1
end,
+ GlobalRoutes = proplists:get_value(global_routes, Opts, true),
+ Timeout = ejabberd_config:negotiation_timeout(),
State1 = xmpp_stream_in:change_shaper(State, Shaper),
- State2 = State1#{access => Access,
- xmlns => ?NS_COMPONENT,
- lang => ?MYLANG,
- server => ?MYNAME,
- host_opts => dict:from_list(HostOpts1),
- stream_version => undefined,
- tls_options => TLSOpts,
- check_from => CheckFrom},
- ejabberd_hooks:run_fold(component_init, {ok, State2}, [Opts]).
+ State2 = xmpp_stream_in:set_timeout(State1, Timeout),
+ State3 = State2#{access => Access,
+ xmlns => ?NS_COMPONENT,
+ lang => ?MYLANG,
+ server => ?MYNAME,
+ host_opts => dict:from_list(HostOpts1),
+ stream_version => undefined,
+ tls_options => TLSOpts,
+ global_routes => GlobalRoutes,
+ check_from => CheckFrom},
+ ejabberd_hooks:run_fold(component_init, {ok, State3}, [Opts]).
handle_stream_start(_StreamStart,
#{remote_server := RemoteServer,
@@ -140,7 +144,7 @@ get_password_fun(#{remote_server := RemoteServer,
host_opts := HostOpts}) ->
fun(_) ->
case dict:find(RemoteServer, HostOpts) of
- {ok, Password} ->
+ {ok, Password} ->
{Password, undefined};
error ->
?INFO_MSG("(~s) Domain ~s is unconfigured for "
@@ -153,16 +157,22 @@ get_password_fun(#{remote_server := RemoteServer,
handle_auth_success(_, Mech, _,
#{remote_server := RemoteServer, host_opts := HostOpts,
- socket := Socket, ip := IP} = State) ->
+ socket := Socket, ip := IP,
+ global_routes := GlobalRoutes} = State) ->
?INFO_MSG("(~s) Accepted external component ~s authentication "
"for ~s from ~s",
[xmpp_socket:pp(Socket), Mech, RemoteServer,
ejabberd_config:may_hide_data(misc:ip_to_list(IP))]),
- lists:foreach(
- fun (H) ->
- ejabberd_router:register_route(H, ?MYNAME),
- ejabberd_hooks:run(component_connected, [H])
- end, dict:fetch_keys(HostOpts)),
+ Routes = if GlobalRoutes ->
+ dict:fetch_keys(HostOpts);
+ true ->
+ [RemoteServer]
+ end,
+ lists:foreach(
+ fun(H) ->
+ ejabberd_router:register_route(H, ?MYNAME),
+ ejabberd_hooks:run(component_connected, [H])
+ end, Routes),
State.
handle_auth_failure(_, Mech, Reason,
@@ -180,11 +190,11 @@ handle_authenticated_packet(Pkt0, #{ip := {IP, _}, lang := Lang} = State)
Pkt = xmpp:put_meta(Pkt0, ip, IP),
From = xmpp:get_from(Pkt),
case check_from(From, State) of
- true ->
+ true ->
ejabberd_router:route(Pkt),
State;
- false ->
- Txt = <<"Improper domain part of 'from' attribute">>,
+ false ->
+ Txt = <<"Improper domain part of 'from' attribute">>,
Err = xmpp:serr_invalid_from(Txt, Lang),
xmpp_stream_in:send(State, Err)
end;
@@ -193,7 +203,7 @@ handle_authenticated_packet(_Pkt, State) ->
handle_info({route, Packet}, #{access := Access} = State) ->
case acl:match_rule(global, Access, xmpp:get_from(Packet)) of
- allow ->
+ allow ->
xmpp_stream_in:send(State, Packet);
deny ->
Lang = xmpp:get_lang(Packet),
@@ -205,17 +215,27 @@ handle_info(Info, State) ->
?ERROR_MSG("Unexpected info: ~p", [Info]),
State.
-terminate(Reason, #{stream_state := StreamState, host_opts := HostOpts}) ->
+terminate(Reason, #{stream_state := StreamState,
+ host_opts := HostOpts,
+ remote_server := RemoteServer,
+ global_routes := GlobalRoutes}) ->
case StreamState of
established ->
+ Routes = if GlobalRoutes ->
+ dict:fetch_keys(HostOpts);
+ true ->
+ [RemoteServer]
+ end,
lists:foreach(
fun(H) ->
- ejabberd_router:unregister_route(H),
+ ejabberd_router:unregister_route(H),
ejabberd_hooks:run(component_disconnected, [H, Reason])
- end, dict:fetch_keys(HostOpts));
+ end, Routes);
_ ->
- ok
- end.
+ ok
+ end;
+terminate(_Reason, _State) ->
+ ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
@@ -268,9 +288,12 @@ transform_listen_option(Opt, Opts) ->
(check_from) -> fun((boolean()) -> boolean());
(password) -> fun((boolean()) -> boolean());
(hosts) -> fun(([{binary(), [{password, binary()}]}]) ->
- [{binary(), binary() | undefined}]);
+ [{binary(), binary() | undefined}]);
(max_stanza_type) -> fun((timeout()) -> timeout());
(max_fsm_queue) -> fun((pos_integer()) -> pos_integer());
+ (inet) -> fun((boolean()) -> boolean());
+ (inet6) -> fun((boolean()) -> boolean());
+ (backlog) -> fun((timeout()) -> timeout());
(atom()) -> [atom()].
listen_opt_type(access) -> fun acl:access_rules_validator/1;
listen_opt_type(shaper_rule) -> fun acl:shaper_rules_validator/1;
@@ -299,6 +322,8 @@ listen_opt_type(hosts) ->
{iolist_to_binary(Host), Password}
end, HostOpts)
end;
+listen_opt_type(global_routes) ->
+ fun(B) when is_boolean(B) -> B end;
listen_opt_type(max_stanza_size) ->
fun(I) when is_integer(I) -> I;
(unlimited) -> infinity;
@@ -306,7 +331,11 @@ listen_opt_type(max_stanza_size) ->
end;
listen_opt_type(max_fsm_queue) ->
fun(I) when is_integer(I), I>0 -> I end;
+listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(backlog) ->
+ fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
[access, shaper_rule, certfile, ciphers, dhfile, cafile, tls,
protocol_options, tls_compression, password, hosts, check_from,
- max_fsm_queue].
+ max_fsm_queue, global_routes, backlog, inet, inet6].
diff --git a/src/ejabberd_stun.erl b/src/ejabberd_stun.erl
index 342f6302a..adae05f00 100644
--- a/src/ejabberd_stun.erl
+++ b/src/ejabberd_stun.erl
@@ -172,8 +172,10 @@ listen_opt_type(turn_max_permissions) ->
end;
listen_opt_type(server_name) ->
fun iolist_to_binary/1;
+listen_opt_type(backlog) ->
+ fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
[shaper, auth_type, auth_realm, tls, certfile, turn_min_port,
turn_max_port, turn_max_allocations, turn_max_permissions,
- server_name].
+ server_name, backlog].
-endif.
diff --git a/src/ejabberd_sup.erl b/src/ejabberd_sup.erl
index e829ea61d..f692575c1 100644
--- a/src/ejabberd_sup.erl
+++ b/src/ejabberd_sup.erl
@@ -30,150 +30,58 @@
-export([start_link/0, init/1]).
+-define(SHUTDOWN_TIMEOUT, timer:seconds(30)).
+
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
- Hooks =
- {ejabberd_hooks,
- {ejabberd_hooks, start_link, []},
- permanent,
- brutal_kill,
- worker,
- [ejabberd_hooks]},
- Cluster = {ejabberd_cluster,
- {ejabberd_cluster, start_link, []},
- permanent,
- 5000,
- worker,
- [ejabberd_cluster]},
- S2S =
- {ejabberd_s2s,
- {ejabberd_s2s, start_link, []},
- permanent,
- brutal_kill,
- worker,
- [ejabberd_s2s]},
- Captcha =
- {ejabberd_captcha,
- {ejabberd_captcha, start_link, []},
- permanent,
- brutal_kill,
- worker,
- [ejabberd_captcha]},
- Listener =
- {ejabberd_listener,
- {ejabberd_listener, start_link, []},
- permanent,
- infinity,
- supervisor,
- [ejabberd_listener]},
- S2SInSupervisor =
- {ejabberd_s2s_in_sup,
- {ejabberd_tmp_sup, start_link,
- [ejabberd_s2s_in_sup, ejabberd_s2s_in]},
- permanent,
- infinity,
- supervisor,
- [ejabberd_tmp_sup]},
- S2SOutSupervisor =
- {ejabberd_s2s_out_sup,
- {ejabberd_tmp_sup, start_link,
- [ejabberd_s2s_out_sup, ejabberd_s2s_out]},
- permanent,
- infinity,
- supervisor,
- [ejabberd_tmp_sup]},
- ServiceSupervisor =
- {ejabberd_service_sup,
- {ejabberd_tmp_sup, start_link,
- [ejabberd_service_sup, ejabberd_service]},
- permanent,
- infinity,
- supervisor,
- [ejabberd_tmp_sup]},
- BackendSupervisor = {ejabberd_backend_sup,
- {ejabberd_backend_sup, start_link, []},
- permanent, infinity, supervisor,
- [ejabberd_backend_sup]},
- ACL = {acl, {acl, start_link, []},
- permanent, 5000, worker, [acl]},
- Shaper = {shaper, {shaper, start_link, []},
- permanent, 5000, worker, [shaper]},
- SQLSupervisor = {ejabberd_rdbms,
- {ejabberd_rdbms, start_link, []},
- permanent, infinity, supervisor, [ejabberd_rdbms]},
- RiakSupervisor = {ejabberd_riak_sup,
- {ejabberd_riak_sup, start_link, []},
- permanent, infinity, supervisor, [ejabberd_riak_sup]},
- RedisSupervisor = {ejabberd_redis_sup,
- {ejabberd_redis_sup, start_link, []},
- permanent, infinity, supervisor, [ejabberd_redis_sup]},
- Router = {ejabberd_router, {ejabberd_router, start_link, []},
- permanent, 5000, worker, [ejabberd_router]},
- RouterMulticast = {ejabberd_router_multicast,
- {ejabberd_router_multicast, start_link, []},
- permanent, 5000, worker, [ejabberd_router_multicast]},
- Local = {ejabberd_local, {ejabberd_local, start_link, []},
- permanent, 5000, worker, [ejabberd_local]},
- SM = {ejabberd_sm, {ejabberd_sm, start_link, []},
- permanent, 5000, worker, [ejabberd_sm]},
- GenModSupervisor = {ejabberd_gen_mod_sup, {gen_mod, start_link, []},
- permanent, infinity, supervisor, [gen_mod]},
- ExtMod = {ext_mod, {ext_mod, start_link, []},
- permanent, 5000, worker, [ext_mod]},
- Auth = {ejabberd_auth, {ejabberd_auth, start_link, []},
- permanent, 5000, worker, [ejabberd_auth]},
- OAuth = {ejabberd_oauth, {ejabberd_oauth, start_link, []},
- permanent, 5000, worker, [ejabberd_oauth]},
- Translation = {translate, {translate, start_link, []},
- permanent, 5000, worker, [translate]},
- AccessPerms = {ejabberd_access_permissions,
- {ejabberd_access_permissions, start_link, []},
- permanent, 5000, worker, [ejabberd_access_permissions]},
- Ctl = {ejabberd_ctl, {ejabberd_ctl, start_link, []},
- permanent, 5000, worker, [ejabberd_ctl]},
- Commands = {ejabberd_commands, {ejabberd_commands, start_link, []},
- permanent, 5000, worker, [ejabberd_commands]},
- Admin = {ejabberd_admin, {ejabberd_admin, start_link, []},
- permanent, 5000, worker, [ejabberd_admin]},
- CyrSASL = {cyrsasl, {cyrsasl, start_link, []},
- permanent, 5000, worker, [cyrsasl]},
- PKIX = {ejabberd_pkix, {ejabberd_pkix, start_link, []},
- permanent, 5000, worker, [ejabberd_pkix]},
- ACME = {ejabberd_acme, {ejabberd_acme, start_link, []},
- permanent, 5000, worker, [ejabberd_acme]},
- IQ = {ejabberd_iq, {ejabberd_iq, start_link, []},
- permanent, 5000, worker, [ejabberd_iq]},
{ok, {{one_for_one, 10, 1},
- [Hooks,
- Cluster,
- CyrSASL,
- Translation,
- AccessPerms,
- Ctl,
- Commands,
- Admin,
- PKIX,
- ACME,
- Listener,
- S2S,
- S2SInSupervisor,
- S2SOutSupervisor,
- ServiceSupervisor,
- ACL,
- Shaper,
- BackendSupervisor,
- SQLSupervisor,
- RiakSupervisor,
- RedisSupervisor,
- IQ,
- Router,
- RouterMulticast,
- Local,
- SM,
- Captcha,
- ExtMod,
- GenModSupervisor,
- Auth,
- OAuth]}}.
+ [worker(ejabberd_hooks),
+ worker(ejabberd_cluster),
+ worker(cyrsasl),
+ worker(translate),
+ worker(ejabberd_access_permissions),
+ worker(ejabberd_ctl),
+ worker(ejabberd_commands),
+ worker(ejabberd_admin),
+ worker(ejabberd_pkix),
+ worker(ejabberd_acme),
+ supervisor(ejabberd_listener),
+ worker(ejabberd_s2s),
+ simple_supervisor(ejabberd_s2s_in),
+ simple_supervisor(ejabberd_s2s_out),
+ simple_supervisor(ejabberd_service),
+ worker(acl),
+ worker(shaper),
+ supervisor(ejabberd_backend_sup),
+ supervisor(ejabberd_rdbms),
+ supervisor(ejabberd_riak_sup),
+ supervisor(ejabberd_redis_sup),
+ worker(ejabberd_iq),
+ worker(ejabberd_router),
+ worker(ejabberd_router_multicast),
+ worker(ejabberd_local),
+ worker(ejabberd_sm),
+ worker(ejabberd_captcha),
+ worker(ext_mod),
+ supervisor(ejabberd_gen_mod_sup, gen_mod),
+ worker(ejabberd_auth),
+ worker(ejabberd_oauth)]}}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+worker(Mod) ->
+ {Mod, {Mod, start_link, []}, permanent, ?SHUTDOWN_TIMEOUT, worker, [Mod]}.
+
+supervisor(Mod) ->
+ supervisor(Mod, Mod).
+
+supervisor(Name, Mod) ->
+ {Name, {Mod, start_link, []}, permanent, infinity, supervisor, [Mod]}.
+
+simple_supervisor(Mod) ->
+ Name = list_to_atom(atom_to_list(Mod) ++ "_sup"),
+ {Name, {ejabberd_tmp_sup, start_link, [Name, Mod]},
+ permanent, infinity, supervisor, [ejabberd_tmp_sup]}.
diff --git a/src/ejabberd_xmlrpc.erl b/src/ejabberd_xmlrpc.erl
index 3e0e4fb0b..fb5cbd850 100644
--- a/src/ejabberd_xmlrpc.erl
+++ b/src/ejabberd_xmlrpc.erl
@@ -581,5 +581,9 @@ listen_opt_type(maxsessions) ->
fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(timeout) ->
fun(I) when is_integer(I), I>0 -> I end;
+listen_opt_type(inet) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(inet6) -> fun(B) when is_boolean(B) -> B end;
+listen_opt_type(backlog) ->
+ fun(I) when is_integer(I), I>0 -> I end;
listen_opt_type(_) ->
- [access_commands, maxsessions, timeout].
+ [access_commands, maxsessions, timeout, backlog, inet, inet6].
diff --git a/src/gen_mod.erl b/src/gen_mod.erl
index 5f8283df7..a477ec295 100644
--- a/src/gen_mod.erl
+++ b/src/gen_mod.erl
@@ -516,9 +516,16 @@ get_validators(Host, {Module, SubMods}) ->
[] ->
case have_validators(Module) of
false ->
- ?WARNING_MSG("Third-party module '~s' doesn't export "
- "options validator; consider to upgrade "
- "the module", [Module]),
+ case code:ensure_loaded(Module) of
+ {module, _} ->
+ ?WARNING_MSG("Third-party module '~s' doesn't export "
+ "options validator; consider to upgrade "
+ "the module", [Module]);
+ _ ->
+ %% Silently ignore this, the error will be
+ %% generated later
+ ok
+ end,
undef;
true ->
[]
diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
index 8d530e5c8..1f3ec0397 100644
--- a/src/mod_admin_extra.erl
+++ b/src/mod_admin_extra.erl
@@ -59,6 +59,7 @@
add_rosteritem/7, delete_rosteritem/4,
process_rosteritems/5, get_roster/2, push_roster/3,
push_roster_all/1, push_alltoall/2,
+ push_roster_item/5, build_roster_item/3,
% Private storage
private_get/4, private_set/3,
@@ -1506,11 +1507,12 @@ srg_get_info(Group, Host) ->
Os when is_list(Os) -> Os;
error -> []
end,
- [{misc:atom_to_binary(Title), btl(Value)} || {Title, Value} <- Opts].
+ [{misc:atom_to_binary(Title), to_list(Value)} || {Title, Value} <- Opts].
-btl([]) -> [];
-btl([B|L]) -> [btl(B)|btl(L)];
-btl(B) -> binary_to_list(B).
+to_list([]) -> [];
+to_list([H|T]) -> [to_list(H)|to_list(T)];
+to_list(E) when is_atom(E) -> atom_to_list(E);
+to_list(E) -> binary_to_list(E).
srg_get_members(Group, Host) ->
Members = mod_shared_roster:get_group_explicit_users(Host,Group),
diff --git a/src/mod_block_strangers.erl b/src/mod_block_strangers.erl
index b60003c84..d6d5c50cb 100644
--- a/src/mod_block_strangers.erl
+++ b/src/mod_block_strangers.erl
@@ -199,8 +199,10 @@ need_check(Pkt) ->
false
end,
AllowLocalUsers = gen_mod:get_module_opt(LServer, ?MODULE, allow_local_users),
- not (IsEmpty orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>)
- andalso ejabberd_router:is_my_host(From#jid.lserver))).
+ Access = gen_mod:get_module_opt(LServer, ?MODULE, access),
+ not (IsEmpty orelse acl:match_rule(LServer, Access, From) == allow
+ orelse ((AllowLocalUsers orelse From#jid.luser == <<"">>)
+ andalso ejabberd_router:is_my_host(From#jid.lserver))).
-spec check_subscription(jid(), jid()) -> boolean().
check_subscription(From, To) ->
@@ -265,10 +267,14 @@ mod_opt_type(allow_local_users) ->
mod_opt_type(allow_transports) ->
fun (B) when is_boolean(B) -> B end;
mod_opt_type(captcha) ->
- fun (B) when is_boolean(B) -> B end.
+ fun (B) when is_boolean(B) -> B end;
+mod_opt_type(access) ->
+ fun acl:access_rules_validator/1.
+
mod_options(_) ->
- [{drop, true},
+ [{access, none},
+ {drop, true},
{log, false},
{captcha, false},
{allow_local_users, true},
diff --git a/src/mod_blocking.erl b/src/mod_blocking.erl
index dc49ccb11..e3ac1e3e1 100644
--- a/src/mod_blocking.erl
+++ b/src/mod_blocking.erl
@@ -234,10 +234,11 @@ process_unblock(#iq{from = From} = IQ, LJIDs) ->
-spec broadcast_event(jid(), block() | unblock()) -> ok.
broadcast_event(#jid{luser = LUser, lserver = LServer} = From, Event) ->
+ BFrom = jid:remove_resource(From),
lists:foreach(
fun(R) ->
To = jid:replace_resource(From, R),
- IQ = #iq{type = set, from = From, to = To,
+ IQ = #iq{type = set, from = BFrom, to = To,
id = <<"push", (randoms:get_string())/binary>>,
sub_els = [Event]},
ejabberd_router:route(IQ)
diff --git a/src/mod_carboncopy.erl b/src/mod_carboncopy.erl
index de0c7fc85..5b7983ddf 100644
--- a/src/mod_carboncopy.erl
+++ b/src/mod_carboncopy.erl
@@ -115,10 +115,10 @@ iq_handler(#iq{type = set, lang = Lang, from = From,
{U, S, R} = jid:tolower(From),
Result = case El of
#carbons_enable{} ->
- ?INFO_MSG("carbons enabled for user ~s@~s/~s", [U,S,R]),
+ ?DEBUG("Carbons enabled for user ~s@~s/~s", [U,S,R]),
enable(S, U, R, ?NS_CARBONS_2);
#carbons_disable{} ->
- ?INFO_MSG("carbons disabled for user ~s@~s/~s", [U,S,R]),
+ ?DEBUG("Carbons disabled for user ~s@~s/~s", [U,S,R]),
disable(S, U, R)
end,
case Result of
@@ -164,9 +164,9 @@ user_receive_packet({Packet, #{jid := JID} = C2SState}) ->
stanza() | {stop, stanza()}.
check_and_forward(JID, To, Packet, Direction)->
case is_chat_message(Packet) andalso
- not is_muc_pm(To, Packet) andalso
- xmpp:has_subtag(Packet, #carbons_private{}) == false andalso
- xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) == false of
+ not is_received_muc_pm(To, Packet, Direction) andalso
+ not xmpp:has_subtag(Packet, #carbons_private{}) andalso
+ not xmpp:has_subtag(Packet, #hint{type = 'no-copy'}) of
true ->
case is_carbon_copy(Packet) of
false ->
@@ -282,9 +282,12 @@ is_chat_message(#message{type = normal, body = [_|_]}) ->
is_chat_message(_) ->
false.
-is_muc_pm(#jid{lresource = <<>>}, _Packet) ->
+-spec is_received_muc_pm(jid(), message(), direction()) -> boolean().
+is_received_muc_pm(#jid{lresource = <<>>}, _Packet, _Direction) ->
false;
-is_muc_pm(_To, Packet) ->
+is_received_muc_pm(_To, _Packet, sent) ->
+ false;
+is_received_muc_pm(_To, Packet, received) ->
xmpp:has_subtag(Packet, #muc_user{}).
-spec list(binary(), binary()) -> [{Resource :: binary(), Namespace :: binary()}].
diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl
index 9f5894499..9a5952926 100644
--- a/src/mod_http_upload.erl
+++ b/src/mod_http_upload.erl
@@ -589,7 +589,7 @@ create_slot(#state{service_url = undefined,
case ejabberd_hooks:run_fold(http_upload_slot_request, ServerHost, allow,
[JID, UserDir, Size, Lang]) of
allow ->
- RandStr = make_rand_string(SecretLength),
+ RandStr = randoms:get_alphanum_string(SecretLength),
FileStr = make_file_string(File),
?INFO_MSG("Got HTTP upload slot for ~s (file: ~s)",
[jid:encode(JID), File]),
@@ -687,27 +687,6 @@ make_user_string(#jid{luser = U}, node) ->
make_file_string(File) ->
re:replace(File, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
--spec make_rand_string(non_neg_integer()) -> binary().
-
-make_rand_string(Length) ->
- list_to_binary(make_rand_string([], Length)).
-
--spec make_rand_string(string(), non_neg_integer()) -> string().
-
-make_rand_string(S, 0) -> S;
-make_rand_string(S, N) -> make_rand_string([make_rand_char() | S], N - 1).
-
--spec make_rand_char() -> char().
-
-make_rand_char() ->
- map_int_to_char(randoms:uniform(0, 61)).
-
--spec map_int_to_char(0..61) -> char().
-
-map_int_to_char(N) when N =< 9 -> N + 48; % Digit.
-map_int_to_char(N) when N =< 35 -> N + 55; % Upper-case character.
-map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character.
-
-spec yield_content_type(binary()) -> binary().
yield_content_type(<<"">>) -> ?DEFAULT_CONTENT_TYPE;
diff --git a/src/mod_last.erl b/src/mod_last.erl
index e6232e67c..7e53fe5dc 100644
--- a/src/mod_last.erl
+++ b/src/mod_last.erl
@@ -156,9 +156,9 @@ privacy_check_packet(allow, C2SState,
case xmpp:has_subtag(IQ, #last{}) of
true ->
#jid{luser = LUser, lserver = LServer} = To,
- {Sub, _} = ejabberd_hooks:run_fold(
- roster_get_jid_info, LServer,
- {none, []}, [LUser, LServer, From]),
+ {Sub, _, _} = ejabberd_hooks:run_fold(
+ roster_get_jid_info, LServer,
+ {none, none, []}, [LUser, LServer, From]),
if Sub == from; Sub == both ->
Pres = #presence{from = To, to = From},
case ejabberd_hooks:run_fold(
diff --git a/src/mod_mam.erl b/src/mod_mam.erl
index bc80d47ed..ac88c82cd 100644
--- a/src/mod_mam.erl
+++ b/src/mod_mam.erl
@@ -67,9 +67,10 @@
-callback select(binary(), jid(), jid(), mam_query:result(),
#rsm_set{} | undefined, chat | groupchat) ->
{[{binary(), non_neg_integer(), xmlel()}], boolean(), non_neg_integer()}.
--callback use_cache(binary(), gen_mod:opts()) -> boolean().
+-callback use_cache(binary()) -> boolean().
+-callback cache_nodes(binary()) -> [node()].
--optional_callbacks([use_cache/2]).
+-optional_callbacks([use_cache/1, cache_nodes/1]).
%%%===================================================================
%%% API
@@ -77,7 +78,7 @@
start(Host, Opts) ->
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
Mod:init(Host, Opts),
- init_cache(Host, Opts),
+ init_cache(Mod, Host, Opts),
register_iq_handlers(Host),
ejabberd_hooks:add(sm_receive_packet, Host, ?MODULE,
sm_receive_packet, 50),
@@ -113,19 +114,24 @@ start(Host, Opts) ->
ejabberd_commands:register_commands(get_commands_spec()),
ok.
-use_cache(Host, Opts) ->
- Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
+use_cache(Mod, Host) ->
case erlang:function_exported(Mod, use_cache, 2) of
- true -> Mod:use_cache(Host, Opts);
- false -> gen_mod:get_opt(use_cache, Opts)
+ true -> Mod:use_cache(Host);
+ false -> gen_mod:get_module_opt(Host, ?MODULE, use_cache)
+ end.
+
+cache_nodes(Mod, Host) ->
+ case erlang:function_exported(Mod, cache_nodes, 1) of
+ true -> Mod:cache_nodes(Host);
+ false -> ejabberd_cluster:get_nodes()
end.
-init_cache(Host, Opts) ->
- case use_cache(Host, Opts) of
+init_cache(Mod, Host, Opts) ->
+ case use_cache(Mod, Host) of
true ->
ets_cache:new(archive_prefs_cache, cache_opts(Opts));
false ->
- ok
+ ets_cache:delete(archive_prefs_cache)
end.
cache_opts(Opts) ->
@@ -185,7 +191,7 @@ reload(Host, NewOpts, OldOpts) ->
true ->
ok
end,
- ets_cache:setopts(archive_prefs_cache, cache_opts(NewOpts)),
+ init_cache(NewMod, Host, NewOpts),
case gen_mod:is_equal_opt(assume_mam_usage, NewOpts, OldOpts) of
{false, true, _} ->
ejabberd_hooks:add(message_is_archived, Host, ?MODULE,
@@ -236,8 +242,13 @@ remove_user(User, Server) ->
LServer = jid:nameprep(Server),
Mod = gen_mod:db_mod(LServer, ?MODULE),
Mod:remove_user(LUser, LServer),
- ets_cache:delete(archive_prefs_cache, {LUser, LServer},
- ejabberd_cluster:get_nodes()).
+ case use_cache(Mod, LServer) of
+ true ->
+ ets_cache:delete(archive_prefs_cache, {LUser, LServer},
+ cache_nodes(Mod, LServer));
+ false ->
+ ok
+ end.
-spec remove_room(binary(), binary(), binary()) -> ok.
remove_room(LServer, Name, Host) ->
@@ -792,16 +803,26 @@ write_prefs(LUser, LServer, Host, Default, Always, Never) ->
Mod = gen_mod:db_mod(Host, ?MODULE),
case Mod:write_prefs(LUser, LServer, Prefs, Host) of
ok ->
- ets_cache:delete(archive_prefs_cache, {LUser, LServer},
- ejabberd_cluster:get_nodes());
+ case use_cache(Mod, LServer) of
+ true ->
+ ets_cache:delete(archive_prefs_cache, {LUser, LServer},
+ cache_nodes(Mod, LServer));
+ false ->
+ ok
+ end;
_Err ->
{error, db_failure}
end.
get_prefs(LUser, LServer) ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
- fun() -> Mod:get_prefs(LUser, LServer) end),
+ Res = case use_cache(Mod, LServer) of
+ true ->
+ ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
+ fun() -> Mod:get_prefs(LUser, LServer) end);
+ false ->
+ Mod:get_prefs(LUser, LServer)
+ end,
case Res of
{ok, Prefs} ->
Prefs;
@@ -831,10 +852,16 @@ maybe_activate_mam(LUser, LServer) ->
case ActivateOpt of
true ->
Mod = gen_mod:db_mod(LServer, ?MODULE),
- Res = ets_cache:lookup(archive_prefs_cache, {LUser, LServer},
- fun() ->
- Mod:get_prefs(LUser, LServer)
- end),
+ Res = case use_cache(Mod, LServer) of
+ true ->
+ ets_cache:lookup(archive_prefs_cache,
+ {LUser, LServer},
+ fun() ->
+ Mod:get_prefs(LUser, LServer)
+ end);
+ false ->
+ Mod:get_prefs(LUser, LServer)
+ end,
case Res of
{ok, _Prefs} ->
ok;
diff --git a/src/mod_muc.erl b/src/mod_muc.erl
index acfbb90a0..debda4cab 100644
--- a/src/mod_muc.erl
+++ b/src/mod_muc.erl
@@ -107,8 +107,7 @@
-callback unregister_online_user(binary(), ljid(), binary(), binary()) -> any().
-callback count_online_rooms_by_user(binary(), binary(), binary()) -> non_neg_integer().
-callback get_online_rooms_by_user(binary(), binary(), binary()) -> [{binary(), binary()}].
--callback get_subscribed_rooms(binary(), binary(), jid()) ->
- {ok, [{ljid(), binary(), [binary()]}]} | {error, any()}.
+-callback get_subscribed_rooms(binary(), binary(), jid()) -> [ljid()] | [].
%%====================================================================
%% API
@@ -727,7 +726,7 @@ iq_get_register_info(ServerHost, Host, From, Lang) ->
instructions = [Inst], fields = Fields},
#register{nick = Nick,
registered = Registered,
- instructions =
+ instructions =
translate:translate(
Lang, <<"You need a client that supports x:data "
"to register the nickname">>),
diff --git a/src/mod_muc_admin.erl b/src/mod_muc_admin.erl
index 0bd47dcaa..6b6c7d8ca 100644
--- a/src/mod_muc_admin.erl
+++ b/src/mod_muc_admin.erl
@@ -37,7 +37,7 @@
get_user_rooms/2, get_room_occupants/2,
get_room_occupants_number/2, send_direct_invitation/5,
change_room_option/4, get_room_options/2,
- set_room_affiliation/4, get_room_affiliations/2,
+ set_room_affiliation/4, get_room_affiliations/2, get_room_affiliation/3,
web_menu_main/2, web_page_main/2, web_menu_host/3,
subscribe_room/4, unsubscribe_room/2, get_subscribers/2,
web_page_host/3, mod_options/1, get_commands_spec/0]).
@@ -313,8 +313,17 @@ get_commands_spec() ->
{affiliation, atom},
{reason, string}
]}}
- }}}
- ].
+ }}},
+ #ejabberd_commands{name = get_room_affiliation, tags = [muc_room],
+ desc = "Get affiliation of a user in MUC room",
+ module = ?MODULE, function = get_room_affiliation,
+ args_desc = ["Room name", "MUC service", "User JID"],
+ args_example = ["room1", "muc.example.com", "user1@example.com"],
+ result_desc = "Affiliation of the user",
+ result_example = member,
+ args = [{name, binary}, {service, binary}, {jid, binary}],
+ result = {affiliation, atom}}
+ ].
%%%
@@ -569,8 +578,10 @@ prepare_room_info(Room_info) ->
%% ok | error
%% @doc Create a room immediately with the default options.
create_room(Name1, Host1, ServerHost) ->
- create_room_with_opts(Name1, Host1, ServerHost, []),
- change_room_option(Name1, Host1, <<"persistent">>, <<"true">>).
+ case create_room_with_opts(Name1, Host1, ServerHost, []) of
+ ok -> change_room_option(Name1, Host1, <<"persistent">>, <<"true">>);
+ Error -> Error
+ end.
create_room_with_opts(Name1, Host1, ServerHost, CustomRoomOpts) ->
true = (error /= (Name = jid:nodeprep(Name1))),
@@ -1033,6 +1044,25 @@ get_room_affiliations(Name, Service) ->
end.
%%----------------------------
+%% Get Room Affiliation
+%%----------------------------
+
+%% @spec(Name::binary(), Service::binary(), JID::binary()) ->
+%% {Affiliation::string()}
+%% @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} ->
+ %% Get the PID of the online room, then request its state
+ {ok, StateData} = p1_fsm:sync_send_all_state_event(Pid, get_state),
+ UserJID = jid:decode(JID),
+ mod_muc_room:get_affiliation(UserJID, StateData);
+ error ->
+ throw({error, "The room does not exist."})
+ end.
+
+%%----------------------------
%% Change Room Affiliation
%%----------------------------
diff --git a/src/mod_muc_room.erl b/src/mod_muc_room.erl
index 71dd09084..158e937c7 100644
--- a/src/mod_muc_room.erl
+++ b/src/mod_muc_room.erl
@@ -37,7 +37,9 @@
get_role/2,
get_affiliation/2,
is_occupant_or_admin/2,
- route/2]).
+ route/2,
+ expand_opts/1,
+ config_fields/0]).
%% gen_fsm callbacks
-export([init/1,
@@ -506,7 +508,7 @@ handle_event(_Event, StateName, StateData) ->
{next_state, StateName, StateData}.
handle_sync_event({get_disco_item, Filter, JID, Lang}, _From, StateName, StateData) ->
- Len = ?DICT:size(StateData#state.users),
+ Len = ?DICT:size(StateData#state.nicks),
Reply = case (Filter == all) or (Filter == Len) or ((Filter /= 0) and (Len /= 0)) of
true ->
get_roomdesc_reply(JID, StateData,
@@ -3609,6 +3611,35 @@ make_opts(StateData) ->
{subject_author, StateData#state.subject_author},
{subscribers, Subscribers}].
+expand_opts(CompactOpts) ->
+ DefConfig = #config{},
+ Fields = record_info(fields, config),
+ {_, Opts1} =
+ lists:foldl(
+ fun(Field, {Pos, Opts}) ->
+ case lists:keyfind(Field, 1, CompactOpts) of
+ false ->
+ DefV = element(Pos, DefConfig),
+ DefVal = case (?SETS):is_set(DefV) of
+ true -> (?SETS):to_list(DefV);
+ false -> DefV
+ end,
+ {Pos+1, [{Field, DefVal}|Opts]};
+ {_, Val} ->
+ {Pos+1, [{Field, Val}|Opts]}
+ end
+ end, {2, []}, Fields),
+ SubjectAuthor = proplists:get_value(subject_author, CompactOpts, <<"">>),
+ Subject = proplists:get_value(subject, CompactOpts, <<"">>),
+ Subscribers = proplists:get_value(subscribers, CompactOpts, []),
+ [{subject, Subject},
+ {subject_author, SubjectAuthor},
+ {subscribers, Subscribers}
+ | lists:reverse(Opts1)].
+
+config_fields() ->
+ [subject, subject_author, subscribers | record_info(fields, config)].
+
-spec destroy_room(muc_destroy(), state()) -> {result, undefined, stop}.
destroy_room(DEl, StateData) ->
Destroy = DEl#muc_destroy{xmlns = ?NS_MUC_USER},
@@ -3703,7 +3734,7 @@ process_iq_disco_info(From, #iq{type = get, lang = Lang,
-spec iq_disco_info_extras(binary(), state()) -> xdata().
iq_disco_info_extras(Lang, StateData) ->
Fs1 = [{description, (StateData#state.config)#config.description},
- {occupants, ?DICT:size(StateData#state.users)},
+ {occupants, ?DICT:size(StateData#state.nicks)},
{contactjid, get_owners(StateData)}],
Fs2 = case (StateData#state.config)#config.pubsub of
Node when is_binary(Node), Node /= <<"">> ->
@@ -3938,20 +3969,18 @@ get_roomdesc_tail(StateData, Lang) ->
true -> <<"">>;
_ -> translate:translate(Lang, <<"private, ">>)
end,
- Len = (?DICT):size(StateData#state.users),
+ Len = (?DICT):size(StateData#state.nicks),
<<" (", Desc/binary, (integer_to_binary(Len))/binary, ")">>.
-spec get_mucroom_disco_items(state()) -> disco_items().
get_mucroom_disco_items(StateData) ->
- Items = lists:map(
- fun({_LJID, Info}) ->
- Nick = Info#user.nick,
- #disco_item{jid = jid:make(StateData#state.room,
- StateData#state.host,
- Nick),
- name = Nick}
- end,
- (?DICT):to_list(StateData#state.users)),
+ Items = ?DICT:fold(
+ fun(Nick, _, Acc) ->
+ [#disco_item{jid = jid:make(StateData#state.room,
+ StateData#state.host,
+ Nick),
+ name = Nick}|Acc]
+ end, [], StateData#state.nicks),
#disco_items{items = Items}.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/src/mod_s2s_dialback.erl b/src/mod_s2s_dialback.erl
index 6f088b349..d5ba83b0c 100644
--- a/src/mod_s2s_dialback.erl
+++ b/src/mod_s2s_dialback.erl
@@ -121,13 +121,14 @@ s2s_out_init(Acc, _Opts) ->
s2s_out_closed(#{server := LServer,
remote_server := RServer,
+ lang := Lang,
db_verify := {StreamID, _Key, _Pid}} = State, Reason) ->
%% Outbound s2s verificating connection (created at step 1) is
%% closed suddenly without receiving the response.
%% Building a response on our own
Response = #db_verify{from = RServer, to = LServer,
id = StreamID, type = error,
- sub_els = [mk_error(Reason)]},
+ sub_els = [mk_error(Reason, Lang)]},
s2s_out_packet(State, Response);
s2s_out_closed(State, _Reason) ->
State.
@@ -171,7 +172,7 @@ s2s_out_downgraded(#{db_enabled := true,
s2s_out_downgraded(State, _) ->
State.
-s2s_in_packet(#{stream_id := StreamID} = State,
+s2s_in_packet(#{stream_id := StreamID, lang := Lang} = State,
#db_result{from = From, to = To, key = Key, type = undefined}) ->
%% Received dialback request, section 2.2.1, step 1
try
@@ -186,7 +187,7 @@ s2s_in_packet(#{stream_id := StreamID} = State,
{stop,
send_db_result(State,
#db_verify{from = From, to = To, type = error,
- sub_els = [mk_error(Reason)]})}
+ sub_els = [mk_error(Reason, Lang)]})}
end;
s2s_in_packet(State, #db_verify{to = To, from = From, key = Key,
id = StreamID, type = undefined}) ->
@@ -204,7 +205,7 @@ s2s_in_packet(State, Pkt) when is_record(Pkt, db_result);
s2s_in_packet(State, _) ->
State.
-s2s_in_recv(State, El, {error, Why}) ->
+s2s_in_recv(#{lang := Lang} = State, El, {error, Why}) ->
case xmpp:get_name(El) of
Tag when Tag == <<"db:result">>;
Tag == <<"db:verify">> ->
@@ -212,7 +213,7 @@ s2s_in_recv(State, El, {error, Why}) ->
T when T /= <<"valid">>,
T /= <<"invalid">>,
T /= <<"error">> ->
- Err = xmpp:make_error(El, mk_error({codec_error, Why})),
+ Err = xmpp:make_error(El, mk_error({codec_error, Why}, Lang)),
{stop, ejabberd_s2s_in:send(State, Err)};
_ ->
State
@@ -316,17 +317,17 @@ check_from_to(From, To) ->
end
end.
--spec mk_error(term()) -> stanza_error().
-mk_error(forbidden) ->
- xmpp:err_forbidden(<<"Access denied by service policy">>, ?MYLANG);
-mk_error(host_unknown) ->
- xmpp:err_not_allowed(<<"Host unknown">>, ?MYLANG);
-mk_error({codec_error, Why}) ->
- xmpp:err_bad_request(xmpp:io_format_error(Why), ?MYLANG);
-mk_error({_Class, _Reason} = Why) ->
+-spec mk_error(term(), binary()) -> stanza_error().
+mk_error(forbidden, Lang) ->
+ xmpp:err_forbidden(<<"Access denied by service policy">>, Lang);
+mk_error(host_unknown, Lang) ->
+ xmpp:err_not_allowed(<<"Host unknown">>, Lang);
+mk_error({codec_error, Why}, Lang) ->
+ xmpp:err_bad_request(xmpp:io_format_error(Why), Lang);
+mk_error({_Class, _Reason} = Why, Lang) ->
Txt = xmpp_stream_out:format_error(Why),
- xmpp:err_remote_server_not_found(Txt, ?MYLANG);
-mk_error(_) ->
+ xmpp:err_remote_server_not_found(Txt, Lang);
+mk_error(_, _) ->
xmpp:err_internal_server_error().
-spec format_error(db_result()) -> binary().
diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl
index 48e7ac985..6ca5a8b39 100644
--- a/src/mod_stream_mgmt.erl
+++ b/src/mod_stream_mgmt.erl
@@ -176,14 +176,24 @@ c2s_authenticated_packet(#{mgmt_state := MgmtState} = State, Pkt)
c2s_authenticated_packet(State, Pkt) ->
update_num_stanzas_in(State, Pkt).
-c2s_handle_recv(#{lang := Lang} = State, El, {error, Why}) ->
+c2s_handle_recv(#{mgmt_state := MgmtState,
+ lang := Lang} = State, El, {error, Why}) ->
Xmlns = xmpp:get_ns(El),
+ IsStanza = xmpp:is_stanza(El),
if Xmlns == ?NS_STREAM_MGMT_2; Xmlns == ?NS_STREAM_MGMT_3 ->
Txt = xmpp:io_format_error(Why),
Err = #sm_failed{reason = 'bad-request',
text = xmpp:mk_text(Txt, Lang),
xmlns = Xmlns},
send(State, Err);
+ IsStanza andalso (MgmtState == pending orelse MgmtState == active) ->
+ State1 = update_num_stanzas_in(State, El),
+ case xmpp:get_type(El) of
+ <<"result">> -> State1;
+ <<"error">> -> State1;
+ _ ->
+ State1#{mgmt_force_enqueue => true}
+ end;
true ->
State
end;
@@ -193,24 +203,24 @@ c2s_handle_recv(State, _, _) ->
c2s_handle_send(#{mgmt_state := MgmtState, mod := Mod,
lang := Lang} = State, Pkt, SendResult)
when MgmtState == pending; MgmtState == active ->
+ IsStanza = xmpp:is_stanza(Pkt),
case Pkt of
- _ when ?is_stanza(Pkt) ->
- Meta = xmpp:get_meta(Pkt),
- case maps:get(mgmt_is_resent, Meta, false) of
- false ->
- case mgmt_queue_add(State, Pkt) of
- #{mgmt_max_queue := exceeded} = State1 ->
- State2 = State1#{mgmt_resend => false},
+ _ when IsStanza ->
+ case need_to_enqueue(State, Pkt) of
+ {true, State1} ->
+ case mgmt_queue_add(State1, Pkt) of
+ #{mgmt_max_queue := exceeded} = State2 ->
+ State3 = State2#{mgmt_resend => false},
Err = xmpp:serr_policy_violation(
<<"Too many unacked stanzas">>, Lang),
- send(State2, Err);
- State1 when SendResult == ok ->
- send_rack(State1);
- State1 ->
- State1
+ send(State3, Err);
+ State2 when SendResult == ok ->
+ send_rack(State2);
+ State2 ->
+ State2
end;
- true ->
- State
+ {false, State1} ->
+ State1
end;
#stream_error{} ->
case MgmtState of
@@ -242,13 +252,13 @@ c2s_handle_info(#{mgmt_ack_timer := TRef, jid := JID, mod := Mod} = State,
[jid:encode(JID)]),
State1 = Mod:close(State),
{stop, transition_to_pending(State1)};
-c2s_handle_info(#{mgmt_state := pending,
+c2s_handle_info(#{mgmt_state := pending, lang := Lang,
mgmt_pending_timer := TRef, jid := JID, mod := Mod} = State,
{timeout, TRef, pending_timeout}) ->
?DEBUG("Timed out waiting for resumption of stream for ~s",
[jid:encode(JID)]),
Txt = <<"Timed out waiting for stream resumption">>,
- Err = xmpp:serr_connection_timeout(Txt, ?MYLANG),
+ Err = xmpp:serr_connection_timeout(Txt, Lang),
Mod:stop(State#{mgmt_state => timeout,
stop_reason => {stream, {out, Err}}});
c2s_handle_info(#{jid := JID} = State, {_Ref, {resume, OldState}}) ->
@@ -269,8 +279,8 @@ c2s_closed(State, _Reason) ->
State.
c2s_terminated(#{mgmt_state := resumed, jid := JID} = State, _Reason) ->
- ?INFO_MSG("Closing former stream of resumed session for ~s",
- [jid:encode(JID)]),
+ ?DEBUG("Closing former stream of resumed session for ~s",
+ [jid:encode(JID)]),
bounce_message_queue(),
{stop, State};
c2s_terminated(#{mgmt_state := MgmtState, mgmt_stanzas_in := In, sid := SID,
@@ -361,15 +371,15 @@ handle_enable(#{mgmt_timeout := DefaultTimeout,
DefaultTimeout
end,
Res = if Timeout > 0 ->
- ?INFO_MSG("Stream management with resumption enabled for ~s",
- [jid:encode(JID)]),
+ ?DEBUG("Stream management with resumption enabled for ~s",
+ [jid:encode(JID)]),
#sm_enabled{xmlns = Xmlns,
id = make_resume_id(State),
resume = true,
max = Timeout};
true ->
- ?INFO_MSG("Stream management without resumption enabled for ~s",
- [jid:encode(JID)]),
+ ?DEBUG("Stream management without resumption enabled for ~s",
+ [jid:encode(JID)]),
#sm_enabled{xmlns = Xmlns}
end,
State1 = State#{mgmt_state => active,
@@ -491,7 +501,7 @@ resend_rack(#{mgmt_ack_timer := _,
resend_rack(State) ->
State.
--spec mgmt_queue_add(state(), xmpp_element()) -> state().
+-spec mgmt_queue_add(state(), xmlel() | xmpp_element()) -> state().
mgmt_queue_add(#{mgmt_stanzas_out := NumStanzasOut,
mgmt_queue := Queue} = State, Pkt) ->
NewNum = case NumStanzasOut of
@@ -531,8 +541,13 @@ resend_unacked_stanzas(#{mgmt_state := MgmtState,
[p1_queue:len(Queue), jid:encode(JID)]),
p1_queue:foldl(
fun({_, Time, Pkt}, AccState) ->
- NewPkt = add_resent_delay_info(AccState, Pkt, Time),
- send(AccState, xmpp:put_meta(NewPkt, mgmt_is_resent, true))
+ Pkt1 = add_resent_delay_info(AccState, Pkt, Time),
+ Pkt2 = if ?is_stanza(Pkt1) ->
+ xmpp:put_meta(Pkt1, mgmt_is_resent, true);
+ true ->
+ Pkt1
+ end,
+ send(AccState, Pkt2)
end, State, Queue);
resend_unacked_stanzas(State) ->
State.
@@ -646,6 +661,8 @@ inherit_session_state(#{user := U, server := S,
{error, Msg}
catch exit:{noproc, _} ->
{error, <<"Previous session PID is dead">>};
+ exit:{normal, _} ->
+ {error, <<"Previous session PID has exited">>};
exit:{timeout, _} ->
{error, <<"Session state copying timed out">>}
end
@@ -669,6 +686,7 @@ add_resent_delay_info(#{lserver := LServer}, El, Time)
when is_record(El, message); is_record(El, presence) ->
xmpp_util:add_delay_info(El, jid:make(LServer), Time, <<"Resent">>);
add_resent_delay_info(_State, El, _Time) ->
+ %% TODO
El.
-spec send(state(), xmpp_element()) -> state().
@@ -711,6 +729,14 @@ bounce_message_queue() ->
ok
end.
+-spec need_to_enqueue(state(), xmlel() | stanza()) -> {boolean(), state()}.
+need_to_enqueue(State, Pkt) when ?is_stanza(Pkt) ->
+ {not xmpp:get_meta(Pkt, mgmt_is_resent, false), State};
+need_to_enqueue(#{mgmt_force_enqueue := true} = State, #xmlel{}) ->
+ {true, maps:remove(mgmt_is_resent, State)};
+need_to_enqueue(State, _) ->
+ {false, State}.
+
%%%===================================================================
%%% Configuration processing
%%%===================================================================
diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl
index 14d78702e..62cb32417 100644
--- a/src/prosody2ejabberd.erl
+++ b/src/prosody2ejabberd.erl
@@ -201,8 +201,10 @@ convert_data(Host, "offline", User, [Data]) ->
fun({_, RawXML}) ->
case deserialize(RawXML) of
[El] ->
- Msg = el_to_offline_msg(LUser, LServer, El),
- ok = mod_offline:store_offline_msg(Msg);
+ case el_to_offline_msg(LUser, LServer, El) of
+ [Msg] -> ok = mod_offline:store_offline_msg(Msg);
+ [] -> ok
+ end;
_ ->
ok
end
diff --git a/src/randoms.erl b/src/randoms.erl
index a64012ca3..101241a3c 100644
--- a/src/randoms.erl
+++ b/src/randoms.erl
@@ -28,7 +28,7 @@
-author('alexey@process-one.net').
-export([get_string/0, uniform/0, uniform/1, uniform/2, bytes/1,
- round_robin/1]).
+ round_robin/1, get_alphanum_string/1]).
-define(THRESHOLD, 16#10000000000000000).
@@ -71,3 +71,21 @@ bytes(N) ->
-spec round_robin(pos_integer()) -> non_neg_integer().
round_robin(N) ->
p1_time_compat:unique_integer([monotonic, positive]) rem N.
+
+-spec get_alphanum_string(non_neg_integer()) -> binary().
+get_alphanum_string(Length) ->
+ list_to_binary(get_alphanum_string([], Length)).
+
+-spec get_alphanum_string(string(), non_neg_integer()) -> string().
+get_alphanum_string(S, 0) -> S;
+get_alphanum_string(S, N) ->
+ get_alphanum_string([make_rand_char() | S], N - 1).
+
+-spec make_rand_char() -> char().
+make_rand_char() ->
+ map_int_to_char(uniform(0, 61)).
+
+-spec map_int_to_char(0..61) -> char().
+map_int_to_char(N) when N =< 9 -> N + 48; % Digit.
+map_int_to_char(N) when N =< 35 -> N + 55; % Upper-case character.
+map_int_to_char(N) when N =< 61 -> N + 61. % Lower-case character.
diff --git a/src/xmpp_stream_in.erl b/src/xmpp_stream_in.erl
index 4f8be911e..55fa3a4bf 100644
--- a/src/xmpp_stream_in.erl
+++ b/src/xmpp_stream_in.erl
@@ -836,13 +836,13 @@ process_sasl_success(Props, ServerOut,
AuthModule = proplists:get_value(auth_module, Props),
Socket1 = xmpp_socket:reset_stream(Socket),
State0 = State#{socket => Socket1},
- State1 = send_pkt(State0, #sasl_success{text = ServerOut}),
+ State1 = try Mod:handle_auth_success(User, Mech, AuthModule, State0)
+ catch _:undef -> State
+ end,
case is_disconnected(State1) of
true -> State1;
false ->
- State2 = try Mod:handle_auth_success(User, Mech, AuthModule, State1)
- catch _:undef -> State1
- end,
+ State2 = send_pkt(State1, #sasl_success{text = ServerOut}),
case is_disconnected(State2) of
true -> State2;
false ->
@@ -867,16 +867,22 @@ process_sasl_continue(ServerOut, NewSASLState, State) ->
process_sasl_failure(Err, User,
#{mod := Mod, sasl_mech := Mech, lang := Lang} = State) ->
{Reason, Text} = format_sasl_error(Mech, Err),
- State1 = send_pkt(State, #sasl_failure{reason = Reason,
- text = xmpp:mk_text(Text, Lang)}),
+ State1 = try Mod:handle_auth_failure(User, Mech, Text, State)
+ catch _:undef -> State
+ end,
case is_disconnected(State1) of
true -> State1;
false ->
- State2 = try Mod:handle_auth_failure(User, Mech, Text, State1)
- catch _:undef -> State1
- end,
- State3 = maps:remove(sasl_state, maps:remove(sasl_mech, State2)),
- State3#{stream_state => wait_for_sasl_request}
+ State2 = send_pkt(State1,
+ #sasl_failure{reason = Reason,
+ text = xmpp:mk_text(Text, Lang)}),
+ case is_disconnected(State2) of
+ true -> State2;
+ false ->
+ State3 = maps:remove(sasl_state,
+ maps:remove(sasl_mech, State2)),
+ State3#{stream_state => wait_for_sasl_request}
+ end
end.
-spec process_sasl_abort(state()) -> state().