]> 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:42:11 +0000 (19:42 +0200)
committerBadlop <badlop@process-one.net>
Wed, 17 Aug 2011 17:44:39 +0000 (19:44 +0200)
doc/guide.tex
src/ejabberd_c2s.erl
src/ejabberd_sm.erl

index 49cfd5bf88b9da0a5ee2cf4f43bccb26368992b4..008363a28502559b370ef3585796854b8792fd71 100644 (file)
@@ -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}
 
index 851c818b5c7261afb4cb7c2aca0288d43446fa72..b093648d232c6e2b95614441d56dfb3c2e5cabee 100644 (file)
@@ -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)
index 950b551a359cc627eeae036042649226642da3bb..80076db52fa6cdc0f7c70d9542871c7d96f51a29 100644 (file)
@@ -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