diff options
Diffstat (limited to 'src/ejabberd_sm.erl')
-rw-r--r-- | src/ejabberd_sm.erl | 694 |
1 files changed, 356 insertions, 338 deletions
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl index 6a41be0ae..4ab5975fa 100644 --- a/src/ejabberd_sm.erl +++ b/src/ejabberd_sm.erl @@ -25,6 +25,7 @@ %%%---------------------------------------------------------------------- -module(ejabberd_sm). + -author('alexey@process-one.net'). -behaviour(gen_server). @@ -58,12 +59,15 @@ ]). %% gen_server callbacks --export([init/1, handle_call/3, handle_cast/2, handle_info/2, - terminate/2, code_change/3]). +-export([init/1, handle_call/3, handle_cast/2, + handle_info/2, terminate/2, code_change/3]). -include("ejabberd.hrl"). + -include("jlib.hrl"). + -include("ejabberd_commands.hrl"). + -include("mod_privacy.hrl"). -record(session, {sid, usr, us, priority, info}). @@ -80,26 +84,40 @@ %% Function: start_link() -> {ok,Pid} | ignore | {error,Error} %% Description: Starts the server %%-------------------------------------------------------------------- +-type sid() :: {erlang:timestamp(), pid()}. +-type ip() :: {inet:ip_address(), inet:port_number()} | undefined. +-type info() :: [{conn, atom()} | {ip, ip()} | {node, atom()} + | {oor, boolean()} | {auth_module, atom()}]. +-type prio() :: undefined | integer(). + +-export_type([sid/0]). + start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + gen_server:start_link({local, ?MODULE}, ?MODULE, [], + []). + +-spec route(jid(), jid(), xmlel() | broadcast()) -> ok. route(From, To, Packet) -> case catch do_route(From, To, Packet) of - {'EXIT', Reason} -> - ?ERROR_MSG("~p~nwhen processing: ~p", - [Reason, {From, To, Packet}]); - _ -> - ok + {'EXIT', Reason} -> + ?ERROR_MSG("~p~nwhen processing: ~p", + [Reason, {From, To, Packet}]); + _ -> ok end. +-spec open_session(sid(), binary(), binary(), binary(), info()) -> ok. + open_session(SID, User, Server, Resource, Info) -> set_session(SID, User, Server, Resource, undefined, Info), mnesia:dirty_update_counter(session_counter, jlib:nameprep(Server), 1), check_for_sessions_to_replace(User, Server, Resource), JID = jlib:make_jid(User, Server, Resource), - ejabberd_hooks:run(sm_register_connection_hook, JID#jid.lserver, - [SID, JID, Info]). + ejabberd_hooks:run(sm_register_connection_hook, + JID#jid.lserver, [SID, JID, Info]). + +-spec close_session(sid(), binary(), binary(), binary()) -> ok. close_session(SID, User, Server, Resource) -> Info = case mnesia:dirty_read({session, SID}) of @@ -113,27 +131,29 @@ close_session(SID, User, Server, Resource) -> end, mnesia:sync_dirty(F), JID = jlib:make_jid(User, Server, Resource), - ejabberd_hooks:run(sm_remove_connection_hook, JID#jid.lserver, - [SID, JID, Info]). + ejabberd_hooks:run(sm_remove_connection_hook, + JID#jid.lserver, [SID, JID, Info]). check_in_subscription(Acc, User, Server, _JID, _Type, _Reason) -> case ejabberd_auth:is_user_exists(User, Server) of - true -> - Acc; - false -> - {stop, false} + true -> Acc; + false -> {stop, false} end. +-spec bounce_offline_message(jid(), jid(), xmlel()) -> stop. + bounce_offline_message(From, To, Packet) -> - Err = jlib:make_error_reply(Packet, ?ERR_SERVICE_UNAVAILABLE), + Err = jlib:make_error_reply(Packet, + ?ERR_SERVICE_UNAVAILABLE), ejabberd_router:route(To, From, Err), stop. +-spec disconnect_removed_user(binary(), binary()) -> ok. + disconnect_removed_user(User, Server) -> - ejabberd_sm:route(jlib:make_jid("", "", ""), - jlib:make_jid(User, Server, ""), - {xmlelement, "broadcast", [], - [{exit, "User removed"}]}). + ejabberd_sm:route(jlib:make_jid(<<"">>, <<"">>, <<"">>), + jlib:make_jid(User, Server, <<"">>), + {broadcast, {exit, <<"User removed">>}}). get_user_resources(User, Server) -> LUser = jlib:nodeprep(User), @@ -146,6 +166,8 @@ get_user_resources(User, Server) -> [element(3, S#session.usr) || S <- clean_session_list(Ss)] end. +-spec get_user_ip(binary(), binary(), binary()) -> ip(). + get_user_ip(User, Server, Resource) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), @@ -159,6 +181,8 @@ get_user_ip(User, Server, Resource) -> proplists:get_value(ip, Session#session.info) end. +-spec get_user_info(binary(), binary(), binary()) -> info() | offline. + get_user_info(User, Server, Resource) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), @@ -175,21 +199,40 @@ get_user_info(User, Server, Resource) -> [{node, Node}, {conn, Conn}, {ip, IP}] end. -set_presence(SID, User, Server, Resource, Priority, Presence, Info) -> - set_session(SID, User, Server, Resource, Priority, Info), - ejabberd_hooks:run(set_presence_hook, jlib:nameprep(Server), +-spec set_presence(sid(), binary(), binary(), binary(), + prio(), xmlel(), info()) -> ok. + +set_presence(SID, User, Server, Resource, Priority, + Presence, Info) -> + set_session(SID, User, Server, Resource, Priority, + Info), + ejabberd_hooks:run(set_presence_hook, + jlib:nameprep(Server), [User, Server, Resource, Presence]). -unset_presence(SID, User, Server, Resource, Status, Info) -> - set_session(SID, User, Server, Resource, undefined, Info), - ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server), +-spec unset_presence(sid(), binary(), binary(), + binary(), binary(), info()) -> ok. + +unset_presence(SID, User, Server, Resource, Status, + Info) -> + set_session(SID, User, Server, Resource, undefined, + Info), + ejabberd_hooks:run(unset_presence_hook, + jlib:nameprep(Server), [User, Server, Resource, Status]). -close_session_unset_presence(SID, User, Server, Resource, Status) -> +-spec close_session_unset_presence(sid(), binary(), binary(), + binary(), binary()) -> ok. + +close_session_unset_presence(SID, User, Server, + Resource, Status) -> close_session(SID, User, Server, Resource), - ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server), + ejabberd_hooks:run(unset_presence_hook, + jlib:nameprep(Server), [User, Server, Resource, Status]). +-spec get_session_pid(binary(), binary(), binary()) -> none | pid(). + get_session_pid(User, Server, Resource) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), @@ -200,6 +243,8 @@ get_session_pid(User, Server, Resource) -> _ -> none end. +-spec dirty_get_sessions_list() -> [ljid()]. + dirty_get_sessions_list() -> mnesia:dirty_select( session, @@ -216,11 +261,11 @@ dirty_get_my_sessions_list() -> get_vh_session_list(Server) -> LServer = jlib:nameprep(Server), - mnesia:dirty_select( - session, - [{#session{usr = '$1', _ = '_'}, - [{'==', {element, 2, '$1'}, LServer}], - ['$1']}]). + mnesia:dirty_select(session, + [{#session{usr = '$1', _ = '_'}, + [{'==', {element, 2, '$1'}, LServer}], ['$1']}]). + +-spec get_vh_session_list(binary()) -> [ljid()]. get_vh_session_number(Server) -> LServer = jlib:nameprep(Server), @@ -234,12 +279,18 @@ get_vh_session_number(Server) -> Count; _ -> 0 end. - + register_iq_handler(Host, XMLNS, Module, Fun) -> - ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun}. + ejabberd_sm ! + {register_iq_handler, Host, XMLNS, Module, Fun}. + +-spec register_iq_handler(binary(), binary(), atom(), atom(), list()) -> any(). register_iq_handler(Host, XMLNS, Module, Fun, Opts) -> - ejabberd_sm ! {register_iq_handler, Host, XMLNS, Module, Fun, Opts}. + ejabberd_sm ! + {register_iq_handler, Host, XMLNS, Module, Fun, Opts}. + +-spec unregister_iq_handler(binary(), binary()) -> any(). unregister_iq_handler(Host, XMLNS) -> ejabberd_sm ! {unregister_iq_handler, Host, XMLNS}. @@ -293,8 +344,7 @@ init([]) -> %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call(_Request, _From, State) -> - Reply = ok, - {reply, Reply, State}. + Reply = ok, {reply, Reply, State}. %%-------------------------------------------------------------------- %% Function: handle_cast(Msg, State) -> {noreply, State} | @@ -302,8 +352,7 @@ handle_call(_Request, _From, State) -> %% {stop, Reason, State} %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast(_Msg, State) -> - {noreply, State}. +handle_cast(_Msg, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | @@ -326,20 +375,22 @@ handle_info({mnesia_system_event, {mnesia_down, Node}}, State) -> handle_info({register_iq_handler, Host, XMLNS, Module, Function}, State) -> ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function}), {noreply, State}; -handle_info({register_iq_handler, Host, XMLNS, Module, Function, Opts}, State) -> - ets:insert(sm_iqtable, {{XMLNS, Host}, Module, Function, Opts}), +handle_info({register_iq_handler, Host, XMLNS, Module, + Function, Opts}, + State) -> + ets:insert(sm_iqtable, + {{XMLNS, Host}, Module, Function, Opts}), {noreply, State}; -handle_info({unregister_iq_handler, Host, XMLNS}, State) -> +handle_info({unregister_iq_handler, Host, XMLNS}, + State) -> case ets:lookup(sm_iqtable, {XMLNS, Host}) of - [{_, Module, Function, Opts}] -> - gen_iq_handler:stop_iq_handler(Module, Function, Opts); - _ -> - ok + [{_, Module, Function, Opts}] -> + gen_iq_handler:stop_iq_handler(Module, Function, Opts); + _ -> ok end, ets:delete(sm_iqtable, {XMLNS, Host}), {noreply, State}; -handle_info(_Info, State) -> - {noreply, State}. +handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> void() @@ -356,8 +407,7 @@ terminate(_Reason, _State) -> %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> - {ok, State}. +code_change(_OldVsn, State, _Extra) -> {ok, State}. %%-------------------------------------------------------------------- %%% Internal functions @@ -369,12 +419,9 @@ set_session(SID, User, Server, Resource, Priority, Info) -> LResource = jlib:resourceprep(Resource), US = {LUser, LServer}, USR = {LUser, LServer, LResource}, - F = fun() -> - mnesia:write(#session{sid = SID, - usr = USR, - us = US, - priority = Priority, - info = Info}) + F = fun () -> + mnesia:write(#session{sid = SID, usr = USR, us = US, + priority = Priority, info = Info}) end, mnesia:sync_dirty(F). @@ -408,108 +455,107 @@ recount_session_table(Node) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_route(From, To, Packet) -> - ?DEBUG("session manager~n\tfrom ~p~n\tto ~p~n\tpacket ~P~n", + ?DEBUG("session manager~n\tfrom ~p~n\tto ~p~n\tpacket " + "~P~n", [From, To, Packet, 8]), #jid{user = User, server = Server, luser = LUser, lserver = LServer, lresource = LResource} = To, - {xmlelement, Name, Attrs, _Els} = Packet, + #xmlel{name = Name, attrs = Attrs} = Packet, case LResource of - "" -> - case Name of - "presence" -> - {Pass, _Subsc} = - case xml:get_attr_s("type", Attrs) of - "subscribe" -> - Reason = xml:get_path_s( - Packet, - [{elem, "status"}, cdata]), - {is_privacy_allow(From, To, Packet) andalso - ejabberd_hooks:run_fold( - roster_in_subscription, - LServer, - false, - [User, Server, From, subscribe, Reason]), - true}; - "subscribed" -> - {is_privacy_allow(From, To, Packet) andalso - ejabberd_hooks:run_fold( - roster_in_subscription, - LServer, - false, - [User, Server, From, subscribed, ""]), - true}; - "unsubscribe" -> - {is_privacy_allow(From, To, Packet) andalso - ejabberd_hooks:run_fold( - roster_in_subscription, - LServer, - false, - [User, Server, From, unsubscribe, ""]), - true}; - "unsubscribed" -> - {is_privacy_allow(From, To, Packet) andalso - ejabberd_hooks:run_fold( - roster_in_subscription, - LServer, - false, - [User, Server, From, unsubscribed, ""]), - true}; - _ -> - {true, false} - end, - if Pass -> - PResources = get_user_present_resources( - LUser, LServer), - lists:foreach( - fun({_, R}) -> - do_route( - From, - jlib:jid_replace_resource(To, R), - Packet) - end, PResources); - true -> - ok - end; - "message" -> - route_message(From, To, Packet); - "iq" -> - process_iq(From, To, Packet); - "broadcast" -> - lists:foreach( - fun(R) -> - do_route(From, - jlib:jid_replace_resource(To, R), - Packet) - end, get_user_resources(User, Server)); - _ -> - ok - end; - _ -> - USR = {LUser, LServer, LResource}, - case mnesia:dirty_index_read(session, USR, #session.usr) of - [] -> - case Name of - "message" -> - route_message(From, To, Packet); - "iq" -> - case xml:get_attr_s("type", Attrs) of - "error" -> ok; - "result" -> ok; - _ -> - Err = - jlib:make_error_reply( - Packet, ?ERR_SERVICE_UNAVAILABLE), - ejabberd_router:route(To, From, Err) - end; + <<"">> -> + case Name of + <<"presence">> -> + {Pass, _Subsc} = case xml:get_attr_s(<<"type">>, Attrs) + of + <<"subscribe">> -> + Reason = xml:get_path_s(Packet, + [{elem, + <<"status">>}, + cdata]), + {is_privacy_allow(From, To, Packet) + andalso + ejabberd_hooks:run_fold(roster_in_subscription, + LServer, + false, + [User, Server, + From, + subscribe, + Reason]), + true}; + <<"subscribed">> -> + {is_privacy_allow(From, To, Packet) + andalso + ejabberd_hooks:run_fold(roster_in_subscription, + LServer, + false, + [User, Server, + From, + subscribed, + <<"">>]), + true}; + <<"unsubscribe">> -> + {is_privacy_allow(From, To, Packet) + andalso + ejabberd_hooks:run_fold(roster_in_subscription, + LServer, + false, + [User, Server, + From, + unsubscribe, + <<"">>]), + true}; + <<"unsubscribed">> -> + {is_privacy_allow(From, To, Packet) + andalso + ejabberd_hooks:run_fold(roster_in_subscription, + LServer, + false, + [User, Server, + From, + unsubscribed, + <<"">>]), + true}; + _ -> {true, false} + end, + if Pass -> + PResources = get_user_present_resources(LUser, LServer), + lists:foreach(fun ({_, R}) -> + do_route(From, + jlib:jid_replace_resource(To, + R), + Packet) + end, + PResources); + true -> ok + end; + <<"message">> -> route_message(From, To, Packet); + <<"iq">> -> process_iq(From, To, Packet); + _ -> ok + end; + _ -> + USR = {LUser, LServer, LResource}, + case mnesia:dirty_index_read(session, USR, #session.usr) + of + [] -> + case Name of + <<"message">> -> route_message(From, To, Packet); + <<"iq">> -> + case xml:get_attr_s(<<"type">>, Attrs) of + <<"error">> -> ok; + <<"result">> -> ok; _ -> - ?DEBUG("packet droped~n", []) - end; - Ss -> - Session = lists:max(Ss), - Pid = element(2, Session#session.sid), - ?DEBUG("sending to process ~p~n", [Pid]), - Pid ! {route, From, To, Packet} - end + Err = jlib:make_error_reply(Packet, + ?ERR_SERVICE_UNAVAILABLE), + ejabberd_router:route(To, From, Err) + end; + _ -> ?DEBUG("packet droped~n", []) + end; + Ss -> + Session = lists:max(Ss), + Pid = element(2, Session#session.sid), + ?DEBUG("sending to process ~p~n", [Pid]), + Pid ! {route, From, To, Packet} + end end. %% The default list applies to the user as a whole, @@ -519,8 +565,9 @@ do_route(From, To, Packet) -> is_privacy_allow(From, To, Packet) -> User = To#jid.user, Server = To#jid.server, - PrivacyList = ejabberd_hooks:run_fold(privacy_get_user_list, Server, - #userlist{}, [User, Server]), + PrivacyList = + ejabberd_hooks:run_fold(privacy_get_user_list, Server, + #userlist{}, [User, Server]), is_privacy_allow(From, To, Packet, PrivacyList). %% Check if privacy rules allow this delivery @@ -528,102 +575,89 @@ is_privacy_allow(From, To, Packet) -> is_privacy_allow(From, To, Packet, PrivacyList) -> User = To#jid.user, Server = To#jid.server, - allow == ejabberd_hooks:run_fold( - privacy_check_packet, Server, - allow, - [User, - Server, - PrivacyList, - {From, To, Packet}, - in]). + allow == + ejabberd_hooks:run_fold(privacy_check_packet, Server, + allow, + [User, Server, PrivacyList, {From, To, Packet}, + in]). route_message(From, To, Packet) -> LUser = To#jid.luser, LServer = To#jid.lserver, PrioRes = get_user_present_resources(LUser, LServer), case catch lists:max(PrioRes) of - {Priority, _R} when is_integer(Priority), Priority >= 0 -> - lists:foreach( - %% Route messages to all priority that equals the max, if - %% positive - fun({P, R}) when P == Priority -> - LResource = jlib:resourceprep(R), - USR = {LUser, LServer, LResource}, - case mnesia:dirty_index_read(session, USR, #session.usr) of - [] -> - ok; % Race condition - Ss -> - Session = lists:max(Ss), - Pid = element(2, Session#session.sid), - ?DEBUG("sending to process ~p~n", [Pid]), - Pid ! {route, From, To, Packet} - end; - %% Ignore other priority: - ({_Prio, _Res}) -> - ok - end, - PrioRes); - _ -> - case xml:get_tag_attr_s("type", Packet) of - "error" -> - ok; - "groupchat" -> - bounce_offline_message(From, To, Packet); - "headline" -> - bounce_offline_message(From, To, Packet); - _ -> - case ejabberd_auth:is_user_exists(LUser, LServer) of + {Priority, _R} + when is_integer(Priority), Priority >= 0 -> + lists:foreach(fun ({P, R}) when P == Priority -> + LResource = jlib:resourceprep(R), + USR = {LUser, LServer, LResource}, + case mnesia:dirty_index_read(session, USR, + #session.usr) + of + [] -> + ok; % Race condition + Ss -> + Session = lists:max(Ss), + Pid = element(2, Session#session.sid), + ?DEBUG("sending to process ~p~n", [Pid]), + Pid ! {route, From, To, Packet} + end; + %% Ignore other priority: + ({_Prio, _Res}) -> ok + end, + PrioRes); + _ -> + case xml:get_tag_attr_s(<<"type">>, Packet) of + <<"error">> -> ok; + <<"groupchat">> -> + bounce_offline_message(From, To, Packet); + <<"headline">> -> + bounce_offline_message(From, To, Packet); + _ -> + case ejabberd_auth:is_user_exists(LUser, LServer) of + true -> + case is_privacy_allow(From, To, Packet) of true -> - case is_privacy_allow(From, To, Packet) of - true -> - ejabberd_hooks:run(offline_message_hook, - LServer, - [From, To, Packet]); - false -> - ok - end; - _ -> - Err = jlib:make_error_reply( - Packet, ?ERR_SERVICE_UNAVAILABLE), - ejabberd_router:route(To, From, Err) - end - end + ejabberd_hooks:run(offline_message_hook, LServer, + [From, To, Packet]); + false -> ok + end; + _ -> + Err = jlib:make_error_reply(Packet, + ?ERR_SERVICE_UNAVAILABLE), + ejabberd_router:route(To, From, Err) + end + end end. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% clean_session_list(Ss) -> clean_session_list(lists:keysort(#session.usr, Ss), []). -clean_session_list([], Res) -> - Res; -clean_session_list([S], Res) -> - [S | Res]; +clean_session_list([], Res) -> Res; +clean_session_list([S], Res) -> [S | Res]; clean_session_list([S1, S2 | Rest], Res) -> - if - S1#session.usr == S2#session.usr -> - if - S1#session.sid > S2#session.sid -> - clean_session_list([S1 | Rest], Res); - true -> - clean_session_list([S2 | Rest], Res) - end; - true -> - clean_session_list([S2 | Rest], [S1 | Res]) + if S1#session.usr == S2#session.usr -> + if S1#session.sid > S2#session.sid -> + clean_session_list([S1 | Rest], Res); + true -> clean_session_list([S2 | Rest], Res) + end; + true -> clean_session_list([S2 | Rest], [S1 | Res]) end. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% get_user_present_resources(LUser, LServer) -> US = {LUser, LServer}, - case catch mnesia:dirty_index_read(session, US, #session.us) of - {'EXIT', _Reason} -> - []; - Ss -> - [{S#session.priority, element(3, S#session.usr)} || - S <- clean_session_list(Ss), is_integer(S#session.priority)] + case catch mnesia:dirty_index_read(session, US, + #session.us) + of + {'EXIT', _Reason} -> []; + Ss -> + [{S#session.priority, element(3, S#session.usr)} + || S <- clean_session_list(Ss), + is_integer(S#session.priority)] end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -633,62 +667,54 @@ check_for_sessions_to_replace(User, Server, Resource) -> LUser = jlib:nodeprep(User), LServer = jlib:nameprep(Server), LResource = jlib:resourceprep(Resource), - - %% TODO: Depending on how this is executed, there could be an unneeded - %% replacement for max_sessions. We need to check this at some point. check_existing_resources(LUser, LServer, LResource), check_max_sessions(LUser, LServer). check_existing_resources(LUser, LServer, LResource) -> SIDs = get_resource_sessions(LUser, LServer, LResource), - if - SIDs == [] -> ok; - true -> - %% A connection exist with the same resource. We replace it: - MaxSID = lists:max(SIDs), - lists:foreach( - fun({_, Pid} = S) when S /= MaxSID -> - Pid ! replaced; - (_) -> ok - end, SIDs) + if SIDs == [] -> ok; + true -> + MaxSID = lists:max(SIDs), + lists:foreach(fun ({_, Pid} = S) when S /= MaxSID -> + Pid ! replaced; + (_) -> ok + end, + SIDs) end. +-spec is_existing_resource(binary(), binary(), binary()) -> boolean(). + is_existing_resource(LUser, LServer, LResource) -> [] /= get_resource_sessions(LUser, LServer, LResource). get_resource_sessions(User, Server, Resource) -> - USR = {jlib:nodeprep(User), jlib:nameprep(Server), jlib:resourceprep(Resource)}, - mnesia:dirty_select( - session, - [{#session{sid = '$1', usr = USR, _ = '_'}, [], ['$1']}]). + USR = {jlib:nodeprep(User), jlib:nameprep(Server), + jlib:resourceprep(Resource)}, + mnesia:dirty_select(session, + [{#session{sid = '$1', usr = USR, _ = '_'}, [], + ['$1']}]). check_max_sessions(LUser, LServer) -> - %% If the max number of sessions for a given is reached, we replace the - %% first one - SIDs = mnesia:dirty_select( - session, - [{#session{sid = '$1', us = {LUser, LServer}, _ = '_'}, [], - ['$1']}]), + SIDs = mnesia:dirty_select(session, + [{#session{sid = '$1', us = {LUser, LServer}, + _ = '_'}, + [], ['$1']}]), MaxSessions = get_max_user_sessions(LUser, LServer), - if - length(SIDs) =< MaxSessions -> - ok; - true -> - {_, Pid} = lists:min(SIDs), - Pid ! replaced + if length(SIDs) =< MaxSessions -> ok; + true -> {_, Pid} = lists:min(SIDs), Pid ! replaced end. - %% Get the user_max_session setting %% This option defines the max number of time a given users are allowed to %% log in %% Defaults to infinity get_max_user_sessions(LUser, Host) -> - case acl:match_rule( - Host, max_user_sessions, jlib:make_jid(LUser, Host, "")) of - Max when is_integer(Max) -> Max; - infinity -> infinity; - _ -> ?MAX_USER_SESSIONS + case acl:match_rule(Host, max_user_sessions, + jlib:make_jid(LUser, Host, <<"">>)) + of + Max when is_integer(Max) -> Max; + infinity -> infinity; + _ -> ?MAX_USER_SESSIONS end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -696,80 +722,78 @@ get_max_user_sessions(LUser, Host) -> process_iq(From, To, Packet) -> IQ = jlib:iq_query_info(Packet), case IQ of - #iq{xmlns = XMLNS} -> - Host = To#jid.lserver, - case ets:lookup(sm_iqtable, {XMLNS, Host}) of - [{_, Module, Function}] -> - ResIQ = Module:Function(From, To, IQ), - if - ResIQ /= ignore -> - ejabberd_router:route(To, From, - jlib:iq_to_xml(ResIQ)); - true -> - ok - end; - [{_, Module, Function, Opts}] -> - gen_iq_handler:handle(Host, Module, Function, Opts, - From, To, IQ); - [] -> - Err = jlib:make_error_reply( - Packet, ?ERR_SERVICE_UNAVAILABLE), - ejabberd_router:route(To, From, Err) - end; - reply -> - ok; - _ -> - Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST), - ejabberd_router:route(To, From, Err), - ok + #iq{xmlns = XMLNS} -> + Host = To#jid.lserver, + case ets:lookup(sm_iqtable, {XMLNS, Host}) of + [{_, Module, Function}] -> + ResIQ = Module:Function(From, To, IQ), + if ResIQ /= ignore -> + ejabberd_router:route(To, From, jlib:iq_to_xml(ResIQ)); + true -> ok + end; + [{_, Module, Function, Opts}] -> + gen_iq_handler:handle(Host, Module, Function, Opts, + From, To, IQ); + [] -> + Err = jlib:make_error_reply(Packet, + ?ERR_SERVICE_UNAVAILABLE), + ejabberd_router:route(To, From, Err) + end; + reply -> ok; + _ -> + Err = jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST), + ejabberd_router:route(To, From, Err), + ok end. +-spec force_update_presence({binary(), binary()}) -> any(). + force_update_presence({LUser, _LServer} = US) -> - case catch mnesia:dirty_index_read(session, US, #session.us) of - {'EXIT', _Reason} -> - ok; - Ss -> - lists:foreach(fun(#session{sid = {_, Pid}}) -> - Pid ! {force_update_presence, LUser} - end, Ss) + case catch mnesia:dirty_index_read(session, US, + #session.us) + of + {'EXIT', _Reason} -> ok; + Ss -> + lists:foreach(fun (#session{sid = {_, Pid}}) -> + Pid ! {force_update_presence, LUser} + end, + Ss) end. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% ejabberd commands commands() -> - [ - #ejabberd_commands{name = connected_users, - tags = [session], - desc = "List all established sessions", - module = ?MODULE, function = connected_users, - args = [], - result = {connected_users, {list, {sessions, string}}}}, + [#ejabberd_commands{name = connected_users, + tags = [session], + desc = "List all established sessions", + module = ?MODULE, function = connected_users, args = [], + result = {connected_users, {list, {sessions, string}}}}, #ejabberd_commands{name = connected_users_number, - tags = [session, stats], - desc = "Get the number of established sessions", - module = ?MODULE, function = connected_users_number, - args = [], - result = {num_sessions, integer}}, + tags = [session, stats], + desc = "Get the number of established sessions", + module = ?MODULE, function = connected_users_number, + args = [], result = {num_sessions, integer}}, #ejabberd_commands{name = user_resources, - tags = [session], - desc = "List user's connected resources", - module = ?MODULE, function = user_resources, - args = [{user, string}, {host, string}], - result = {resources, {list, {resource, string}}}} - ]. + tags = [session], + desc = "List user's connected resources", + module = ?MODULE, function = user_resources, + args = [{user, string}, {host, string}], + result = {resources, {list, {resource, string}}}}]. + +-spec connected_users() -> [binary()]. connected_users() -> USRs = dirty_get_sessions_list(), SUSRs = lists:sort(USRs), - lists:map(fun({U, S, R}) -> [U, $@, S, $/, R] end, SUSRs). + lists:map(fun ({U, S, R}) -> <<U/binary, $@, S/binary, $/, R/binary>> end, + SUSRs). connected_users_number() -> length(dirty_get_sessions_list()). user_resources(User, Server) -> - Resources = get_user_resources(User, Server), + Resources = get_user_resources(User, Server), lists:sort(Resources). @@ -778,24 +802,18 @@ user_resources(User, Server) -> update_tables() -> case catch mnesia:table_info(session, attributes) of - [ur, user, node] -> - mnesia:delete_table(session); - [ur, user, pid] -> - mnesia:delete_table(session); - [usr, us, pid] -> - mnesia:delete_table(session); - [sid, usr, us, priority] -> - mnesia:delete_table(session); - [sid, usr, us, priority, info] -> - ok; - {'EXIT', _} -> - ok + [ur, user, node] -> mnesia:delete_table(session); + [ur, user, pid] -> mnesia:delete_table(session); + [usr, us, pid] -> mnesia:delete_table(session); + [sid, usr, us, priority] -> + mnesia:delete_table(session); + [sid, usr, us, priority, info] -> ok; + {'EXIT', _} -> ok end, - case lists:member(presence, mnesia:system_info(tables)) of - true -> - mnesia:delete_table(presence); - false -> - ok + case lists:member(presence, mnesia:system_info(tables)) + of + true -> mnesia:delete_table(presence); + false -> ok end, case lists:member(local_session, mnesia:system_info(tables)) of true -> |