mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
Allow multiple entry with same nick to MUC rooms (thanks to Magnus Henoch)(EJAB-305)
This commit is contained in:
parent
7d3633e91c
commit
8ddbf1c1c5
@ -451,8 +451,8 @@ normal_state({route, From, ToNick,
|
||||
ToNick),
|
||||
From, Err);
|
||||
_ ->
|
||||
ToJID = find_jid_by_nick(ToNick, StateData),
|
||||
case ToJID of
|
||||
ToJIDs = find_jids_by_nick(ToNick, StateData),
|
||||
case ToJIDs of
|
||||
false ->
|
||||
ErrText = "Recipient is not in the conference room",
|
||||
Err = exmpp_stanza:reply_with_error(Packet,
|
||||
@ -465,7 +465,7 @@ normal_state({route, From, ToNick,
|
||||
From, Err);
|
||||
_ ->
|
||||
SrcIsVisitor = is_visitor(From, StateData),
|
||||
DstIsModerator = is_moderator(ToJID, StateData),
|
||||
DstIsModerator = is_moderator(hd(ToJIDs), StateData),
|
||||
PmFromVisitors = (StateData#state.config)#config.allow_private_messages_from_visitors,
|
||||
if SrcIsVisitor == false;
|
||||
PmFromVisitors == anyone;
|
||||
@ -473,11 +473,8 @@ normal_state({route, From, ToNick,
|
||||
{ok, #user{nick = FromNick}} =
|
||||
?DICT:find(jlib:jid_tolower(From),
|
||||
StateData#state.users),
|
||||
ejabberd_router:route(
|
||||
jid_replace_resource(
|
||||
StateData#state.jid,
|
||||
FromNick),
|
||||
ToJID, Packet);
|
||||
FromNickJID = jid_replace_resource(StateData#state.jid, FromNick),
|
||||
[ejabberd_router:route(FromNickJID, ToJID, Packet) || ToJID <- ToJIDs];
|
||||
true ->
|
||||
ErrText = "It is not allowed to send private messages",
|
||||
Err = exmpp_stanza:reply_with_error(Packet,
|
||||
@ -953,7 +950,10 @@ process_presence(From, Nick, #xmlel{name = 'presence'} = Packet,
|
||||
true ->
|
||||
NewState =
|
||||
add_user_presence_un(From, Packet, StateData),
|
||||
send_new_presence(From, NewState),
|
||||
case ?DICT:find(Nick, StateData#state.nicks) of
|
||||
{ok, [_, _ | _]} -> ok;
|
||||
_ -> send_new_presence(From, NewState)
|
||||
end,
|
||||
Reason = case exmpp_xml:get_element(Packet, 'status') of
|
||||
undefined -> <<>>;
|
||||
Status_el -> exmpp_xml:get_cdata(Status_el)
|
||||
@ -977,7 +977,7 @@ process_presence(From, Nick, #xmlel{name = 'presence'} = Packet,
|
||||
true ->
|
||||
case is_nick_change(From, Nick, StateData) of
|
||||
true ->
|
||||
case {is_nick_exists(Nick, StateData),
|
||||
case {nick_collision(From, Nick, StateData),
|
||||
mod_muc:can_use_nick(
|
||||
StateData#state.host, From, Nick),
|
||||
{(StateData#state.config)#config.allow_visitor_nickchange,
|
||||
@ -1296,21 +1296,31 @@ set_role(JID, Role, StateData) ->
|
||||
[]
|
||||
end
|
||||
end,
|
||||
Users = case Role of
|
||||
none ->
|
||||
lists:foldl(fun(J, Us) ->
|
||||
?DICT:erase(J,
|
||||
Us)
|
||||
end, StateData#state.users, LJIDs);
|
||||
_ ->
|
||||
lists:foldl(fun(J, Us) ->
|
||||
{ok, User} = ?DICT:find(J, Us),
|
||||
?DICT:store(J,
|
||||
User#user{role = Role},
|
||||
Us)
|
||||
end, StateData#state.users, LJIDs)
|
||||
end,
|
||||
StateData#state{users = Users}.
|
||||
{Users, Nicks}
|
||||
= case Role of
|
||||
none ->
|
||||
lists:foldl(fun(J, {Us, Ns}) ->
|
||||
NewNs =
|
||||
case ?DICT:find(J, Us) of
|
||||
{ok, #user{nick = Nick}} ->
|
||||
?DICT:erase(Nick, Ns);
|
||||
_ ->
|
||||
Ns
|
||||
end,
|
||||
{?DICT:erase(J, Us), NewNs}
|
||||
end,
|
||||
{StateData#state.users, StateData#state.nicks},
|
||||
LJIDs);
|
||||
_ ->
|
||||
{lists:foldl(fun(J, Us) ->
|
||||
{ok, User} = ?DICT:find(J, Us),
|
||||
?DICT:store(J,
|
||||
User#user{role = Role},
|
||||
Us)
|
||||
end, StateData#state.users, LJIDs),
|
||||
StateData#state.nicks}
|
||||
end,
|
||||
StateData#state{users = Users, nicks = Nicks}.
|
||||
|
||||
get_role(JID, StateData) ->
|
||||
LJID = jlib:short_prepd_jid(JID),
|
||||
@ -1493,8 +1503,19 @@ add_online_user(JID, Nick, Role, StateData) ->
|
||||
role = Role},
|
||||
StateData#state.users),
|
||||
add_to_log(join, Nick, StateData),
|
||||
Nicks = ?DICT:update(Nick,
|
||||
fun(Entry) ->
|
||||
case lists:member(LJID, Entry) of
|
||||
true ->
|
||||
Entry;
|
||||
false ->
|
||||
[LJID|Entry]
|
||||
end
|
||||
end,
|
||||
[LJID],
|
||||
StateData#state.nicks),
|
||||
tab_add_online_user(JID, StateData),
|
||||
StateData#state{users = Users}.
|
||||
StateData#state{users = Users, nicks = Nicks}.
|
||||
|
||||
remove_online_user(JID, StateData) ->
|
||||
remove_online_user(JID, StateData, <<>>).
|
||||
@ -1506,7 +1527,15 @@ remove_online_user(JID, StateData, Reason) ->
|
||||
add_to_log(leave, {Nick, Reason}, StateData),
|
||||
tab_remove_online_user(JID, StateData),
|
||||
Users = ?DICT:erase(LJID, StateData#state.users),
|
||||
StateData#state{users = Users}.
|
||||
Nicks = case ?DICT:find(Nick, StateData#state.nicks) of
|
||||
{ok, [LJID]} ->
|
||||
?DICT:erase(Nick, StateData#state.nicks);
|
||||
{ok, U} ->
|
||||
?DICT:store(Nick, U -- [LJID], StateData#state.nicks);
|
||||
false ->
|
||||
StateData#state.nicks
|
||||
end,
|
||||
StateData#state{users = Users, nicks = Nicks}.
|
||||
|
||||
|
||||
filter_presence(#xmlel{name = 'presence'} = Packet) ->
|
||||
@ -1556,19 +1585,48 @@ add_user_presence_un(JID, Presence, StateData) ->
|
||||
StateData#state{users = Users}.
|
||||
|
||||
|
||||
is_nick_exists(Nick, StateData) ->
|
||||
?DICT:fold(fun(_, #user{nick = N}, B) ->
|
||||
B orelse (N == Nick)
|
||||
end, false, StateData#state.users).
|
||||
%% Find and return a list of the full JIDs of the users of Nick.
|
||||
%% Return jid record.
|
||||
find_jids_by_nick(Nick, StateData) ->
|
||||
case ?DICT:find(Nick, StateData#state.nicks) of
|
||||
{ok, [User]} ->
|
||||
[exmpp_jid:make(User)];
|
||||
{ok, Users} ->
|
||||
[exmpp_jid:make(LJID) || LJID <- Users];
|
||||
error ->
|
||||
false
|
||||
end.
|
||||
|
||||
%% @spec(Nick::binary(), StateData) -> JID::jid() | false
|
||||
%% Find and return the full JID of the user of Nick with
|
||||
%% highest-priority presence. Return jid record.
|
||||
find_jid_by_nick(Nick, StateData) ->
|
||||
?DICT:fold(fun(_, #user{jid = JID, nick = N}, R) ->
|
||||
case Nick of
|
||||
N -> JID;
|
||||
_ -> R
|
||||
end
|
||||
end, false, StateData#state.users).
|
||||
case ?DICT:find(Nick, StateData#state.nicks) of
|
||||
{ok, [User]} ->
|
||||
exmpp_jid:make(User);
|
||||
{ok, [FirstUser|Users]} ->
|
||||
#user{last_presence = FirstPresence} =
|
||||
?DICT:fetch(FirstUser, StateData#state.users),
|
||||
{LJID, _} =
|
||||
lists:foldl(fun(Compare, {HighestUser, HighestPresence}) ->
|
||||
#user{last_presence = P1} =
|
||||
?DICT:fetch(Compare, StateData#state.users),
|
||||
case higher_presence(P1, HighestPresence) of
|
||||
true ->
|
||||
{Compare, P1};
|
||||
false ->
|
||||
{HighestUser, HighestPresence}
|
||||
end
|
||||
end, {FirstUser, FirstPresence}, Users),
|
||||
exmpp_jid:make(LJID);
|
||||
error ->
|
||||
false
|
||||
end.
|
||||
|
||||
higher_presence(Pres1, Pres2) ->
|
||||
Pri1 = exmpp_presence:get_priority(Pres1),
|
||||
Pri2 = exmpp_presence:get_priority(Pres2),
|
||||
Pri1 > Pri2.
|
||||
|
||||
is_nick_change(JID, Nick, StateData) ->
|
||||
LJID = jlib:short_prepd_jid(JID),
|
||||
@ -1581,6 +1639,13 @@ is_nick_change(JID, Nick, StateData) ->
|
||||
Nick /= OldNick
|
||||
end.
|
||||
|
||||
nick_collision(User, Nick, StateData) ->
|
||||
UserOfNick = find_jid_by_nick(Nick, StateData),
|
||||
%% if nick is not used, or is used by another resource of the same
|
||||
%% user, it's ok.
|
||||
UserOfNick /= false andalso
|
||||
not exmpp_jid:bare_compare(UserOfNick, User).
|
||||
|
||||
add_new_user(From, Nick, Packet, StateData) ->
|
||||
Lang = exmpp_stanza:get_lang(Packet),
|
||||
MaxUsers = get_max_users(StateData),
|
||||
@ -1593,13 +1658,14 @@ add_new_user(From, Nick, Packet, StateData) ->
|
||||
MaxConferences = gen_mod:get_module_opt(
|
||||
StateData#state.server_host,
|
||||
mod_muc, max_user_conferences, 10),
|
||||
Collision = nick_collision(From, Nick, StateData),
|
||||
case {(ServiceAffiliation == owner orelse
|
||||
MaxUsers < 0 orelse
|
||||
((Affiliation == admin orelse Affiliation == owner) andalso
|
||||
NUsers < MaxAdminUsers) orelse
|
||||
NUsers < MaxUsers) andalso
|
||||
NConferences < MaxConferences,
|
||||
is_nick_exists(Nick, StateData),
|
||||
Collision,
|
||||
mod_muc:can_use_nick(StateData#state.host, From, Nick),
|
||||
get_default_role(Affiliation, StateData)} of
|
||||
{false, _, _, _} ->
|
||||
@ -1937,13 +2003,19 @@ send_new_presence(NJID, StateData) ->
|
||||
%% @spec(NJID::jid(), Reason::binary(), StateData) ->
|
||||
send_new_presence({U, S, R}, Reason, StateData) ->
|
||||
send_new_presence(exmpp_jid:make(U, S, R), Reason, StateData);
|
||||
send_new_presence(NJID, Reason, StateData) ->
|
||||
send_new_presence(NJID1, Reason, StateData) ->
|
||||
NJID = {exmpp_jid:node(NJID1), exmpp_jid:domain(NJID1), exmpp_jid:resource(NJID1)},
|
||||
%% First, find the nick associated with this JID.
|
||||
#user{nick = Nick} = ?DICT:fetch(NJID, StateData#state.users),
|
||||
%% Then find the JID using this nick with highest priority.
|
||||
LJID1 = find_jid_by_nick(Nick, StateData),
|
||||
LJID = {exmpp_jid:node(LJID1), exmpp_jid:domain(LJID1), exmpp_jid:resource(LJID1)},
|
||||
%% Then we get the presence data we're supposed to send.
|
||||
{ok, #user{jid = RealJID,
|
||||
nick = Nick,
|
||||
role = Role,
|
||||
last_presence = Presence}} =
|
||||
?DICT:find(jlib:short_prepd_jid(NJID), StateData#state.users),
|
||||
Affiliation = get_affiliation(NJID, StateData),
|
||||
?DICT:find(LJID, StateData#state.users),
|
||||
Affiliation = get_affiliation(LJID1, StateData),
|
||||
SAffiliation = affiliation_to_binary(Affiliation),
|
||||
SRole = role_to_binary(Role),
|
||||
lists:foreach(
|
||||
@ -2003,21 +2075,23 @@ send_new_presence(NJID, Reason, StateData) ->
|
||||
|
||||
|
||||
send_existing_presences(ToJID, StateData) ->
|
||||
LToJID = jlib:short_prepd_jid(ToJID),
|
||||
LToJID = {exmpp_jid:node(ToJID), exmpp_jid:domain(ToJID), exmpp_jid:resource(ToJID)},
|
||||
{ok, #user{jid = RealToJID,
|
||||
role = Role}} =
|
||||
?DICT:find(LToJID, StateData#state.users),
|
||||
lists:foreach(
|
||||
fun({LJID, #user{jid = FromJID,
|
||||
nick = FromNick,
|
||||
role = FromRole,
|
||||
last_presence = Presence
|
||||
}}) ->
|
||||
fun({FromNick, _Users}) ->
|
||||
LJID1 = find_jid_by_nick(FromNick, StateData),
|
||||
LJID = {exmpp_jid:node(LJID1), exmpp_jid:domain(LJID1), exmpp_jid:resource(LJID1)},
|
||||
#user{jid = FromJID,
|
||||
role = FromRole,
|
||||
last_presence = Presence
|
||||
} = ?DICT:fetch(LJID, StateData#state.users),
|
||||
case RealToJID of
|
||||
FromJID ->
|
||||
ok;
|
||||
_ ->
|
||||
{N,D,R} = LJID,
|
||||
{N,D,R} = LJID,
|
||||
FromAffiliation = get_affiliation(exmpp_jid:make(N,D,R),
|
||||
StateData),
|
||||
ItemAttrs =
|
||||
@ -2044,7 +2118,7 @@ send_existing_presences(ToJID, StateData) ->
|
||||
RealToJID,
|
||||
Packet)
|
||||
end
|
||||
end, ?DICT:to_list(StateData#state.users)).
|
||||
end, ?DICT:to_list(StateData#state.nicks)).
|
||||
|
||||
|
||||
|
||||
@ -2062,12 +2136,37 @@ change_nick(JID, Nick, StateData) ->
|
||||
fun(#user{} = User) ->
|
||||
User#user{nick = Nick}
|
||||
end, StateData#state.users),
|
||||
NewStateData = StateData#state{users = Users},
|
||||
send_nick_changing(JID, OldNick, NewStateData),
|
||||
OldNickUsers = ?DICT:fetch(OldNick, StateData#state.nicks),
|
||||
NewNickUsers = case ?DICT:find(Nick, StateData#state.nicks) of
|
||||
{ok, U} -> U;
|
||||
error -> []
|
||||
end,
|
||||
%% Send unavailable presence from the old nick if it's no longer
|
||||
%% used.
|
||||
SendOldUnavailable = length(OldNickUsers) == 1,
|
||||
%% If we send unavailable presence from the old nick, we should
|
||||
%% probably send presence from the new nick, in order not to
|
||||
%% confuse clients. Otherwise, do it only if the new nick was
|
||||
%% unused.
|
||||
SendNewAvailable = SendOldUnavailable orelse
|
||||
NewNickUsers == [],
|
||||
Nicks =
|
||||
case OldNickUsers of
|
||||
[LJID] ->
|
||||
?DICT:store(Nick, [LJID|NewNickUsers],
|
||||
?DICT:erase(OldNick, StateData#state.nicks));
|
||||
[_|_] ->
|
||||
?DICT:store(Nick, [LJID|NewNickUsers],
|
||||
?DICT:store(OldNick, OldNickUsers -- [LJID],
|
||||
StateData#state.nicks))
|
||||
end,
|
||||
NewStateData = StateData#state{users = Users, nicks = Nicks},
|
||||
send_nick_changing(JID, OldNick, NewStateData, SendOldUnavailable, SendNewAvailable),
|
||||
add_to_log(nickchange, {OldNick, Nick}, StateData),
|
||||
NewStateData.
|
||||
|
||||
send_nick_changing(JID, OldNick, StateData) ->
|
||||
send_nick_changing(JID, OldNick, StateData,
|
||||
SendOldUnavailable, SendNewAvailable) ->
|
||||
{ok, #user{jid = RealJID,
|
||||
nick = Nick,
|
||||
role = Role,
|
||||
@ -2120,15 +2219,22 @@ send_nick_changing(JID, OldNick, StateData) ->
|
||||
children =[#xmlel{ns = ?NS_MUC_USER,
|
||||
name = 'item',
|
||||
attrs = ItemAttrs2}]}),
|
||||
|
||||
ejabberd_router:route(
|
||||
jid_replace_resource(StateData#state.jid, OldNick),
|
||||
Info#user.jid,
|
||||
Packet1),
|
||||
ejabberd_router:route(
|
||||
jid_replace_resource(StateData#state.jid, Nick),
|
||||
Info#user.jid,
|
||||
Packet2)
|
||||
if SendOldUnavailable ->
|
||||
ejabberd_router:route(
|
||||
jid_replace_resource(StateData#state.jid, OldNick),
|
||||
Info#user.jid,
|
||||
Packet1);
|
||||
true ->
|
||||
ok
|
||||
end,
|
||||
if SendNewAvailable ->
|
||||
ejabberd_router:route(
|
||||
jid_replace_resource(StateData#state.jid, Nick),
|
||||
Info#user.jid,
|
||||
Packet2);
|
||||
true ->
|
||||
ok
|
||||
end
|
||||
end, ?DICT:to_list(StateData#state.users)).
|
||||
|
||||
|
||||
@ -3530,7 +3636,7 @@ process_iq_disco_info(_From, get, Lang, StateData) ->
|
||||
#xmlcdata{cdata = list_to_binary(Val)}]}]}).
|
||||
|
||||
iq_disco_info_extras(Lang, StateData) ->
|
||||
Len = length(?DICT:to_list(StateData#state.users)),
|
||||
Len = ?DICT:size(StateData#state.users),
|
||||
RoomDescription = (StateData#state.config)#config.description,
|
||||
[#xmlel{ns = ?NS_DATA_FORMS, name = 'x',
|
||||
attrs = [?XMLATTR(<<"type">>, <<"result">>)],
|
||||
@ -3795,27 +3901,29 @@ add_to_log(Type, Data, StateData) ->
|
||||
tab_add_online_user(JID, StateData) ->
|
||||
LUser = exmpp_jid:prep_node(JID),
|
||||
LServer = exmpp_jid:prep_domain(JID),
|
||||
LResource = exmpp_jid:prep_resource(JID),
|
||||
US = {LUser, LServer},
|
||||
Room = StateData#state.room,
|
||||
Host = StateData#state.host,
|
||||
catch ets:insert(
|
||||
muc_online_users,
|
||||
#muc_online_users{us = US, room = Room, host = Host}).
|
||||
#muc_online_users{us = US, resource = LResource, room = Room, host = Host}).
|
||||
|
||||
|
||||
|
||||
tab_remove_online_user(JID, StateData) when ?IS_JID(JID) ->
|
||||
LUser = exmpp_jid:prep_node(JID),
|
||||
LServer = exmpp_jid:prep_domain(JID),
|
||||
tab_remove_online_user({LUser, LServer, none},StateData);
|
||||
LResource = exmpp_jid:prep_resource(JID),
|
||||
tab_remove_online_user({LUser, LServer, LResource},StateData);
|
||||
|
||||
tab_remove_online_user({LUser, LServer,_}, StateData) ->
|
||||
tab_remove_online_user({LUser, LServer, LResource}, StateData) ->
|
||||
US = {LUser, LServer},
|
||||
Room = StateData#state.room,
|
||||
Host = StateData#state.host,
|
||||
catch ets:delete_object(
|
||||
muc_online_users,
|
||||
#muc_online_users{us = US, room = Room, host = Host}).
|
||||
#muc_online_users{us = US, resource = LResource, room = Room, host = Host}).
|
||||
|
||||
tab_count_user(JID) ->
|
||||
LUser = exmpp_jid:prep_node(JID),
|
||||
|
@ -69,6 +69,7 @@
|
||||
config = #config{},
|
||||
users = ?DICT:new(),
|
||||
robots = ?DICT:new(),
|
||||
nicks = ?DICT:new(),
|
||||
affiliations = ?DICT:new(),
|
||||
history,
|
||||
subject = "",
|
||||
@ -79,6 +80,7 @@
|
||||
room_queue = queue:new()}).
|
||||
|
||||
-record(muc_online_users, {us,
|
||||
resource,
|
||||
room,
|
||||
host}).
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user