mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-22 17:28:25 +01:00
Add support to register nick in a room (#3455)
Registering a nick in the MUC service or in a room is mutually exclusive: - A nick that is registered in the service cannot be registered in any room, not even the original owner can register it. - Similarly, a nick registered in any room cannot be registered in the service.
This commit is contained in:
parent
9534ca2da1
commit
10245b40ee
@ -51,6 +51,7 @@
|
||||
process_disco_items/1,
|
||||
process_vcard/1,
|
||||
process_register/1,
|
||||
process_iq_register/1,
|
||||
process_muc_unique/1,
|
||||
process_mucsub/1,
|
||||
broadcast_service_message/3,
|
||||
@ -675,29 +676,33 @@ process_vcard(#iq{lang = Lang} = IQ) ->
|
||||
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||
|
||||
-spec process_register(iq()) -> iq().
|
||||
process_register(#iq{type = Type, from = From, to = To, lang = Lang,
|
||||
sub_els = [El = #register{}]} = IQ) ->
|
||||
process_register(IQ) ->
|
||||
case process_iq_register(IQ) of
|
||||
{result, Result} ->
|
||||
xmpp:make_iq_result(IQ, Result);
|
||||
{error, Err} ->
|
||||
xmpp:make_error(IQ, Err)
|
||||
end.
|
||||
|
||||
-spec process_iq_register(iq()) -> {result, register()} | {error, stanza_error()}.
|
||||
process_iq_register(#iq{type = Type, from = From, to = To, lang = Lang,
|
||||
sub_els = [El = #register{}]}) ->
|
||||
Host = To#jid.lserver,
|
||||
RegisterDestination = jid:encode(To),
|
||||
ServerHost = ejabberd_router:host_of_route(Host),
|
||||
AccessRegister = mod_muc_opt:access_register(ServerHost),
|
||||
case acl:match_rule(ServerHost, AccessRegister, From) of
|
||||
allow ->
|
||||
case Type of
|
||||
get ->
|
||||
xmpp:make_iq_result(
|
||||
IQ, iq_get_register_info(ServerHost, Host, From, Lang));
|
||||
{result, iq_get_register_info(ServerHost, RegisterDestination, From, Lang)};
|
||||
set ->
|
||||
case process_iq_register_set(ServerHost, Host, From, El, Lang) of
|
||||
{result, Result} ->
|
||||
xmpp:make_iq_result(IQ, Result);
|
||||
{error, Err} ->
|
||||
xmpp:make_error(IQ, Err)
|
||||
end
|
||||
process_iq_register_set(ServerHost, RegisterDestination, From, El, Lang)
|
||||
end;
|
||||
deny ->
|
||||
ErrText = ?T("Access denied by service policy"),
|
||||
Err = xmpp:err_forbidden(ErrText, Lang),
|
||||
xmpp:make_error(IQ, Err)
|
||||
{error, Err}
|
||||
end.
|
||||
|
||||
-spec process_disco_info(iq()) -> iq().
|
||||
@ -1416,6 +1421,11 @@ mod_doc() ->
|
||||
"nobody else can use that nickname in any room in the MUC "
|
||||
"service. To register a nickname, open the Service Discovery in "
|
||||
"your XMPP client and register in the MUC service."), "",
|
||||
?T("It is also possible to register a nickname in a room, so "
|
||||
"nobody else can use that nickname in that room. If a nick is "
|
||||
"registered in the MUC service, that nick cannot be registered in "
|
||||
"any room, and vice versa: a nick that is registered in a room "
|
||||
"cannot be registered at the MUC service."), "",
|
||||
?T("This module supports clustering and load balancing. One module "
|
||||
"can be started per cluster node. Rooms are distributed at "
|
||||
"creation time on all available MUC module instances. The "
|
||||
@ -1461,11 +1471,12 @@ mod_doc() ->
|
||||
"modify that option.")}},
|
||||
{access_register,
|
||||
#{value => ?T("AccessName"),
|
||||
note => "improved in 23.xx",
|
||||
desc =>
|
||||
?T("This option specifies who is allowed to register nickname "
|
||||
"within the Multi-User Chat service. The default is 'all' for "
|
||||
"within the Multi-User Chat service and rooms. The default is 'all' for "
|
||||
"backward compatibility, which means that any user is allowed "
|
||||
"to register any free nick.")}},
|
||||
"to register any free nick in the MUC service and in the rooms.")}},
|
||||
{db_type,
|
||||
#{value => "mnesia | sql",
|
||||
desc =>
|
||||
|
@ -82,13 +82,19 @@ forget_room(_LServer, Host, Name) ->
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
can_use_nick(_LServer, Host, JID, Nick) ->
|
||||
can_use_nick(_LServer, ServiceOrRoom, JID, Nick) ->
|
||||
{LUser, LServer, _} = jid:tolower(JID),
|
||||
LUS = {LUser, LServer},
|
||||
MatchSpec = case (jid:decode(ServiceOrRoom))#jid.lserver of
|
||||
ServiceOrRoom -> [{'==', {element, 2, '$1'}, ServiceOrRoom}];
|
||||
Service -> [{'orelse',
|
||||
{'==', {element, 2, '$1'}, Service},
|
||||
{'==', {element, 2, '$1'}, ServiceOrRoom} }]
|
||||
end,
|
||||
case catch mnesia:dirty_select(muc_registered,
|
||||
[{#muc_registered{us_host = '$1',
|
||||
nick = Nick, _ = '_'},
|
||||
[{'==', {element, 2, '$1'}, Host}],
|
||||
MatchSpec,
|
||||
['$_']}])
|
||||
of
|
||||
{'EXIT', _Reason} -> true;
|
||||
@ -110,31 +116,46 @@ get_nick(_LServer, Host, From) ->
|
||||
[#muc_registered{nick = Nick}] -> Nick
|
||||
end.
|
||||
|
||||
set_nick(_LServer, Host, From, Nick) ->
|
||||
set_nick(_LServer, ServiceOrRoom, From, Nick) ->
|
||||
{LUser, LServer, _} = jid:tolower(From),
|
||||
LUS = {LUser, LServer},
|
||||
F = fun () ->
|
||||
case Nick of
|
||||
<<"">> ->
|
||||
mnesia:delete({muc_registered, {LUS, Host}}),
|
||||
mnesia:delete({muc_registered, {LUS, ServiceOrRoom}}),
|
||||
ok;
|
||||
_ ->
|
||||
Service = (jid:decode(ServiceOrRoom))#jid.lserver,
|
||||
MatchSpec = case (ServiceOrRoom == Service) of
|
||||
true -> [{'==', {element, 2, '$1'}, ServiceOrRoom}];
|
||||
false -> [{'orelse',
|
||||
{'==', {element, 2, '$1'}, Service},
|
||||
{'==', {element, 2, '$1'}, ServiceOrRoom} }]
|
||||
end,
|
||||
Allow = case mnesia:select(
|
||||
muc_registered,
|
||||
[{#muc_registered{us_host =
|
||||
'$1',
|
||||
nick = Nick,
|
||||
_ = '_'},
|
||||
[{'==', {element, 2, '$1'},
|
||||
Host}],
|
||||
[{#muc_registered{us_host = '$1', nick = Nick, _ = '_'},
|
||||
MatchSpec,
|
||||
['$_']}]) of
|
||||
[] when (ServiceOrRoom == Service) ->
|
||||
NickRegistrations = mnesia:select(
|
||||
muc_registered,
|
||||
[{#muc_registered{us_host = '$1', nick = Nick, _ = '_'},
|
||||
[],
|
||||
['$_']}]),
|
||||
not lists:any(fun({_, {_NRUS, NRServiceOrRoom}, _Nick}) ->
|
||||
Service == (jid:decode(NRServiceOrRoom))#jid.lserver end,
|
||||
NickRegistrations);
|
||||
[] -> true;
|
||||
[#muc_registered{us_host = {_U, Host}}]
|
||||
when (Host == Service) and (ServiceOrRoom /= Service) ->
|
||||
false;
|
||||
[#muc_registered{us_host = {U, _Host}}] ->
|
||||
U == LUS
|
||||
end,
|
||||
if Allow ->
|
||||
mnesia:write(#muc_registered{
|
||||
us_host = {LUS, Host},
|
||||
us_host = {LUS, ServiceOrRoom},
|
||||
nick = Nick}),
|
||||
ok;
|
||||
true ->
|
||||
|
@ -501,6 +501,8 @@ normal_state({route, <<"">>,
|
||||
process_iq_captcha(From, IQ, StateData);
|
||||
#adhoc_command{} ->
|
||||
process_iq_adhoc(From, IQ, StateData);
|
||||
#register{} ->
|
||||
mod_muc:process_iq_register(IQ);
|
||||
#fasten_apply_to{} = ApplyTo ->
|
||||
case xmpp:get_subtag(ApplyTo, #message_moderate{}) of
|
||||
#message_moderate{} = Moderate ->
|
||||
@ -1406,7 +1408,7 @@ do_process_presence(Nick, #presence{from = From, type = available, lang = Lang}
|
||||
true ->
|
||||
case {nick_collision(From, Nick, StateData),
|
||||
mod_muc:can_use_nick(StateData#state.server_host,
|
||||
StateData#state.host,
|
||||
jid:encode(StateData#state.jid),
|
||||
From, Nick),
|
||||
{(StateData#state.config)#config.allow_visitor_nickchange,
|
||||
is_visitor(From, StateData)}} of
|
||||
@ -2290,7 +2292,7 @@ add_new_user(From, Nick, Packet, StateData) ->
|
||||
andalso NConferences < MaxConferences),
|
||||
Collision,
|
||||
mod_muc:can_use_nick(StateData#state.server_host,
|
||||
StateData#state.host, From, Nick),
|
||||
jid:encode(StateData#state.jid), From, Nick),
|
||||
get_occupant_initial_role(From, Affiliation, StateData)}
|
||||
of
|
||||
{false, _, _, _} when NUsers >= MaxUsers orelse NUsers >= MaxAdminUsers ->
|
||||
@ -4385,8 +4387,10 @@ maybe_forget_room(StateData) ->
|
||||
end).
|
||||
|
||||
-spec make_disco_info(jid(), state()) -> disco_info().
|
||||
make_disco_info(_From, StateData) ->
|
||||
make_disco_info(From, StateData) ->
|
||||
Config = StateData#state.config,
|
||||
ServerHost = StateData#state.server_host,
|
||||
AccessRegister = mod_muc_opt:access_register(ServerHost),
|
||||
Feats = [?NS_VCARD, ?NS_MUC, ?NS_DISCO_INFO, ?NS_DISCO_ITEMS,
|
||||
?NS_COMMANDS, ?NS_MESSAGE_MODERATE, ?NS_MESSAGE_RETRACT,
|
||||
?CONFIG_OPT_TO_FEATURE((Config#config.public),
|
||||
@ -4401,6 +4405,10 @@ make_disco_info(_From, StateData) ->
|
||||
<<"muc_moderated">>, <<"muc_unmoderated">>),
|
||||
?CONFIG_OPT_TO_FEATURE((Config#config.password_protected),
|
||||
<<"muc_passwordprotected">>, <<"muc_unsecured">>)]
|
||||
++ case acl:match_rule(ServerHost, AccessRegister, From) of
|
||||
allow -> [?NS_REGISTER];
|
||||
deny -> []
|
||||
end
|
||||
++ case Config#config.allow_subscription of
|
||||
true -> [?NS_MUCSUB];
|
||||
false -> []
|
||||
@ -4703,7 +4711,7 @@ process_iq_mucsub(From,
|
||||
{error, xmpp:err_conflict(ErrText, Lang)};
|
||||
false ->
|
||||
case mod_muc:can_use_nick(StateData#state.server_host,
|
||||
StateData#state.host,
|
||||
jid:encode(StateData#state.jid),
|
||||
From, Nick) of
|
||||
false ->
|
||||
Err = case Nick of
|
||||
|
@ -159,13 +159,19 @@ forget_room(LServer, Host, Name) ->
|
||||
end,
|
||||
ejabberd_sql:sql_transaction(LServer, F).
|
||||
|
||||
can_use_nick(LServer, Host, JID, Nick) ->
|
||||
can_use_nick(LServer, ServiceOrRoom, JID, Nick) ->
|
||||
SJID = jid:encode(jid:tolower(jid:remove_resource(JID))),
|
||||
case catch ejabberd_sql:sql_query(
|
||||
LServer,
|
||||
?SQL("select @(jid)s from muc_registered "
|
||||
"where nick=%(Nick)s"
|
||||
" and host=%(Host)s")) of
|
||||
SqlQuery = case (jid:decode(ServiceOrRoom))#jid.lserver of
|
||||
ServiceOrRoom ->
|
||||
?SQL("select @(jid)s from muc_registered "
|
||||
"where nick=%(Nick)s"
|
||||
" and host=%(ServiceOrRoom)s");
|
||||
Service ->
|
||||
?SQL("select @(jid)s from muc_registered "
|
||||
"where nick=%(Nick)s"
|
||||
" and (host=%(ServiceOrRoom)s or host=%(Service)s)")
|
||||
end,
|
||||
case catch ejabberd_sql:sql_query(LServer, SqlQuery) of
|
||||
{selected, [{SJID1}]} -> SJID == SJID1;
|
||||
_ -> true
|
||||
end.
|
||||
@ -258,28 +264,57 @@ get_nick(LServer, Host, From) ->
|
||||
_ -> error
|
||||
end.
|
||||
|
||||
set_nick(LServer, Host, From, Nick) ->
|
||||
set_nick(LServer, ServiceOrRoom, From, Nick) ->
|
||||
JID = jid:encode(jid:tolower(jid:remove_resource(From))),
|
||||
F = fun () ->
|
||||
case Nick of
|
||||
<<"">> ->
|
||||
ejabberd_sql:sql_query_t(
|
||||
?SQL("delete from muc_registered where"
|
||||
" jid=%(JID)s and host=%(Host)s")),
|
||||
" jid=%(JID)s and host=%(ServiceOrRoom)s")),
|
||||
ok;
|
||||
_ ->
|
||||
Allow = case ejabberd_sql:sql_query_t(
|
||||
?SQL("select @(jid)s from muc_registered"
|
||||
" where nick=%(Nick)s"
|
||||
" and host=%(Host)s")) of
|
||||
{selected, [{J}]} -> J == JID;
|
||||
_ -> true
|
||||
Service = (jid:decode(ServiceOrRoom))#jid.lserver,
|
||||
SqlQuery = case (ServiceOrRoom == Service) of
|
||||
true ->
|
||||
?SQL("select @(jid)s, @(host)s from muc_registered "
|
||||
"where nick=%(Nick)s"
|
||||
" and host=%(ServiceOrRoom)s");
|
||||
false ->
|
||||
?SQL("select @(jid)s, @(host)s from muc_registered "
|
||||
"where nick=%(Nick)s"
|
||||
" and (host=%(ServiceOrRoom)s or host=%(Service)s)")
|
||||
end,
|
||||
Allow = case ejabberd_sql:sql_query_t(SqlQuery) of
|
||||
{selected, []}
|
||||
when (ServiceOrRoom == Service) ->
|
||||
%% Registering in the service...
|
||||
%% check if nick is registered for some room in this service
|
||||
{selected, NickRegistrations} =
|
||||
ejabberd_sql:sql_query_t(
|
||||
?SQL("select @(jid)s, @(host)s from muc_registered "
|
||||
"where nick=%(Nick)s")),
|
||||
not lists:any(fun({_NRJid, NRServiceOrRoom}) ->
|
||||
Service == (jid:decode(NRServiceOrRoom))#jid.lserver end,
|
||||
NickRegistrations);
|
||||
{selected, []} ->
|
||||
%% Nick not registered in any service or room
|
||||
true;
|
||||
{selected, [{_J, Host}]}
|
||||
when (Host == Service) and (ServiceOrRoom /= Service) ->
|
||||
%% Registering in a room, but the nick is already registered in the service
|
||||
false;
|
||||
{selected, [{J, _Host}]} ->
|
||||
%% Registering in room (or service) a nick that is
|
||||
%% already registered in this room (or service)
|
||||
%% Only the owner of this registration can use the nick
|
||||
J == JID
|
||||
end,
|
||||
if Allow ->
|
||||
?SQL_UPSERT_T(
|
||||
"muc_registered",
|
||||
["!jid=%(JID)s",
|
||||
"!host=%(Host)s",
|
||||
"!host=%(ServiceOrRoom)s",
|
||||
"server_host=%(LServer)s",
|
||||
"nick=%(Nick)s"]),
|
||||
ok;
|
||||
|
Loading…
Reference in New Issue
Block a user