25
1
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:
Badlop 2011-08-23 21:52:08 +02:00
parent 7d3633e91c
commit 8ddbf1c1c5
2 changed files with 177 additions and 67 deletions

View File

@ -451,8 +451,8 @@ normal_state({route, From, ToNick,
ToNick), ToNick),
From, Err); From, Err);
_ -> _ ->
ToJID = find_jid_by_nick(ToNick, StateData), ToJIDs = find_jids_by_nick(ToNick, StateData),
case ToJID of case ToJIDs of
false -> false ->
ErrText = "Recipient is not in the conference room", ErrText = "Recipient is not in the conference room",
Err = exmpp_stanza:reply_with_error(Packet, Err = exmpp_stanza:reply_with_error(Packet,
@ -465,7 +465,7 @@ normal_state({route, From, ToNick,
From, Err); From, Err);
_ -> _ ->
SrcIsVisitor = is_visitor(From, StateData), 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, PmFromVisitors = (StateData#state.config)#config.allow_private_messages_from_visitors,
if SrcIsVisitor == false; if SrcIsVisitor == false;
PmFromVisitors == anyone; PmFromVisitors == anyone;
@ -473,11 +473,8 @@ normal_state({route, From, ToNick,
{ok, #user{nick = FromNick}} = {ok, #user{nick = FromNick}} =
?DICT:find(jlib:jid_tolower(From), ?DICT:find(jlib:jid_tolower(From),
StateData#state.users), StateData#state.users),
ejabberd_router:route( FromNickJID = jid_replace_resource(StateData#state.jid, FromNick),
jid_replace_resource( [ejabberd_router:route(FromNickJID, ToJID, Packet) || ToJID <- ToJIDs];
StateData#state.jid,
FromNick),
ToJID, Packet);
true -> true ->
ErrText = "It is not allowed to send private messages", ErrText = "It is not allowed to send private messages",
Err = exmpp_stanza:reply_with_error(Packet, Err = exmpp_stanza:reply_with_error(Packet,
@ -953,7 +950,10 @@ process_presence(From, Nick, #xmlel{name = 'presence'} = Packet,
true -> true ->
NewState = NewState =
add_user_presence_un(From, Packet, StateData), 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 Reason = case exmpp_xml:get_element(Packet, 'status') of
undefined -> <<>>; undefined -> <<>>;
Status_el -> exmpp_xml:get_cdata(Status_el) Status_el -> exmpp_xml:get_cdata(Status_el)
@ -977,7 +977,7 @@ process_presence(From, Nick, #xmlel{name = 'presence'} = Packet,
true -> true ->
case is_nick_change(From, Nick, StateData) of case is_nick_change(From, Nick, StateData) of
true -> true ->
case {is_nick_exists(Nick, StateData), case {nick_collision(From, Nick, StateData),
mod_muc:can_use_nick( mod_muc:can_use_nick(
StateData#state.host, From, Nick), StateData#state.host, From, Nick),
{(StateData#state.config)#config.allow_visitor_nickchange, {(StateData#state.config)#config.allow_visitor_nickchange,
@ -1296,21 +1296,31 @@ set_role(JID, Role, StateData) ->
[] []
end end
end, end,
Users = case Role of {Users, Nicks}
none -> = case Role of
lists:foldl(fun(J, Us) -> none ->
?DICT:erase(J, lists:foldl(fun(J, {Us, Ns}) ->
Us) NewNs =
end, StateData#state.users, LJIDs); case ?DICT:find(J, Us) of
_ -> {ok, #user{nick = Nick}} ->
lists:foldl(fun(J, Us) -> ?DICT:erase(Nick, Ns);
{ok, User} = ?DICT:find(J, Us), _ ->
?DICT:store(J, Ns
User#user{role = Role}, end,
Us) {?DICT:erase(J, Us), NewNs}
end, StateData#state.users, LJIDs) end,
end, {StateData#state.users, StateData#state.nicks},
StateData#state{users = 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),
StateData#state.nicks}
end,
StateData#state{users = Users, nicks = Nicks}.
get_role(JID, StateData) -> get_role(JID, StateData) ->
LJID = jlib:short_prepd_jid(JID), LJID = jlib:short_prepd_jid(JID),
@ -1493,8 +1503,19 @@ add_online_user(JID, Nick, Role, StateData) ->
role = Role}, role = Role},
StateData#state.users), StateData#state.users),
add_to_log(join, Nick, StateData), 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), 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) ->
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), add_to_log(leave, {Nick, Reason}, StateData),
tab_remove_online_user(JID, StateData), tab_remove_online_user(JID, StateData),
Users = ?DICT:erase(LJID, StateData#state.users), 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) -> filter_presence(#xmlel{name = 'presence'} = Packet) ->
@ -1556,19 +1585,48 @@ add_user_presence_un(JID, Presence, StateData) ->
StateData#state{users = Users}. StateData#state{users = Users}.
is_nick_exists(Nick, StateData) -> %% Find and return a list of the full JIDs of the users of Nick.
?DICT:fold(fun(_, #user{nick = N}, B) -> %% Return jid record.
B orelse (N == Nick) find_jids_by_nick(Nick, StateData) ->
end, false, StateData#state.users). 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 %% @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) -> find_jid_by_nick(Nick, StateData) ->
?DICT:fold(fun(_, #user{jid = JID, nick = N}, R) -> case ?DICT:find(Nick, StateData#state.nicks) of
case Nick of {ok, [User]} ->
N -> JID; exmpp_jid:make(User);
_ -> R {ok, [FirstUser|Users]} ->
end #user{last_presence = FirstPresence} =
end, false, StateData#state.users). ?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) -> is_nick_change(JID, Nick, StateData) ->
LJID = jlib:short_prepd_jid(JID), LJID = jlib:short_prepd_jid(JID),
@ -1581,6 +1639,13 @@ is_nick_change(JID, Nick, StateData) ->
Nick /= OldNick Nick /= OldNick
end. 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) -> add_new_user(From, Nick, Packet, StateData) ->
Lang = exmpp_stanza:get_lang(Packet), Lang = exmpp_stanza:get_lang(Packet),
MaxUsers = get_max_users(StateData), MaxUsers = get_max_users(StateData),
@ -1593,13 +1658,14 @@ add_new_user(From, Nick, Packet, StateData) ->
MaxConferences = gen_mod:get_module_opt( MaxConferences = gen_mod:get_module_opt(
StateData#state.server_host, StateData#state.server_host,
mod_muc, max_user_conferences, 10), mod_muc, max_user_conferences, 10),
Collision = nick_collision(From, Nick, StateData),
case {(ServiceAffiliation == owner orelse case {(ServiceAffiliation == owner orelse
MaxUsers < 0 orelse MaxUsers < 0 orelse
((Affiliation == admin orelse Affiliation == owner) andalso ((Affiliation == admin orelse Affiliation == owner) andalso
NUsers < MaxAdminUsers) orelse NUsers < MaxAdminUsers) orelse
NUsers < MaxUsers) andalso NUsers < MaxUsers) andalso
NConferences < MaxConferences, NConferences < MaxConferences,
is_nick_exists(Nick, StateData), Collision,
mod_muc:can_use_nick(StateData#state.host, From, Nick), mod_muc:can_use_nick(StateData#state.host, From, Nick),
get_default_role(Affiliation, StateData)} of get_default_role(Affiliation, StateData)} of
{false, _, _, _} -> {false, _, _, _} ->
@ -1937,13 +2003,19 @@ send_new_presence(NJID, StateData) ->
%% @spec(NJID::jid(), Reason::binary(), StateData) -> %% @spec(NJID::jid(), Reason::binary(), StateData) ->
send_new_presence({U, S, R}, Reason, StateData) -> send_new_presence({U, S, R}, Reason, StateData) ->
send_new_presence(exmpp_jid:make(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, {ok, #user{jid = RealJID,
nick = Nick,
role = Role, role = Role,
last_presence = Presence}} = last_presence = Presence}} =
?DICT:find(jlib:short_prepd_jid(NJID), StateData#state.users), ?DICT:find(LJID, StateData#state.users),
Affiliation = get_affiliation(NJID, StateData), Affiliation = get_affiliation(LJID1, StateData),
SAffiliation = affiliation_to_binary(Affiliation), SAffiliation = affiliation_to_binary(Affiliation),
SRole = role_to_binary(Role), SRole = role_to_binary(Role),
lists:foreach( lists:foreach(
@ -2003,21 +2075,23 @@ send_new_presence(NJID, Reason, StateData) ->
send_existing_presences(ToJID, 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, {ok, #user{jid = RealToJID,
role = Role}} = role = Role}} =
?DICT:find(LToJID, StateData#state.users), ?DICT:find(LToJID, StateData#state.users),
lists:foreach( lists:foreach(
fun({LJID, #user{jid = FromJID, fun({FromNick, _Users}) ->
nick = FromNick, LJID1 = find_jid_by_nick(FromNick, StateData),
role = FromRole, LJID = {exmpp_jid:node(LJID1), exmpp_jid:domain(LJID1), exmpp_jid:resource(LJID1)},
last_presence = Presence #user{jid = FromJID,
}}) -> role = FromRole,
last_presence = Presence
} = ?DICT:fetch(LJID, StateData#state.users),
case RealToJID of case RealToJID of
FromJID -> FromJID ->
ok; ok;
_ -> _ ->
{N,D,R} = LJID, {N,D,R} = LJID,
FromAffiliation = get_affiliation(exmpp_jid:make(N,D,R), FromAffiliation = get_affiliation(exmpp_jid:make(N,D,R),
StateData), StateData),
ItemAttrs = ItemAttrs =
@ -2044,7 +2118,7 @@ send_existing_presences(ToJID, StateData) ->
RealToJID, RealToJID,
Packet) Packet)
end 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) -> fun(#user{} = User) ->
User#user{nick = Nick} User#user{nick = Nick}
end, StateData#state.users), end, StateData#state.users),
NewStateData = StateData#state{users = Users}, OldNickUsers = ?DICT:fetch(OldNick, StateData#state.nicks),
send_nick_changing(JID, OldNick, NewStateData), 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), add_to_log(nickchange, {OldNick, Nick}, StateData),
NewStateData. NewStateData.
send_nick_changing(JID, OldNick, StateData) -> send_nick_changing(JID, OldNick, StateData,
SendOldUnavailable, SendNewAvailable) ->
{ok, #user{jid = RealJID, {ok, #user{jid = RealJID,
nick = Nick, nick = Nick,
role = Role, role = Role,
@ -2120,15 +2219,22 @@ send_nick_changing(JID, OldNick, StateData) ->
children =[#xmlel{ns = ?NS_MUC_USER, children =[#xmlel{ns = ?NS_MUC_USER,
name = 'item', name = 'item',
attrs = ItemAttrs2}]}), attrs = ItemAttrs2}]}),
if SendOldUnavailable ->
ejabberd_router:route( ejabberd_router:route(
jid_replace_resource(StateData#state.jid, OldNick), jid_replace_resource(StateData#state.jid, OldNick),
Info#user.jid, Info#user.jid,
Packet1), Packet1);
ejabberd_router:route( true ->
jid_replace_resource(StateData#state.jid, Nick), ok
Info#user.jid, end,
Packet2) 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)). 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)}]}]}). #xmlcdata{cdata = list_to_binary(Val)}]}]}).
iq_disco_info_extras(Lang, StateData) -> 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, RoomDescription = (StateData#state.config)#config.description,
[#xmlel{ns = ?NS_DATA_FORMS, name = 'x', [#xmlel{ns = ?NS_DATA_FORMS, name = 'x',
attrs = [?XMLATTR(<<"type">>, <<"result">>)], attrs = [?XMLATTR(<<"type">>, <<"result">>)],
@ -3795,27 +3901,29 @@ add_to_log(Type, Data, StateData) ->
tab_add_online_user(JID, StateData) -> tab_add_online_user(JID, StateData) ->
LUser = exmpp_jid:prep_node(JID), LUser = exmpp_jid:prep_node(JID),
LServer = exmpp_jid:prep_domain(JID), LServer = exmpp_jid:prep_domain(JID),
LResource = exmpp_jid:prep_resource(JID),
US = {LUser, LServer}, US = {LUser, LServer},
Room = StateData#state.room, Room = StateData#state.room,
Host = StateData#state.host, Host = StateData#state.host,
catch ets:insert( catch ets:insert(
muc_online_users, 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) -> tab_remove_online_user(JID, StateData) when ?IS_JID(JID) ->
LUser = exmpp_jid:prep_node(JID), LUser = exmpp_jid:prep_node(JID),
LServer = exmpp_jid:prep_domain(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}, US = {LUser, LServer},
Room = StateData#state.room, Room = StateData#state.room,
Host = StateData#state.host, Host = StateData#state.host,
catch ets:delete_object( catch ets:delete_object(
muc_online_users, 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) -> tab_count_user(JID) ->
LUser = exmpp_jid:prep_node(JID), LUser = exmpp_jid:prep_node(JID),

View File

@ -69,6 +69,7 @@
config = #config{}, config = #config{},
users = ?DICT:new(), users = ?DICT:new(),
robots = ?DICT:new(), robots = ?DICT:new(),
nicks = ?DICT:new(),
affiliations = ?DICT:new(), affiliations = ?DICT:new(),
history, history,
subject = "", subject = "",
@ -79,6 +80,7 @@
room_queue = queue:new()}). room_queue = queue:new()}).
-record(muc_online_users, {us, -record(muc_online_users, {us,
resource,
room, room,
host}). host}).