]> granicus.if.org Git - ejabberd/commitdiff
Improve MUC test cases
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Sat, 19 Jul 2014 13:23:13 +0000 (17:23 +0400)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Sat, 19 Jul 2014 13:30:02 +0000 (17:30 +0400)
test/ejabberd_SUITE.erl
test/suite.erl
tools/xmpp_codec.erl
tools/xmpp_codec.hrl
tools/xmpp_codec.spec

index a504acfc568d149b4a9263653276d9fc771f8af1..5151f728d26ab87153f6bc46741e9edea3dbeabd 100644 (file)
@@ -177,7 +177,6 @@ db_tests() ->
        privacy,
        blocking,
        vcard,
-       muc_single,
        pubsub,
        test_unregister]},
      {test_roster_subscribe, [parallel],
@@ -185,6 +184,8 @@ db_tests() ->
        roster_subscribe_slave]},
      {test_offline, [sequence],
       [offline_master, offline_slave]},
+     {test_muc, [parallel],
+      [muc_master, muc_slave]},
      {test_roster_remove, [parallel],
       [roster_remove_master,
        roster_remove_slave]}].
@@ -202,13 +203,14 @@ db_tests(riak) ->
        privacy,
        blocking,
        vcard,
-       muc_single,
        test_unregister]},
      {test_roster_subscribe, [parallel],
       [roster_subscribe_master,
        roster_subscribe_slave]},
      {test_offline, [sequence],
       [offline_master, offline_slave]},
+     {test_muc, [parallel],
+      [muc_master, muc_slave]},
      {test_roster_remove, [parallel],
       [roster_remove_master,
        roster_remove_slave]}];
@@ -225,7 +227,6 @@ db_tests(_) ->
        privacy,
        blocking,
        vcard,
-       muc_single,
        pubsub,
        test_unregister]},
      {test_roster_subscribe, [parallel],
@@ -233,6 +234,8 @@ db_tests(_) ->
        roster_subscribe_slave]},
      {test_offline, [sequence],
       [offline_master, offline_slave]},
+     {test_muc, [parallel],
+      [muc_master, muc_slave]},
      {test_roster_remove, [parallel],
       [roster_remove_master,
        roster_remove_slave]}].
@@ -916,15 +919,22 @@ proxy65_slave(Config) ->
     socks5_recv(Socks5, Data),
     disconnect(Config).
 
-muc_single(Config) ->
+muc_master(Config) ->
     MyJID = my_jid(Config),
+    PeerJID = ?config(slave, Config),
+    PeerBareJID = jlib:jid_remove_resource(PeerJID),
+    PeerJIDStr = jlib:jid_to_string(PeerJID),
     MUC = muc_jid(Config),
     Room = muc_room_jid(Config),
-    Nick = ?config(user, Config),
-    NickJID = jlib:jid_replace_resource(Room, Nick),
+    MyNick = ?config(master_nick, Config),
+    MyNickJID = jlib:jid_replace_resource(Room, MyNick),
+    PeerNick = ?config(slave_nick, Config),
+    PeerNickJID = jlib:jid_replace_resource(Room, PeerNick),
+    Subject = ?config(room_subject, Config),
+    Localhost = jlib:make_jid(<<"">>, <<"localhost">>, <<"">>),
     true = is_feature_advertised(Config, ?NS_MUC, MUC),
     %% Joining
-    send(Config, #presence{to = NickJID, sub_els = [#muc{}]}),
+    send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}),
     %% As per XEP-0045 we MUST receive stanzas in the following order:
     %% 1. In-room presence from other occupants
     %% 2. In-room presence from the joining entity itself (so-called "self-presence")
@@ -933,7 +943,7 @@ muc_single(Config) ->
     %% 5. Live messages, presence updates, new user joins, etc.
     %% As this is the newly created room, we receive only the 2nd stanza.
     #presence{
-          from = NickJID,
+          from = MyNickJID,
           sub_els = [#muc_user{
                         status_codes = Codes,
                         items = [#muc_item{role = moderator,
@@ -941,8 +951,7 @@ muc_single(Config) ->
                                            affiliation = owner}]}]} = recv(),
     %% 110 -> Inform user that presence refers to itself
     %% 201 -> Inform user that a new room has been created
-    true = lists:member(110, Codes),
-    true = lists:member(201, Codes),
+    [110, 201] = lists:sort(Codes),
     %% Request the configuration
     #iq{type = result, sub_els = [#muc_owner{config = #xdata{} = RoomCfg}]} =
         send_recv(Config, #iq{type = get, sub_els = [#muc_owner{}],
@@ -959,10 +968,14 @@ muc_single(Config) ->
                                  [<<"Trying to break the server">>];
                              <<"muc#roomconfig_persistentroom">> ->
                                  [<<"1">>];
-                             <<"muc#roomconfig_changesubject">> ->
-                                 [<<"0">>];
-                             <<"muc#roomconfig_allowinvites">> ->
-                                 [<<"1">>];
+                            <<"members_by_default">> ->
+                                [<<"0">>];
+                            <<"muc#roomconfig_allowvoicerequests">> ->
+                                [<<"1">>];
+                            <<"public_list">> ->
+                                [<<"1">>];
+                            <<"muc#roomconfig_publicroom">> ->
+                                [<<"1">>];
                              _ ->
                                  []
                          end,
@@ -974,20 +987,230 @@ muc_single(Config) ->
           end, RoomCfg#xdata.fields),
     NewRoomCfg = #xdata{type = submit, fields = NewFields},
     %% BUG: We should not receive any sub_els!
-    %% TODO: fix this crap in ejabberd.
     #iq{type = result, sub_els = [_|_]} =
         send_recv(Config, #iq{type = set, to = Room,
                               sub_els = [#muc_owner{config = NewRoomCfg}]}),
     %% Set subject
     send(Config, #message{to = Room, type = groupchat,
-                          body = [#text{data = <<"Subject">>}]}),
-    #message{from = NickJID, type = groupchat,
-             body = [#text{data = <<"Subject">>}]} = recv(),
-    %% Leaving
-    send(Config, #presence{type = unavailable, to = NickJID}),
-    #presence{from = NickJID, type = unavailable,
-              sub_els = [#muc_user{status_codes = NewCodes}]} = recv(),
-    true = lists:member(110, NewCodes),
+                          body = [#text{data = Subject}]}),
+    #message{from = MyNickJID, type = groupchat,
+             body = [#text{data = Subject}]} = recv(),
+    %% Sending messages (and thus, populating history for our peer)
+    lists:foreach(
+      fun(N) ->
+              Text = #text{data = jlib:integer_to_binary(N)},
+              I = send(Config, #message{to = Room, body = [Text],
+                                       type = groupchat}),
+             #message{from = MyNickJID, id = I,
+                      type = groupchat,
+                      body = [Text]} = recv()
+      end, lists:seq(1, 5)),
+    %% Inviting the peer
+    send(Config, #message{to = Room, type = normal,
+                         sub_els =
+                             [#muc_user{
+                                 invites =
+                                     [#muc_invite{to = PeerJID}]}]}),
+    %% Peer is joining
+    #presence{from = PeerNickJID,
+             sub_els = [#muc_user{
+                           items = [#muc_item{role = visitor,
+                                              jid = PeerJID,
+                                              affiliation = none}]}]} = recv(),
+    %% Receiving a voice request
+    #message{from = Room,
+            sub_els = [#xdata{type = form,
+                              instructions = [_],
+                              fields = VoiceReqFs}]} = recv(),
+    %% Approving the voice request
+    ReplyVoiceReqFs =
+       lists:map(
+         fun(#xdata_field{var = Var, values = OrigVals}) ->
+                  Vals = case {Var, OrigVals} of
+                            {<<"FORM_TYPE">>,
+                             [<<"http://jabber.org/protocol/muc#request">>]} ->
+                                OrigVals;
+                            {<<"muc#role">>, [<<"participant">>]} ->
+                                [<<"participant">>];
+                            {<<"muc#jid">>, [PeerJIDStr]} ->
+                                [PeerJIDStr];
+                            {<<"muc#roomnick">>, [PeerNick]} ->
+                                [PeerNick];
+                            {<<"muc#request_allow">>, [<<"0">>]} ->
+                                [<<"1">>]
+                        end,
+                 #xdata_field{values = Vals, var = Var}
+         end, VoiceReqFs),
+    send(Config, #message{to = Room,
+                         sub_els = [#xdata{type = submit,
+                                           fields = ReplyVoiceReqFs}]}),
+    %% Peer is becoming a participant
+    #presence{from = PeerNickJID,
+             sub_els = [#muc_user{
+                           items = [#muc_item{role = participant,
+                                              jid = PeerJID,
+                                              affiliation = none}]}]} = recv(),
+    %% Receive private message from the peer
+    #message{from = PeerNickJID, body = [#text{data = Subject}]} = recv(),
+    %% Granting membership to the peer and localhost server
+    I1 = send(Config,
+             #iq{type = set, to = Room,
+                 sub_els =
+                     [#muc_admin{
+                         items = [#muc_item{jid = Localhost,
+                                            affiliation = member},
+                                  #muc_item{nick = PeerNick,
+                                            jid = PeerBareJID,
+                                            affiliation = member}]}]}),
+    %% Peer became a member
+    #presence{from = PeerNickJID,
+             sub_els = [#muc_user{
+                           items = [#muc_item{affiliation = member,
+                                              jid = PeerJID,
+                                              role = participant}]}]} = recv(),
+    %% BUG: We should not receive any sub_els!
+    #iq{type = result, id = I1, sub_els = [_|_]} = recv(),
+    %% Receive groupchat message from the peer
+    #message{type = groupchat, from = PeerNickJID,
+            body = [#text{data = Subject}]} = recv(),
+    %% Kick the peer
+    I2 = send(Config,
+             #iq{type = set, to = Room,
+                 sub_els = [#muc_admin{
+                               items = [#muc_item{nick = PeerNick,
+                                                  role = none}]}]}),
+    %% Got notification the peer is kicked
+    %% 307 -> Inform user that he or she has been kicked from the room
+    #presence{from = PeerNickJID,
+             sub_els = [#muc_user{
+                           status_codes = [307],
+                           items = [#muc_item{affiliation = member,
+                                              jid = PeerJID,
+                                              role = none}]}]} = recv(),
+    %% BUG: We should not receive any sub_els!
+    #iq{type = result, id = I2, sub_els = [_|_]} = recv(),
+    %% Destroying the room
+    I3 = send(Config,
+             #iq{type = set, to = Room,
+                 sub_els = [#muc_owner{
+                               destroy = #muc_owner_destroy{
+                                            reason = Subject}}]}),
+    %% Kicked off
+    #presence{from = MyNickJID, type = unavailable,
+              sub_els = [#muc_user{items = [#muc_item{role = none,
+                                                     affiliation = none}],
+                                  destroy = #muc_user_destroy{
+                                               reason = Subject}}]} = recv(),
+    %% BUG: We should not receive any sub_els!
+    #iq{type = result, id = I3, sub_els = [_|_]} = recv(),
+    disconnect(Config).
+
+muc_slave(Config) ->
+    MyJID = my_jid(Config),
+    MyBareJID = jlib:jid_remove_resource(MyJID),
+    PeerJID = ?config(master, Config),
+    MUC = muc_jid(Config),
+    Room = muc_room_jid(Config),
+    MyNick = ?config(slave_nick, Config),
+    MyNickJID = jlib:jid_replace_resource(Room, MyNick),
+    PeerNick = ?config(master_nick, Config),
+    PeerNickJID = jlib:jid_replace_resource(Room, PeerNick),
+    Subject = ?config(room_subject, Config),
+    Localhost = jlib:make_jid(<<"">>, <<"localhost">>, <<"">>),
+    %% Receive an invite from the peer
+    #message{from = Room, type = normal,
+            sub_els =
+                [#muc_user{invites =
+                               [#muc_invite{from = PeerJID}]}]} = recv(),
+    %% But before joining we discover the MUC service first
+    %% to check if the room is in the disco list
+    #iq{type = result,
+       sub_els = [#disco_items{items = [#disco_item{jid = Room}]}]} =
+       send_recv(Config, #iq{type = get, to = MUC,
+                             sub_els = [#disco_items{}]}),
+    %% Now check if the peer is in the room. We check this via disco#items
+    #iq{type = result,
+       sub_els = [#disco_items{items = [#disco_item{jid = PeerNickJID,
+                                                    name = PeerNick}]}]} =
+       send_recv(Config, #iq{type = get, to = Room,
+                             sub_els = [#disco_items{}]}),
+    %% Now joining
+    send(Config, #presence{to = MyNickJID, sub_els = [#muc{}]}),
+    %% First presence is from the participant, i.e. from the peer
+    #presence{
+       from = PeerNickJID,
+       sub_els = [#muc_user{
+                    status_codes = [],
+                    items = [#muc_item{role = moderator,
+                                       affiliation = owner}]}]} = recv(),
+    %% The next is the self-presence (code 110 means it)
+    #presence{
+       from = MyNickJID,
+       sub_els = [#muc_user{
+                    status_codes = [110],
+                    items = [#muc_item{role = visitor,
+                                       affiliation = none}]}]} = recv(),
+    %% Receive the room subject
+    #message{from = PeerNickJID, type = groupchat,
+             body = [#text{data = Subject}],
+            sub_els = [#delay{}, #legacy_delay{}]} = recv(),
+    %% Receive MUC history
+    lists:foreach(
+      fun(N) ->
+              Text = #text{data = jlib:integer_to_binary(N)},
+             #message{from = PeerNickJID,
+                      type = groupchat,
+                      body = [Text],
+                      sub_els = [#delay{}, #legacy_delay{}]} = recv()
+      end, lists:seq(1, 5)),
+    %% Sending a voice request
+    VoiceReq = #xdata{
+                 type = submit,
+                 fields =
+                     [#xdata_field{
+                         var = <<"FORM_TYPE">>,
+                         values = [<<"http://jabber.org/protocol/muc#request">>]},
+                      #xdata_field{
+                         var = <<"muc#role">>,
+                         type = 'text-single',
+                         values = [<<"participant">>]}]},
+    send(Config, #message{to = Room, sub_els = [VoiceReq]}),
+    %% Becoming a participant
+    #presence{from = MyNickJID,
+             sub_els = [#muc_user{
+                           items = [#muc_item{role = participant,
+                                              affiliation = none}]}]} = recv(),
+    %% Sending private message to the peer
+    send(Config, #message{to = PeerNickJID,
+                         body = [#text{data = Subject}]}),
+    %% Becoming a member
+    #presence{from = MyNickJID,
+             sub_els = [#muc_user{
+                           items = [#muc_item{role = participant,
+                                              affiliation = member}]}]} = recv(),
+    %% Retrieving a member list
+    #iq{type = result, sub_els = [#muc_admin{items = MemberList}]} =
+       send_recv(Config,
+                 #iq{type = get, to = Room,
+                     sub_els =
+                         [#muc_admin{items = [#muc_item{affiliation = member}]}]}),
+    [#muc_item{affiliation = member,
+              jid = Localhost},
+     #muc_item{affiliation = member,
+              jid = MyBareJID}] = lists:keysort(#muc_item.jid, MemberList),
+    %% Sending groupchat message
+    send(Config, #message{to = Room, type = groupchat,
+                         body = [#text{data = Subject}]}),
+    %% Receive this message back
+    #message{type = groupchat, from = MyNickJID,
+            body = [#text{data = Subject}]} = recv(),
+    %% We're kicked off
+    %% 307 -> Inform user that he or she has been kicked from the room
+    #presence{from = MyNickJID, type = unavailable,
+             sub_els = [#muc_user{
+                           status_codes = [307],
+                           items = [#muc_item{affiliation = member,
+                                              role = none}]}]} = recv(),
     disconnect(Config).
 
 offline_master(Config) ->
index c35592c80aa0091c652d5c690f74d5ddb1ca1f1a..3f343215948a74b6fc53304a6212d20196775c74 100644 (file)
@@ -39,6 +39,9 @@ init_config(Config) ->
      {server_host, "localhost"},
      {server, ?COMMON_VHOST},
      {user, <<"test_single">>},
+     {master_nick, <<"master_nick">>},
+     {slave_nick, <<"slave_nick">>},
+     {room_subject, <<"hello, world!">>},
      {certfile, CertFile},
      {base_dir, BaseDir},
      {resource, <<"resource">>},
index 6cfcc7a22e23b624a1a6e42350394d748c48a9ff..9ffa163548af838270ee674d6d9882cb81ae3e8d 100644 (file)
@@ -14,6 +14,21 @@ decode({xmlel, _name, _attrs, _} = _el) ->
     case {_name, get_attr(<<"xmlns">>, _attrs)} of
       {<<"x">>, <<"http://jabber.org/protocol/muc">>} ->
          decode_muc(_el);
+      {<<"query">>,
+       <<"http://jabber.org/protocol/muc#admin">>} ->
+         decode_muc_admin(_el);
+      {<<"reason">>,
+       <<"http://jabber.org/protocol/muc#admin">>} ->
+         decode_muc_admin_reason(_el);
+      {<<"continue">>,
+       <<"http://jabber.org/protocol/muc#admin">>} ->
+         decode_muc_admin_continue(_el);
+      {<<"actor">>,
+       <<"http://jabber.org/protocol/muc#admin">>} ->
+         decode_muc_admin_actor(_el);
+      {<<"item">>,
+       <<"http://jabber.org/protocol/muc#admin">>} ->
+         decode_muc_admin_item(_el);
       {<<"query">>,
        <<"http://jabber.org/protocol/muc#owner">>} ->
          decode_muc_owner(_el);
@@ -691,6 +706,21 @@ decode({xmlel, _name, _attrs, _} = _el) ->
 is_known_tag({xmlel, _name, _attrs, _} = _el) ->
     case {_name, get_attr(<<"xmlns">>, _attrs)} of
       {<<"x">>, <<"http://jabber.org/protocol/muc">>} -> true;
+      {<<"query">>,
+       <<"http://jabber.org/protocol/muc#admin">>} ->
+         true;
+      {<<"reason">>,
+       <<"http://jabber.org/protocol/muc#admin">>} ->
+         true;
+      {<<"continue">>,
+       <<"http://jabber.org/protocol/muc#admin">>} ->
+         true;
+      {<<"actor">>,
+       <<"http://jabber.org/protocol/muc#admin">>} ->
+         true;
+      {<<"item">>,
+       <<"http://jabber.org/protocol/muc#admin">>} ->
+         true;
       {<<"query">>,
        <<"http://jabber.org/protocol/muc#owner">>} ->
          true;
@@ -1240,6 +1270,10 @@ is_known_tag({xmlel, _name, _attrs, _} = _el) ->
 encode({muc, _, _} = X) ->
     encode_muc(X,
               [{<<"xmlns">>, <<"http://jabber.org/protocol/muc">>}]);
+encode({muc_admin, _} = Query) ->
+    encode_muc_admin(Query,
+                    [{<<"xmlns">>,
+                      <<"http://jabber.org/protocol/muc#admin">>}]);
 encode({muc_owner, _, _} = Query) ->
     encode_muc_owner(Query,
                     [{<<"xmlns">>,
@@ -1252,14 +1286,6 @@ encode({muc_user, _, _, _, _, _, _} = X) ->
     encode_muc_user(X,
                    [{<<"xmlns">>,
                      <<"http://jabber.org/protocol/muc#user">>}]);
-encode({muc_item, _, _, _, _, _, _, _} = Item) ->
-    encode_muc_user_item(Item,
-                        [{<<"xmlns">>,
-                          <<"http://jabber.org/protocol/muc#user">>}]);
-encode({muc_actor, _, _} = Actor) ->
-    encode_muc_user_actor(Actor,
-                         [{<<"xmlns">>,
-                           <<"http://jabber.org/protocol/muc#user">>}]);
 encode({muc_invite, _, _, _} = Invite) ->
     encode_muc_user_invite(Invite,
                           [{<<"xmlns">>,
@@ -1742,14 +1768,15 @@ pp(muc_history, 4) ->
 pp(muc_decline, 3) -> [reason, from, to];
 pp(muc_user_destroy, 2) -> [reason, jid];
 pp(muc_invite, 3) -> [reason, from, to];
-pp(muc_actor, 2) -> [jid, nick];
-pp(muc_item, 7) ->
-    [actor, continue, reason, affiliation, role, jid, nick];
 pp(muc_user, 6) ->
     [decline, destroy, invites, items, status_codes,
      password];
 pp(muc_owner_destroy, 3) -> [jid, reason, password];
 pp(muc_owner, 2) -> [destroy, config];
+pp(muc_item, 7) ->
+    [actor, continue, reason, affiliation, role, jid, nick];
+pp(muc_actor, 2) -> [jid, nick];
+pp(muc_admin, 1) -> [items];
 pp(muc, 2) -> [history, password];
 pp(_, _) -> no.
 
@@ -1834,6 +1861,319 @@ encode_muc_attr_password(undefined, _acc) -> _acc;
 encode_muc_attr_password(_val, _acc) ->
     [{<<"password">>, _val} | _acc].
 
+decode_muc_admin({xmlel, <<"query">>, _attrs, _els}) ->
+    Items = decode_muc_admin_els(_els, []),
+    {muc_admin, Items}.
+
+decode_muc_admin_els([], Items) -> lists:reverse(Items);
+decode_muc_admin_els([{xmlel, <<"item">>, _attrs, _} =
+                         _el
+                     | _els],
+                    Items) ->
+    _xmlns = get_attr(<<"xmlns">>, _attrs),
+    if _xmlns == <<>>;
+       _xmlns == <<"http://jabber.org/protocol/muc#admin">> ->
+          decode_muc_admin_els(_els,
+                               [decode_muc_admin_item(_el) | Items]);
+       true -> decode_muc_admin_els(_els, Items)
+    end;
+decode_muc_admin_els([_ | _els], Items) ->
+    decode_muc_admin_els(_els, Items).
+
+encode_muc_admin({muc_admin, Items}, _xmlns_attrs) ->
+    _els = 'encode_muc_admin_$items'(Items, []),
+    _attrs = _xmlns_attrs,
+    {xmlel, <<"query">>, _attrs, _els}.
+
+'encode_muc_admin_$items'([], _acc) -> _acc;
+'encode_muc_admin_$items'([Items | _els], _acc) ->
+    'encode_muc_admin_$items'(_els,
+                             [encode_muc_admin_item(Items, []) | _acc]).
+
+decode_muc_admin_reason({xmlel, <<"reason">>, _attrs,
+                        _els}) ->
+    Cdata = decode_muc_admin_reason_els(_els, <<>>), Cdata.
+
+decode_muc_admin_reason_els([], Cdata) ->
+    decode_muc_admin_reason_cdata(Cdata);
+decode_muc_admin_reason_els([{xmlcdata, _data} | _els],
+                           Cdata) ->
+    decode_muc_admin_reason_els(_els,
+                               <<Cdata/binary, _data/binary>>);
+decode_muc_admin_reason_els([_ | _els], Cdata) ->
+    decode_muc_admin_reason_els(_els, Cdata).
+
+encode_muc_admin_reason(Cdata, _xmlns_attrs) ->
+    _els = encode_muc_admin_reason_cdata(Cdata, []),
+    _attrs = _xmlns_attrs,
+    {xmlel, <<"reason">>, _attrs, _els}.
+
+decode_muc_admin_reason_cdata(<<>>) -> undefined;
+decode_muc_admin_reason_cdata(_val) -> _val.
+
+encode_muc_admin_reason_cdata(undefined, _acc) -> _acc;
+encode_muc_admin_reason_cdata(_val, _acc) ->
+    [{xmlcdata, _val} | _acc].
+
+decode_muc_admin_continue({xmlel, <<"continue">>,
+                          _attrs, _els}) ->
+    Thread = decode_muc_admin_continue_attrs(_attrs,
+                                            undefined),
+    Thread.
+
+decode_muc_admin_continue_attrs([{<<"thread">>, _val}
+                                | _attrs],
+                               _Thread) ->
+    decode_muc_admin_continue_attrs(_attrs, _val);
+decode_muc_admin_continue_attrs([_ | _attrs], Thread) ->
+    decode_muc_admin_continue_attrs(_attrs, Thread);
+decode_muc_admin_continue_attrs([], Thread) ->
+    decode_muc_admin_continue_attr_thread(Thread).
+
+encode_muc_admin_continue(Thread, _xmlns_attrs) ->
+    _els = [],
+    _attrs = encode_muc_admin_continue_attr_thread(Thread,
+                                                  _xmlns_attrs),
+    {xmlel, <<"continue">>, _attrs, _els}.
+
+decode_muc_admin_continue_attr_thread(undefined) ->
+    undefined;
+decode_muc_admin_continue_attr_thread(_val) -> _val.
+
+encode_muc_admin_continue_attr_thread(undefined,
+                                     _acc) ->
+    _acc;
+encode_muc_admin_continue_attr_thread(_val, _acc) ->
+    [{<<"thread">>, _val} | _acc].
+
+decode_muc_admin_actor({xmlel, <<"actor">>, _attrs,
+                       _els}) ->
+    {Jid, Nick} = decode_muc_admin_actor_attrs(_attrs,
+                                              undefined, undefined),
+    {muc_actor, Jid, Nick}.
+
+decode_muc_admin_actor_attrs([{<<"jid">>, _val}
+                             | _attrs],
+                            _Jid, Nick) ->
+    decode_muc_admin_actor_attrs(_attrs, _val, Nick);
+decode_muc_admin_actor_attrs([{<<"nick">>, _val}
+                             | _attrs],
+                            Jid, _Nick) ->
+    decode_muc_admin_actor_attrs(_attrs, Jid, _val);
+decode_muc_admin_actor_attrs([_ | _attrs], Jid, Nick) ->
+    decode_muc_admin_actor_attrs(_attrs, Jid, Nick);
+decode_muc_admin_actor_attrs([], Jid, Nick) ->
+    {decode_muc_admin_actor_attr_jid(Jid),
+     decode_muc_admin_actor_attr_nick(Nick)}.
+
+encode_muc_admin_actor({muc_actor, Jid, Nick},
+                      _xmlns_attrs) ->
+    _els = [],
+    _attrs = encode_muc_admin_actor_attr_nick(Nick,
+                                             encode_muc_admin_actor_attr_jid(Jid,
+                                                                             _xmlns_attrs)),
+    {xmlel, <<"actor">>, _attrs, _els}.
+
+decode_muc_admin_actor_attr_jid(undefined) -> undefined;
+decode_muc_admin_actor_attr_jid(_val) ->
+    case catch dec_jid(_val) of
+      {'EXIT', _} ->
+         erlang:error({xmpp_codec,
+                       {bad_attr_value, <<"jid">>, <<"actor">>,
+                        <<"http://jabber.org/protocol/muc#admin">>}});
+      _res -> _res
+    end.
+
+encode_muc_admin_actor_attr_jid(undefined, _acc) ->
+    _acc;
+encode_muc_admin_actor_attr_jid(_val, _acc) ->
+    [{<<"jid">>, enc_jid(_val)} | _acc].
+
+decode_muc_admin_actor_attr_nick(undefined) ->
+    undefined;
+decode_muc_admin_actor_attr_nick(_val) -> _val.
+
+encode_muc_admin_actor_attr_nick(undefined, _acc) ->
+    _acc;
+encode_muc_admin_actor_attr_nick(_val, _acc) ->
+    [{<<"nick">>, _val} | _acc].
+
+decode_muc_admin_item({xmlel, <<"item">>, _attrs,
+                      _els}) ->
+    {Actor, Continue, Reason} =
+       decode_muc_admin_item_els(_els, undefined, undefined,
+                                 undefined),
+    {Affiliation, Role, Jid, Nick} =
+       decode_muc_admin_item_attrs(_attrs, undefined,
+                                   undefined, undefined, undefined),
+    {muc_item, Actor, Continue, Reason, Affiliation, Role,
+     Jid, Nick}.
+
+decode_muc_admin_item_els([], Actor, Continue,
+                         Reason) ->
+    {Actor, Continue, Reason};
+decode_muc_admin_item_els([{xmlel, <<"actor">>, _attrs,
+                           _} =
+                              _el
+                          | _els],
+                         Actor, Continue, Reason) ->
+    _xmlns = get_attr(<<"xmlns">>, _attrs),
+    if _xmlns == <<>>;
+       _xmlns == <<"http://jabber.org/protocol/muc#admin">> ->
+          decode_muc_admin_item_els(_els,
+                                    decode_muc_admin_actor(_el), Continue,
+                                    Reason);
+       true ->
+          decode_muc_admin_item_els(_els, Actor, Continue, Reason)
+    end;
+decode_muc_admin_item_els([{xmlel, <<"continue">>,
+                           _attrs, _} =
+                              _el
+                          | _els],
+                         Actor, Continue, Reason) ->
+    _xmlns = get_attr(<<"xmlns">>, _attrs),
+    if _xmlns == <<>>;
+       _xmlns == <<"http://jabber.org/protocol/muc#admin">> ->
+          decode_muc_admin_item_els(_els, Actor,
+                                    decode_muc_admin_continue(_el), Reason);
+       true ->
+          decode_muc_admin_item_els(_els, Actor, Continue, Reason)
+    end;
+decode_muc_admin_item_els([{xmlel, <<"reason">>, _attrs,
+                           _} =
+                              _el
+                          | _els],
+                         Actor, Continue, Reason) ->
+    _xmlns = get_attr(<<"xmlns">>, _attrs),
+    if _xmlns == <<>>;
+       _xmlns == <<"http://jabber.org/protocol/muc#admin">> ->
+          decode_muc_admin_item_els(_els, Actor, Continue,
+                                    decode_muc_admin_reason(_el));
+       true ->
+          decode_muc_admin_item_els(_els, Actor, Continue, Reason)
+    end;
+decode_muc_admin_item_els([_ | _els], Actor, Continue,
+                         Reason) ->
+    decode_muc_admin_item_els(_els, Actor, Continue,
+                             Reason).
+
+decode_muc_admin_item_attrs([{<<"affiliation">>, _val}
+                            | _attrs],
+                           _Affiliation, Role, Jid, Nick) ->
+    decode_muc_admin_item_attrs(_attrs, _val, Role, Jid,
+                               Nick);
+decode_muc_admin_item_attrs([{<<"role">>, _val}
+                            | _attrs],
+                           Affiliation, _Role, Jid, Nick) ->
+    decode_muc_admin_item_attrs(_attrs, Affiliation, _val,
+                               Jid, Nick);
+decode_muc_admin_item_attrs([{<<"jid">>, _val}
+                            | _attrs],
+                           Affiliation, Role, _Jid, Nick) ->
+    decode_muc_admin_item_attrs(_attrs, Affiliation, Role,
+                               _val, Nick);
+decode_muc_admin_item_attrs([{<<"nick">>, _val}
+                            | _attrs],
+                           Affiliation, Role, Jid, _Nick) ->
+    decode_muc_admin_item_attrs(_attrs, Affiliation, Role,
+                               Jid, _val);
+decode_muc_admin_item_attrs([_ | _attrs], Affiliation,
+                           Role, Jid, Nick) ->
+    decode_muc_admin_item_attrs(_attrs, Affiliation, Role,
+                               Jid, Nick);
+decode_muc_admin_item_attrs([], Affiliation, Role, Jid,
+                           Nick) ->
+    {decode_muc_admin_item_attr_affiliation(Affiliation),
+     decode_muc_admin_item_attr_role(Role),
+     decode_muc_admin_item_attr_jid(Jid),
+     decode_muc_admin_item_attr_nick(Nick)}.
+
+encode_muc_admin_item({muc_item, Actor, Continue,
+                      Reason, Affiliation, Role, Jid, Nick},
+                     _xmlns_attrs) ->
+    _els = 'encode_muc_admin_item_$reason'(Reason,
+                                          'encode_muc_admin_item_$continue'(Continue,
+                                                                            'encode_muc_admin_item_$actor'(Actor,
+                                                                                                           []))),
+    _attrs = encode_muc_admin_item_attr_nick(Nick,
+                                            encode_muc_admin_item_attr_jid(Jid,
+                                                                           encode_muc_admin_item_attr_role(Role,
+                                                                                                           encode_muc_admin_item_attr_affiliation(Affiliation,
+                                                                                                                                                  _xmlns_attrs)))),
+    {xmlel, <<"item">>, _attrs, _els}.
+
+'encode_muc_admin_item_$actor'(undefined, _acc) -> _acc;
+'encode_muc_admin_item_$actor'(Actor, _acc) ->
+    [encode_muc_admin_actor(Actor, []) | _acc].
+
+'encode_muc_admin_item_$continue'(undefined, _acc) ->
+    _acc;
+'encode_muc_admin_item_$continue'(Continue, _acc) ->
+    [encode_muc_admin_continue(Continue, []) | _acc].
+
+'encode_muc_admin_item_$reason'(undefined, _acc) ->
+    _acc;
+'encode_muc_admin_item_$reason'(Reason, _acc) ->
+    [encode_muc_admin_reason(Reason, []) | _acc].
+
+decode_muc_admin_item_attr_affiliation(undefined) ->
+    undefined;
+decode_muc_admin_item_attr_affiliation(_val) ->
+    case catch dec_enum(_val,
+                       [admin, member, none, outcast, owner])
+       of
+      {'EXIT', _} ->
+         erlang:error({xmpp_codec,
+                       {bad_attr_value, <<"affiliation">>, <<"item">>,
+                        <<"http://jabber.org/protocol/muc#admin">>}});
+      _res -> _res
+    end.
+
+encode_muc_admin_item_attr_affiliation(undefined,
+                                      _acc) ->
+    _acc;
+encode_muc_admin_item_attr_affiliation(_val, _acc) ->
+    [{<<"affiliation">>, enc_enum(_val)} | _acc].
+
+decode_muc_admin_item_attr_role(undefined) -> undefined;
+decode_muc_admin_item_attr_role(_val) ->
+    case catch dec_enum(_val,
+                       [moderator, none, participant, visitor])
+       of
+      {'EXIT', _} ->
+         erlang:error({xmpp_codec,
+                       {bad_attr_value, <<"role">>, <<"item">>,
+                        <<"http://jabber.org/protocol/muc#admin">>}});
+      _res -> _res
+    end.
+
+encode_muc_admin_item_attr_role(undefined, _acc) ->
+    _acc;
+encode_muc_admin_item_attr_role(_val, _acc) ->
+    [{<<"role">>, enc_enum(_val)} | _acc].
+
+decode_muc_admin_item_attr_jid(undefined) -> undefined;
+decode_muc_admin_item_attr_jid(_val) ->
+    case catch dec_jid(_val) of
+      {'EXIT', _} ->
+         erlang:error({xmpp_codec,
+                       {bad_attr_value, <<"jid">>, <<"item">>,
+                        <<"http://jabber.org/protocol/muc#admin">>}});
+      _res -> _res
+    end.
+
+encode_muc_admin_item_attr_jid(undefined, _acc) -> _acc;
+encode_muc_admin_item_attr_jid(_val, _acc) ->
+    [{<<"jid">>, enc_jid(_val)} | _acc].
+
+decode_muc_admin_item_attr_nick(undefined) -> undefined;
+decode_muc_admin_item_attr_nick(_val) -> _val.
+
+encode_muc_admin_item_attr_nick(undefined, _acc) ->
+    _acc;
+encode_muc_admin_item_attr_nick(_val, _acc) ->
+    [{<<"nick">>, _val} | _acc].
+
 decode_muc_owner({xmlel, <<"query">>, _attrs, _els}) ->
     {Config, Destroy} = decode_muc_owner_els(_els,
                                             undefined, undefined),
index 004d8a8e25bf93da7ab0b87a31b6d10b24ae2f9b..b9b6aa9ebf14bb1b180ce45fb7e98ff5aa921845 100644 (file)
@@ -61,9 +61,6 @@
                             node :: binary(),
                             publisher :: binary()}).
 
--record(muc_actor, {jid :: any(),
-                    nick :: binary()}).
-
 -record(stat, {name :: binary(),
                units :: binary(),
                value :: binary(),
                               subid :: binary(),
                               type :: 'none' | 'pending' | 'subscribed' | 'unconfigured'}).
 
+-record(muc_actor, {jid :: any(),
+                    nick :: binary()}).
+
 -record(shim, {headers = [] :: [{binary(),'undefined' | binary()}]}).
 
 -record(caps, {hash :: binary(),
                    status_codes = [] :: [pos_integer()],
                    password :: binary()}).
 
+-record(muc_admin, {items = [] :: [#muc_item{}]}).
+
 -record(bytestreams, {hosts = [] :: [#streamhost{}],
                       used :: any(),
                       activate :: any(),
index 29e9ef91f2bdd2446f74eed3b017834be40d218e..62f661381f134a22a7d7c60660e1238d5fa25245 100644 (file)
                         label = '$destroy'},
                    #ref{name = xdata, min = 0, max = 1, label = '$config'}]}).
 
+-xml(muc_admin_item,
+     #elem{name = <<"item">>,
+           xmlns = <<"http://jabber.org/protocol/muc#admin">>,
+           result = {muc_item, '$actor', '$continue', '$reason',
+                     '$affiliation', '$role', '$jid', '$nick'},
+           refs = [#ref{name = muc_admin_actor,
+                        min = 0, max = 1, label = '$actor'},
+                   #ref{name = muc_admin_continue,
+                        min = 0, max = 1, label = '$continue'},
+                   #ref{name = muc_admin_reason,
+                        min = 0, max = 1, label = '$reason'}],
+           attrs = [#attr{name = <<"affiliation">>,
+                          dec = {dec_enum, [[admin, member, none,
+                                             outcast, owner]]},
+                          enc = {enc_enum, []}},
+                    #attr{name = <<"role">>,
+                          dec = {dec_enum, [[moderator, none,
+                                             participant, visitor]]},
+                          enc = {enc_enum, []}},
+                    #attr{name = <<"jid">>,
+                          dec = {dec_jid, []},
+                          enc = {enc_jid, []}},
+                    #attr{name = <<"nick">>}]}).
+
+-xml(muc_admin_actor,
+     #elem{name = <<"actor">>,
+           xmlns = <<"http://jabber.org/protocol/muc#admin">>,
+           result = {muc_actor, '$jid', '$nick'},
+           attrs = [#attr{name = <<"jid">>,
+                          dec = {dec_jid, []},
+                          enc = {enc_jid, []}},
+                    #attr{name = <<"nick">>}]}).
+
+-xml(muc_admin_continue,
+     #elem{name = <<"continue">>,
+           xmlns = <<"http://jabber.org/protocol/muc#admin">>,
+           result = '$thread',
+           attrs = [#attr{name = <<"thread">>}]}).
+
+-xml(muc_admin_reason,
+     #elem{name = <<"reason">>,
+           xmlns = <<"http://jabber.org/protocol/muc#admin">>,
+           result = '$cdata'}).
+
+-xml(muc_admin,
+     #elem{name = <<"query">>,
+          xmlns = <<"http://jabber.org/protocol/muc#admin">>,
+          result = {muc_admin, '$items'},
+          refs = [#ref{name = muc_admin_item, label = '$items'}]}).
+
 -xml(muc,
      #elem{name = <<"x">>,
            xmlns = <<"http://jabber.org/protocol/muc">>,