]> granicus.if.org Git - ejabberd/commitdiff
* src/jlib.erl: Implementation of XEP-0059 Result Set
authorBadlop <badlop@process-one.net>
Tue, 23 Dec 2008 13:04:42 +0000 (13:04 +0000)
committerBadlop <badlop@process-one.net>
Tue, 23 Dec 2008 13:04:42 +0000 (13:04 +0000)
Management (thanks to Eric Cestari)(EJAB-807)
* src/jlib.hrl: Likewise
* src/mod_muc/mod_muc.erl: Likewise

SVN Revision: 1750

ChangeLog
src/jlib.erl
src/jlib.hrl
src/mod_muc/mod_muc.erl

index 6d8a3952332e7510962192bce94896e51d840457..f92551ad13fefbf14eb2821e00616992623a354b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2008-12-23  Badlop  <badlop@process-one.net>
+
+       * 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 <christophe.romain@process-one.net>
 
        * src/mod_pubsub/mod_pubsub.erl: Improve handling of PEP sent to
index 765457a5bc34af1885f7cf2d9510bfa77e19c1a7..5bfaa48b2b8ce16d97ee77fa0e36ecfabcd2fd5a 100644 (file)
         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(
index 39731b14a9c7deb09fb7ed4e699adb68a015cee8..2399f3c48952b8392b84f1733833043c877fba6c 100644 (file)
@@ -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").
             lang = "",
             sub_el}).
 
+-record(rsm_in, {max, direction, id, index}).
+-record(rsm_out, {count, index, first, last}).
index 880540bef44fa925c9371ac13f99a0f6dcce007f..022610a2735cf4a223f2de960b4aa00a39685171 100644 (file)
@@ -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