mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
Fix RSM for conference disco#items
This commit is contained in:
parent
13c6039700
commit
049a6d97f1
223
src/mod_muc.erl
223
src/mod_muc.erl
@ -62,7 +62,7 @@
|
||||
|
||||
-include("ejabberd.hrl").
|
||||
-include("logger.hrl").
|
||||
|
||||
-include_lib("stdlib/include/ms_transform.hrl").
|
||||
-include("xmpp.hrl").
|
||||
-include("mod_muc.hrl").
|
||||
|
||||
@ -175,8 +175,10 @@ init([Host, Opts]) ->
|
||||
<<"conference.@HOST@">>),
|
||||
Mod = gen_mod:db_mod(Host, Opts, ?MODULE),
|
||||
Mod:init(Host, [{host, MyHost}|Opts]),
|
||||
update_tables(),
|
||||
mnesia:create_table(muc_online_room,
|
||||
[{ram_copies, [node()]},
|
||||
{type, ordered_set},
|
||||
{attributes, record_info(fields, muc_online_room)}]),
|
||||
mnesia:add_table_copy(muc_online_room, node(), ram_copies),
|
||||
catch ets:new(muc_online_users, [bag, named_table, public, {keypos, 2}]),
|
||||
@ -497,8 +499,12 @@ process_disco_items(#iq{type = get, from = From, to = To, lang = Lang,
|
||||
ServerHost, ?MODULE, max_rooms_discoitems,
|
||||
fun(I) when is_integer(I), I>=0 -> I end,
|
||||
100),
|
||||
Items = iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, Node, RSM),
|
||||
xmpp:make_iq_result(IQ, #disco_items{node = Node, items = Items});
|
||||
case iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, Node, RSM) of
|
||||
{error, Err} ->
|
||||
xmpp:make_error(IQ, Err);
|
||||
{result, Result} ->
|
||||
xmpp:make_iq_result(IQ, Result)
|
||||
end;
|
||||
process_disco_items(#iq{lang = Lang} = IQ) ->
|
||||
Txt = <<"No module is handling this query">>,
|
||||
xmpp:make_error(IQ, xmpp:err_service_unavailable(Txt, Lang)).
|
||||
@ -597,76 +603,112 @@ register_room(Host, Room, Pid) ->
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, <<"">>, undefined) ->
|
||||
Rooms = get_vh_rooms(Host),
|
||||
case erlang:length(Rooms) < MaxRoomsDiscoItems of
|
||||
true ->
|
||||
iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang});
|
||||
false ->
|
||||
iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, <<"nonemptyrooms">>, undefined)
|
||||
end;
|
||||
iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, <<"nonemptyrooms">>, undefined) ->
|
||||
Empty = #disco_item{jid = jid:make(<<"conference.localhost">>),
|
||||
node = <<"emptyrooms">>,
|
||||
name = translate:translate(Lang, <<"Empty Rooms">>)},
|
||||
Query = {get_disco_item, only_non_empty, From, Lang},
|
||||
[Empty | iq_disco_items_list(Host, get_vh_rooms(Host), Query)];
|
||||
iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, <<"emptyrooms">>, undefined) ->
|
||||
iq_disco_items_list(Host, get_vh_rooms(Host), {get_disco_item, 0, From, Lang});
|
||||
iq_disco_items(Host, From, Lang, _MaxRoomsDiscoItems, _DiscoNode, Rsm) ->
|
||||
{Rooms, RsmO} = get_vh_rooms(Host, Rsm),
|
||||
RsmOut = jlib:rsm_encode(RsmO),
|
||||
iq_disco_items_list(Host, Rooms, {get_disco_item, all, From, Lang}) ++ RsmOut.
|
||||
-spec iq_disco_items(binary(), jid(), binary(), integer(), binary(),
|
||||
rsm_set() | undefined) ->
|
||||
{result, disco_items()} | {error, stanza_error()}.
|
||||
iq_disco_items(Host, From, Lang, MaxRoomsDiscoItems, Node, RSM)
|
||||
when Node == <<"">>; Node == <<"nonemptyrooms">>; Node == <<"emptyrooms">> ->
|
||||
Count = get_vh_rooms_count(Host),
|
||||
Query = if Node == <<"">>, RSM == undefined, Count > MaxRoomsDiscoItems ->
|
||||
{get_disco_item, only_non_empty, From, Lang};
|
||||
Node == <<"nonemptyrooms">> ->
|
||||
{get_disco_item, only_non_empty, From, Lang};
|
||||
Node == <<"emptyrooms">> ->
|
||||
{get_disco_item, 0, From, Lang};
|
||||
true ->
|
||||
{get_disco_item, all, From, Lang}
|
||||
end,
|
||||
Items = get_vh_rooms(Host, Query, RSM),
|
||||
ResRSM = case Items of
|
||||
[_|_] when RSM /= undefined ->
|
||||
#disco_item{jid = #jid{luser = First}} = hd(Items),
|
||||
#disco_item{jid = #jid{luser = Last}} = lists:last(Items),
|
||||
#rsm_set{first = #rsm_first{data = First},
|
||||
last = Last,
|
||||
count = Count};
|
||||
[] when RSM /= undefined ->
|
||||
#rsm_set{count = Count};
|
||||
_ ->
|
||||
undefined
|
||||
end,
|
||||
{result, #disco_items{node = Node, items = Items, rsm = ResRSM}};
|
||||
iq_disco_items(_Host, _From, Lang, _MaxRoomsDiscoItems, _Node, _RSM) ->
|
||||
{error, xmpp:err_item_not_found(<<"Node not found">>, Lang)}.
|
||||
|
||||
iq_disco_items_list(Host, Rooms, Query) ->
|
||||
lists:zf(
|
||||
fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) ->
|
||||
case catch gen_fsm:sync_send_all_state_event(Pid, Query, 100) of
|
||||
{item, Desc} ->
|
||||
flush(),
|
||||
{true, #disco_item{jid = jid:make(Name, Host),
|
||||
name = Desc}};
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end, Rooms).
|
||||
-spec get_vh_rooms(binary, term(), rsm_set() | undefined) -> [disco_item()].
|
||||
get_vh_rooms(Host, Query,
|
||||
#rsm_set{max = Max, 'after' = After, before = undefined})
|
||||
when is_binary(After), After /= <<"">> ->
|
||||
lists:reverse(get_vh_rooms(next, {After, Host}, Query, 0, Max, []));
|
||||
get_vh_rooms(Host, Query,
|
||||
#rsm_set{max = Max, 'after' = undefined, before = Before})
|
||||
when is_binary(Before), Before /= <<"">> ->
|
||||
get_vh_rooms(prev, {Before, Host}, Query, 0, Max, []);
|
||||
get_vh_rooms(Host, Query,
|
||||
#rsm_set{max = Max, 'after' = undefined, before = <<"">>}) ->
|
||||
get_vh_rooms(last, {<<"">>, Host}, Query, 0, Max, []);
|
||||
get_vh_rooms(Host, Query, #rsm_set{max = Max}) ->
|
||||
lists:reverse(get_vh_rooms(first, {<<"">>, Host}, Query, 0, Max, []));
|
||||
get_vh_rooms(Host, Query, undefined) ->
|
||||
lists:reverse(get_vh_rooms(first, {<<"">>, Host}, Query, 0, undefined, [])).
|
||||
|
||||
get_vh_rooms(_, _) ->
|
||||
todo.
|
||||
%% AllRooms = lists:sort(get_vh_rooms(Host)),
|
||||
%% Count = erlang:length(AllRooms),
|
||||
%% Guard = case Direction of
|
||||
%% _ when Index =/= undefined -> [{'==', {element, 2, '$1'}, Host}];
|
||||
%% aft -> [{'==', {element, 2, '$1'}, Host}, {'>=',{element, 1, '$1'} ,I}];
|
||||
%% before when I =/= []-> [{'==', {element, 2, '$1'}, Host}, {'=<',{element, 1, '$1'} ,I}];
|
||||
%% _ -> [{'==', {element, 2, '$1'}, Host}]
|
||||
%% end,
|
||||
%% L = lists:sort(
|
||||
%% mnesia:dirty_select(muc_online_room,
|
||||
%% [{#muc_online_room{name_host = '$1', _ = '_'},
|
||||
%% Guard,
|
||||
%% ['$_']}])),
|
||||
%% L2 = if
|
||||
%% Index == undefined andalso Direction == before ->
|
||||
%% lists:reverse(lists:sublist(lists:reverse(L), 1, M));
|
||||
%% Index == undefined ->
|
||||
%% lists:sublist(L, 1, M);
|
||||
%% Index > Count orelse Index < 0 ->
|
||||
%% [];
|
||||
%% true ->
|
||||
%% lists:sublist(L, Index+1, M)
|
||||
%% end,
|
||||
%% if L2 == [] -> {L2, #rsm_out{count = Count}};
|
||||
%% true ->
|
||||
%% H = hd(L2),
|
||||
%% NewIndex = get_room_pos(H, AllRooms),
|
||||
%% T = lists:last(L2),
|
||||
%% {F, _} = H#muc_online_room.name_host,
|
||||
%% {Last, _} = T#muc_online_room.name_host,
|
||||
%% {L2,
|
||||
%% #rsm_out{first = F, last = Last, count = Count,
|
||||
%% index = NewIndex}}
|
||||
%% end.
|
||||
-spec get_vh_rooms(prev | next | last | first,
|
||||
{binary(), binary()}, term(),
|
||||
non_neg_integer(), non_neg_integer() | undefined,
|
||||
[disco_item()]) -> [disco_item()].
|
||||
get_vh_rooms(_Action, _Key, _Query, Count, Max, Items) when Count >= Max ->
|
||||
Items;
|
||||
get_vh_rooms(Action, {_, Host} = Key, Query, Count, Max, Items) ->
|
||||
Call = fun() ->
|
||||
case Action of
|
||||
prev -> mnesia:dirty_prev(muc_online_room, Key);
|
||||
next -> mnesia:dirty_next(muc_online_room, Key);
|
||||
last -> mnesia:dirty_last(muc_online_room);
|
||||
first -> mnesia:dirty_first(muc_online_room)
|
||||
end
|
||||
end,
|
||||
NewAction = case Action of
|
||||
last -> prev;
|
||||
first -> next;
|
||||
_ -> Action
|
||||
end,
|
||||
try Call() of
|
||||
'$end_of_table' ->
|
||||
Items;
|
||||
{_, Host} = NewKey ->
|
||||
case get_room_disco_item(NewKey, Query) of
|
||||
{ok, Item} ->
|
||||
get_vh_rooms(NewAction, NewKey, Query,
|
||||
Count + 1, Max, [Item|Items]);
|
||||
{error, _} ->
|
||||
get_vh_rooms(NewAction, NewKey, Query,
|
||||
Count, Max, Items)
|
||||
end;
|
||||
NewKey ->
|
||||
get_vh_rooms(NewAction, NewKey, Query, Count, Max, Items)
|
||||
catch _:{aborted, {badarg, _}} ->
|
||||
Items
|
||||
end.
|
||||
|
||||
-spec get_room_disco_item({binary(), binary()}, term()) -> {ok, disco_item()} |
|
||||
{error, timeout | notfound}.
|
||||
get_room_disco_item({Name, Host}, Query) ->
|
||||
case mnesia:dirty_read(muc_online_room, {Name, Host}) of
|
||||
[#muc_online_room{pid = Pid}|_] ->
|
||||
RoomJID = jid:make(Name, Host),
|
||||
try gen_fsm:sync_send_all_state_event(Pid, Query, 100) of
|
||||
{item, Desc} ->
|
||||
{ok, #disco_item{jid = RoomJID, name = Desc}};
|
||||
false ->
|
||||
{error, notfound}
|
||||
catch _:{timeout, _} ->
|
||||
{error, timeout};
|
||||
_:{noproc, _} ->
|
||||
{error, notfound}
|
||||
end;
|
||||
_ ->
|
||||
{error, notfound}
|
||||
end.
|
||||
|
||||
get_subscribed_rooms(_ServerHost, Host, From) ->
|
||||
Rooms = get_vh_rooms(Host),
|
||||
@ -681,21 +723,6 @@ get_subscribed_rooms(_ServerHost, Host, From) ->
|
||||
[]
|
||||
end, Rooms).
|
||||
|
||||
%% @doc Return the position of desired room in the list of rooms.
|
||||
%% The room must exist in the list. The count starts in 0.
|
||||
%% @spec (Desired::muc_online_room(), Rooms::[muc_online_room()]) -> integer()
|
||||
get_room_pos(Desired, Rooms) ->
|
||||
get_room_pos(Desired, Rooms, 0).
|
||||
|
||||
get_room_pos(Desired, [HeadRoom | _], HeadPosition)
|
||||
when Desired#muc_online_room.name_host ==
|
||||
HeadRoom#muc_online_room.name_host ->
|
||||
HeadPosition;
|
||||
get_room_pos(Desired, [_ | Rooms], HeadPosition) ->
|
||||
get_room_pos(Desired, Rooms, HeadPosition + 1).
|
||||
|
||||
flush() -> receive _ -> flush() after 0 -> ok end.
|
||||
|
||||
get_nick(ServerHost, Host, From) ->
|
||||
LServer = jid:nameprep(ServerHost),
|
||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||
@ -782,6 +809,13 @@ get_vh_rooms(Host) ->
|
||||
[{'==', {element, 2, '$1'}, Host}],
|
||||
['$_']}]).
|
||||
|
||||
-spec get_vh_rooms_count(binary()) -> non_neg_integer().
|
||||
get_vh_rooms_count(Host) ->
|
||||
ets:select_count(muc_online_room,
|
||||
ets:fun2ms(
|
||||
fun(#muc_online_room{name_host = {_, H}}) ->
|
||||
H == Host
|
||||
end)).
|
||||
|
||||
clean_table_from_bad_node(Node) ->
|
||||
F = fun() ->
|
||||
@ -811,6 +845,23 @@ clean_table_from_bad_node(Node, Host) ->
|
||||
end,
|
||||
mnesia:async_dirty(F).
|
||||
|
||||
update_tables() ->
|
||||
try
|
||||
case mnesia:table_info(muc_online_room, type) of
|
||||
ordered_set -> ok;
|
||||
_ ->
|
||||
case mnesia:delete_table(muc_online_room) of
|
||||
{atomic, ok} -> ok;
|
||||
Err -> erlang:error(Err)
|
||||
end
|
||||
end
|
||||
catch _:{aborted, {no_exists, muc_online_room}} -> ok;
|
||||
_:{aborted, {no_exists, muc_online_room, type}} -> ok;
|
||||
E:R ->
|
||||
?ERROR_MSG("failed to update mnesia table '~s': ~p",
|
||||
[muc_online_room, {E, R}])
|
||||
end.
|
||||
|
||||
opts_to_binary(Opts) ->
|
||||
lists:map(
|
||||
fun({title, Title}) ->
|
||||
|
@ -504,8 +504,7 @@ handle_event(_Event, StateName, StateData) ->
|
||||
{next_state, StateName, StateData}.
|
||||
|
||||
handle_sync_event({get_disco_item, Filter, JID, Lang}, _From, StateName, StateData) ->
|
||||
Len = ?DICT:fold(fun(_, _, Acc) -> Acc + 1 end, 0,
|
||||
StateData#state.users),
|
||||
Len = ?DICT:size(StateData#state.users),
|
||||
Reply = case (Filter == all) or (Filter == Len) or ((Filter /= 0) and (Len /= 0)) of
|
||||
true ->
|
||||
get_roomdesc_reply(JID, StateData,
|
||||
|
Loading…
Reference in New Issue
Block a user