]> granicus.if.org Git - ejabberd/commitdiff
New option resource_conflict defines server action (thanks to Lee Boynton)(EJAB-650)
authorBadlop <badlop@process-one.net>
Wed, 17 Aug 2011 17:33:08 +0000 (19:33 +0200)
committerBadlop <badlop@process-one.net>
Wed, 17 Aug 2011 17:44:56 +0000 (19:44 +0200)
doc/guide.tex
src/ejabberd_c2s.erl
src/ejabberd_sm.erl

index 890b1bff735aea95d60c167ddcf40b150d2fb73e..47bed9f727b63e1f09553b485350f875b3de0b0b 100644 (file)
@@ -1243,6 +1243,16 @@ defines in what format the users passwords are stored:
     This format allows clients to authenticate using: \term{SASL PLAIN} and \term{SASL SCRAM-SHA-1}.
 \end{description}
 
+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}.
+
 Examples:
 \begin{itemize}
 \item To use internal Mnesia storage with hashed passwords on all virtual hosts:
index 7c2872e6536c1057e45cbff90a2dc4e181840aff..d4df773c2b1df7e97d30319c572859f794059632 100644 (file)
@@ -907,6 +907,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,binary_to_list(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) ->
     try
@@ -923,11 +946,17 @@ wait_for_bind({xmlstreamelement, El}, StateData) ->
        %%send_element(StateData,
        %%           exmpp_stream:features([exmpp_server_session:feature()
        %%                                  | RosterVersioningFeature])),
-       JID = exmpp_jid:make(StateData#state.user, StateData#state.server, R),
-       Res = exmpp_server_binding:bind(El, JID),
-       send_element(StateData, Res),
-       fsm_next_state(wait_for_session,
+       case resource_conflict_action(StateData#state.user, StateData#state.server, list_to_binary(R)) of
+       closenew ->
+               send_element(StateData, ?SERRT_CONFLICT), %% (Lang, "Replaced by new connection")),
+               fsm_next_state(wait_for_bind, StateData);
+       {accept_resource, R2} ->
+               JID = exmpp_jid:make(StateData#state.user, StateData#state.server, R2),
+               Res = exmpp_server_binding:bind(El, JID),
+               send_element(StateData, Res),
+               fsm_next_state(wait_for_session,
                       StateData#state{resource = exmpp_jid:resource(JID), jid = JID})
+       end
     catch
        throw:{stringprep, resourceprep, _, _} ->
            Err = exmpp_server_binding:error(El, 'bad-request'),
index dc476355e9c8ea7f0399c5c3cc0aeef6b15f82d2..0a46d0c61b831f6d3b69d6a816bd3e6f2adfa257 100644 (file)
@@ -59,6 +59,7 @@
         get_session_pid/1,
         get_user_info/3,
         get_user_ip/1,
+        is_existing_resource/3,
         migrate/1
        ]).
 
@@ -727,16 +728,12 @@ check_for_sessions_to_replace(JID) ->
     check_max_sessions(JID).
 
 check_existing_resources(JID) ->
-    USR = {exmpp_jid:prep_node(JID),
-           exmpp_jid:prep_domain(JID),
-           exmpp_jid:prep_resource(JID)},
     %% 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(JID),
     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 ->
@@ -745,6 +742,17 @@ check_existing_resources(JID) ->
              end, SIDs)
     end.
 
+is_existing_resource(U, S, R) ->
+    [] /= get_resource_sessions(exmpp_jid:make(U, S, R)).
+
+get_resource_sessions(JID) ->
+    USR = {exmpp_jid:prep_node(JID),
+           exmpp_jid:prep_domain(JID),
+           exmpp_jid:prep_resource(JID)},
+    mnesia:dirty_select(
+            session,
+            [{#session{sid = '$1', usr = USR, _ = '_'}, [], ['$1']}]).
+
 check_max_sessions(JID) ->
     %% If the max number of sessions for a given is reached, we replace the
     %% first one