authenticated = false,
jid,
user = "", server = ?MYNAME, resource = "",
+ sid,
pres_t = ?SETS:new(),
pres_f = ?SETS:new(),
pres_a = ?SETS:new(),
"(~w) Accepted legacy authentication for ~s",
[StateData#state.socket,
jlib:jid_to_string(JID)]),
+ SID = {now(), self()},
ejabberd_sm:open_session(
- U, StateData#state.server, R),
+ SID, U, StateData#state.server, R),
Res1 = jlib:make_result_iq_reply(El),
Res = setelement(4, Res1, []),
send_element(StateData, Res),
StateData#state{user = U,
resource = R,
jid = JID,
+ sid = SID,
pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList}};
?INFO_MSG("(~w) Opened session for ~s",
[StateData#state.socket,
jlib:jid_to_string(JID)]),
+ SID = {now(), self()},
ejabberd_sm:open_session(
- U, StateData#state.server, R),
+ SID, U, StateData#state.server, R),
Res = jlib:make_result_iq_reply(El),
send_element(StateData, Res),
change_shaper(StateData, JID),
PL -> PL
end,
{next_state, session_established,
- StateData#state{pres_f = ?SETS:from_list(Fs1),
+ StateData#state{sid = SID,
+ pres_f = ?SETS:from_list(Fs1),
pres_t = ?SETS:from_list(Ts1),
privacy_list = PrivList}};
_ ->
[{"type", "unavailable"}],
[{xmlelement, "status", [],
[{xmlcdata, "Replaced by new connection"}]}]},
- ejabberd_sm:unset_presence(StateData#state.user,
- StateData#state.server,
- StateData#state.resource,
- "Replaced by new connection"),
+ ejabberd_sm:close_session_unset_presence(
+ StateData#state.sid,
+ StateData#state.user,
+ StateData#state.server,
+ StateData#state.resource,
+ "Replaced by new connection"),
presence_broadcast(
StateData, From, StateData#state.pres_a, Packet),
presence_broadcast(
?INFO_MSG("(~w) Close session for ~s",
[StateData#state.socket,
jlib:jid_to_string(StateData#state.jid)]),
- ejabberd_sm:close_session(StateData#state.user,
- StateData#state.server,
- StateData#state.resource),
- Tmp = ?SETS:new(),
+ EmptySet = ?SETS:new(),
case StateData of
#state{pres_last = undefined,
- pres_a = Tmp,
- pres_i = Tmp,
+ pres_a = EmptySet,
+ pres_i = EmptySet,
pres_invis = false} ->
- ok;
+ ejabberd_sm:close_session(StateData#state.sid);
_ ->
From = StateData#state.jid,
Packet = {xmlelement, "presence",
[{"type", "unavailable"}], []},
- ejabberd_sm:unset_presence(StateData#state.user,
- StateData#state.server,
- StateData#state.resource,
- ""),
+ ejabberd_sm:close_session_unset_presence(
+ StateData#state.sid,
+ StateData#state.user,
+ StateData#state.server,
+ StateData#state.resource,
+ ""),
presence_broadcast(
StateData, From, StateData#state.pres_a, Packet),
presence_broadcast(
StatusTag ->
xml:get_tag_cdata(StatusTag)
end,
- ejabberd_sm:unset_presence(StateData#state.user,
+ ejabberd_sm:unset_presence(StateData#state.sid,
+ StateData#state.user,
StateData#state.server,
StateData#state.resource,
Status),
0
end
end,
- ejabberd_sm:set_presence(StateData#state.user,
+ ejabberd_sm:set_presence(StateData#state.sid,
+ StateData#state.user,
StateData#state.server,
StateData#state.resource,
Pri).
-export([start_link/0, init/0,
route/3,
- open_session/3, close_session/3,
+ open_session/4, close_session/1,
bounce_offline_message/3,
disconnect_removed_user/2,
get_user_resources/2,
- set_presence/4,
- unset_presence/4,
+ set_presence/5,
+ unset_presence/5,
+ close_session_unset_presence/5,
dirty_get_sessions_list/0,
dirty_get_my_sessions_list/0,
get_vh_session_list/1,
-include("ejabberd.hrl").
-include("jlib.hrl").
--record(session, {usr, us, pid}).
--record(presence, {usr, us, priority}).
+-record(session, {sid, usr, us, priority}).
start_link() ->
Pid = proc_lib:spawn_link(ejabberd_sm, init, []),
init() ->
update_tables(),
- mnesia:create_table(session, [{ram_copies, [node()]},
- {attributes, record_info(fields, session)}]),
+ mnesia:create_table(session,
+ [{ram_copies, [node()]},
+ {attributes, record_info(fields, session)}]),
+ mnesia:add_table_index(session, usr),
mnesia:add_table_index(session, us),
mnesia:add_table_copy(session, node(), ram_copies),
- mnesia:create_table(presence,
- [{ram_copies, [node()]},
- {attributes, record_info(fields, presence)}]),
- mnesia:add_table_index(presence, us),
mnesia:subscribe(system),
ets:new(sm_iqtable, [named_table]),
lists:foreach(
ok
end.
-open_session(User, Server, Resource) ->
- register_connection(User, Server, Resource, self()).
-
-close_session(User, Server, Resource) ->
- remove_connection(User, Server, Resource).
+open_session(SID, User, Server, Resource) ->
+ set_session(SID, User, Server, Resource, undefined).
-
-register_connection(User, Server, Resource, Pid) ->
+set_session(SID, User, Server, Resource, Priority) ->
LUser = jlib:nodeprep(User),
LServer = jlib:nameprep(Server),
LResource = jlib:resourceprep(Resource),
US = {LUser, LServer},
USR = {LUser, LServer, LResource},
F = fun() ->
- Ss = mnesia:wread({session, USR}),
- mnesia:write(#session{usr = USR, us = US, pid = Pid}),
- Ss
- end,
- case mnesia:transaction(F) of
- {atomic, Ss} ->
+ mnesia:write(#session{sid = SID,
+ usr = USR,
+ us = US,
+ priority = Priority})
+ end,
+ mnesia:sync_dirty(F),
+ SIDs = mnesia:dirty_select(
+ session,
+ [{#session{sid = '$1', usr = USR, _ = '_'}, [], ['$1']}]),
+ if
+ SIDs == [] ->
+ ok;
+ true ->
+ MaxSID = lists:max(SIDs),
lists:foreach(
- fun(R) ->
- R#session.pid ! replaced
- end, Ss);
- _ ->
- false
+ fun({_, Pid} = S) when S /= MaxSID ->
+ Pid ! replaced;
+ (_) ->
+ ok
+ end, SIDs)
end.
-
-remove_connection(User, Server, Resource) ->
- LUser = jlib:nodeprep(User),
- LResource = jlib:resourceprep(Resource),
- LServer = jlib:nameprep(Server),
- USR = {LUser, LServer, LResource},
+close_session(SID) ->
F = fun() ->
- mnesia:delete({session, USR})
+ mnesia:delete({session, SID})
end,
- mnesia:transaction(F).
+ mnesia:sync_dirty(F).
clean_table_from_bad_node(Node) ->
F = fun() ->
Es = mnesia:select(
session,
- [{#session{pid = '$1', _ = '_'},
+ [{#session{sid = {'_', '$1'}, _ = '_'},
[{'==', {node, '$1'}, Node}],
['$_']}]),
lists:foreach(fun(E) ->
- mnesia:delete_object(E),
- mnesia:delete({presence, E#session.usr})
+ mnesia:delete_object(E)
end, Es)
end,
- mnesia:transaction(F).
+ mnesia:sync_dirty(F).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
end;
_ ->
USR = {LUser, LServer, LResource},
- case mnesia:dirty_read({session, USR}) of
+ case mnesia:dirty_index_read(session, USR, #session.usr) of
[] ->
case Name of
"message" ->
_ ->
?DEBUG("packet droped~n", [])
end;
- [Sess] ->
- Pid = Sess#session.pid,
+ Ss ->
+ Session = lists:max(Ss),
+ Pid = element(2, Session#session.sid),
?DEBUG("sending to process ~p~n", [Pid]),
Pid ! {route, From, To, Packet}
end
Priority >= 0 ->
LResource = jlib:resourceprep(R),
USR = {LUser, LServer, LResource},
- case mnesia:dirty_read({session, USR}) of
+ case mnesia:dirty_index_read(session, USR, #session.usr) of
[] ->
ok; % Race condition
- [Sess] ->
- Pid = Sess#session.pid,
+ Ss ->
+ Session = lists:max(Ss),
+ Pid = element(2, Session#session.sid),
?DEBUG("sending to process ~p~n", [Pid]),
Pid ! {route, From, To, Packet}
end;
case catch mnesia:dirty_index_read(session, US, #session.us) of
{'EXIT', _Reason} ->
[];
- Rs ->
- lists:map(fun(R) ->
- element(3, R#session.usr)
- end, Rs)
+ Ss ->
+ [element(3, S#session.usr) || S <- clean_session_list(Ss)]
+ 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([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])
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-set_presence(User, Server, Resource, Priority) ->
- LUser = jlib:nodeprep(User),
- LServer = jlib:nameprep(Server),
- USR = {User, Server, Resource},
- US = {LUser, LServer},
- F = fun() ->
- mnesia:write(#presence{usr = USR, us = US,
- priority = Priority})
- end,
- mnesia:transaction(F).
+set_presence(SID, User, Server, Resource, Priority) ->
+ set_session(SID, User, Server, Resource, Priority).
-unset_presence(User, Server, Resource, Status) ->
- USR = {User, Server, Resource},
- F = fun() ->
- mnesia:delete({presence, USR})
- end,
- mnesia:transaction(F),
+unset_presence(SID, User, Server, Resource, Status) ->
+ set_session(SID, User, Server, Resource, undefined),
+ ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
+ [User, Server, Resource, Status]).
+
+close_session_unset_presence(SID, User, Server, Resource, Status) ->
+ close_session(SID),
ejabberd_hooks:run(unset_presence_hook, jlib:nameprep(Server),
[User, Server, Resource, Status]).
get_user_present_resources(LUser, LServer) ->
US = {LUser, LServer},
- case catch mnesia:dirty_index_read(presence, US, #presence.us) of
+ case catch mnesia:dirty_index_read(session, US, #session.us) of
{'EXIT', _Reason} ->
[];
- Rs ->
- lists:map(fun(R) ->
- {R#presence.priority, element(3, R#presence.usr)}
- end, Rs)
+ Ss ->
+ [{S#session.priority, element(3, S#session.usr)} ||
+ S <- clean_session_list(Ss), is_integer(S#session.priority)]
end.
dirty_get_sessions_list() ->
- mnesia:dirty_all_keys(session).
+ mnesia:dirty_select(
+ session,
+ [{#session{usr = '$1', _ = '_'},
+ [],
+ ['$1']}]).
dirty_get_my_sessions_list() ->
mnesia:dirty_select(
session,
- [{#session{pid = '$1', _ = '_'},
+ [{#session{sid = {'_', '$1'}, _ = '_'},
[{'==', {node, '$1'}, node()}],
['$_']}]).
[ur, user, pid] ->
mnesia:delete_table(session);
[usr, us, pid] ->
+ mnesia:delete_table(session);
+ [sid, usr, us, priority] ->
ok;
{'EXIT', _} ->
ok
end,
- case catch mnesia:table_info(presence, attributes) of
- [ur, user, priority] ->
+ case lists:member(presence, mnesia:system_info(tables)) of
+ true ->
mnesia:delete_table(presence);
- [usr, us, priority] ->
- ok;
- {'EXIT', _} ->
+ false ->
ok
end,
case lists:member(local_session, mnesia:system_info(tables)) of