New option resource_conflict defines server action (thanks to Lee Boynton)(EJAB-650)
This commit is contained in:
parent
3edea6c806
commit
a0f8a2c3a4
|
@ -1215,6 +1215,16 @@ The following authentication methods are supported by \ejabberd{}:
|
|||
|
||||
Account creation is only supported by internal, external and odbc methods.
|
||||
|
||||
The option \option{resource\_conflict} defines the action when a client attempts to
|
||||
login to an account with a resource that is already connected.
|
||||
The option syntax is:
|
||||
\esyntax{\{resource\_conflict, setresource|closenew|closeold\}.}
|
||||
The possible values match exactly the three possibilities described in
|
||||
\footahref{http://tools.ietf.org/html/rfc6120\#section-7.7.2.2}{XMPP Core: section 7.7.2.2}.
|
||||
The default value is \term{closeold}.
|
||||
If the client uses old Jabber Non-SASL authentication (\xepref{0078}),
|
||||
then this option is not respected, and the action performed is \term{closeold}.
|
||||
|
||||
\makesubsubsection{internalauth}{Internal}
|
||||
\ind{internal authentication}\ind{Mnesia}
|
||||
|
||||
|
|
|
@ -836,6 +836,29 @@ wait_for_sasl_response(closed, StateData) ->
|
|||
{stop, normal, StateData}.
|
||||
|
||||
|
||||
resource_conflict_action(U, S, R) ->
|
||||
OptionRaw = case ejabberd_sm:is_existing_resource(U, S, R) of
|
||||
true ->
|
||||
ejabberd_config:get_local_option({resource_conflict,S});
|
||||
false ->
|
||||
acceptnew
|
||||
end,
|
||||
Option = case OptionRaw of
|
||||
setresource -> setresource;
|
||||
closeold -> acceptnew; %% ejabberd_sm will close old session
|
||||
closenew -> closenew;
|
||||
acceptnew -> acceptnew;
|
||||
_ -> acceptnew %% default ejabberd behavior
|
||||
end,
|
||||
case Option of
|
||||
acceptnew ->
|
||||
{accept_resource, R};
|
||||
closenew ->
|
||||
closenew;
|
||||
setresource ->
|
||||
Rnew = lists:concat([randoms:get_string() | tuple_to_list(now())]),
|
||||
{accept_resource, Rnew}
|
||||
end.
|
||||
|
||||
wait_for_bind({xmlstreamelement, El}, StateData) ->
|
||||
case jlib:iq_query_info(El) of
|
||||
|
@ -855,7 +878,6 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
|
|||
send_element(StateData, Err),
|
||||
fsm_next_state(wait_for_bind, StateData);
|
||||
_ ->
|
||||
JID = jlib:make_jid(U, StateData#state.server, R),
|
||||
%%Server = StateData#state.server,
|
||||
%%RosterVersioningFeature =
|
||||
%% ejabberd_hooks:run_fold(
|
||||
|
@ -865,15 +887,23 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
|
|||
%% RosterVersioningFeature],
|
||||
%%send_element(StateData, {xmlelement, "stream:features",
|
||||
%% [], StreamFeatures}),
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}],
|
||||
[{xmlelement, "jid", [],
|
||||
[{xmlcdata,
|
||||
jlib:jid_to_string(JID)}]}]}]},
|
||||
send_element(StateData, jlib:iq_to_xml(Res)),
|
||||
fsm_next_state(wait_for_session,
|
||||
StateData#state{resource = R, jid = JID})
|
||||
case resource_conflict_action(U, StateData#state.server, R) of
|
||||
closenew ->
|
||||
Err = jlib:make_error_reply(El, ?STANZA_ERROR("409", "modify", "conflict")),
|
||||
send_element(StateData, Err),
|
||||
fsm_next_state(wait_for_bind, StateData);
|
||||
{accept_resource, R2} ->
|
||||
JID = jlib:make_jid(U, StateData#state.server, R2),
|
||||
Res = IQ#iq{type = result,
|
||||
sub_el = [{xmlelement, "bind",
|
||||
[{"xmlns", ?NS_BIND}],
|
||||
[{xmlelement, "jid", [],
|
||||
[{xmlcdata,
|
||||
jlib:jid_to_string(JID)}]}]}]},
|
||||
send_element(StateData, jlib:iq_to_xml(Res)),
|
||||
fsm_next_state(wait_for_session,
|
||||
StateData#state{resource = R2, jid = JID})
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
fsm_next_state(wait_for_bind, StateData)
|
||||
|
|
|
@ -53,7 +53,8 @@
|
|||
user_resources/2,
|
||||
get_session_pid/3,
|
||||
get_user_info/3,
|
||||
get_user_ip/3
|
||||
get_user_ip/3,
|
||||
is_existing_resource/3
|
||||
]).
|
||||
|
||||
%% gen_server callbacks
|
||||
|
@ -639,14 +640,11 @@ check_for_sessions_to_replace(User, Server, Resource) ->
|
|||
check_max_sessions(LUser, LServer).
|
||||
|
||||
check_existing_resources(LUser, LServer, LResource) ->
|
||||
USR = {LUser, LServer, LResource},
|
||||
%% A connection exist with the same resource. We replace it:
|
||||
SIDs = mnesia:dirty_select(
|
||||
session,
|
||||
[{#session{sid = '$1', usr = USR, _ = '_'}, [], ['$1']}]),
|
||||
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 ->
|
||||
|
@ -655,6 +653,15 @@ check_existing_resources(LUser, LServer, LResource) ->
|
|||
end, SIDs)
|
||||
end.
|
||||
|
||||
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']}]).
|
||||
|
||||
check_max_sessions(LUser, LServer) ->
|
||||
%% If the max number of sessions for a given is reached, we replace the
|
||||
%% first one
|
||||
|
|
Loading…
Reference in New Issue