diff options
author | Badlop <badlop@process-one.net> | 2011-08-17 19:42:11 +0200 |
---|---|---|
committer | Badlop <badlop@process-one.net> | 2011-08-17 19:44:39 +0200 |
commit | a0f8a2c3a4f5d30a4a48adc18e252cfcc47d6ea0 (patch) | |
tree | 41402c6d9c90585044ec3f025392e3b2caf783a4 | |
parent | Fix bug in scram is_alpha (diff) |
New option resource_conflict defines server action (thanks to Lee Boynton)(EJAB-650)
-rw-r--r-- | doc/guide.tex | 10 | ||||
-rw-r--r-- | src/ejabberd_c2s.erl | 50 | ||||
-rw-r--r-- | src/ejabberd_sm.erl | 19 |
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 |