diff options
Diffstat (limited to 'src/ejabberd_sm.erl')
-rw-r--r-- | src/ejabberd_sm.erl | 212 |
1 files changed, 114 insertions, 98 deletions
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index d9e211656..2c7135365 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -5,7 +5,7 @@ %%% Created : 24 Nov 2002 by Alexey Shchepin <alexey@process-one.net> %%% %%% -%%% ejabberd, Copyright (C) 2002-2018 ProcessOne +%%% ejabberd, Copyright (C) 2002-2019 ProcessOne %%% %%% This program is free software; you can redistribute it and/or %%% modify it under the terms of the GNU General Public License as @@ -47,11 +47,9 @@ disconnect_removed_user/2, get_user_resources/2, get_user_present_resources/2, - set_presence/7, - unset_presence/6, + set_presence/6, + unset_presence/5, close_session_unset_presence/5, - set_offline_info/5, - get_offline_info/4, dirty_get_sessions_list/0, dirty_get_my_sessions_list/0, get_vh_session_list/1, @@ -68,6 +66,8 @@ get_session_sids/2, get_user_info/2, get_user_info/3, + set_user_info/5, + del_user_info/4, get_user_ip/3, get_max_user_sessions/2, get_all_pids/0, @@ -78,8 +78,7 @@ host_down/1, make_sid/0, clean_cache/1, - config_reloaded/0, - is_online/1 + config_reloaded/0 ]). -export([init/1, handle_call/3, handle_cast/2, @@ -91,6 +90,7 @@ -include("ejabberd_commands.hrl"). -include("ejabberd_sm.hrl"). +-include("ejabberd_stacktrace.hrl"). -callback init() -> ok | {error, any()}. -callback set_session(#session{}) -> ok | {error, any()}. @@ -141,11 +141,10 @@ route(Packet) -> ?DEBUG("hook dropped stanza:~n~s", [xmpp:pp(Packet)]); Packet1 -> try do_route(Packet1), ok - catch E:R -> - St = erlang:get_stacktrace(), + catch ?EX_RULE(E, R, St) -> ?ERROR_MSG("failed to route packet:~n~s~nReason = ~p", [xmpp:pp(Packet1), - {E, {R, St}}]) + {E, {R, ?EX_STACK(St)}}]) end end. @@ -211,14 +210,14 @@ get_user_resources(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = get_sm_backend(LServer), - Ss = online(get_sessions(Mod, LUser, LServer)), + Ss = get_sessions(Mod, LUser, LServer), [element(3, S#session.usr) || S <- clean_session_list(Ss)]. -spec get_user_present_resources(binary(), binary()) -> [tuple()]. get_user_present_resources(LUser, LServer) -> Mod = get_sm_backend(LServer), - Ss = online(get_sessions(Mod, LUser, LServer)), + Ss = get_sessions(Mod, LUser, LServer), [{S#session.priority, element(3, S#session.usr)} || S <- clean_session_list(Ss), is_integer(S#session.priority)]. @@ -229,7 +228,7 @@ get_user_ip(User, Server, Resource) -> LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), - case online(get_sessions(Mod, LUser, LServer, LResource)) of + case get_sessions(Mod, LUser, LServer, LResource) of [] -> undefined; Ss -> @@ -242,7 +241,7 @@ get_user_info(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = get_sm_backend(LServer), - Ss = online(get_sessions(Mod, LUser, LServer)), + Ss = get_sessions(Mod, LUser, LServer), [{LResource, [{node, node(Pid)}, {ts, Ts}, {pid, Pid}, {priority, Priority} | Info]} || #session{usr = {_, _, LResource}, @@ -257,7 +256,7 @@ get_user_info(User, Server, Resource) -> LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), - case online(get_sessions(Mod, LUser, LServer, LResource)) of + case get_sessions(Mod, LUser, LServer, LResource) of [] -> offline; Ss -> @@ -269,27 +268,87 @@ get_user_info(User, Server, Resource) -> |Session#session.info] end. +-spec set_user_info(binary(), binary(), binary(), atom(), term()) -> ok | {error, any()}. +set_user_info(User, Server, Resource, Key, Val) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + LResource = jid:resourceprep(Resource), + Mod = get_sm_backend(LServer), + case get_sessions(Mod, LUser, LServer, LResource) of + [] -> {error, notfound}; + Ss -> + lists:foldl( + fun(#session{sid = {_, Pid}, + info = Info} = Session, _) when Pid == self() -> + Info1 = lists:keystore(Key, 1, Info, {Key, Val}), + set_session(Session#session{info = Info1}); + (_, Acc) -> + Acc + end, {error, not_owner}, Ss) + end. + +-spec del_user_info(binary(), binary(), binary(), atom()) -> ok | {error, any()}. +del_user_info(User, Server, Resource, Key) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + LResource = jid:resourceprep(Resource), + Mod = get_sm_backend(LServer), + case get_sessions(Mod, LUser, LServer, LResource) of + [] -> {error, notfound}; + Ss -> + lists:foldl( + fun(#session{sid = {_, Pid}, + info = Info} = Session, _) when Pid == self() -> + Info1 = lists:keydelete(Key, 1, Info), + set_session(Session#session{info = Info1}); + (_, Acc) -> + Acc + end, {error, not_owner}, Ss) + end. + -spec set_presence(sid(), binary(), binary(), binary(), - prio(), presence(), info()) -> ok. + prio(), presence()) -> ok | {error, notfound}. -set_presence(SID, User, Server, Resource, Priority, - Presence, Info) -> - set_session(SID, User, Server, Resource, Priority, - Info), - ejabberd_hooks:run(set_presence_hook, - jid:nameprep(Server), - [User, Server, Resource, Presence]). +set_presence(SID, User, Server, Resource, Priority, Presence) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + LResource = jid:resourceprep(Resource), + Mod = get_sm_backend(LServer), + case get_sessions(Mod, LUser, LServer, LResource) of + [] -> {error, notfound}; + Ss -> + case lists:keyfind(SID, #session.sid, Ss) of + #session{info = Info} -> + set_session(SID, User, Server, Resource, Priority, Info), + ejabberd_hooks:run(set_presence_hook, + LServer, + [User, Server, Resource, Presence]); + false -> + {error, notfound} + end + end. -spec unset_presence(sid(), binary(), binary(), - binary(), binary(), info()) -> ok. + binary(), binary()) -> ok | {error, notfound}. -unset_presence(SID, User, Server, Resource, Status, - Info) -> - set_session(SID, User, Server, Resource, undefined, - Info), - ejabberd_hooks:run(unset_presence_hook, - jid:nameprep(Server), - [User, Server, Resource, Status]). +unset_presence(SID, User, Server, Resource, Status) -> + LUser = jid:nodeprep(User), + LServer = jid:nameprep(Server), + LResource = jid:resourceprep(Resource), + Mod = get_sm_backend(LServer), + case get_sessions(Mod, LUser, LServer, LResource) of + [] -> {error, notfound}; + Ss -> + case lists:keyfind(SID, #session.sid, Ss) of + #session{info = Info} -> + set_session(SID, User, Server, Resource, undefined, Info), + ejabberd_hooks:run(unset_presence_hook, + LServer, + [User, Server, Resource, Status]); + false -> + {error, notfound} + end + end. -spec close_session_unset_presence(sid(), binary(), binary(), binary(), binary()) -> ok. @@ -316,7 +375,7 @@ get_session_sid(User, Server, Resource) -> LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), - case online(get_sessions(Mod, LUser, LServer, LResource)) of + case get_sessions(Mod, LUser, LServer, LResource) of [] -> none; Ss -> @@ -330,43 +389,15 @@ get_session_sids(User, Server) -> LUser = jid:nodeprep(User), LServer = jid:nameprep(Server), Mod = get_sm_backend(LServer), - Sessions = online(get_sessions(Mod, LUser, LServer)), + Sessions = get_sessions(Mod, LUser, LServer), [SID || #session{sid = SID} <- Sessions]. --spec set_offline_info(sid(), binary(), binary(), binary(), info()) -> ok. - -set_offline_info(SID, User, Server, Resource, Info) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - LResource = jid:resourceprep(Resource), - set_session(SID, LUser, LServer, LResource, undefined, [offline | Info]). - --spec get_offline_info(erlang:timestamp(), binary(), binary(), - binary()) -> none | info(). - -get_offline_info(Time, User, Server, Resource) -> - LUser = jid:nodeprep(User), - LServer = jid:nameprep(Server), - LResource = jid:resourceprep(Resource), - Mod = get_sm_backend(LServer), - case get_sessions(Mod, LUser, LServer, LResource) of - [#session{sid = {Time, _}, info = Info}] -> - case proplists:get_bool(offline, Info) of - true -> - Info; - false -> - none - end; - _ -> - none - end. - -spec dirty_get_sessions_list() -> [ljid()]. dirty_get_sessions_list() -> lists:flatmap( fun(Mod) -> - [S#session.usr || S <- online(get_sessions(Mod))] + [S#session.usr || S <- get_sessions(Mod)] end, get_sm_backends()). -spec dirty_get_my_sessions_list() -> [#session{}]. @@ -374,7 +405,7 @@ dirty_get_sessions_list() -> dirty_get_my_sessions_list() -> lists:flatmap( fun(Mod) -> - [S || S <- online(get_sessions(Mod)), + [S || S <- get_sessions(Mod), node(element(2, S#session.sid)) == node()] end, get_sm_backends()). @@ -383,14 +414,14 @@ dirty_get_my_sessions_list() -> get_vh_session_list(Server) -> LServer = jid:nameprep(Server), Mod = get_sm_backend(LServer), - [S#session.usr || S <- online(get_sessions(Mod, LServer))]. + [S#session.usr || S <- get_sessions(Mod, LServer)]. -spec get_all_pids() -> [pid()]. get_all_pids() -> lists:flatmap( fun(Mod) -> - [element(2, S#session.sid) || S <- online(get_sessions(Mod))] + [element(2, S#session.sid) || S <- get_sessions(Mod)] end, get_sm_backends()). -spec get_vh_session_number(binary()) -> non_neg_integer(). @@ -398,7 +429,7 @@ get_all_pids() -> get_vh_session_number(Server) -> LServer = jid:nameprep(Server), Mod = get_sm_backend(LServer), - length(online(get_sessions(Mod, LServer))). + length(get_sessions(Mod, LServer)). %% Why the hell do we have so many similar kicks? c2s_handle_info(#{lang := Lang} = State, replaced) -> @@ -514,9 +545,13 @@ set_session(SID, User, Server, Resource, Priority, Info) -> LResource = jid:resourceprep(Resource), US = {LUser, LServer}, USR = {LUser, LServer, LResource}, + set_session(#session{sid = SID, usr = USR, us = US, + priority = Priority, info = Info}). + +-spec set_session(#session{}) -> ok | {error, any()}. +set_session(#session{us = {LUser, LServer}} = Session) -> Mod = get_sm_backend(LServer), - case Mod:set_session(#session{sid = SID, usr = USR, us = US, - priority = Priority, info = Info}) of + case Mod:set_session(Session) of ok -> case use_cache(Mod, LServer) of true -> @@ -579,16 +614,6 @@ delete_session(Mod, #session{usr = {LUser, LServer, _}} = Session) -> ok end. --spec online([#session{}]) -> [#session{}]. - -online(Sessions) -> - lists:filter(fun is_online/1, Sessions). - --spec is_online(#session{}) -> boolean(). - -is_online(#session{info = Info}) -> - not proplists:get_bool(offline, Info). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec do_route(jid(), term()) -> any(). do_route(#jid{lresource = <<"">>} = To, Term) -> @@ -600,7 +625,7 @@ do_route(To, Term) -> ?DEBUG("broadcasting ~p to ~s", [Term, jid:encode(To)]), {U, S, R} = jid:tolower(To), Mod = get_sm_backend(S), - case online(get_sessions(Mod, U, S, R)) of + case get_sessions(Mod, U, S, R) of [] -> ?DEBUG("dropping broadcast to unavailable resourse: ~p", [Term]); Ss -> @@ -631,7 +656,7 @@ do_route(#presence{to = To, type = T} = Packet) ejabberd_c2s:route(Pid, {route, Packet1}); (_) -> ok - end, online(get_sessions(Mod, LUser, LServer))); + end, get_sessions(Mod, LUser, LServer)); false -> ok end; @@ -660,7 +685,7 @@ do_route(Packet) -> To = xmpp:get_to(Packet), {LUser, LServer, LResource} = jid:tolower(To), Mod = get_sm_backend(LServer), - case online(get_sessions(Mod, LUser, LServer, LResource)) of + case get_sessions(Mod, LUser, LServer, LResource) of [] -> case Packet of #message{type = T} when T == chat; T == normal -> @@ -708,8 +733,8 @@ route_message(#message{to = To, type = Type} = Packet) -> (P >= 0) and (Type == headline) -> LResource = jid:resourceprep(R), Mod = get_sm_backend(LServer), - case online(get_sessions(Mod, LUser, LServer, - LResource)) of + case get_sessions(Mod, LUser, LServer, + LResource) of [] -> ok; % Race condition Ss -> @@ -780,13 +805,9 @@ check_for_sessions_to_replace(User, Server, Resource) -> check_existing_resources(LUser, LServer, LResource) -> Mod = get_sm_backend(LServer), Ss = get_sessions(Mod, LUser, LServer, LResource), - {OnlineSs, OfflineSs} = lists:partition(fun is_online/1, Ss), - lists:foreach(fun(S) -> - delete_session(Mod, S) - end, OfflineSs), - if OnlineSs == [] -> ok; + if Ss == [] -> ok; true -> - SIDs = [SID || #session{sid = SID} <- OnlineSs], + SIDs = [SID || #session{sid = SID} <- Ss], MaxSID = lists:max(SIDs), lists:foreach(fun ({_, Pid} = S) when S /= MaxSID -> ejabberd_c2s:route(Pid, replaced); @@ -806,22 +827,17 @@ get_resource_sessions(User, Server, Resource) -> LServer = jid:nameprep(Server), LResource = jid:resourceprep(Resource), Mod = get_sm_backend(LServer), - [S#session.sid || S <- online(get_sessions(Mod, LUser, LServer, LResource))]. + [S#session.sid || S <- get_sessions(Mod, LUser, LServer, LResource)]. -spec check_max_sessions(binary(), binary()) -> ok | replaced. check_max_sessions(LUser, LServer) -> Mod = get_sm_backend(LServer), Ss = get_sessions(Mod, LUser, LServer), - {OnlineSs, OfflineSs} = lists:partition(fun is_online/1, Ss), MaxSessions = get_max_user_sessions(LUser, LServer), - if length(OnlineSs) =< MaxSessions -> ok; + if length(Ss) =< MaxSessions -> ok; true -> - #session{sid = {_, Pid}} = lists:min(OnlineSs), + #session{sid = {_, Pid}} = lists:min(Ss), ejabberd_c2s:route(Pid, replaced) - end, - if length(OfflineSs) =< MaxSessions -> ok; - true -> - delete_session(Mod, lists:min(OfflineSs)) end. %% Get the user_max_session setting @@ -843,7 +859,7 @@ get_max_user_sessions(LUser, Host) -> force_update_presence({LUser, LServer}) -> Mod = get_sm_backend(LServer), - Ss = online(get_sessions(Mod, LUser, LServer)), + Ss = get_sessions(Mod, LUser, LServer), lists:foreach(fun (#session{sid = {_, Pid}}) -> ejabberd_c2s:resend_presence(Pid) end, |