aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ejabberd.erl11
-rw-r--r--src/ejabberd_access_permissions.erl6
-rw-r--r--src/ejabberd_app.erl3
-rw-r--r--src/ejabberd_config.erl3
-rw-r--r--src/ejabberd_http.erl57
-rw-r--r--src/ejabberd_http_ws.erl1
-rw-r--r--src/ejabberd_pkix.erl54
-rw-r--r--src/ejabberd_service.erl10
-rw-r--r--src/ejabberd_sql.erl14
-rw-r--r--src/ejabberd_sql_pt.erl197
-rw-r--r--src/ejabberd_web_admin.erl20
-rw-r--r--src/gen_mod.erl120
-rw-r--r--src/misc.erl25
-rw-r--r--src/mod_admin_extra.erl34
-rw-r--r--src/mod_avatar.erl27
-rw-r--r--src/mod_client_state.erl24
-rw-r--r--src/mod_configure.erl7
-rw-r--r--src/mod_http_upload.erl20
-rw-r--r--src/mod_last.erl4
-rw-r--r--src/mod_mam_sql.erl10
-rw-r--r--src/mod_muc_log.erl14
-rw-r--r--src/mod_multicast.erl496
-rw-r--r--src/mod_pubsub.erl230
-rw-r--r--src/mod_push_keepalive.erl12
-rw-r--r--src/mod_register.erl10
-rw-r--r--src/mod_roster.erl73
-rw-r--r--src/mod_shared_roster.erl80
-rw-r--r--src/mod_stream_mgmt.erl6
-rw-r--r--src/mod_vcard_ldap.erl10
-rw-r--r--src/mod_vcard_sql.erl8
-rw-r--r--src/mod_vcard_xupdate.erl20
-rw-r--r--src/prosody2ejabberd.erl41
-rw-r--r--src/pubsub_migrate.erl3
-rw-r--r--src/rest.erl2
34 files changed, 793 insertions, 859 deletions
diff --git a/src/ejabberd.erl b/src/ejabberd.erl
index 356fd66c7..4740bd034 100644
--- a/src/ejabberd.erl
+++ b/src/ejabberd.erl
@@ -25,6 +25,7 @@
-module(ejabberd).
-author('alexey@process-one.net').
+-compile({no_auto_import, [{halt, 0}]}).
-protocol({xep, 4, '2.9'}).
-protocol({xep, 86, '1.0'}).
@@ -36,7 +37,7 @@
-protocol({xep, 243, '1.0'}).
-protocol({xep, 270, '1.0'}).
--export([start/0, stop/0, start_app/1, start_app/2,
+-export([start/0, stop/0, halt/0, start_app/1, start_app/2,
get_pid_file/0, check_app/1, module_name/1]).
-include("logger.hrl").
@@ -49,6 +50,11 @@ stop() ->
application:stop(ejabberd).
%%ejabberd_cover:stop().
+halt() ->
+ application:stop(lager),
+ application:stop(sasl),
+ erlang:halt(1, [{flush, true}]).
+
%% @spec () -> false | string()
get_pid_file() ->
case os:getenv("EJABBERD_PID_PATH") of
@@ -131,8 +137,7 @@ exit_or_halt(Reason, StartFlag) ->
?CRITICAL_MSG(Reason, []),
if StartFlag ->
%% Wait for the critical message is written in the console/log
- timer:sleep(1000),
- halt(string:substr(lists:flatten(Reason), 1, 199));
+ halt();
true ->
erlang:error(application_start_failed)
end.
diff --git a/src/ejabberd_access_permissions.erl b/src/ejabberd_access_permissions.erl
index a863e1557..3a7f81d53 100644
--- a/src/ejabberd_access_permissions.erl
+++ b/src/ejabberd_access_permissions.erl
@@ -492,6 +492,8 @@ parse_single_what(Binary) when is_binary(Binary) ->
_ ->
{error, <<"Invalid value">>}
end;
+parse_single_what(Atom) when is_atom(Atom) ->
+ parse_single_what(atom_to_binary(Atom, latin1));
parse_single_what(_) ->
{error, <<"Invalid value">>}.
@@ -502,7 +504,9 @@ is_valid_command_name(Val) ->
is_valid_command_name2(<<>>) ->
true;
-is_valid_command_name2(<<K:8, Rest/binary>>) when K >= $a andalso K =< $z orelse K == $_ ->
+is_valid_command_name2(<<K:8, Rest/binary>>) when (K >= $a andalso K =< $z)
+ orelse (K >= $0 andalso K =< $9)
+ orelse K == $_ ->
is_valid_command_name2(Rest);
is_valid_command_name2(_) ->
false.
diff --git a/src/ejabberd_app.erl b/src/ejabberd_app.erl
index a7e03d990..f1066815f 100644
--- a/src/ejabberd_app.erl
+++ b/src/ejabberd_app.erl
@@ -62,8 +62,7 @@ start(normal, _Args) ->
{ok, SupPid};
Err ->
?CRITICAL_MSG("Failed to start ejabberd application: ~p", [Err]),
- timer:sleep(1000),
- halt("Refer to ejabberd log files to diagnose the problem")
+ ejabberd:halt()
end;
start(_, _) ->
{error, badarg}.
diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl
index cf3d099cc..3f88df59a 100644
--- a/src/ejabberd_config.erl
+++ b/src/ejabberd_config.erl
@@ -455,8 +455,7 @@ get_config_lines2(Fd, Data, CurrLine, [NextWanted | LNumbers], R) when is_list(D
exit_or_halt(ExitText) ->
case [Vsn || {ejabberd, _Desc, Vsn} <- application:which_applications()] of
[] ->
- timer:sleep(1000),
- halt(string:substr(ExitText, 1, 199));
+ ejabberd:halt();
[_] ->
exit(ExitText)
end.
diff --git a/src/ejabberd_http.erl b/src/ejabberd_http.erl
index 474304a5d..dee16927e 100644
--- a/src/ejabberd_http.erl
+++ b/src/ejabberd_http.erl
@@ -31,7 +31,7 @@
%% External exports
-export([start/2, start_link/2, become_controller/1,
- socket_type/0, receive_headers/1, url_encode/1,
+ socket_type/0, receive_headers/1,
transform_listen_option/2, listen_opt_type/1]).
-export([init/2, opt_type/1]).
@@ -679,7 +679,7 @@ url_decode_q_split(<<>>, Ack) ->
path_decode(Path) -> path_decode(Path, <<>>).
path_decode(<<$%, Hi, Lo, Tail/binary>>, Acc) ->
- Hex = hex_to_integer([Hi, Lo]),
+ Hex = list_to_integer([Hi, Lo], 16),
if Hex == 0 -> exit(badurl);
true -> ok
end,
@@ -716,22 +716,6 @@ expand_custom_headers(Headers) ->
{K, misc:expand_keyword(<<"@VERSION@">>, V, ?VERSION)}
end, Headers).
-%% hex_to_integer
-
-hex_to_integer(Hex) ->
- case catch list_to_integer(Hex, 16) of
- {'EXIT', _} -> old_hex_to_integer(Hex);
- X -> X
- end.
-
-old_hex_to_integer(Hex) ->
- DEHEX = fun (H) when H >= $a, H =< $f -> H - $a + 10;
- (H) when H >= $A, H =< $F -> H - $A + 10;
- (H) when H >= $0, H =< $9 -> H - $0
- end,
- lists:foldl(fun (E, Acc) -> Acc * 16 + DEHEX(E) end, 0,
- Hex).
-
code_to_phrase(100) -> <<"Continue">>;
code_to_phrase(101) -> <<"Switching Protocols ">>;
code_to_phrase(200) -> <<"OK">>;
@@ -802,7 +786,7 @@ parse_urlencoded(S) ->
parse_urlencoded(<<$%, Hi, Lo, Tail/binary>>, Last, Cur,
State) ->
- Hex = hex_to_integer([Hi, Lo]),
+ Hex = list_to_integer([Hi, Lo], 16),
parse_urlencoded(Tail, Last, <<Cur/binary, Hex>>, State);
parse_urlencoded(<<$&, Tail/binary>>, _Last, Cur, key) ->
[{Cur, <<"">>} | parse_urlencoded(Tail,
@@ -822,41 +806,6 @@ parse_urlencoded(<<>>, Last, Cur, _State) ->
[{Last, Cur}];
parse_urlencoded(undefined, _, _, _) -> [].
-
-url_encode(A) ->
- url_encode(A, <<>>).
-
-url_encode(<<H:8, T/binary>>, Acc) when
- (H >= $a andalso H =< $z) orelse
- (H >= $A andalso H =< $Z) orelse
- (H >= $0 andalso H =< $9) orelse
- H == $_ orelse
- H == $. orelse
- H == $- orelse
- H == $/ orelse
- H == $: ->
- url_encode(T, <<Acc/binary, H>>);
-url_encode(<<H:8, T/binary>>, Acc) ->
- case integer_to_hex(H) of
- [X, Y] -> url_encode(T, <<Acc/binary, $%, X, Y>>);
- [X] -> url_encode(T, <<Acc/binary, $%, $0, X>>)
- end;
-url_encode(<<>>, Acc) ->
- Acc.
-
-
-integer_to_hex(I) ->
- case catch erlang:integer_to_list(I, 16) of
- {'EXIT', _} -> old_integer_to_hex(I);
- Int -> Int
- end.
-
-old_integer_to_hex(I) when I < 10 -> integer_to_list(I);
-old_integer_to_hex(I) when I < 16 -> [I - 10 + $A];
-old_integer_to_hex(I) when I >= 16 ->
- N = trunc(I / 16),
- old_integer_to_hex(N) ++ old_integer_to_hex(I rem 16).
-
% The following code is mostly taken from yaws_ssl.erl
toupper(C) when C >= $a andalso C =< $z -> C - 32;
diff --git a/src/ejabberd_http_ws.erl b/src/ejabberd_http_ws.erl
index 1edaeb073..0c15ab6c0 100644
--- a/src/ejabberd_http_ws.erl
+++ b/src/ejabberd_http_ws.erl
@@ -134,6 +134,7 @@ init([{#ws{ip = IP, http_opts = HOpts}, _} = WS]) ->
({resume_timeout, _}) -> true;
({max_resume_timeout, _}) -> true;
({resend_on_timeout, _}) -> true;
+ ({access, _}) -> true;
(_) -> false
end, HOpts),
Opts = ejabberd_c2s_config:get_c2s_limits() ++ SOpts,
diff --git a/src/ejabberd_pkix.erl b/src/ejabberd_pkix.erl
index 217c27d55..229492bea 100644
--- a/src/ejabberd_pkix.erl
+++ b/src/ejabberd_pkix.erl
@@ -37,7 +37,6 @@
-include("logger.hrl").
-record(state, {validate = true :: boolean(),
- notify = false :: boolean(),
paths = [] :: [file:filename()],
certs = #{} :: map(),
graph :: digraph:graph(),
@@ -173,6 +172,7 @@ config_reloaded() ->
true -> init_cache();
false -> delete_cache()
end,
+ fast_tls:clear_cache(),
gen_server:call(?MODULE, config_reloaded, 60000).
opt_type(ca_path) ->
@@ -197,7 +197,6 @@ opt_type(_) ->
%%% gen_server callbacks
%%%===================================================================
init([]) ->
- Notify = start_fs(),
process_flag(trap_exit, true),
ets:new(?MODULE, [named_table, public]),
ejabberd_hooks:add(route_registered, ?MODULE, route_registered, 50),
@@ -214,7 +213,7 @@ init([]) ->
end,
G = digraph:new([acyclic]),
init_cache(),
- State = #state{validate = Validate, notify = Notify, graph = G},
+ State = #state{validate = Validate, graph = G},
case filelib:ensure_dir(filename:join(certs_dir(), "foo")) of
ok ->
clean_dir(certs_dir()),
@@ -279,20 +278,6 @@ handle_call(_Request, _From, State) ->
handle_cast(_Msg, State) ->
{noreply, State}.
-handle_info({_, {fs, file_event}, {File, Events}}, State) ->
- ?DEBUG("got FS events for ~s: ~p", [File, Events]),
- Path = iolist_to_binary(File),
- case lists:member(modified, Events) of
- true ->
- case lists:member(Path, State#state.paths) of
- true ->
- handle_cast(config_reloaded, State);
- false ->
- {noreply, State}
- end;
- false ->
- {noreply, State}
- end;
handle_info(_Info, State) ->
?WARNING_MSG("unexpected info: ~p", [_Info]),
{noreply, State}.
@@ -419,7 +404,6 @@ build_chain_and_check(State) ->
?DEBUG("Validating certificates", []),
Errors = validate(CertPaths, State#state.validate),
?DEBUG("Subscribing to file events", []),
- subscribe(State),
lists:foreach(
fun({Cert, Why}) ->
Path = maps:get(Cert, State#state.certs),
@@ -854,40 +838,6 @@ short_name_hash(_) ->
"".
-endif.
--spec subscribe(state()) -> ok.
-subscribe(#state{notify = true} = State) ->
- lists:foreach(
- fun(Path) ->
- Dir = filename:dirname(Path),
- Name = list_to_atom(integer_to_list(erlang:phash2(Dir))),
- case fs:start_link(Name, Dir) of
- {ok, _} ->
- ?DEBUG("Subscribed to FS events from ~s", [Dir]),
- fs:subscribe(Name);
- {error, _} ->
- ok
- end
- end, State#state.paths);
-subscribe(_) ->
- ok.
-
--spec start_fs() -> boolean().
-start_fs() ->
- application:load(fs),
- application:set_env(fs, backwards_compatible, false),
- case application:ensure_all_started(fs) of
- {ok, _} -> true;
- {error, {already_loaded, _}} -> true;
- {error, Reason} ->
- ?ERROR_MSG("Failed to load 'fs' Erlang application: ~p; "
- "certificates change detection will be disabled. "
- "You should now manually run `ejabberdctl "
- "reload_config` whenever certificates are changed "
- "on disc",
- [Reason]),
- false
- end.
-
wildcard(Path) when is_binary(Path) ->
wildcard(binary_to_list(Path));
wildcard(Path) ->
diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl
index 03b768bdf..6445406d2 100644
--- a/src/ejabberd_service.erl
+++ b/src/ejabberd_service.erl
@@ -191,8 +191,14 @@ handle_authenticated_packet(Pkt0, #{ip := {IP, _}, lang := Lang} = State)
From = xmpp:get_from(Pkt),
case check_from(From, State) of
true ->
- ejabberd_router:route(Pkt),
- State;
+ {Pkt2, State2} = ejabberd_hooks:run_fold(component_send_packet, {Pkt, State}, []),
+ case Pkt2 of
+ drop ->
+ ok;
+ _ ->
+ ejabberd_router:route(Pkt2)
+ end,
+ State2;
false ->
Txt = <<"Improper domain part of 'from' attribute">>,
Err = xmpp:serr_invalid_from(Txt, Lang),
diff --git a/src/ejabberd_sql.erl b/src/ejabberd_sql.erl
index 1db61b70d..675941aea 100644
--- a/src/ejabberd_sql.erl
+++ b/src/ejabberd_sql.erl
@@ -39,6 +39,7 @@
sql_bloc/2,
abort/1,
restart/1,
+ use_new_schema/0,
sql_query_to_iolist/1,
escape/1,
standard_escape/1,
@@ -95,6 +96,12 @@
-define(PREPARE_KEY, ejabberd_sql_prepare).
+-ifdef(NEW_SQL_SCHEMA).
+-define(USE_NEW_SCHEMA_DEFAULT, true).
+-else.
+-define(USE_NEW_SCHEMA_DEFAULT, false).
+-endif.
+
%%-define(DBGFSM, true).
-ifdef(DBGFSM).
@@ -272,6 +279,9 @@ sqlite_file(Host) ->
binary_to_list(File)
end.
+use_new_schema() ->
+ ejabberd_config:get_option(new_sql_schema, ?USE_NEW_SCHEMA_DEFAULT).
+
%%%----------------------------------------------------------------------
%%% Callback functions from gen_fsm
%%%----------------------------------------------------------------------
@@ -1133,9 +1143,11 @@ opt_type(sql_connect_timeout) ->
fun (I) when is_integer(I), I > 0 -> I end;
opt_type(sql_queue_type) ->
fun(ram) -> ram; (file) -> file end;
+opt_type(new_sql_schema) -> fun(B) when is_boolean(B) -> B end;
opt_type(_) ->
[sql_database, sql_keepalive_interval,
sql_password, sql_port, sql_server,
sql_username, sql_ssl, sql_ssl_verify, sql_ssl_certfile,
sql_ssl_cafile, sql_queue_type, sql_query_timeout,
- sql_connect_timeout].
+ sql_connect_timeout,
+ new_sql_schema].
diff --git a/src/ejabberd_sql_pt.erl b/src/ejabberd_sql_pt.erl
index 10697f692..eb7905bf0 100644
--- a/src/ejabberd_sql_pt.erl
+++ b/src/ejabberd_sql_pt.erl
@@ -28,7 +28,7 @@
%% API
-export([parse_transform/2, format_error/1]).
--export([parse/2]).
+%-export([parse/2]).
-include("ejabberd_sql_pt.hrl").
@@ -41,7 +41,8 @@
res_vars = [],
res_pos = 0,
server_host_used = false,
- used_vars = []}).
+ used_vars = [],
+ use_new_schema}).
-define(QUERY_RECORD, "sql_query").
@@ -88,26 +89,7 @@ transform(Form) ->
[Arg] ->
case erl_syntax:type(Arg) of
string ->
- S = erl_syntax:string_value(Arg),
- Pos = erl_syntax:get_pos(Arg),
- ParseRes = parse(S, Pos),
- UnusedVars =
- case ParseRes#state.server_host_used of
- {true, SHVar} ->
- case ?USE_NEW_SCHEMA of
- true -> [];
- false -> [SHVar]
- end;
- false ->
- add_warning(
- Pos, no_server_host),
- []
- end,
- set_pos(
- add_unused_vars(
- make_sql_query(ParseRes),
- UnusedVars),
- Pos);
+ transform_sql(Arg);
_ ->
throw({error, erl_syntax:get_pos(Form),
"?SQL argument must be "
@@ -123,26 +105,7 @@ transform(Form) ->
case {erl_syntax:type(TableArg),
erl_syntax:is_proper_list(FieldsArg)}of
{string, true} ->
- Table = erl_syntax:string_value(TableArg),
- ParseRes =
- parse_upsert(
- erl_syntax:list_elements(FieldsArg)),
- Pos = erl_syntax:get_pos(Form),
- case lists:keymember(
- "server_host", 1, ParseRes) of
- true ->
- ok;
- false ->
- add_warning(Pos, no_server_host)
- end,
- {ParseRes2, UnusedVars} =
- filter_upsert_sh(Table, ParseRes),
- set_pos(
- add_unused_vars(
- make_sql_upsert(Table, ParseRes2, Pos),
- UnusedVars
- ),
- Pos);
+ transform_upsert(Form, TableArg, FieldsArg);
_ ->
throw({error, erl_syntax:get_pos(Form),
"?SQL_UPSERT arguments must be "
@@ -158,26 +121,7 @@ transform(Form) ->
case {erl_syntax:type(TableArg),
erl_syntax:is_proper_list(FieldsArg)}of
{string, true} ->
- Table = erl_syntax:string_value(TableArg),
- ParseRes =
- parse_insert(
- erl_syntax:list_elements(FieldsArg)),
- Pos = erl_syntax:get_pos(Form),
- case lists:keymember(
- "server_host", 1, ParseRes) of
- true ->
- ok;
- false ->
- add_warning(Pos, no_server_host)
- end,
- {ParseRes2, UnusedVars} =
- filter_upsert_sh(Table, ParseRes),
- set_pos(
- add_unused_vars(
- make_sql_insert(Table, ParseRes2),
- UnusedVars
- ),
- Pos);
+ transform_insert(Form, TableArg, FieldsArg);
_ ->
throw({error, erl_syntax:get_pos(Form),
"?SQL_INSERT arguments must be "
@@ -226,11 +170,81 @@ top_transform(Forms) when is_list(Forms) ->
end
end, Forms).
-parse(S, Loc) ->
- parse1(S, [], #state{loc = Loc}).
-
-parse(S, ParamPos, Loc) ->
- parse1(S, [], #state{loc = Loc, param_pos = ParamPos}).
+transform_sql(Arg) ->
+ S = erl_syntax:string_value(Arg),
+ Pos = erl_syntax:get_pos(Arg),
+ ParseRes = parse(S, Pos, true),
+ ParseResOld = parse(S, Pos, false),
+ case ParseRes#state.server_host_used of
+ {true, _SHVar} ->
+ ok;
+ false ->
+ add_warning(
+ Pos, no_server_host),
+ []
+ end,
+ set_pos(
+ make_schema_check(
+ make_sql_query(ParseRes),
+ make_sql_query(ParseResOld)
+ ),
+ Pos).
+
+transform_upsert(Form, TableArg, FieldsArg) ->
+ Table = erl_syntax:string_value(TableArg),
+ ParseRes =
+ parse_upsert(
+ erl_syntax:list_elements(FieldsArg)),
+ Pos = erl_syntax:get_pos(Form),
+ case lists:keymember(
+ "server_host", 1, ParseRes) of
+ true ->
+ ok;
+ false ->
+ add_warning(Pos, no_server_host)
+ end,
+ ParseResOld =
+ filter_upsert_sh(Table, ParseRes),
+ set_pos(
+ make_schema_check(
+ make_sql_upsert(Table, ParseRes, Pos),
+ make_sql_upsert(Table, ParseResOld, Pos)
+ ),
+ Pos).
+
+transform_insert(Form, TableArg, FieldsArg) ->
+ Table = erl_syntax:string_value(TableArg),
+ ParseRes =
+ parse_insert(
+ erl_syntax:list_elements(FieldsArg)),
+ Pos = erl_syntax:get_pos(Form),
+ case lists:keymember(
+ "server_host", 1, ParseRes) of
+ true ->
+ ok;
+ false ->
+ add_warning(Pos, no_server_host)
+ end,
+ ParseResOld =
+ filter_upsert_sh(Table, ParseRes),
+ set_pos(
+ make_schema_check(
+ make_sql_insert(Table, ParseRes),
+ make_sql_insert(Table, ParseResOld)
+ ),
+ Pos).
+
+
+parse(S, Loc, UseNewSchema) ->
+ parse1(S, [],
+ #state{loc = Loc,
+ use_new_schema = UseNewSchema}).
+
+parse(S, ParamPos, Loc, UseNewSchema) ->
+ parse1(S, [],
+ #state{loc = Loc,
+ param_pos = ParamPos,
+ use_new_schema = UseNewSchema}).
parse1([], Acc, State) ->
State1 = append_string(lists:reverse(Acc), State),
@@ -274,7 +288,7 @@ parse1([$%, $( | S], Acc, State) ->
State3 =
State2#state{server_host_used = {true, Name},
used_vars = [Name | State2#state.used_vars]},
- case ?USE_NEW_SCHEMA of
+ case State#state.use_new_schema of
true ->
Convert =
erl_syntax:application(
@@ -350,7 +364,7 @@ make_var(V) ->
make_sql_query(State) ->
- Hash = erlang:phash2(State#state{loc = undefined}),
+ Hash = erlang:phash2(State#state{loc = undefined, use_new_schema = true}),
SHash = <<"Q", (integer_to_binary(Hash))/binary>>,
Query = pack_query(State#state.'query'),
EQuery =
@@ -442,7 +456,7 @@ parse_upsert_field1([], _Acc, _ParamPos, Loc) ->
"?SQL_UPSERT fields must have the "
"following form: \"[!-]name=value\""});
parse_upsert_field1([$= | S], Acc, ParamPos, Loc) ->
- {lists:reverse(Acc), parse(S, ParamPos, Loc)};
+ {lists:reverse(Acc), parse(S, ParamPos, Loc, true)};
parse_upsert_field1([C | S], Acc, ParamPos, Loc) ->
parse_upsert_field1(S, [C | Acc], ParamPos, Loc).
@@ -632,7 +646,7 @@ parse_insert_field1([], _Acc, _ParamPos, Loc) ->
"?SQL_INSERT fields must have the "
"following form: \"name=value\""});
parse_insert_field1([$= | S], Acc, ParamPos, Loc) ->
- {lists:reverse(Acc), parse(S, ParamPos, Loc)};
+ {lists:reverse(Acc), parse(S, ParamPos, Loc, true)};
parse_insert_field1([C | S], Acc, ParamPos, Loc) ->
parse_insert_field1(S, [C | Acc], ParamPos, Loc).
@@ -640,6 +654,23 @@ parse_insert_field1([C | S], Acc, ParamPos, Loc) ->
make_sql_insert(Table, ParseRes) ->
make_sql_query(make_sql_upsert_insert(Table, ParseRes)).
+make_schema_check(Tree, Tree) ->
+ Tree;
+make_schema_check(New, Old) ->
+ erl_syntax:case_expr(
+ erl_syntax:application(
+ erl_syntax:atom(ejabberd_sql),
+ erl_syntax:atom(use_new_schema),
+ []),
+ [erl_syntax:clause(
+ [erl_syntax:abstract(true)],
+ none,
+ [New]),
+ erl_syntax:clause(
+ [erl_syntax:abstract(false)],
+ none,
+ [Old])]).
+
concat_states(States) ->
lists:foldr(
@@ -711,26 +742,10 @@ set_pos(Tree, Pos) ->
end, Tree).
filter_upsert_sh(Table, ParseRes) ->
- case ?USE_NEW_SCHEMA of
- true ->
- {ParseRes, []};
- false ->
- lists:foldr(
- fun({Field, _Match, ST} = P, {Acc, Vars}) ->
- if
- Field /= "server_host" orelse Table == "route" ->
- {[P | Acc], Vars};
- true ->
- {Acc, ST#state.used_vars ++ Vars}
- end
- end, {[], []}, ParseRes)
- end.
-
-add_unused_vars(Tree, []) ->
- Tree;
-add_unused_vars(Tree, Vars) ->
- erl_syntax:block_expr(
- lists:map(fun erl_syntax:variable/1, Vars) ++ [Tree]).
+ lists:filter(
+ fun({Field, _Match, _ST}) ->
+ Field /= "server_host" orelse Table == "route"
+ end, ParseRes).
-ifdef(ENABLE_PT_WARNINGS).
diff --git a/src/ejabberd_web_admin.erl b/src/ejabberd_web_admin.erl
index da9664a48..524c80f50 100644
--- a/src/ejabberd_web_admin.erl
+++ b/src/ejabberd_web_admin.erl
@@ -74,21 +74,15 @@ get_acl_rule([<<"vhosts">>], _) ->
%% The pages of a vhost are only accesible if the user is admin of that vhost:
get_acl_rule([<<"server">>, VHost | _RPath], Method)
when Method =:= 'GET' orelse Method =:= 'HEAD' ->
- AC = ejabberd_config:get_option({access, VHost}, configure),
- ACR = ejabberd_config:get_option({access_readonly, VHost}, webadmin_view),
- {VHost, [AC, ACR]};
+ {VHost, [configure, webadmin_view]};
get_acl_rule([<<"server">>, VHost | _RPath], 'POST') ->
- AC = ejabberd_config:get_option({access, VHost}, configure),
- {VHost, [AC]};
+ {VHost, [configure]};
%% Default rule: only global admins can access any other random page
get_acl_rule(_RPath, Method)
when Method =:= 'GET' orelse Method =:= 'HEAD' ->
- AC = ejabberd_config:get_option(access, configure),
- ACR = ejabberd_config:get_option(access_readonly, webadmin_view),
- {global, [AC, ACR]};
+ {global, [configure, webadmin_view]};
get_acl_rule(_RPath, 'POST') ->
- AC = ejabberd_config:get_option(access, configure),
- {global, [AC]}.
+ {global, [configure]}.
%%%==================================
%%%% Menu Items Access
@@ -269,7 +263,7 @@ get_auth_account(HostOfRule, AccessRule, User, Server,
case ejabberd_auth:check_password(User, <<"">>, Server, Pass) of
true ->
case acl:any_rules_allowed(HostOfRule, AccessRule,
- jid:make(User, Server))
+ jid:make(User, Server))
of
false -> {unauthorized, <<"unprivileged-account">>};
true -> {ok, {User, Server}}
@@ -1246,7 +1240,7 @@ list_given_users(Host, Users, Prefix, Lang, URLFunc) ->
?XE(<<"tr">>,
[?XE(<<"td">>,
[?AC((URLFunc({user, Prefix,
- ejabberd_http:url_encode(User),
+ misc:url_encode(User),
Server})),
(us_to_list(US)))]),
?XE(<<"td">>, FQueueLen),
@@ -1325,7 +1319,7 @@ list_online_users(Host, _Lang) ->
SUsers = lists:usort(Users),
lists:flatmap(fun ({_S, U} = SU) ->
[?AC(<<"../user/",
- (ejabberd_http:url_encode(U))/binary, "/">>,
+ (misc:url_encode(U))/binary, "/">>,
(su_to_list(SU))),
?BR]
end,
diff --git a/src/gen_mod.erl b/src/gen_mod.erl
index a477ec295..3b63ed74a 100644
--- a/src/gen_mod.erl
+++ b/src/gen_mod.erl
@@ -32,8 +32,7 @@
-export([init/1, start_link/0, start_child/3, start_child/4,
stop_child/1, stop_child/2, config_reloaded/0]).
--export([start_module/2, start_module/3,
- stop_module/2, stop_module_keep_config/2,
+-export([start_module/2, stop_module/2, stop_module_keep_config/2,
get_opt/2, get_opt_hosts/2, opt_type/1, is_equal_opt/3,
get_module_opt/3, get_module_opt_host/3,
loaded_modules/1, loaded_modules_with_opts/1,
@@ -63,7 +62,8 @@
-record(ejabberd_module,
{module_host = {undefined, <<"">>} :: {atom(), binary()},
- opts = [] :: opts() | '_' | '$2'}).
+ opts = [] :: opts() | '_' | '$2',
+ order = 0 :: integer()}).
-type opts() :: [{atom(), any()}].
-type db_type() :: atom().
@@ -152,7 +152,7 @@ sort_modules(Host, ModOpts) ->
[Mod, DepMod]),
?ERROR_MSG(ErrTxt, []),
digraph:del_vertex(G, Mod),
- maybe_halt_ejabberd(ErrTxt);
+ maybe_halt_ejabberd();
false when Type == soft ->
?WARNING_MSG("Module '~s' is recommended for "
"module '~s' but is not found in "
@@ -171,7 +171,11 @@ sort_modules(Host, ModOpts) ->
end
end, Deps)
end, ModOpts),
- Result = [digraph:vertex(G, V) || V <- digraph_utils:topsort(G)],
+ {Result, _} = lists:mapfoldl(
+ fun(V, Order) ->
+ {M, O} = digraph:vertex(G, V),
+ {{M, O, Order}, Order+1}
+ end, 1, digraph_utils:topsort(G)),
digraph:delete(G),
Result.
@@ -180,8 +184,8 @@ sort_modules(Host, ModOpts) ->
start_modules(Host) ->
Modules = get_modules_options(Host),
lists:foreach(
- fun({Module, Opts}) ->
- start_module(Host, Module, Opts)
+ fun({Module, Opts, Order}) ->
+ start_module(Host, Module, Opts, Order)
end, Modules).
-spec start_module(binary(), atom()) -> ok | {ok, pid()} | {error, not_found_in_config}.
@@ -189,18 +193,18 @@ start_modules(Host) ->
start_module(Host, Module) ->
Modules = get_modules_options(Host),
case lists:keyfind(Module, 1, Modules) of
- {_, Opts} ->
- start_module(Host, Module, Opts);
+ {_, Opts, Order} ->
+ start_module(Host, Module, Opts, Order);
false ->
{error, not_found_in_config}
end.
--spec start_module(binary(), atom(), opts()) -> ok | {ok, pid()}.
-start_module(Host, Module, Opts) ->
- start_module(Host, Module, Opts, true).
+-spec start_module(binary(), atom(), opts(), integer()) -> ok | {ok, pid()}.
+start_module(Host, Module, Opts, Order) ->
+ start_module(Host, Module, Opts, Order, true).
--spec start_module(binary(), atom(), opts(), boolean()) -> ok | {ok, pid()}.
-start_module(Host, Module, Opts0, NeedValidation) ->
+-spec start_module(binary(), atom(), opts(), integer(), boolean()) -> ok | {ok, pid()}.
+start_module(Host, Module, Opts0, Order, NeedValidation) ->
?DEBUG("Loading ~s at ~s", [Module, Host]),
Res = if NeedValidation ->
validate_opts(Host, Module, Opts0);
@@ -209,7 +213,7 @@ start_module(Host, Module, Opts0, NeedValidation) ->
end,
case Res of
{ok, Opts} ->
- store_options(Host, Module, Opts),
+ store_options(Host, Module, Opts, Order),
try case Module:start(Host, Opts) of
ok -> ok;
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
@@ -236,22 +240,17 @@ start_module(Host, Module, Opts0, NeedValidation) ->
erlang:get_stacktrace()])
end,
?CRITICAL_MSG(ErrorText, []),
- maybe_halt_ejabberd(ErrorText),
+ maybe_halt_ejabberd(),
erlang:raise(Class, Reason, erlang:get_stacktrace())
end;
- {error, ErrorText} ->
- maybe_halt_ejabberd(ErrorText)
+ {error, _ErrorText} ->
+ maybe_halt_ejabberd()
end.
-spec reload_modules(binary()) -> ok.
reload_modules(Host) ->
- NewMods = ejabberd_config:get_option({modules, Host}, []),
- OldMods = ets:select(
- ejabberd_modules,
- ets:fun2ms(
- fun(#ejabberd_module{module_host = {M, H}, opts = O})
- when H == Host -> {M, O}
- end)),
+ NewMods = get_modules_options(Host),
+ OldMods = lists:reverse(loaded_modules_with_opts(Host)),
lists:foreach(
fun({Mod, _Opts}) ->
case lists:keymember(Mod, 1, NewMods) of
@@ -262,10 +261,10 @@ reload_modules(Host) ->
end
end, OldMods),
lists:foreach(
- fun({Mod, Opts}) ->
+ fun({Mod, Opts, Order}) ->
case lists:keymember(Mod, 1, OldMods) of
false ->
- start_module(Host, Mod, Opts);
+ start_module(Host, Mod, Opts, Order);
true ->
ok
end
@@ -273,12 +272,12 @@ reload_modules(Host) ->
lists:foreach(
fun({Mod, OldOpts}) ->
case lists:keyfind(Mod, 1, NewMods) of
- {_, NewOpts0} ->
+ {_, NewOpts0, Order} ->
case validate_opts(Host, Mod, NewOpts0) of
{ok, OldOpts} ->
ok;
{ok, NewOpts} ->
- reload_module(Host, Mod, NewOpts, OldOpts);
+ reload_module(Host, Mod, NewOpts, OldOpts, Order);
{error, _} ->
ok
end;
@@ -287,12 +286,12 @@ reload_modules(Host) ->
end
end, OldMods).
--spec reload_module(binary(), module(), opts(), opts()) -> ok | {ok, pid()}.
-reload_module(Host, Module, NewOpts, OldOpts) ->
+-spec reload_module(binary(), module(), opts(), opts(), integer()) -> ok | {ok, pid()}.
+reload_module(Host, Module, NewOpts, OldOpts, Order) ->
case erlang:function_exported(Module, reload, 3) of
true ->
?DEBUG("Reloading ~s at ~s", [Module, Host]),
- store_options(Host, Module, NewOpts),
+ store_options(Host, Module, NewOpts, Order),
try case Module:reload(Host, NewOpts, OldOpts) of
ok -> ok;
{ok, Pid} when is_pid(Pid) -> {ok, Pid};
@@ -310,23 +309,22 @@ reload_module(Host, Module, NewOpts, OldOpts) ->
?WARNING_MSG("Module ~s doesn't support reloading "
"and will be restarted", [Module]),
stop_module(Host, Module),
- start_module(Host, Module, NewOpts, false)
+ start_module(Host, Module, NewOpts, Order, false)
end.
--spec store_options(binary(), module(), opts()) -> true.
-store_options(Host, Module, Opts) ->
+-spec store_options(binary(), module(), opts(), integer()) -> true.
+store_options(Host, Module, Opts, Order) ->
ets:insert(ejabberd_modules,
#ejabberd_module{module_host = {Module, Host},
- opts = Opts}).
+ opts = Opts, order = Order}).
-maybe_halt_ejabberd(ErrorText) ->
+maybe_halt_ejabberd() ->
case is_app_running(ejabberd) of
false ->
?CRITICAL_MSG("ejabberd initialization was aborted "
"because a module start failed.",
[]),
- timer:sleep(3000),
- erlang:halt(string:substr(lists:flatten(ErrorText), 1, 199));
+ ejabberd:halt();
true ->
ok
end.
@@ -347,7 +345,7 @@ stop_modules() ->
-spec stop_modules(binary()) -> ok.
stop_modules(Host) ->
- Modules = lists:reverse(get_modules_options(Host)),
+ Modules = lists:reverse(loaded_modules_with_opts(Host)),
lists:foreach(
fun({Module, _Args}) ->
stop_module_keep_config(Host, Module)
@@ -555,7 +553,8 @@ validate_opts(Host, Module, Opts0) ->
undef ->
Opts;
Validators ->
- validate_opts(Host, Module, Opts, Required, Validators)
+ Opts1 = validate_opts(Host, Module, Opts, Required, Validators),
+ remove_duplicated_opts(Opts1)
end}
catch _:{missing_required_option, Opt} ->
ErrTxt = io_lib:format("Module '~s' is missing required option '~s'",
@@ -680,6 +679,16 @@ merge_opts(Opts, DefaultOpts) ->
end
end, Result, Opts).
+remove_duplicated_opts([{Opt, Val}, {Opt, _Default}|Opts]) ->
+ [{Opt, Val}|remove_duplicated_opts(Opts)];
+remove_duplicated_opts([{Opt, [{SubOpt, _}|_] = SubOpts}|Opts])
+ when is_atom(SubOpt) ->
+ [{Opt, remove_duplicated_opts(SubOpts)}|remove_duplicated_opts(Opts)];
+remove_duplicated_opts([OptVal|Opts]) ->
+ [OptVal|remove_duplicated_opts(Opts)];
+remove_duplicated_opts([]) ->
+ [].
+
-spec get_submodules(binary(), module(), opts()) -> [module()].
get_submodules(Host, Module, Opts) ->
try Module:mod_options(Host) of
@@ -788,17 +797,26 @@ is_db_configured(Type, Host) ->
-spec loaded_modules(binary()) -> [atom()].
loaded_modules(Host) ->
- ets:select(ejabberd_modules,
- [{#ejabberd_module{_ = '_', module_host = {'$1', Host}},
- [], ['$1']}]).
+ Mods = ets:select(
+ ejabberd_modules,
+ ets:fun2ms(
+ fun(#ejabberd_module{module_host = {Mod, H},
+ order = Order}) when H == Host ->
+ {Mod, Order}
+ end)),
+ [Mod || {Mod, _} <- lists:keysort(2, Mods)].
-spec loaded_modules_with_opts(binary()) -> [{atom(), opts()}].
loaded_modules_with_opts(Host) ->
- ets:select(ejabberd_modules,
- [{#ejabberd_module{_ = '_', module_host = {'$1', Host},
- opts = '$2'},
- [], [{{'$1', '$2'}}]}]).
+ Mods = ets:select(
+ ejabberd_modules,
+ ets:fun2ms(
+ fun(#ejabberd_module{module_host = {Mod, H}, opts = Opts,
+ order = Order}) when H == Host ->
+ {Mod, Opts, Order}
+ end)),
+ [{Mod, Opts} || {Mod, Opts, _} <- lists:keysort(3, Mods)].
-spec get_hosts(opts(), binary()) -> [binary()].
@@ -815,9 +833,9 @@ get_hosts(Opts, Prefix) ->
Hosts
end.
--spec get_module_proc(binary(), {frontend, atom()} | atom()) -> atom().
-get_module_proc(Host, {frontend, Base}) ->
- get_module_proc(<<"frontend_", Host/binary>>, Base);
+-spec get_module_proc(binary() | global, atom()) -> atom().
+get_module_proc(global, Base) ->
+ get_module_proc(<<"global">>, Base);
get_module_proc(Host, Base) ->
binary_to_atom(
<<(erlang:atom_to_binary(Base, latin1))/binary, "_", Host/binary>>,
diff --git a/src/misc.erl b/src/misc.erl
index 9f3210d5e..cd8641e61 100644
--- a/src/misc.erl
+++ b/src/misc.erl
@@ -29,7 +29,7 @@
%% API
-export([tolower/1, term_to_base64/1, base64_to_term/1, ip_to_list/1,
- hex_to_bin/1, hex_to_base64/1, expand_keyword/3,
+ hex_to_bin/1, hex_to_base64/1, url_encode/1, expand_keyword/3,
atom_to_binary/1, binary_to_atom/1, tuple_to_binary/1,
l2i/1, i2l/1, i2l/2, expr_to_term/1, term_to_expr/1,
now_to_usec/1, usec_to_now/1, encode_pid/1, decode_pid/2,
@@ -105,6 +105,10 @@ hex_to_bin([H1, H2 | T], Acc) ->
hex_to_base64(Hex) ->
base64:encode(hex_to_bin(Hex)).
+-spec url_encode(binary()) -> binary().
+url_encode(A) ->
+ url_encode(A, <<>>).
+
-spec expand_keyword(binary(), binary(), binary()) -> binary().
expand_keyword(Keyword, Input, Replacement) ->
Parts = binary:split(Input, Keyword, [global]),
@@ -262,6 +266,25 @@ read_js(File) ->
%%%===================================================================
%%% Internal functions
%%%===================================================================
+-spec url_encode(binary(), binary()) -> binary().
+url_encode(<<H:8, T/binary>>, Acc) when
+ (H >= $a andalso H =< $z) orelse
+ (H >= $A andalso H =< $Z) orelse
+ (H >= $0 andalso H =< $9) orelse
+ H == $_ orelse
+ H == $. orelse
+ H == $- orelse
+ H == $/ orelse
+ H == $: ->
+ url_encode(T, <<Acc/binary, H>>);
+url_encode(<<H:8, T/binary>>, Acc) ->
+ case integer_to_list(H, 16) of
+ [X, Y] -> url_encode(T, <<Acc/binary, $%, X, Y>>);
+ [X] -> url_encode(T, <<Acc/binary, $%, $0, X>>)
+ end;
+url_encode(<<>>, Acc) ->
+ Acc.
+
-spec set_node_id(string(), binary()) -> pid().
set_node_id(PidStr, NodeBin) ->
ExtPidStr = erlang:pid_to_list(
diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl
index 1f3ec0397..251c09614 100644
--- a/src/mod_admin_extra.erl
+++ b/src/mod_admin_extra.erl
@@ -225,7 +225,7 @@ get_commands_spec() ->
result_desc = "Status code: 0 on success, 1 otherwise"},
#ejabberd_commands{name = check_password_hash, tags = [accounts],
desc = "Check if the password hash is correct",
- longdesc = "Allowed hash methods: md5, sha.",
+ longdesc = "Allows hash methods from crypto application",
module = ?MODULE, function = check_password_hash,
args = [{user, binary}, {host, binary}, {passwordhash, binary},
{hashmethod, binary}],
@@ -786,24 +786,23 @@ get_cookie() ->
restart_module(Host, Module) when is_binary(Module) ->
restart_module(Host, misc:binary_to_atom(Module));
restart_module(Host, Module) when is_atom(Module) ->
- List = gen_mod:loaded_modules_with_opts(Host),
- case proplists:get_value(Module, List) of
- undefined ->
+ case gen_mod:is_loaded(Host, Module) of
+ false ->
% not a running module, force code reload anyway
code:purge(Module),
code:delete(Module),
code:load_file(Module),
1;
- Opts ->
+ true ->
gen_mod:stop_module(Host, Module),
case code:soft_purge(Module) of
true ->
code:delete(Module),
code:load_file(Module),
- gen_mod:start_module(Host, Module, Opts),
+ gen_mod:start_module(Host, Module),
0;
false ->
- gen_mod:start_module(Host, Module, Opts),
+ gen_mod:start_module(Host, Module),
2
end
end.
@@ -822,13 +821,15 @@ check_password(User, Host, Password) ->
%% Copied some code from ejabberd_commands.erl
check_password_hash(User, Host, PasswordHash, HashMethod) ->
AccountPass = ejabberd_auth:get_password_s(User, Host),
- AccountPassHash = case {AccountPass, HashMethod} of
+ Methods = lists:map(fun(A) -> atom_to_binary(A, latin1) end,
+ proplists:get_value(hashs, crypto:supports())),
+ MethodAllowed = lists:member(HashMethod, Methods),
+ AccountPassHash = case {AccountPass, MethodAllowed} of
{A, _} when is_tuple(A) -> scrammed;
- {_, <<"md5">>} -> get_md5(AccountPass);
- {_, <<"sha">>} -> get_sha(AccountPass);
- {_, Method} ->
+ {_, true} -> get_hash(AccountPass, HashMethod);
+ {_, false} ->
?ERROR_MSG("check_password_hash called "
- "with hash method: ~p", [Method]),
+ "with hash method: ~p", [HashMethod]),
undefined
end,
case AccountPassHash of
@@ -839,12 +840,11 @@ check_password_hash(User, Host, PasswordHash, HashMethod) ->
PasswordHash -> ok;
_ -> false
end.
-get_md5(AccountPass) ->
- iolist_to_binary([io_lib:format("~2.16.0B", [X])
- || X <- binary_to_list(erlang:md5(AccountPass))]).
-get_sha(AccountPass) ->
+
+get_hash(AccountPass, Method) ->
iolist_to_binary([io_lib:format("~2.16.0B", [X])
- || X <- binary_to_list(crypto:hash(sha, AccountPass))]).
+ || X <- binary_to_list(
+ crypto:hash(binary_to_atom(Method, latin1), AccountPass))]).
num_active_users(Host, Days) ->
DB_Type = gen_mod:get_module_opt(Host, mod_last, db_type),
diff --git a/src/mod_avatar.erl b/src/mod_avatar.erl
index 640f5f6b4..e92a87594 100644
--- a/src/mod_avatar.erl
+++ b/src/mod_avatar.erl
@@ -23,10 +23,13 @@
-module(mod_avatar).
-behaviour(gen_mod).
+-protocol({xep, 398, '0.2.0'}).
+
%% gen_mod API
-export([start/2, stop/1, reload/3, depends/2, mod_opt_type/1, mod_options/1]).
%% Hooks
--export([pubsub_publish_item/6, vcard_iq_convert/1, vcard_iq_publish/1]).
+-export([pubsub_publish_item/6, vcard_iq_convert/1, vcard_iq_publish/1,
+ get_sm_features/5]).
-include("xmpp.hrl").
-include("logger.hrl").
@@ -43,13 +46,17 @@ start(Host, _Opts) ->
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE,
vcard_iq_convert, 30),
ejabberd_hooks:add(vcard_iq_set, Host, ?MODULE,
- vcard_iq_publish, 100).
+ vcard_iq_publish, 100),
+ ejabberd_hooks:add(disco_sm_features, Host, ?MODULE,
+ get_sm_features, 50).
stop(Host) ->
ejabberd_hooks:delete(pubsub_publish_item, Host, ?MODULE,
pubsub_publish_item, 50),
ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_convert, 30),
- ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_publish, 100).
+ ejabberd_hooks:delete(vcard_iq_set, Host, ?MODULE, vcard_iq_publish, 100),
+ ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE,
+ get_sm_features, 50).
reload(_Host, _NewOpts, _OldOpts) ->
ok.
@@ -144,6 +151,20 @@ vcard_iq_publish(#iq{sub_els = [#vcard_temp{
vcard_iq_publish(Acc) ->
Acc.
+-spec get_sm_features({error, stanza_error()} | empty | {result, [binary()]},
+ jid(), jid(), binary(), binary()) ->
+ {error, stanza_error()} | empty | {result, [binary()]}.
+get_sm_features({error, _Error} = Acc, _From, _To, _Node, _Lang) ->
+ Acc;
+get_sm_features(Acc, _From, _To, <<"">>, _Lang) ->
+ {result, [?NS_DISCO_INFO, ?NS_PEP_VCARD_CONVERSION_0 |
+ case Acc of
+ {result, Features} -> Features;
+ empty -> []
+ end]};
+get_sm_features(Acc, _From, _To, _Node, _Lang) ->
+ Acc.
+
%%%===================================================================
%%% Internal functions
%%%===================================================================
diff --git a/src/mod_client_state.erl b/src/mod_client_state.erl
index dda22ed2b..3ed52bd24 100644
--- a/src/mod_client_state.erl
+++ b/src/mod_client_state.erl
@@ -37,8 +37,8 @@
-export([filter_presence/1, filter_chat_states/1,
filter_pep/1, filter_other/1,
c2s_stream_started/2, add_stream_feature/2,
- c2s_copy_session/2, c2s_authenticated_packet/2,
- c2s_session_resumed/1]).
+ c2s_authenticated_packet/2, csi_activity/2,
+ c2s_copy_session/2, c2s_session_resumed/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
@@ -164,6 +164,8 @@ register_hooks(Host) ->
add_stream_feature, 50),
ejabberd_hooks:add(c2s_authenticated_packet, Host, ?MODULE,
c2s_authenticated_packet, 50),
+ ejabberd_hooks:add(csi_activity, Host, ?MODULE,
+ csi_activity, 50),
ejabberd_hooks:add(c2s_copy_session, Host, ?MODULE,
c2s_copy_session, 50),
ejabberd_hooks:add(c2s_session_resumed, Host, ?MODULE,
@@ -179,6 +181,8 @@ unregister_hooks(Host) ->
add_stream_feature, 50),
ejabberd_hooks:delete(c2s_authenticated_packet, Host, ?MODULE,
c2s_authenticated_packet, 50),
+ ejabberd_hooks:delete(csi_activity, Host, ?MODULE,
+ csi_activity, 50),
ejabberd_hooks:delete(c2s_copy_session, Host, ?MODULE,
c2s_copy_session, 50),
ejabberd_hooks:delete(c2s_session_resumed, Host, ?MODULE,
@@ -194,14 +198,20 @@ c2s_stream_started(State, _) ->
init_csi_state(State).
-spec c2s_authenticated_packet(c2s_state(), xmpp_element()) -> c2s_state().
-c2s_authenticated_packet(C2SState, #csi{type = active}) ->
- C2SState1 = C2SState#{csi_state => active},
- flush_queue(C2SState1);
-c2s_authenticated_packet(C2SState, #csi{type = inactive}) ->
- C2SState#{csi_state => inactive};
+c2s_authenticated_packet(#{lserver := LServer} = C2SState, #csi{type = active}) ->
+ ejabberd_hooks:run_fold(csi_activity, LServer, C2SState, [active]);
+c2s_authenticated_packet(#{lserver := LServer} = C2SState, #csi{type = inactive}) ->
+ ejabberd_hooks:run_fold(csi_activity, LServer, C2SState, [inactive]);
c2s_authenticated_packet(C2SState, _) ->
C2SState.
+-spec csi_activity(c2s_state(), active | inactive) -> c2s_state().
+csi_activity(C2SState, active) ->
+ C2SState1 = C2SState#{csi_state => active},
+ flush_queue(C2SState1);
+csi_activity(C2SState, inactive) ->
+ C2SState#{csi_state => inactive}.
+
-spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state().
c2s_copy_session(C2SState, #{csi_queue := Q}) ->
C2SState#{csi_queue => Q};
diff --git a/src/mod_configure.erl b/src/mod_configure.erl
index db0780834..471e2bcdc 100644
--- a/src/mod_configure.erl
+++ b/src/mod_configure.erl
@@ -1528,8 +1528,11 @@ set_form(From, Host, ?NS_ADMINL(<<"add-user">>), _Lang,
true = lists:member(Server, ?MYHOSTS),
true = Server == Host orelse
get_permission_level(From) == global,
- ejabberd_auth:try_register(User, Server, Password),
- {result, undefined};
+ case ejabberd_auth:try_register(User, Server, Password) of
+ ok -> {result, undefined};
+ {error, exists} -> {error, xmpp:err_conflict()};
+ {error, not_allowed} -> {error, xmpp:err_not_allowed()}
+ end;
set_form(From, Host, ?NS_ADMINL(<<"delete-user">>),
_Lang, XData) ->
AccountStringList = get_values(<<"accountjids">>,
diff --git a/src/mod_http_upload.erl b/src/mod_http_upload.erl
index 9a5952926..4e522e9b1 100644
--- a/src/mod_http_upload.erl
+++ b/src/mod_http_upload.erl
@@ -31,7 +31,7 @@
-define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds.
-define(SLOT_TIMEOUT, 18000000). % 5 hours.
-define(FORMAT(Error), file:format_error(Error)).
--define(URL_ENC(URL), binary_to_list(ejabberd_http:url_encode(URL))).
+-define(URL_ENC(URL), binary_to_list(misc:url_encode(URL))).
-define(ADDR_TO_STR(IP), ejabberd_config:may_hide_data(misc:ip_to_list(IP))).
-define(STR_TO_INT(Str, B), binary_to_integer(iolist_to_binary(Str), B)).
-define(DEFAULT_CONTENT_TYPE, <<"application/octet-stream">>).
@@ -671,21 +671,31 @@ mk_slot(Slot, #state{put_url = PutPrefix, get_url = GetPrefix}, XMLNS) ->
GetURL = str:join([GetPrefix | Slot], <<$/>>),
mk_slot(PutURL, GetURL, XMLNS);
mk_slot(PutURL, GetURL, ?NS_HTTP_UPLOAD_0) ->
- #upload_slot_0{get = GetURL, put = PutURL, xmlns = ?NS_HTTP_UPLOAD_0};
+ #upload_slot_0{get = misc:url_encode(GetURL),
+ put = misc:url_encode(PutURL),
+ xmlns = ?NS_HTTP_UPLOAD_0};
mk_slot(PutURL, GetURL, XMLNS) ->
- #upload_slot{get = GetURL, put = PutURL, xmlns = XMLNS}.
+ #upload_slot{get = misc:url_encode(GetURL),
+ put = misc:url_encode(PutURL),
+ xmlns = XMLNS}.
-spec make_user_string(jid(), sha1 | node) -> binary().
make_user_string(#jid{luser = U, lserver = S}, sha1) ->
str:sha(<<U/binary, $@, S/binary>>);
make_user_string(#jid{luser = U}, node) ->
- re:replace(U, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
+ replace_special_chars(U).
-spec make_file_string(binary()) -> binary().
make_file_string(File) ->
- re:replace(File, <<"[^a-zA-Z0-9_.-]">>, <<$_>>, [global, {return, binary}]).
+ replace_special_chars(File).
+
+-spec replace_special_chars(binary()) -> binary().
+
+replace_special_chars(S) ->
+ re:replace(S, <<"[^\\p{Xan}_.-]">>, <<$_>>,
+ [unicode, global, {return, binary}]).
-spec yield_content_type(binary()) -> binary().
diff --git a/src/mod_last.erl b/src/mod_last.erl
index 7e53fe5dc..ae02b15ad 100644
--- a/src/mod_last.erl
+++ b/src/mod_last.erl
@@ -188,7 +188,9 @@ get_last(LUser, LServer) ->
?LAST_CACHE, {LUser, LServer},
fun() -> Mod:get_last(LUser, LServer) end);
false ->
- Mod:get_last(LUser, LServer)
+ Mod:get_last(LUser, LServer);
+ undefined ->
+ error
end,
case Res of
{ok, {TimeStamp, Status}} -> {ok, TimeStamp, Status};
diff --git a/src/mod_mam_sql.erl b/src/mod_mam_sql.erl
index 174cc8de1..1609ff79e 100644
--- a/src/mod_mam_sql.erl
+++ b/src/mod_mam_sql.erl
@@ -38,12 +38,6 @@
-include("logger.hrl").
-include("ejabberd_sql_pt.hrl").
--ifdef(NEW_SQL_SCHEMA).
--define(USE_NEW_SCHEMA, true).
--else.
--define(USE_NEW_SCHEMA, false).
--endif.
-
%%%===================================================================
%%% API
%%%===================================================================
@@ -332,7 +326,7 @@ make_sql_query(User, LServer, MAMQuery, RSM) ->
SServer = Escape(LServer),
Query =
- case ?USE_NEW_SCHEMA of
+ case ejabberd_sql:use_new_schema() of
true ->
[<<"SELECT ">>, TopClause,
<<" timestamp, xml, peer, kind, nick"
@@ -361,7 +355,7 @@ make_sql_query(User, LServer, MAMQuery, RSM) ->
[Query, <<" ORDER BY timestamp ASC ">>,
LimitClause, <<";">>]
end,
- case ?USE_NEW_SCHEMA of
+ case ejabberd_sql:use_new_schema() of
true ->
{QueryPage,
[<<"SELECT COUNT(*) FROM archive WHERE username='">>,
diff --git a/src/mod_muc_log.erl b/src/mod_muc_log.erl
index bf88b2d8b..4a936c66e 100644
--- a/src/mod_muc_log.erl
+++ b/src/mod_muc_log.erl
@@ -375,31 +375,31 @@ add_message_to_log(Nick1, Message, RoomJID, Opts,
io_lib:format("<font class=\"ml\">~s ~s: ~s</font><br/>",
[Nick, ?T(<<"leaves the room">>),
htmlize(Reason, NoFollow, FileFormat)]);
- {kickban, <<"301">>, <<"">>} ->
+ {kickban, 301, <<"">>} ->
io_lib:format("<font class=\"mb\">~s ~s</font><br/>",
[Nick, ?T(<<"has been banned">>)]);
- {kickban, <<"301">>, Reason} ->
+ {kickban, 301, Reason} ->
io_lib:format("<font class=\"mb\">~s ~s: ~s</font><br/>",
[Nick, ?T(<<"has been banned">>),
htmlize(Reason, FileFormat)]);
- {kickban, <<"307">>, <<"">>} ->
+ {kickban, 307, <<"">>} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick, ?T(<<"has been kicked">>)]);
- {kickban, <<"307">>, Reason} ->
+ {kickban, 307, Reason} ->
io_lib:format("<font class=\"mk\">~s ~s: ~s</font><br/>",
[Nick, ?T(<<"has been kicked">>),
htmlize(Reason, FileFormat)]);
- {kickban, <<"321">>, <<"">>} ->
+ {kickban, 321, <<"">>} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick,
?T(<<"has been kicked because of an affiliation "
"change">>)]);
- {kickban, <<"322">>, <<"">>} ->
+ {kickban, 322, <<"">>} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick,
?T(<<"has been kicked because the room has "
"been changed to members-only">>)]);
- {kickban, <<"332">>, <<"">>} ->
+ {kickban, 332, <<"">>} ->
io_lib:format("<font class=\"mk\">~s ~s</font><br/>",
[Nick,
?T(<<"has been kicked because of a system "
diff --git a/src/mod_multicast.erl b/src/mod_multicast.erl
index e6b2a93d2..846049a55 100644
--- a/src/mod_multicast.erl
+++ b/src/mod_multicast.erl
@@ -47,52 +47,36 @@
-include("translate.hrl").
-include("xmpp.hrl").
--record(state,
- {lserver, lservice, access, service_limits}).
+-record(multicastc, {rserver :: binary(),
+ response,
+ ts :: integer()}).
+
+-record(dest, {jid_string :: binary() | none,
+ jid_jid :: xmpp:jid(),
+ type :: to | cc | bcc,
+ address :: address()}).
+
+-type limit_value() :: {default | custom, integer()}.
+-record(limits, {message :: limit_value(),
+ presence :: limit_value()}).
+
+-record(service_limits, {local :: #limits{},
+ remote :: #limits{}}).
+
+-type routing() :: route_single | {route_multicast, binary(), #service_limits{}}.
+
+-record(group, {server :: binary(),
+ dests :: [#dest{}],
+ multicast :: routing(),
+ others :: [#address{}],
+ addresses :: [#address{}]}).
+
+-record(state, {lserver :: binary(),
+ lservice :: binary(),
+ access :: atom(),
+ service_limits :: #service_limits{}}).
-type state() :: #state{}.
--record(multicastc, {rserver, response, ts}).
-
-%% ts: timestamp (in seconds) when the cache item was last updated
-
--record(dest, {jid_string = none :: binary(),
- jid_jid :: jid(),
- type :: atom(),
- full_xml :: address()}).
-
-%% jid_string = string()
-%% jid_jid = jid()
-%% full_xml = xml()
-
--record(group,
- {server, dests, multicast, others, addresses}).
-
-%% server = string()
-%% dests = [string()]
-%% multicast = {cached, local_server} | {cached, string()} | {cached, not_supported} | {obsolete, not_supported} | {obsolete, string()} | not_cached
-%% after being updated, possible values are: local | multicast_not_supported | {multicast_supported, string(), limits()}
-%% others = [xml()]
-%% packet = xml()
-
--record(waiter,
- {awaiting, group, renewal = false, sender, packet,
- aattrs, addresses}).
-
-%% awaiting = {[Remote_service], Local_service, Type_awaiting}
-%% Remote_service = Local_service = string()
-%% Type_awaiting = info | items
-%% group = #group
-%% renewal = true | false
-%% sender = From
-%% packet = xml()
-%% aattrs = [xml()]
-
--record(limits, {message, presence}).
-
-%% message = presence = integer() | infinite
-
--record(service_limits, {local, remote}).
-
%% All the elements are of type value()
-define(VERSION_MULTICAST, <<"$Revision: 440 $ ">>).
@@ -104,6 +88,8 @@
-define(MAXTIME_CACHE_NEGATIVE, 86400).
+-define(MAXTIME_CACHE_NEGOTIATING, 600).
+
-define(CACHE_PURGE_TIMER, 86400000).
-define(DISCO_QUERY_TIMEOUT, 10000).
@@ -130,6 +116,7 @@ reload(LServerS, NewOpts, OldOpts) ->
%% gen_server callbacks
%%====================================================================
+-spec init(list()) -> {ok, state()}.
init([LServerS, Opts]) ->
process_flag(trap_exit, true),
[LServiceS|_] = gen_mod:get_opt_hosts(LServerS, Opts),
@@ -137,7 +124,6 @@ init([LServerS, Opts]) ->
SLimits = build_service_limit_record(gen_mod:get_opt(limits, Opts)),
create_cache(),
try_start_loop(),
- create_pool(),
ejabberd_router_multicast:register_route(LServerS),
ejabberd_router:register_route(LServiceS, LServerS),
{ok,
@@ -277,21 +263,22 @@ iq_vcard(Lang) ->
%%% Route
%%%-------------------------
+-spec route_trusted(binary(), binary(), jid(), [jid()], stanza()) -> 'ok'.
route_trusted(LServiceS, LServerS, FromJID,
Destinations, Packet) ->
Packet_stripped = Packet,
- AAttrs = [],
Delivereds = [],
Dests2 = lists:map(
fun(D) ->
#dest{jid_string = jid:encode(D),
- jid_jid = D, type = bcc,
- full_xml = #address{type = bcc, jid = D}}
+ jid_jid = D, type = bcc,
+ address = #address{type = bcc, jid = D}}
end, Destinations),
Groups = group_dests(Dests2),
route_common(LServerS, LServiceS, FromJID, Groups,
- Delivereds, Packet_stripped, AAttrs).
+ Delivereds, Packet_stripped).
+-spec route_untrusted(binary(), binary(), atom(), #service_limits{}, stanza()) -> 'ok'.
route_untrusted(LServiceS, LServerS, Access, SLimits, Packet) ->
try route_untrusted2(LServiceS, LServerS, Access,
SLimits, Packet)
@@ -321,6 +308,7 @@ route_untrusted(LServiceS, LServerS, Access, SLimits, Packet) ->
<<"Unknown problem">>)
end.
+-spec route_untrusted2(binary(), binary(), atom(), #service_limits{}, stanza()) -> 'ok'.
route_untrusted2(LServiceS, LServerS, Access, SLimits, Packet) ->
FromJID = xmpp:get_from(Packet),
ok = check_access(LServerS, Access, FromJID),
@@ -333,53 +321,40 @@ route_untrusted2(LServiceS, LServerS, Access, SLimits, Packet) ->
Groups = group_dests(Dests2),
ok = check_relay(FromJID#jid.server, LServerS, Groups),
route_common(LServerS, LServiceS, FromJID, Groups,
- Delivereds, Packet_stripped, []).
+ Delivereds, Packet_stripped).
-spec route_common(binary(), binary(), jid(), [#group{}],
- [address()], stanza(), list()) -> any().
+ [address()], stanza()) -> 'ok'.
route_common(LServerS, LServiceS, FromJID, Groups,
- Delivereds, Packet_stripped, AAttrs) ->
- Groups2 = look_cached_servers(LServerS, Groups),
+ Delivereds, Packet_stripped) ->
+ Groups2 = look_cached_servers(LServerS, LServiceS, Groups),
Groups3 = build_others_xml(Groups2),
Groups4 = add_addresses(Delivereds, Groups3),
AGroups = decide_action_groups(Groups4),
- act_groups(FromJID, Packet_stripped, AAttrs, LServiceS,
+ act_groups(FromJID, Packet_stripped, LServiceS,
AGroups).
-act_groups(FromJID, Packet_stripped, AAttrs, LServiceS,
- AGroups) ->
- [perform(FromJID, Packet_stripped, AAttrs, LServiceS,
- AGroup)
- || AGroup <- AGroups].
-
-perform(From, Packet, AAttrs, _,
+-spec act_groups(jid(), stanza(), binary(), [{routing(), #group{}}]) -> 'ok'.
+act_groups(FromJID, Packet_stripped, LServiceS, AGroups) ->
+ lists:foreach(
+ fun(AGroup) ->
+ perform(FromJID, Packet_stripped, LServiceS,
+ AGroup)
+ end, AGroups).
+
+-spec perform(jid(), stanza(), binary(),
+ {routing(), #group{}}) -> 'ok'.
+perform(From, Packet, _,
{route_single, Group}) ->
- [route_packet(From, ToUser, Packet, AAttrs,
- Group#group.others, Group#group.addresses)
- || ToUser <- Group#group.dests];
-perform(From, Packet, AAttrs, _,
+ lists:foreach(
+ fun(ToUser) ->
+ route_packet(From, ToUser, Packet,
+ Group#group.others, Group#group.addresses)
+ end, Group#group.dests);
+perform(From, Packet, _,
{{route_multicast, JID, RLimits}, Group}) ->
- route_packet_multicast(From, JID, Packet, AAttrs,
- Group#group.dests, Group#group.addresses, RLimits);
-perform(From, Packet, AAttrs, LServiceS,
- {{ask, Old_service, renewal}, Group}) ->
- send_query_info(Old_service, LServiceS),
- add_waiter(#waiter{awaiting =
- {[Old_service], LServiceS, info},
- group = Group, renewal = true, sender = From,
- packet = Packet, aattrs = AAttrs,
- addresses = Group#group.addresses});
-perform(_From, _Packet, _AAttrs, LServiceS,
- {{ask, LServiceS, _}, _Group}) ->
- ok;
-perform(From, Packet, AAttrs, LServiceS,
- {{ask, Server, not_renewal}, Group}) ->
- send_query_info(Server, LServiceS),
- add_waiter(#waiter{awaiting =
- {[Server], LServiceS, info},
- group = Group, renewal = false, sender = From,
- packet = Packet, aattrs = AAttrs,
- addresses = Group#group.addresses}).
+ route_packet_multicast(From, JID, Packet,
+ Group#group.dests, Group#group.addresses, RLimits).
%%%-------------------------
%%% Check access permission
@@ -427,7 +402,7 @@ split_addresses_todeliver(Addresses) ->
%%% Check does not exceed limit of destinations
%%%-------------------------
--spec check_limit_dests(_, jid(), stanza(), [address()]) -> ok.
+-spec check_limit_dests(#service_limits{}, jid(), stanza(), [address()]) -> ok.
check_limit_dests(SLimits, FromJID, Packet,
Addresses) ->
SenderT = sender_type(FromJID),
@@ -448,10 +423,10 @@ check_limit_dests(SLimits, FromJID, Packet,
convert_dest_record(Addrs) ->
lists:map(
fun(#address{jid = undefined} = Addr) ->
- #dest{jid_string = none, full_xml = Addr};
+ #dest{jid_string = none, address = Addr};
(#address{jid = JID, type = Type} = Addr) ->
#dest{jid_string = jid:encode(JID), jid_jid = JID,
- type = Type, full_xml = Addr}
+ type = Type, address = Addr}
end, Addrs).
%%%-------------------------
@@ -469,9 +444,9 @@ split_dests_jid(Dests) ->
end,
Dests).
--spec report_not_jid(jid(), stanza(), #dest{}) -> any().
+-spec report_not_jid(jid(), stanza(), [#dest{}]) -> any().
report_not_jid(From, Packet, Dests) ->
- Dests2 = [fxml:element_to_binary(xmpp:encode(Dest#dest.full_xml))
+ Dests2 = [fxml:element_to_binary(xmpp:encode(Dest#dest.address))
|| Dest <- Dests],
[route_error(xmpp:set_from_to(Packet, From, From), jid_malformed,
<<"This service can not process the address: ",
@@ -497,14 +472,14 @@ group_dests(Dests) ->
%%% Look for cached responses
%%%-------------------------
-look_cached_servers(LServerS, Groups) ->
- [look_cached(LServerS, Group) || Group <- Groups].
+look_cached_servers(LServerS, LServiceS, Groups) ->
+ [look_cached(LServerS, LServiceS, Group) || Group <- Groups].
-look_cached(LServerS, G) ->
+look_cached(LServerS, LServiceS, G) ->
Maxtime_positive = (?MAXTIME_CACHE_POSITIVE),
Maxtime_negative = (?MAXTIME_CACHE_NEGATIVE),
Cached_response = search_server_on_cache(G#group.server,
- LServerS,
+ LServerS, LServiceS,
{Maxtime_positive,
Maxtime_negative}),
G#group{multicast = Cached_response}.
@@ -520,7 +495,7 @@ build_others_xml(Groups) ->
build_other_xml(Dests) ->
lists:foldl(fun (Dest, R) ->
- XML = Dest#dest.full_xml,
+ XML = Dest#dest.address,
case Dest#dest.type of
to -> [add_delivered(XML) | R];
cc -> [add_delivered(XML) | R];
@@ -554,53 +529,38 @@ add_addresses2(Delivereds, [Group | Groups], Res, Pa,
%%% Decide action groups
%%%-------------------------
+-spec decide_action_groups([#group{}]) -> [{routing(), #group{}}].
decide_action_groups(Groups) ->
- [{decide_action_group(Group), Group}
+ [{Group#group.multicast, Group}
|| Group <- Groups].
-decide_action_group(Group) ->
- Server = Group#group.server,
- case Group#group.multicast of
- {cached, local_server} ->
- %% Send a copy of the packet to each local user on Dests
- route_single;
- {cached, not_supported} ->
- %% Send a copy of the packet to each remote user on Dests
- route_single;
- {cached, {multicast_supported, JID, RLimits}} ->
- {route_multicast, JID, RLimits};
- {obsolete,
- {multicast_supported, Old_service, _RLimits}} ->
- {ask, Old_service, renewal};
- {obsolete, not_supported} -> {ask, Server, not_renewal};
- not_cached -> {ask, Server, not_renewal}
- end.
-
%%%-------------------------
%%% Route packet
%%%-------------------------
-route_packet(From, ToDest, Packet, AAttrs, Others, Addresses) ->
+-spec route_packet(jid(), #dest{}, xmpp:stanza(), [addresses()], [addresses()]) -> 'ok'.
+route_packet(From, ToDest, Packet, Others, Addresses) ->
Dests = case ToDest#dest.type of
bcc -> [];
_ -> [ToDest]
end,
route_packet2(From, ToDest#dest.jid_string, Dests,
- Packet, AAttrs, {Others, Addresses}).
+ Packet, {Others, Addresses}).
-route_packet_multicast(From, ToS, Packet, AAttrs, Dests,
+-spec route_packet_multicast(jid(), binary(), xmpp:stanza(), [#dest{}], [address()], #limits{}) -> 'ok'.
+route_packet_multicast(From, ToS, Packet, Dests,
Addresses, Limits) ->
Type_of_stanza = type_of_stanza(Packet),
{_Type, Limit_number} = get_limit_number(Type_of_stanza,
Limits),
Fragmented_dests = fragment_dests(Dests, Limit_number),
- [route_packet2(From, ToS, DFragment, Packet, AAttrs,
- Addresses)
- || DFragment <- Fragmented_dests].
+ lists:foreach(fun(DFragment) ->
+ route_packet2(From, ToS, DFragment, Packet,
+ Addresses)
+ end, Fragmented_dests).
--spec route_packet2(jid(), binary(), [#dest{}], stanza(), list(), [address()]) -> ok.
-route_packet2(From, ToS, Dests, Packet, _AAttrs,
- Addresses) ->
+-spec route_packet2(jid(), binary(), [#dest{}], xmpp:stanza(), {[address()], [address()]} | [address()]) -> 'ok'.
+route_packet2(From, ToS, Dests, Packet, Addresses) ->
Els = case append_dests(Dests, Addresses) of
[] ->
xmpp:get_els(Packet);
@@ -613,10 +573,10 @@ route_packet2(From, ToS, Dests, Packet, _AAttrs,
-spec append_dests([#dest{}], {[address()], [address()]} | [address()]) -> [address()].
append_dests(_Dests, {Others, Addresses}) ->
- Addresses++Others;
+ Addresses ++ Others;
append_dests([], Addresses) -> Addresses;
append_dests([Dest | Dests], Addresses) ->
- append_dests(Dests, [Dest#dest.full_xml | Addresses]).
+ append_dests(Dests, [Dest#dest.address | Addresses]).
%%%-------------------------
%%% Check relay
@@ -647,20 +607,22 @@ check_relay_required(LServerS, Groups) ->
%%% Check protocol support: Send request
%%%-------------------------
-send_query_info(RServerS, LServiceS) ->
+-spec send_query_info(binary(), binary(), binary()) -> ok.
+send_query_info(RServerS, LServiceS, ID) ->
case str:str(RServerS, <<"echo.">>) of
- 1 -> false;
- _ -> send_query(RServerS, LServiceS, #disco_info{})
+ 1 -> ok;
+ _ -> send_query(RServerS, LServiceS, ID, #disco_info{})
end.
-send_query_items(RServerS, LServiceS) ->
- send_query(RServerS, LServiceS, #disco_items{}).
+-spec send_query_items(binary(), binary(), binary()) -> ok.
+send_query_items(RServerS, LServiceS, ID) ->
+ send_query(RServerS, LServiceS, ID, #disco_items{}).
--spec send_query(binary(), binary(), [disco_info()|disco_items()]) -> ok.
-send_query(RServerS, LServiceS, SubEl) ->
+-spec send_query(binary(), binary(), binary(), disco_info()|disco_items()) -> ok.
+send_query(RServerS, LServiceS, ID, SubEl) ->
Packet = #iq{from = stj(LServiceS),
to = stj(RServerS),
- id = randoms:get_string(),
+ id = ID,
type = get, sub_els = [SubEl]},
ejabberd_router:route(Packet).
@@ -670,10 +632,31 @@ send_query(RServerS, LServiceS, SubEl) ->
process_iqreply_error(LServiceS, Packet) ->
FromS = jts(xmpp:get_from(Packet)),
- case search_waiter(FromS, LServiceS, info) of
- {found_waiter, Waiter} ->
- received_awaiter(FromS, Waiter, LServiceS);
- _ -> ok
+ ID = Packet#iq.id,
+ case str:tokens(ID, <<"/">>) of
+ [RServer, _] ->
+ case look_server(RServer) of
+ {cached, {_Response, {wait_for_info, ID}}, _TS}
+ when RServer == FromS ->
+ add_response(RServer, not_supported, cached);
+ {cached, {_Response, {wait_for_items, ID}}, _TS}
+ when RServer == FromS ->
+ add_response(RServer, not_supported, cached);
+ {cached, {Response, {wait_for_items_info, ID, Items}},
+ _TS} ->
+ case lists:member(FromS, Items) of
+ true ->
+ received_awaiter(
+ FromS, RServer, Response, ID, Items,
+ LServiceS);
+ false ->
+ ok
+ end;
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
end.
%%%-------------------------
@@ -681,12 +664,12 @@ process_iqreply_error(LServiceS, Packet) ->
%%%-------------------------
-spec process_iqreply_result(binary(), iq()) -> any().
-process_iqreply_result(LServiceS, #iq{from = From, sub_els = [SubEl]}) ->
+process_iqreply_result(LServiceS, #iq{from = From, id = ID, sub_els = [SubEl]}) ->
case SubEl of
#disco_info{} ->
- process_discoinfo_result(From, LServiceS, SubEl);
+ process_discoinfo_result(From, LServiceS, ID, SubEl);
#disco_items{} ->
- process_discoitems_result(From, LServiceS, SubEl);
+ process_discoitems_result(From, LServiceS, ID, SubEl);
_ ->
ok
end.
@@ -695,46 +678,53 @@ process_iqreply_result(LServiceS, #iq{from = From, sub_els = [SubEl]}) ->
%%% Check protocol support: Receive response: Disco Info
%%%-------------------------
-process_discoinfo_result(From, LServiceS, DiscoInfo) ->
+process_discoinfo_result(From, LServiceS, ID, DiscoInfo) ->
FromS = jts(From),
- case search_waiter(FromS, LServiceS, info) of
- {found_waiter, Waiter} ->
- process_discoinfo_result2(From, FromS, LServiceS, DiscoInfo,
- Waiter);
- _ -> ok
+ case str:tokens(ID, <<"/">>) of
+ [RServer, _] ->
+ case look_server(RServer) of
+ {cached, {Response, {wait_for_info, ID} = ST}, _TS}
+ when RServer == FromS ->
+ process_discoinfo_result2(
+ From, FromS, LServiceS, DiscoInfo,
+ RServer, Response, ST);
+ {cached, {Response, {wait_for_items_info, ID, Items} = ST},
+ _TS} ->
+ case lists:member(FromS, Items) of
+ true ->
+ process_discoinfo_result2(
+ From, FromS, LServiceS, DiscoInfo,
+ RServer, Response, ST);
+ false ->
+ ok
+ end;
+ _ ->
+ ok
+ end;
+ _ ->
+ ok
end.
process_discoinfo_result2(From, FromS, LServiceS,
#disco_info{features = Feats} = DiscoInfo,
- Waiter) ->
+ RServer, Response, ST) ->
Multicast_support = lists:member(?NS_ADDRESS, Feats),
- Group = Waiter#waiter.group,
- RServer = Group#group.server,
case Multicast_support of
true ->
SenderT = sender_type(From),
RLimits = get_limits_xml(DiscoInfo, SenderT),
- add_response(RServer, {multicast_supported, FromS, RLimits}),
- FromM = Waiter#waiter.sender,
- DestsM = Group#group.dests,
- PacketM = Waiter#waiter.packet,
- AAttrsM = Waiter#waiter.aattrs,
- AddressesM = Waiter#waiter.addresses,
- RServiceM = FromS,
- route_packet_multicast(FromM, RServiceM, PacketM,
- AAttrsM, DestsM, AddressesM, RLimits),
- delo_waiter(Waiter);
+ add_response(RServer, {multicast_supported, FromS, RLimits}, cached);
false ->
- case FromS of
- RServer ->
- send_query_items(FromS, LServiceS),
- delo_waiter(Waiter),
- add_waiter(Waiter#waiter{awaiting =
- {[FromS], LServiceS, items},
- renewal = false});
- %% We asked a component, and it does not support XEP33
- _ -> received_awaiter(FromS, Waiter, LServiceS)
- end
+ case ST of
+ {wait_for_info, _ID} ->
+ Random = randoms:get_string(),
+ ID = <<RServer/binary, $/, Random/binary>>,
+ send_query_items(FromS, LServiceS, ID),
+ add_response(RServer, Response, {wait_for_items, ID});
+ %% We asked a component, and it does not support XEP33
+ {wait_for_items_info, ID, Items} ->
+ received_awaiter(FromS, RServer, Response, ID, Items, LServiceS)
+ end
end.
get_limits_xml(DiscoInfo, SenderT) ->
@@ -778,26 +768,32 @@ get_limits_values(Fields) ->
%%% Check protocol support: Receive response: Disco Items
%%%-------------------------
-process_discoitems_result(From, LServiceS, #disco_items{items = Items}) ->
+process_discoitems_result(From, LServiceS, ID, #disco_items{items = Items}) ->
FromS = jts(From),
- case search_waiter(FromS, LServiceS, items) of
- {found_waiter, Waiter} ->
- List = lists:flatmap(
- fun(#disco_item{jid = #jid{luser = <<"">>,
- lresource = <<"">>} = J}) ->
- [J];
- (_) ->
- []
- end, Items),
- case List of
- [] ->
- received_awaiter(FromS, Waiter, LServiceS);
+ case str:tokens(ID, <<"/">>) of
+ [FromS = RServer, _] ->
+ case look_server(RServer) of
+ {cached, {Response, {wait_for_items, ID}}, _TS} ->
+ List = lists:flatmap(
+ fun(#disco_item{jid = #jid{luser = <<"">>,
+ lserver = LServer,
+ lresource = <<"">>}}) ->
+ [LServer];
+ (_) ->
+ []
+ end, Items),
+ case List of
+ [] ->
+ add_response(RServer, not_supported, cached);
+ _ ->
+ Random = randoms:get_string(),
+ ID2 = <<RServer/binary, $/, Random/binary>>,
+ [send_query_info(Item, LServiceS, ID2) || Item <- List],
+ add_response(RServer, Response,
+ {wait_for_items_info, ID2, List})
+ end;
_ ->
- [send_query_info(Item, LServiceS) || Item <- List],
- delo_waiter(Waiter),
- add_waiter(Waiter#waiter{awaiting =
- {List, LServiceS, info},
- renewal = false})
+ ok
end;
_ ->
ok
@@ -807,33 +803,12 @@ process_discoitems_result(From, LServiceS, #disco_items{items = Items}) ->
%%% Check protocol support: Receive response: Received awaiter
%%%-------------------------
-received_awaiter(JID, Waiter, LServiceS) ->
- {JIDs, LServiceS, _} = Waiter#waiter.awaiting,
- delo_waiter(Waiter),
- Group = Waiter#waiter.group,
- RServer = Group#group.server,
+received_awaiter(JID, RServer, Response, ID, JIDs, _LServiceS) ->
case lists:delete(JID, JIDs) of
- [] ->
- case Waiter#waiter.renewal of
- false ->
- add_response(RServer, not_supported),
- From = Waiter#waiter.sender,
- Packet = Waiter#waiter.packet,
- AAttrs = Waiter#waiter.aattrs,
- Others = Group#group.others,
- Addresses = Waiter#waiter.addresses,
- [route_packet(From, ToUser, Packet, AAttrs, Others, Addresses)
- || ToUser <- Group#group.dests];
- true ->
- send_query_info(RServer, LServiceS),
- add_waiter(Waiter#waiter{awaiting =
- {[RServer], LServiceS, info},
- renewal = false})
- end;
- JIDs2 ->
- add_waiter(Waiter#waiter{awaiting =
- {JIDs2, LServiceS, info},
- renewal = false})
+ [] ->
+ add_response(RServer, not_supported, cached);
+ JIDs2 ->
+ add_response(RServer, Response, {wait_for_items_info, ID, JIDs2})
end.
%%%-------------------------
@@ -845,25 +820,52 @@ create_cache() ->
[{ram_copies, [node()]},
{attributes, record_info(fields, multicastc)}]).
-add_response(RServer, Response) ->
+add_response(RServer, Response, State) ->
Secs = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
mnesia:dirty_write(#multicastc{rserver = RServer,
- response = Response, ts = Secs}).
+ response = {Response, State}, ts = Secs}).
-search_server_on_cache(RServer, LServerS, _Maxmins)
+search_server_on_cache(RServer, LServerS, _LServiceS, _Maxmins)
when RServer == LServerS ->
- {cached, local_server};
-search_server_on_cache(RServer, _LServerS, Maxmins) ->
+ route_single;
+search_server_on_cache(RServer, _LServerS, LServiceS, Maxmins) ->
case look_server(RServer) of
- not_cached -> not_cached;
- {cached, Response, Ts} ->
- Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
- case is_obsolete(Response, Ts, Now, Maxmins) of
- false -> {cached, Response};
- true -> {obsolete, Response}
- end
+ not_cached ->
+ query_info(RServer, LServiceS, not_supported),
+ route_single;
+ {cached, {Response, State}, TS} ->
+ Now = calendar:datetime_to_gregorian_seconds(calendar:local_time()),
+ Response2 =
+ case State of
+ cached ->
+ case is_obsolete(Response, TS, Now, Maxmins) of
+ false -> ok;
+ true ->
+ query_info(RServer, LServiceS, Response)
+ end,
+ Response;
+ _ ->
+ if
+ Now - TS > ?MAXTIME_CACHE_NEGOTIATING ->
+ query_info(RServer, LServiceS, not_supported),
+ not_supported;
+ true ->
+ Response
+ end
+ end,
+ case Response2 of
+ not_supported -> route_single;
+ {multicast_supported, Service, Limits} ->
+ {route_multicast, Service, Limits}
+ end
end.
+query_info(RServer, LServiceS, Response) ->
+ Random = randoms:get_string(),
+ ID = <<RServer/binary, $/, Random/binary>>,
+ send_query_info(RServer, LServiceS, ID),
+ add_response(RServer, Response, {wait_for_info, ID}).
+
look_server(RServer) ->
case mnesia:dirty_read(multicastc, RServer) of
[] -> not_cached;
@@ -935,44 +937,6 @@ purge_loop(NM) ->
end.
%%%-------------------------
-%%% Pool
-%%%-------------------------
-
-create_pool() ->
- catch
- begin
- ets:new(multicastp,
- [duplicate_bag, public, named_table, {keypos, 2}]),
- ets:give_away(multicastp, whereis(ejabberd), ok)
- end.
-
-add_waiter(Waiter) ->
- true = ets:insert(multicastp, Waiter).
-
-delo_waiter(Waiter) ->
- true = ets:delete_object(multicastp, Waiter).
-
--spec search_waiter(binary(), binary(), info | items) ->
- {found_waiter, #waiter{}} | waiter_not_found.
-
-search_waiter(JID, LServiceS, Type) ->
- Rs = ets:foldl(fun (W, Res) ->
- {JIDs, LServiceS1, Type1} = W#waiter.awaiting,
- case lists:member(JID, JIDs) and
- (LServiceS == LServiceS1)
- and (Type1 == Type)
- of
- true -> Res ++ [W];
- false -> Res
- end
- end,
- [], multicastp),
- case Rs of
- [R | _] -> {found_waiter, R};
- [] -> waiter_not_found
- end.
-
-%%%-------------------------
%%% Limits: utils
%%%-------------------------
@@ -1005,11 +969,13 @@ get_from_limitopts(LimitOpts, SenderT) ->
build_remote_limit_record(LimitOpts, SenderT) ->
build_limit_record(LimitOpts, SenderT).
+-spec build_limit_record(any(), local | remote) -> #limits{}.
build_limit_record(LimitOpts, SenderT) ->
Limits = [get_limit_value(Name, Default, LimitOpts)
|| {Name, Default} <- list_of_limits(SenderT)],
list_to_tuple([limits | Limits]).
+-spec get_limit_value(atom(), integer(), any()) -> limit_value().
get_limit_value(Name, Default, LimitOpts) ->
case lists:keysearch(Name, 1, LimitOpts) of
{value, {Name, Number}} -> {custom, Number};
@@ -1018,11 +984,13 @@ get_limit_value(Name, Default, LimitOpts) ->
type_of_stanza(Stanza) -> element(1, Stanza).
+-spec get_limit_number(message | presence, #limits{}) -> limit_value().
get_limit_number(message, Limits) ->
Limits#limits.message;
get_limit_number(presence, Limits) ->
Limits#limits.presence.
+-spec get_slimit_group(local | remote, #service_limits{}) -> #limits{}.
get_slimit_group(local, SLimits) ->
SLimits#service_limits.local;
get_slimit_group(remote, SLimits) ->
diff --git a/src/mod_pubsub.erl b/src/mod_pubsub.erl
index cd9aedef2..5d02d6518 100644
--- a/src/mod_pubsub.erl
+++ b/src/mod_pubsub.erl
@@ -261,8 +261,8 @@ init([ServerHost, Opts]) ->
{Plugins, NodeTree, PepMapping} = init_plugins(Host, ServerHost, Opts),
DefaultModule = plugin(Host, hd(Plugins)),
DefaultNodeCfg = merge_config(
- gen_mod:get_opt(default_node_config, Opts),
- DefaultModule:options()),
+ [gen_mod:get_opt(default_node_config, Opts),
+ DefaultModule:options()]),
lists:foreach(
fun(H) ->
T = gen_mod:get_module_proc(H, config),
@@ -373,13 +373,9 @@ init_plugins(Host, ServerHost, Opts) ->
PluginsOK = lists:foldl(
fun (Name, Acc) ->
Plugin = plugin(Host, Name),
- case catch apply(Plugin, init, [Host, ServerHost, Opts]) of
- {'EXIT', _Error} ->
- Acc;
- _ ->
- ?DEBUG("** init ~s plugin", [Name]),
- [Name | Acc]
- end
+ apply(Plugin, init, [Host, ServerHost, Opts]),
+ ?DEBUG("** init ~s plugin", [Name]),
+ [Name | Acc]
end,
[], Plugins),
{lists:reverse(PluginsOK), TreePlugin, PepMapping}.
@@ -689,12 +685,17 @@ remove_user(User, Server) ->
({#pubsub_node{nodeid = {H, N}, type = Type}, owner})
when N == HomeTreeBase, Type == <<"hometree">> ->
delete_node(H, N, Entity);
- ({#pubsub_node{id = Nidx}, publisher}) ->
+ ({#pubsub_node{id = Nidx}, _}) ->
+ {result, State} = node_action(Host, PType,
+ get_state,
+ [Nidx, jid:tolower(Entity)]),
+ ItemIds = State#pubsub_state.items,
node_action(Host, PType,
- set_affiliation,
- [Nidx, Entity, none]);
- (_) ->
- ok
+ remove_extra_items,
+ [Nidx, 0, ItemIds]),
+ node_action(Host, PType,
+ set_affiliation,
+ [Nidx, Entity, none])
end,
Affs)
end,
@@ -1116,7 +1117,7 @@ iq_pubsub(Host, Access, #iq{from = From, type = IQType, lang = Lang,
Payload, PubOpts, Access)
end;
[] ->
- {error, extended_error(xmpp:err_bad_request(), err_item_required())};
+ publish_item(Host, ServerHost, Node, From, <<>>, [], [], Access);
_ ->
{error, extended_error(xmpp:err_bad_request(), err_invalid_payload())}
end;
@@ -1480,7 +1481,9 @@ create_node(Host, ServerHost, <<>>, Owner, Type, Access, Configuration) ->
end;
create_node(Host, ServerHost, Node, Owner, GivenType, Access, Configuration) ->
Type = select_type(ServerHost, Host, Node, GivenType),
- NodeOptions = merge_config(Configuration, node_options(Host, Type)),
+ NodeOptions = merge_config(
+ [node_config(Node, ServerHost),
+ Configuration, node_options(Host, Type)]),
CreateNode =
fun() ->
Parent = case node_call(Host, Type, node_to_path, [Node]) of
@@ -1701,7 +1704,7 @@ subscribe_node(Host, Node, From, JID, Configuration) ->
Nidx = TNode#pubsub_node.id,
Type = TNode#pubsub_node.type,
Options = TNode#pubsub_node.options,
- send_items(Host, Node, Nidx, Type, Options, Subscriber, 1),
+ send_items(Host, Node, Nidx, Type, Options, Subscriber, last),
ServerHost = serverhost(Host),
ejabberd_hooks:run(pubsub_subscribe_node, ServerHost,
[ServerHost, Host, Node, Subscriber, SubId]),
@@ -1787,19 +1790,15 @@ publish_item(Host, ServerHost, Node, Publisher, ItemId, Payload, PubOpts, Access
PayloadSize > PayloadMaxSize ->
{error, extended_error(xmpp:err_not_acceptable(),
err_payload_too_big())};
- (PayloadCount == 0) and (Payload == []) ->
+ (DeliverPayloads or PersistItems) and (PayloadCount == 0) ->
{error, extended_error(xmpp:err_bad_request(),
- err_payload_required())};
- (PayloadCount > 1) or (PayloadCount == 0) ->
+ err_item_required())};
+ (DeliverPayloads or PersistItems) and (PayloadCount > 1) ->
{error, extended_error(xmpp:err_bad_request(),
err_invalid_payload())};
- (DeliverPayloads == false) and (PersistItems == false) and
- (PayloadSize > 0) ->
+ (not (DeliverPayloads or PersistItems)) and (PayloadCount > 0) ->
{error, extended_error(xmpp:err_bad_request(),
err_item_forbidden())};
- ((DeliverPayloads == true) or (PersistItems == true)) and (PayloadSize == 0) ->
- {error, extended_error(xmpp:err_bad_request(),
- err_item_required())};
true ->
node_call(Host, Type, publish_item,
[Nidx, Publisher, PublishModel, MaxItems, ItemId, Payload, PubOpts])
@@ -2005,8 +2004,13 @@ get_items(Host, Node, From, SubId, _MaxItems, ItemIds, RSM) ->
Host, From, Owners, AccessModel, AllowedGroups),
case ItemIds of
[ItemId] ->
- node_call(Host, Type, get_item,
- [Nidx, ItemId, From, AccessModel, PS, RG, undefined]);
+ NotFound = xmpp:err_item_not_found(),
+ case node_call(Host, Type, get_item,
+ [Nidx, ItemId, From, AccessModel, PS, RG, undefined])
+ of
+ {error, NotFound} -> {result, {[], undefined}};
+ Result -> Result
+ end;
_ ->
node_call(Host, Type, get_items,
[Nidx, From, AccessModel, PS, RG, SubId, RSM])
@@ -2014,7 +2018,7 @@ get_items(Host, Node, From, SubId, _MaxItems, ItemIds, RSM) ->
end
end,
case transaction(Host, Node, Action, sync_dirty) of
- {result, {_, {Items, RsmOut}}} ->
+ {result, {TNode, {Items, RsmOut}}} ->
SendItems = case ItemIds of
[] ->
Items;
@@ -2024,14 +2028,12 @@ get_items(Host, Node, From, SubId, _MaxItems, ItemIds, RSM) ->
lists:member(ItemId, ItemIds)
end, Items)
end,
- {result,
- #pubsub{items = #ps_items{node = Node,
- items = itemsEls(SendItems)},
- rsm = RsmOut}};
- {result, {_, Item}} ->
- {result,
- #pubsub{items = #ps_items{node = Node,
- items = itemsEls([Item])}}};
+ Options = TNode#pubsub_node.options,
+ {result, #pubsub{items = items_els(Node, Options, SendItems),
+ rsm = RsmOut}};
+ {result, {TNode, Item}} ->
+ Options = TNode#pubsub_node.options,
+ {result, #pubsub{items = items_els(Node, Options, [Item])}};
Error ->
Error
end.
@@ -2065,6 +2067,9 @@ get_allowed_items_call(Host, Nidx, From, Type, Options, Owners, RSM) ->
{PS, RG} = get_presence_and_roster_permissions(Host, From, Owners, AccessModel, AllowedGroups),
node_call(Host, Type, get_items, [Nidx, From, AccessModel, PS, RG, undefined, RSM]).
+get_last_items(Host, Type, Nidx, LJID, last) ->
+ % hack to handle section 6.1.7 of XEP-0060
+ get_last_items(Host, Type, Nidx, LJID, 1);
get_last_items(Host, Type, Nidx, LJID, 1) ->
case get_cached_item(Host, Nidx) of
undefined ->
@@ -2631,45 +2636,37 @@ payload_xmlelements([#xmlel{} | Tail], Count) ->
payload_xmlelements([_ | Tail], Count) ->
payload_xmlelements(Tail, Count).
-items_event_stanza(Node, Options, Items) ->
- MoreEls = case Items of
- [LastItem] ->
- {ModifNow, ModifUSR} = LastItem#pubsub_item.modification,
- [#delay{stamp = ModifNow, from = jid:make(ModifUSR)}];
+items_els(Node, Options, Items) ->
+ Els = case get_option(Options, itemreply) of
+ publisher ->
+ [#ps_item{id = ItemId, sub_els = Payload, publisher = jid:encode(USR)}
+ || #pubsub_item{itemid = {ItemId, _}, payload = Payload, modification = {_, USR}}
+ <- Items];
_ ->
- []
+ [#ps_item{id = ItemId, sub_els = Payload}
+ || #pubsub_item{itemid = {ItemId, _}, payload = Payload}
+ <- Items]
end,
- BaseStanza = #message{
- sub_els = [#ps_event{items = #ps_items{
- node = Node,
- items = itemsEls(Items)}}
- | MoreEls]},
- NotificationType = get_option(Options, notification_type, headline),
- add_message_type(BaseStanza, NotificationType).
+ #ps_items{node = Node, items = Els}.
%%%%%% broadcast functions
broadcast_publish_item(Host, Node, Nidx, Type, NodeOptions, ItemId, From, Payload, Removed) ->
case get_collection_subscriptions(Host, Node) of
SubsByDepth when is_list(SubsByDepth) ->
- EventItem0 = case get_option(NodeOptions, deliver_payloads) of
- true -> #ps_item{sub_els = Payload, id = ItemId};
- false -> #ps_item{id = ItemId}
- end,
- EventItem = case get_option(NodeOptions, itemreply, none) of
- owner -> %% owner not supported
- EventItem0;
- publisher ->
- EventItem0#ps_item{
- publisher = jid:encode(From)};
- none ->
- EventItem0
- end,
- Stanza = #message{
- sub_els =
- [#ps_event{items =
- #ps_items{node = Node,
- items = [EventItem]}}]},
+ ItemPublisher = case get_option(NodeOptions, itemreply) of
+ publisher -> jid:encode(From);
+ _ -> <<>>
+ end,
+ ItemPayload = case get_option(NodeOptions, deliver_payloads) of
+ true -> Payload;
+ false -> []
+ end,
+ ItemsEls = #ps_items{node = Node,
+ items = [#ps_item{id = ItemId,
+ publisher = ItemPublisher,
+ sub_els = ItemPayload}]},
+ Stanza = #message{ sub_els = [#ps_event{items = ItemsEls}]},
broadcast_stanza(Host, From, Node, Nidx, Type,
NodeOptions, SubsByDepth, items, Stanza, true),
case Removed of
@@ -2896,8 +2893,20 @@ send_items(Host, Node, Nidx, Type, Options, Publisher, SubLJID, ToLJID, Number)
[] ->
ok;
Items ->
- Stanza = items_event_stanza(Node, Options, Items),
- send_stanza(Publisher, ToLJID, Node, Stanza)
+ Delay = case Number of
+ last -> % handle section 6.1.7 of XEP-0060
+ [Last] = Items,
+ {Stamp, _USR} = Last#pubsub_item.modification,
+ [#delay{stamp = Stamp}];
+ _ ->
+ []
+ end,
+ Stanza = #message{
+ sub_els = [#ps_event{items = items_els(Node, Options, Items)}
+ | Delay]},
+ NotificationType = get_option(Options, notification_type, headline),
+ send_stanza(Publisher, ToLJID, Node,
+ add_message_type(Stanza, NotificationType))
end.
send_stanza({LUser, LServer, _} = Publisher, USR, Node, BaseStanza) ->
@@ -3153,6 +3162,20 @@ node_owners_call(Host, Type, Nidx, []) ->
node_owners_call(_Host, _Type, _Nidx, Owners) ->
Owners.
+node_config(Node, ServerHost) ->
+ Opts = gen_mod:get_module_opt(ServerHost, ?MODULE, force_node_config),
+ node_config(Node, ServerHost, Opts).
+
+node_config(Node, ServerHost, [{RE, Opts}|NodeOpts]) ->
+ case re:run(Node, RE) of
+ {match, _} ->
+ Opts;
+ nomatch ->
+ node_config(Node, ServerHost, NodeOpts)
+ end;
+node_config(_, _, []) ->
+ [].
+
%% @spec (Host, Options) -> MaxItems
%% Host = host()
%% Options = [Option]
@@ -3215,7 +3238,9 @@ set_configure(Host, Node, From, Config, Lang) ->
[] -> node_options(Host, Type);
_ -> Options
end,
- NewOpts = merge_config(Config, OldOpts),
+ NewOpts = merge_config(
+ [node_config(Node, serverhost(Host)),
+ Config, OldOpts]),
case tree_call(Host,
set_node,
[N#pubsub_node{options = NewOpts}]) of
@@ -3238,12 +3263,9 @@ set_configure(Host, Node, From, Config, Lang) ->
Other
end.
--spec merge_config([proplists:property()], [proplists:property()]) -> [proplists:property()].
-merge_config(CustomConfig, DefaultConfig) ->
- lists:foldl(
- fun({Opt, Val}, Acc) ->
- lists:keystore(Opt, 1, Acc, {Opt, Val})
- end, DefaultConfig, CustomConfig).
+-spec merge_config([[proplists:property()]]) -> [proplists:property()].
+merge_config(ListOfConfigs) ->
+ lists:ukeysort(1, lists:flatten(ListOfConfigs)).
-spec decode_node_config(undefined | xdata(), binary(), binary()) ->
pubsub_node_config:result() |
@@ -3512,7 +3534,7 @@ tree_call({_User, Server, _Resource}, Function, Args) ->
tree_call(Host, Function, Args) ->
Tree = tree(Host),
?DEBUG("tree_call apply(~s, ~s, ~p) @ ~s", [Tree, Function, Args, Host]),
- catch apply(Tree, Function, Args).
+ apply(Tree, Function, Args).
tree_action(Host, Function, Args) ->
?DEBUG("tree_action ~p ~p ~p", [Host, Function, Args]),
@@ -3520,9 +3542,9 @@ tree_action(Host, Function, Args) ->
Fun = fun () -> tree_call(Host, Function, Args) end,
case gen_mod:get_module_opt(ServerHost, ?MODULE, db_type) of
mnesia ->
- catch mnesia:sync_dirty(Fun);
+ mnesia:sync_dirty(Fun);
sql ->
- case catch ejabberd_sql:sql_bloc(ServerHost, Fun) of
+ case ejabberd_sql:sql_bloc(ServerHost, Fun) of
{atomic, Result} ->
Result;
{aborted, Reason} ->
@@ -3530,15 +3552,8 @@ tree_action(Host, Function, Args) ->
ErrTxt = <<"Database failure">>,
{error, xmpp:err_internal_server_error(ErrTxt, ?MYLANG)}
end;
- Other ->
- case catch Fun() of
- {'EXIT', _} ->
- ?ERROR_MSG("unsupported backend: ~p~n", [Other]),
- ErrTxt = <<"Database failure">>,
- {error, xmpp:err_internal_server_error(ErrTxt, ?MYLANG)};
- Result ->
- Result
- end
+ _ ->
+ Fun()
end.
%% @doc <p>node plugin call.</p>
@@ -3587,26 +3602,20 @@ transaction(Host, Node, Action, Trans) ->
transaction(Host, Fun, Trans) ->
ServerHost = serverhost(Host),
DBType = gen_mod:get_module_opt(ServerHost, ?MODULE, db_type),
- Retry = case DBType of
- sql -> 2;
- _ -> 1
- end,
- transaction_retry(Host, ServerHost, Fun, Trans, DBType, Retry).
+ do_transaction(ServerHost, Fun, Trans, DBType).
-transaction_retry(_Host, _ServerHost, _Fun, _Trans, _DBType, 0) ->
- {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)};
-transaction_retry(Host, ServerHost, Fun, Trans, DBType, Count) ->
+do_transaction(ServerHost, Fun, Trans, DBType) ->
Res = case DBType of
mnesia ->
- catch mnesia:Trans(Fun);
+ mnesia:Trans(Fun);
sql ->
SqlFun = case Trans of
transaction -> sql_transaction;
_ -> sql_bloc
end,
- catch ejabberd_sql:SqlFun(ServerHost, Fun);
+ ejabberd_sql:SqlFun(ServerHost, Fun);
_ ->
- catch Fun()
+ Fun()
end,
case Res of
{result, Result} ->
@@ -3620,12 +3629,6 @@ transaction_retry(Host, ServerHost, Fun, Trans, DBType, Count) ->
{aborted, Reason} ->
?ERROR_MSG("transaction return internal error: ~p~n", [{aborted, Reason}]),
{error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)};
- {'EXIT', {timeout, _} = Reason} ->
- ?ERROR_MSG("transaction return internal error: ~p~n", [Reason]),
- transaction_retry(Host, ServerHost, Fun, Trans, DBType, Count - 1);
- {'EXIT', Reason} ->
- ?ERROR_MSG("transaction return internal error: ~p~n", [{'EXIT', Reason}]),
- {error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)};
Other ->
?ERROR_MSG("transaction return internal error: ~p~n", [Other]),
{error, xmpp:err_internal_server_error(<<"Database failure">>, ?MYLANG)}
@@ -3735,11 +3738,6 @@ uniqid() ->
{T1, T2, T3} = p1_time_compat:timestamp(),
(str:format("~.16B~.16B~.16B", [T1, T2, T3])).
--spec itemsEls([#pubsub_item{}]) -> [ps_item()].
-itemsEls(Items) ->
- [#ps_item{id = ItemId, sub_els = Payload}
- || #pubsub_item{itemid = {ItemId, _}, payload = Payload} <- Items].
-
-spec add_message_type(message(), message_type()) -> message().
add_message_type(#message{} = Message, Type) ->
Message#message{type = Type}.
@@ -3863,6 +3861,15 @@ mod_opt_type(max_subscriptions_node) ->
fun(A) when is_integer(A) andalso A >= 0 -> A;
(undefined) -> undefined
end;
+mod_opt_type(force_node_config) ->
+ fun(NodeOpts) ->
+ lists:map(
+ fun({Node, Opts}) ->
+ {ok, RE} = re:compile(
+ ejabberd_regexp:sh_to_awk(Node)),
+ {RE, lists:keysort(1, Opts)}
+ end, NodeOpts)
+ end;
mod_opt_type(default_node_config) ->
fun (A) when is_list(A) -> A end;
mod_opt_type(nodetree) ->
@@ -3885,4 +3892,5 @@ mod_options(Host) ->
{pep_mapping, []},
{plugins, [?STDNODE]},
{max_subscriptions_node, undefined},
- {default_node_config, []}].
+ {default_node_config, []},
+ {force_node_config, []}].
diff --git a/src/mod_push_keepalive.erl b/src/mod_push_keepalive.erl
index 35932a815..750427ee1 100644
--- a/src/mod_push_keepalive.erl
+++ b/src/mod_push_keepalive.erl
@@ -156,9 +156,15 @@ c2s_session_resumed(State) ->
-spec c2s_copy_session(c2s_state(), c2s_state()) -> c2s_state().
c2s_copy_session(State, #{push_enabled := true,
push_resume_timeout := ResumeTimeout,
- push_wake_on_timeout := WakeOnTimeout}) ->
- State#{push_resume_timeout => ResumeTimeout,
- push_wake_on_timeout => WakeOnTimeout};
+ push_wake_on_timeout := WakeOnTimeout} = OldState) ->
+ State1 = case maps:find(push_resume_timeout_orig, OldState) of
+ {ok, Val} ->
+ State#{push_resume_timeout_orig => Val};
+ error ->
+ State
+ end,
+ State1#{push_resume_timeout => ResumeTimeout,
+ push_wake_on_timeout => WakeOnTimeout};
c2s_copy_session(State, _) ->
State.
diff --git a/src/mod_register.erl b/src/mod_register.erl
index 38df8e225..5c17c5d33 100644
--- a/src/mod_register.erl
+++ b/src/mod_register.erl
@@ -326,7 +326,8 @@ try_register(User, Server, Password, SourceRaw, Lang) ->
?INFO_MSG("The account ~s was registered "
"from IP address ~s",
[jid:encode({User, Server, <<"">>}),
- ip_to_string(Source)]),
+ ejabberd_config:may_hide_data(
+ ip_to_string(Source))]),
send_welcome_message(JID),
send_registration_notifications(
?MODULE, JID, Source),
@@ -387,7 +388,7 @@ send_welcome_message(JID) ->
send_registration_notifications(Mod, UJID, Source) ->
Host = UJID#jid.lserver,
- case gen_mod:get_module_opt(Host, Mod, registration_watchers) of
+ case gen_mod:get_module_opt(Host, ?MODULE, registration_watchers) of
[] -> ok;
JIDs when is_list(JIDs) ->
Body =
@@ -395,8 +396,9 @@ send_registration_notifications(Mod, UJID, Source) ->
"IP address ~s on node ~w using ~p.",
[get_time_string(),
jid:encode(UJID),
- ip_to_string(Source), node(),
- Mod])),
+ ejabberd_config:may_hide_data(
+ ip_to_string(Source)),
+ node(), Mod])),
lists:foreach(
fun(JID) ->
ejabberd_router:route(
diff --git a/src/mod_roster.erl b/src/mod_roster.erl
index 3b893d21d..d35b7dc97 100644
--- a/src/mod_roster.erl
+++ b/src/mod_roster.erl
@@ -42,7 +42,7 @@
-export([start/2, stop/1, reload/3, process_iq/1, export/1,
import_info/0, process_local_iq/1, get_user_roster/2,
- import/5, get_roster/2,
+ import/5, get_roster/2, push_item/3,
import_start/2, import_stop/2,
c2s_self_presence/1, in_subscription/2,
out_subscription/1, set_items/3, remove_user/2,
@@ -173,9 +173,9 @@ process_local_iq(#iq{type = set, from = From, lang = Lang,
Txt = <<"Duplicated groups are not allowed by RFC6121">>,
xmpp:make_error(IQ, xmpp:err_bad_request(Txt, Lang));
false ->
- #jid{server = Server} = From,
- Access = gen_mod:get_module_opt(Server, ?MODULE, access),
- case acl:match_rule(Server, Access, From) of
+ #jid{lserver = LServer} = From,
+ Access = gen_mod:get_module_opt(LServer, ?MODULE, access),
+ case acl:match_rule(LServer, Access, From) of
deny ->
Txt = <<"Access denied by service policy">>,
xmpp:make_error(IQ, xmpp:err_not_allowed(Txt, Lang));
@@ -443,7 +443,7 @@ decode_item(Item, R, Managed) ->
process_iq_set(#iq{from = _From, to = To,
sub_els = [#roster_query{items = [QueryItem]}]} = IQ) ->
- #jid{user = User, luser = LUser, lserver = LServer} = To,
+ #jid{luser = LUser, lserver = LServer} = To,
LJID = jid:tolower(QueryItem#roster_item.jid),
F = fun () ->
Item = get_roster_item(LUser, LServer, LJID),
@@ -463,7 +463,7 @@ process_iq_set(#iq{from = _From, to = To,
end,
case transaction(LUser, LServer, [LJID], F) of
{atomic, {OldItem, Item}} ->
- push_item(User, LServer, To, OldItem, Item),
+ push_item(To, OldItem, Item),
case Item#roster.subscription of
remove ->
send_unsubscribing_presence(To, OldItem);
@@ -477,36 +477,26 @@ process_iq_set(#iq{from = _From, to = To,
xmpp:make_error(IQ, xmpp:err_internal_server_error())
end.
-push_item(User, Server, From, OldItem, NewItem) ->
- case roster_versioning_enabled(Server) of
- true ->
- push_item_version(Server, User, From, OldItem, NewItem,
- roster_version(Server, User));
- false ->
- lists:foreach(
- fun(Resource) ->
- push_item(User, Server, Resource, From, OldItem, NewItem)
- end, ejabberd_sm:get_user_resources(User, Server))
- end.
-
-push_item(User, Server, Resource, From, OldItem, NewItem) ->
- push_item(User, Server, Resource, From, OldItem, NewItem, undefined).
-
-push_item(User, Server, Resource, From, OldItem, NewItem, Ver) ->
- To = jid:make(User, Server, Resource),
- route_presence_change(To, OldItem, NewItem),
- ResIQ = #iq{type = set, from = From, to = To,
- id = <<"push", (randoms:get_string())/binary>>,
- sub_els = [#roster_query{ver = Ver,
- items = [encode_item(NewItem)]}]},
- ejabberd_router:route(ResIQ).
-
-push_item_version(Server, User, From, OldItem, NewItem, RosterVersion) ->
+push_item(To, OldItem, NewItem) ->
+ #jid{luser = LUser, lserver = LServer} = To,
+ Ver = case roster_versioning_enabled(LServer) of
+ true -> roster_version(LServer, LUser);
+ false -> undefined
+ end,
lists:foreach(
fun(Resource) ->
- push_item(User, Server, Resource, From,
- OldItem, NewItem, RosterVersion)
- end, ejabberd_sm:get_user_resources(User, Server)).
+ To1 = jid:replace_resource(To, Resource),
+ push_item(To1, OldItem, NewItem, Ver)
+ end, ejabberd_sm:get_user_resources(LUser, LServer)).
+
+push_item(To, OldItem, NewItem, Ver) ->
+ route_presence_change(To, OldItem, NewItem),
+ IQ = #iq{type = set, to = To,
+ from = jid:remove_resource(To),
+ id = <<"push", (randoms:get_string())/binary>>,
+ sub_els = [#roster_query{ver = Ver,
+ items = [encode_item(NewItem)]}]},
+ ejabberd_router:route(IQ).
-spec route_presence_change(jid(), #roster{}, #roster{}) -> ok.
route_presence_change(From, OldItem, NewItem) ->
@@ -630,8 +620,7 @@ process_subscription(Direction, User, Server, JID1,
NewItem#roster.ask == in ->
ok;
true ->
- push_item(User, Server,
- jid:make(User, Server), OldItem, NewItem)
+ push_item(jid:make(User, Server), OldItem, NewItem)
end,
true;
none ->
@@ -1174,18 +1163,18 @@ import_stop(_LServer, _DBType) ->
ets:delete(rostergroups_tmp),
ok.
--ifdef(NEW_SQL_SCHEMA).
--define(ROW_LENGTH, 10).
--else.
--define(ROW_LENGTH, 9).
--endif.
+row_length() ->
+ case ejabberd_sql:use_new_schema() of
+ true -> 10;
+ false -> 9
+ end.
import(LServer, {sql, _}, _DBType, <<"rostergroups">>, [LUser, SJID, Group]) ->
LJID = jid:tolower(jid:decode(SJID)),
ets:insert(rostergroups_tmp, {{LUser, LServer, LJID}, Group}),
ok;
import(LServer, {sql, _}, DBType, <<"rosterusers">>, Row) ->
- I = mod_roster_sql:raw_to_record(LServer, lists:sublist(Row, ?ROW_LENGTH)),
+ I = mod_roster_sql:raw_to_record(LServer, lists:sublist(Row, row_length())),
Groups = [G || {_, G} <- ets:lookup(rostergroups_tmp, I#roster.usj)],
RosterItem = I#roster{groups = Groups},
Mod = gen_mod:db_mod(DBType, ?MODULE),
diff --git a/src/mod_shared_roster.erl b/src/mod_shared_roster.erl
index a1fda9438..84ab34286 100644
--- a/src/mod_shared_roster.erl
+++ b/src/mod_shared_roster.erl
@@ -553,8 +553,6 @@ add_user_to_group(Host, US, Group) ->
DisplayedGroups = get_displayed_groups(Group, LServer),
push_user_to_displayed(LUser, LServer, Group, Host, both, DisplayedToGroups),
push_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups),
- broadcast_user_to_displayed(LUser, LServer, Host, both, DisplayedToGroups),
- broadcast_displayed_to_user(LUser, LServer, Host, both, DisplayedGroups),
Mod = gen_mod:db_mod(Host, ?MODULE),
Mod:add_user_to_group(Host, US, Group)
end.
@@ -564,11 +562,6 @@ get_displayed_groups(Group, LServer) ->
GroupOpts = proplists:get_value(Group, GroupsOpts, []),
proplists:get_value(displayed_groups, GroupOpts, []).
-broadcast_displayed_to_user(LUser, LServer, Host, Subscription, DisplayedGroups) ->
- [broadcast_members_to_user(LUser, LServer, DGroup, Host,
- Subscription)
- || DGroup <- DisplayedGroups].
-
push_displayed_to_user(LUser, LServer, Host, Subscription, DisplayedGroups) ->
[push_members_to_user(LUser, LServer, DGroup, Host,
Subscription)
@@ -612,13 +605,6 @@ push_members_to_user(LUser, LServer, Group, Host,
end,
Members).
-broadcast_members_to_user(LUser, LServer, Group, Host, Subscription) ->
- Members = get_group_users(Host, Group),
- lists:foreach(
- fun({U, S}) ->
- broadcast_subscription(U, S, {LUser, LServer, <<"">>}, Subscription)
- end, Members).
-
-spec register_user(binary(), binary()) -> ok.
register_user(User, Server) ->
Groups = get_user_groups({User, Server}),
@@ -665,10 +651,6 @@ push_user_to_displayed(LUser, LServer, Group, Host, Subscription, DisplayedToGro
GroupName, Subscription)
|| GroupD <- DisplayedToGroupsOpts].
-broadcast_user_to_displayed(LUser, LServer, Host, Subscription, DisplayedToGroupsOpts) ->
- [broadcast_user_to_group(LUser, LServer, GroupD, Host, Subscription)
- || GroupD <- DisplayedToGroupsOpts].
-
push_user_to_group(LUser, LServer, Group, Host,
GroupName, Subscription) ->
lists:foreach(fun ({U, S})
@@ -680,13 +662,6 @@ push_user_to_group(LUser, LServer, Group, Host,
end,
get_group_users(Host, Group)).
-broadcast_user_to_group(LUser, LServer, Group, Host, Subscription) ->
- lists:foreach(
- fun({U, S}) when (U == LUser) and (S == LServer) -> ok;
- ({U, S}) ->
- broadcast_subscription(LUser, LServer, {U, S, <<"">>}, Subscription)
- end, get_group_users(Host, Group)).
-
%% Get list of groups to which this group is displayed
displayed_to_groups(GroupName, LServer) ->
GroupsOpts = groups_with_opts(LServer),
@@ -699,15 +674,9 @@ displayed_to_groups(GroupName, LServer) ->
[Name || {Name, _} <- Gs].
push_item(User, Server, Item) ->
- Stanza = #iq{type = set, id = <<"push", (randoms:get_string())/binary>>,
- sub_els = [#roster_query{
- items = [mod_roster:encode_item(Item)]}]},
- lists:foreach(fun (Resource) ->
- JID = jid:make(User, Server, Resource),
- ejabberd_router:route(
- xmpp:set_from_to(Stanza, jid:remove_resource(JID), JID))
- end,
- ejabberd_sm:get_user_resources(User, Server)).
+ mod_roster:push_item(jid:make(User, Server),
+ Item#roster{subscription = none},
+ Item).
push_roster_item(User, Server, ContactU, ContactS,
GroupName, Subscription) ->
@@ -720,31 +689,6 @@ push_roster_item(User, Server, ContactU, ContactS,
-spec c2s_self_presence({presence(), ejabberd_c2s:state()})
-> {presence(), ejabberd_c2s:state()}.
-c2s_self_presence({_, #{pres_last := _}} = Acc) ->
- %% This is just a presence update, nothing to do
- Acc;
-c2s_self_presence({#presence{type = available}, #{jid := New}} = Acc) ->
- LUser = New#jid.luser,
- LServer = New#jid.lserver,
- Resources = ejabberd_sm:get_user_resources(LUser, LServer),
- ?DEBUG("user_available for ~p @ ~p (~p resources)",
- [LUser, LServer, length(Resources)]),
- case length(Resources) of
- %% first session for this user
- 1 ->
- UserGroups = get_user_groups({LUser, LServer}),
- lists:foreach(fun (OG) ->
- ?DEBUG("user_available: pushing ~p @ ~p grp ~p",
- [LUser, LServer, OG]),
- DisplayedToGroups = displayed_to_groups(OG, LServer),
- DisplayedGroups = get_displayed_groups(OG, LServer),
- broadcast_displayed_to_user(LUser, LServer, LServer, both, DisplayedGroups),
- broadcast_user_to_displayed(LUser, LServer, LServer, both, DisplayedToGroups)
- end,
- UserGroups);
- _ -> ok
- end,
- Acc;
c2s_self_presence(Acc) ->
Acc.
@@ -1020,21 +964,11 @@ split_grouphost(Host, Group) ->
[_] -> {Host, Group}
end.
-broadcast_subscription(User, Server, ContactJid, Subscription) ->
- ejabberd_sm:route(jid:make(User, Server),
- {item, ContactJid, Subscription}).
-
displayed_groups_update(Members, DisplayedGroups, Subscription) ->
- lists:foreach(fun({U, S}) ->
- push_displayed_to_user(U, S, S, Subscription, DisplayedGroups),
- case Subscription of
- both ->
- broadcast_displayed_to_user(U, S, S, to, DisplayedGroups),
- broadcast_displayed_to_user(U, S, S, from, DisplayedGroups);
- Subscr ->
- broadcast_displayed_to_user(U, S, S, Subscr, DisplayedGroups)
- end
- end, Members).
+ lists:foreach(
+ fun({U, S}) ->
+ push_displayed_to_user(U, S, S, Subscription, DisplayedGroups)
+ end, Members).
opts_to_binary(Opts) ->
lists:map(
diff --git a/src/mod_stream_mgmt.erl b/src/mod_stream_mgmt.erl
index 6ca5a8b39..a8aeaaef0 100644
--- a/src/mod_stream_mgmt.erl
+++ b/src/mod_stream_mgmt.erl
@@ -664,6 +664,8 @@ inherit_session_state(#{user := U, server := S,
exit:{normal, _} ->
{error, <<"Previous session PID has exited">>};
exit:{timeout, _} ->
+ ejabberd_sm:close_session(OldSID, U, S, R),
+ ejabberd_c2s:stop(OldPID),
{error, <<"Session state copying timed out">>}
end
end;
@@ -733,7 +735,9 @@ bounce_message_queue() ->
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)};
+ State1 = maps:remove(mgmt_force_enqueue, State),
+ State2 = maps:remove(mgmt_is_resent, State1),
+ {true, State2};
need_to_enqueue(State, _) ->
{false, State}.
diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl
index ce4fcd702..51de22e4c 100644
--- a/src/mod_vcard_ldap.erl
+++ b/src/mod_vcard_ldap.erl
@@ -143,7 +143,7 @@ search_items(Entries, State) ->
#eldap_entry{attributes = Attrs} = E, Attrs
end,
Entries),
- lists:flatmap(
+ lists:filtermap(
fun(Attrs) ->
case eldap_utils:find_ldap_attrs(UIDs, Attrs) of
{U, UIDAttrFormat} ->
@@ -163,15 +163,15 @@ search_items(Entries, State) ->
end,
SearchReported),
J = <<Username/binary, $@, LServer/binary>>,
- [{<<"jid">>, J} | RFields];
+ {true, [{<<"jid">>, J} | RFields]};
_ ->
- []
+ false
end;
_ ->
- []
+ false
end;
<<"">> ->
- []
+ false
end
end, Attributes).
diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl
index ce7506b6b..57d2052a0 100644
--- a/src/mod_vcard_sql.erl
+++ b/src/mod_vcard_sql.erl
@@ -39,12 +39,6 @@
-include("ejabberd_sql_pt.hrl").
-include("translate.hrl").
--ifdef(NEW_SQL_SCHEMA).
--define(USE_NEW_SCHEMA, true).
--else.
--define(USE_NEW_SCHEMA, false).
--endif.
-
%%%===================================================================
%%% API
%%%===================================================================
@@ -268,7 +262,7 @@ make_matchspec(LServer, Data) ->
filter_fields(Data, <<"">>, LServer).
filter_fields([], Match, LServer) ->
- case ?USE_NEW_SCHEMA of
+ case ejabberd_sql:use_new_schema() of
true ->
SServer = ejabberd_sql:escape(LServer),
case Match of
diff --git a/src/mod_vcard_xupdate.erl b/src/mod_vcard_xupdate.erl
index a44b8ced8..5c10a5a7e 100644
--- a/src/mod_vcard_xupdate.erl
+++ b/src/mod_vcard_xupdate.erl
@@ -24,9 +24,10 @@
%%%----------------------------------------------------------------------
-module(mod_vcard_xupdate).
-
-behaviour(gen_mod).
+-protocol({xep, 398, '0.2.0'}).
+
%% gen_mod callbacks
-export([start/2, stop/1, reload/3]).
@@ -77,11 +78,18 @@ depends(_Host, _Opts) ->
-> {presence(), ejabberd_c2s:state()}.
update_presence({#presence{type = available} = Pres,
#{jid := #jid{luser = LUser, lserver = LServer}} = State}) ->
- Pres1 = case get_xupdate(LUser, LServer) of
- undefined -> xmpp:remove_subtag(Pres, #vcard_xupdate{});
- XUpdate -> xmpp:set_subtag(Pres, XUpdate)
- end,
- {Pres1, State};
+ case xmpp:get_subtag(Pres, #vcard_xupdate{}) of
+ #vcard_xupdate{hash = <<>>} ->
+ %% XEP-0398 forbids overwriting vcard:x:update
+ %% tags with empty <photo/> element
+ {Pres, State};
+ _ ->
+ Pres1 = case get_xupdate(LUser, LServer) of
+ undefined -> xmpp:remove_subtag(Pres, #vcard_xupdate{});
+ XUpdate -> xmpp:set_subtag(Pres, XUpdate)
+ end,
+ {Pres1, State}
+ end;
update_presence(Acc) ->
Acc.
diff --git a/src/prosody2ejabberd.erl b/src/prosody2ejabberd.erl
index 62cb32417..cfee9ae01 100644
--- a/src/prosody2ejabberd.erl
+++ b/src/prosody2ejabberd.erl
@@ -38,23 +38,30 @@
%%% API
%%%===================================================================
from_dir(ProsodyDir) ->
- case file:list_dir(ProsodyDir) of
- {ok, HostDirs} ->
- lists:foreach(
- fun(HostDir) ->
- Host = list_to_binary(HostDir),
- lists:foreach(
- fun(SubDir) ->
- Path = filename:join(
- [ProsodyDir, HostDir, SubDir]),
- convert_dir(Path, Host, SubDir)
- end, ["vcard", "accounts", "roster",
- "private", "config", "offline",
- "privacy", "pep", "pubsub"])
- end, HostDirs);
- {error, Why} = Err ->
- ?ERROR_MSG("failed to list ~s: ~s",
- [ProsodyDir, file:format_error(Why)]),
+ case code:ensure_loaded(luerl) of
+ {module, _} ->
+ case file:list_dir(ProsodyDir) of
+ {ok, HostDirs} ->
+ lists:foreach(
+ fun(HostDir) ->
+ Host = list_to_binary(HostDir),
+ lists:foreach(
+ fun(SubDir) ->
+ Path = filename:join(
+ [ProsodyDir, HostDir, SubDir]),
+ convert_dir(Path, Host, SubDir)
+ end, ["vcard", "accounts", "roster",
+ "private", "config", "offline",
+ "privacy", "pep", "pubsub"])
+ end, HostDirs);
+ {error, Why} = Err ->
+ ?ERROR_MSG("failed to list ~s: ~s",
+ [ProsodyDir, file:format_error(Why)]),
+ Err
+ end;
+ {error, _} = Err ->
+ ?INFO_MSG("The file 'luerl.beam' is not found: maybe "
+ "ejabberd is not compiled with Lua support", []),
Err
end.
diff --git a/src/pubsub_migrate.erl b/src/pubsub_migrate.erl
index 6b5900279..0728da765 100644
--- a/src/pubsub_migrate.erl
+++ b/src/pubsub_migrate.erl
@@ -529,5 +529,4 @@ report_and_stop(Tab, Err) ->
"Failed to convert '~s' table to binary: ~p",
[Tab, Err])),
?CRITICAL_MSG(ErrTxt, []),
- timer:sleep(1000),
- halt(string:substr(ErrTxt, 1, 199)).
+ ejabberd:halt().
diff --git a/src/rest.erl b/src/rest.erl
index 16cf09a34..7e509dec8 100644
--- a/src/rest.erl
+++ b/src/rest.erl
@@ -172,7 +172,7 @@ url(Server, Path, Params) ->
Base = base_url(Server, Path),
[<<$&, ParHead/binary>> | ParTail] =
[<<"&", (iolist_to_binary(Key))/binary, "=",
- (ejabberd_http:url_encode(Value))/binary>>
+ (misc:url_encode(Value))/binary>>
|| {Key, Value} <- Params],
Tail = iolist_to_binary([ParHead | ParTail]),
binary_to_list(<<Base/binary, $?, Tail/binary>>).