aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBadlop <badlop@process-one.net>2011-08-17 19:42:11 +0200
committerBadlop <badlop@process-one.net>2011-08-17 19:44:39 +0200
commita0f8a2c3a4f5d30a4a48adc18e252cfcc47d6ea0 (patch)
tree41402c6d9c90585044ec3f025392e3b2caf783a4
parentFix bug in scram is_alpha (diff)
New option resource_conflict defines server action (thanks to Lee Boynton)(EJAB-650)
Diffstat (limited to '')
-rw-r--r--doc/guide.tex10
-rw-r--r--src/ejabberd_c2s.erl50
-rw-r--r--src/ejabberd_sm.erl19
3 files changed, 63 insertions, 16 deletions
diff --git a/doc/guide.tex b/doc/guide.tex
index 49cfd5bf8..008363a28 100644
--- a/doc/guide.tex
+++ b/doc/guide.tex
@@ -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}
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 851c818b5..b093648d2 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -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)
diff --git a/src/ejabberd_sm.erl b/src/ejabberd_sm.erl
index 950b551a3..80076db52 100644
--- a/src/ejabberd_sm.erl
+++ b/src/ejabberd_sm.erl
@@ -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