From 35b1e2885e0448a3c96bc06941e27f66bc49497e Mon Sep 17 00:00:00 2001 From: Badlop Date: Tue, 23 Dec 2008 13:04:42 +0000 Subject: [PATCH] * src/jlib.erl: Implementation of XEP-0059 Result Set Management (thanks to Eric Cestari)(EJAB-807) * src/jlib.hrl: Likewise * src/mod_muc/mod_muc.erl: Likewise SVN Revision: 1750 --- ChangeLog | 7 ++++ src/jlib.erl | 70 ++++++++++++++++++++++++++++++++++++++- src/jlib.hrl | 3 ++ src/mod_muc/mod_muc.erl | 73 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 149 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6d8a39523..f92551ad1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2008-12-23 Badlop + + * src/jlib.erl: Implementation of XEP-0059 Result Set + Management (thanks to Eric Cestari)(EJAB-807) + * src/jlib.hrl: Likewise + * src/mod_muc/mod_muc.erl: Likewise + 2008-12-23 Christophe Romain * src/mod_pubsub/mod_pubsub.erl: Improve handling of PEP sent to diff --git a/src/jlib.erl b/src/jlib.erl index 765457a5b..5bfaa48b2 100644 --- a/src/jlib.erl +++ b/src/jlib.erl @@ -62,7 +62,10 @@ datetime_string_to_timestamp/1, decode_base64/1, encode_base64/1, - ip_to_list/1]). + ip_to_list/1, + rsm_encode/1, + rsm_encode/2, + rsm_decode/1]). -include("jlib.hrl"). @@ -484,6 +487,71 @@ parse_xdata_values([{xmlelement, Name, _Attrs, SubEls} | Els], Res) -> parse_xdata_values([_ | Els], Res) -> parse_xdata_values(Els, Res). +rsm_decode(#iq{sub_el=SubEl})-> + rsm_decode(SubEl); +rsm_decode({xmlelement, _,_,_}=SubEl)-> + case xml:get_subtag(SubEl,"set") of + false -> + none; + {xmlelement, "set", _Attrs, SubEls}-> + lists:foldl(fun rsm_parse_element/2, #rsm_in{}, SubEls) + end. + +rsm_parse_element({xmlelement, "max",[], _}=Elem, RsmIn)-> + CountStr = xml:get_tag_cdata(Elem), + {Count, _} = string:to_integer(CountStr), + RsmIn#rsm_in{max=Count}; + +rsm_parse_element({xmlelement, "before", [], _}=Elem, RsmIn)-> + UID = xml:get_tag_cdata(Elem), + RsmIn#rsm_in{direction=before, id=UID}; + +rsm_parse_element({xmlelement, "after", [], _}=Elem, RsmIn)-> + UID = xml:get_tag_cdata(Elem), + RsmIn#rsm_in{direction=aft, id=UID}; + +rsm_parse_element({xmlelement, "index",[], _}=Elem, RsmIn)-> + IndexStr = xml:get_tag_cdata(Elem), + {Index, _} = string:to_integer(IndexStr), + RsmIn#rsm_in{index=Index}; + + +rsm_parse_element(_, RsmIn)-> + RsmIn. + +rsm_encode(#iq{sub_el=SubEl}=IQ,RsmOut)-> + Set = {xmlelement, "set", [{"xmlns", ?NS_RSM}], + lists:reverse(rsm_encode_out(RsmOut))}, + {xmlelement, Name, Attrs, SubEls} = SubEl, + New = {xmlelement, Name, Attrs, [Set | SubEls]}, + IQ#iq{sub_el=New}. + +rsm_encode(none)-> + []; +rsm_encode(RsmOut)-> + [{xmlelement, "set", [{"xmlns", ?NS_RSM}], lists:reverse(rsm_encode_out(RsmOut))}]. +rsm_encode_out(#rsm_out{count=Count, index=Index, first=First, last=Last})-> + El = rsm_encode_first(First, Index, []), + El2 = rsm_encode_last(Last,El), + rsm_encode_count(Count, El2). + +rsm_encode_first(undefined, undefined, Arr) -> + Arr; +rsm_encode_first(First, undefined, Arr) -> + [{xmlelement, "first",[], [{xmlcdata, First}]}|Arr]; +rsm_encode_first(First, Index, Arr) -> + [{xmlelement, "first",[{"index", i2l(Index)}], [{xmlcdata, First}]}|Arr]. + +rsm_encode_last(undefined, Arr) -> Arr; +rsm_encode_last(Last, Arr) -> + [{xmlelement, "last",[], [{xmlcdata, Last}]}|Arr]. + +rsm_encode_count(undefined, Arr)-> Arr; +rsm_encode_count(Count, Arr)-> + [{xmlelement, "count",[], [{xmlcdata, i2l(Count)}]} | Arr]. + +i2l(I) when is_integer(I) -> integer_to_list(I); +i2l(L) when is_list(L) -> L. timestamp_to_iso({{Year, Month, Day}, {Hour, Minute, Second}}) -> lists:flatten( diff --git a/src/jlib.hrl b/src/jlib.hrl index 39731b14a..2399f3c48 100644 --- a/src/jlib.hrl +++ b/src/jlib.hrl @@ -54,6 +54,7 @@ -define(NS_BYTESTREAMS, "http://jabber.org/protocol/bytestreams"). -define(NS_ADMIN, "http://jabber.org/protocol/admin"). +-define(NS_RSM, "http://jabber.org/protocol/rsm"). -define(NS_EJABBERD_CONFIG, "ejabberd:config"). -define(NS_STREAM, "http://etherx.jabber.org/streams"). @@ -306,3 +307,5 @@ lang = "", sub_el}). +-record(rsm_in, {max, direction, id, index}). +-record(rsm_out, {count, index, first, last}). diff --git a/src/mod_muc/mod_muc.erl b/src/mod_muc/mod_muc.erl index 880540bef..022610a27 100644 --- a/src/mod_muc/mod_muc.erl +++ b/src/mod_muc/mod_muc.erl @@ -124,10 +124,11 @@ forget_room(Host, Name) -> mnesia:transaction(F). process_iq_disco_items(Host, From, To, #iq{lang = Lang} = IQ) -> + Rsm = jlib:rsm_decode(IQ), Res = IQ#iq{type = result, sub_el = [{xmlelement, "query", [{"xmlns", ?NS_DISCO_ITEMS}], - iq_disco_items(Host, From, Lang)}]}, + iq_disco_items(Host, From, Lang, Rsm)}]}, ejabberd_router:route(To, From, jlib:iq_to_xml(Res)). @@ -512,10 +513,11 @@ iq_disco_info(Lang) -> {xmlelement, "feature", [{"var", ?NS_DISCO_ITEMS}], []}, {xmlelement, "feature", [{"var", ?NS_MUC}], []}, {xmlelement, "feature", [{"var", ?NS_REGISTER}], []}, + {xmlelement, "feature", [{"var", ?NS_RSM}], []}, {xmlelement, "feature", [{"var", ?NS_VCARD}], []}]. -iq_disco_items(Host, From, Lang) -> +iq_disco_items(Host, From, Lang, none) -> lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) -> case catch gen_fsm:sync_send_all_state_event( Pid, {get_disco_item, From, Lang}, 100) of @@ -528,7 +530,72 @@ iq_disco_items(Host, From, Lang) -> _ -> false end - end, get_vh_rooms(Host)). + end, get_vh_rooms(Host)); + +iq_disco_items(Host, From, Lang, Rsm) -> + {Rooms, RsmO} = get_vh_rooms(Host, Rsm), + RsmOut = jlib:rsm_encode(RsmO), + lists:zf(fun(#muc_online_room{name_host = {Name, _Host}, pid = Pid}) -> + case catch gen_fsm:sync_send_all_state_event( + Pid, {get_disco_item, From, Lang}, 100) of + {item, Desc} -> + flush(), + {true, + {xmlelement, "item", + [{"jid", jlib:jid_to_string({Name, Host, ""})}, + {"name", Desc}], []}}; + _ -> + false + end + end, Rooms) ++ RsmOut. + +get_vh_rooms(Host, #rsm_in{max=M, direction=Direction, id=I, index=Index})-> + 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. + +%% @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