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:
{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
%%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'),
get_session_pid/1,
get_user_info/3,
get_user_ip/1,
+ is_existing_resource/3,
migrate/1
]).
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 ->
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