]> granicus.if.org Git - ejabberd/commitdiff
Add tests for MIX
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Thu, 10 Mar 2016 14:42:37 +0000 (17:42 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Thu, 10 Mar 2016 14:42:37 +0000 (17:42 +0300)
test/ejabberd_SUITE.erl
test/ejabberd_SUITE_data/ejabberd.yml
test/suite.erl
tools/xmpp_codec.erl
tools/xmpp_codec.hrl
tools/xmpp_codec.spec

index 1b8d5f4775a8f09f8c2623ecdfbb685bc020e185..f17bfeb4c8be4771bd3a05f5ab06b21f0a785433 100644 (file)
@@ -12,8 +12,8 @@
 
 -import(suite, [init_config/1, connect/1, disconnect/1,
                 recv/0, send/2, send_recv/2, my_jid/1, server_jid/1,
-                pubsub_jid/1, proxy_jid/1, muc_jid/1,
-                muc_room_jid/1, get_features/2, re_register/1,
+                pubsub_jid/1, proxy_jid/1, muc_jid/1, muc_room_jid/1,
+               mix_jid/1, mix_room_jid/1, get_features/2, re_register/1,
                 is_feature_advertised/2, subscribe_to_events/1,
                 is_feature_advertised/3, set_opt/3, auth_SASL/2,
                 wait_for_master/1, wait_for_slave/1,
@@ -249,6 +249,8 @@ db_tests(DB) when DB == mnesia; DB == redis ->
        test_unregister]},
      {test_muc_register, [sequence],
       [muc_register_master, muc_register_slave]},
+     {test_mix, [parallel],
+      [mix_master, mix_slave]},
      {test_roster_subscribe, [parallel],
       [roster_subscribe_master,
        roster_subscribe_slave]},
@@ -882,6 +884,90 @@ pubsub(Config) ->
                                            jid = my_jid(Config)}}]}),
     disconnect(Config).
 
+mix_master(Config) ->
+    MIX = mix_jid(Config),
+    Room = mix_room_jid(Config),
+    MyJID = my_jid(Config),
+    MyBareJID = jid:remove_resource(MyJID),
+    true = is_feature_advertised(Config, ?NS_MIX_0, MIX),
+    #iq{type = result,
+       sub_els =
+           [#disco_info{
+               identities = [#identity{category = <<"conference">>,
+                                       type = <<"text">>}],
+               xdata = [#xdata{type = result, fields = XFields}]}]} =
+       send_recv(Config, #iq{type = get, to = MIX, sub_els = [#disco_info{}]}),
+    true = lists:any(
+            fun(#xdata_field{var = <<"FORM_TYPE">>,
+                             values = [?NS_MIX_SERVICEINFO_0]}) -> true;
+               (_) -> false
+            end, XFields),
+    %% Joining
+    Nodes = [?NS_MIX_NODES_MESSAGES, ?NS_MIX_NODES_PRESENCE,
+            ?NS_MIX_NODES_PARTICIPANTS, ?NS_MIX_NODES_SUBJECT,
+            ?NS_MIX_NODES_CONFIG],
+    I0 = send(Config, #iq{type = set, to = Room,
+                         sub_els = [#mix_join{subscribe = Nodes}]}),
+    {_, #message{sub_els =
+                    [#pubsub_event{
+                        items = [#pubsub_event_items{
+                                    node = ?NS_MIX_NODES_PARTICIPANTS,
+                                    items = [#pubsub_event_item{
+                                                id = ParticipantID,
+                                                xml_els = [PXML]}]}]}]}} =
+       ?recv2(#iq{type = result, id = I0,
+                  sub_els = [#mix_join{subscribe = Nodes, jid = MyBareJID}]},
+              #message{from = Room}),
+    #mix_participant{jid = MyBareJID} = xmpp_codec:decode(PXML),
+    %% Coming online
+    PresenceID = randoms:get_string(),
+    Presence = xmpp_codec:encode(#presence{}),
+    I1 = send(
+          Config,
+          #iq{type = set, to = Room,
+              sub_els =
+                  [#pubsub{
+                      publish = #pubsub_publish{
+                                   node = ?NS_MIX_NODES_PRESENCE,
+                                   items = [#pubsub_item{
+                                               id = PresenceID,
+                                               xml_els = [Presence]}]}}]}),
+    ?recv2(#iq{type = result, id = I1,
+              sub_els =
+                  [#pubsub{
+                      publish = #pubsub_publish{
+                                   node = ?NS_MIX_NODES_PRESENCE,
+                                   items = [#pubsub_item{id = PresenceID}]}}]},
+          #message{from = Room,
+                   sub_els =
+                       [#pubsub_event{
+                           items = [#pubsub_event_items{
+                                       node = ?NS_MIX_NODES_PRESENCE,
+                                       items = [#pubsub_event_item{
+                                                   id = PresenceID,
+                                                   xml_els = [Presence]}]}]}]}),
+    %% Coming offline
+    send(Config, #presence{type = unavailable, to = Room}),
+    %% Receiving presence retract event
+    #message{from = Room,
+            sub_els = [#pubsub_event{
+                          items = [#pubsub_event_items{
+                                      node = ?NS_MIX_NODES_PRESENCE,
+                                      retract = [PresenceID]}]}]} = recv(),
+    %% Leaving
+    I2 = send(Config, #iq{type = set, to = Room, sub_els = [#mix_leave{}]}),
+    ?recv2(#iq{type = result, id = I2, sub_els = []},
+          #message{from = Room,
+                   sub_els =
+                       [#pubsub_event{
+                           items = [#pubsub_event_items{
+                                       node = ?NS_MIX_NODES_PARTICIPANTS,
+                                       retract = [ParticipantID]}]}]}),
+    disconnect(Config).
+
+mix_slave(Config) ->      
+    disconnect(Config).
+      
 roster_subscribe_master(Config) ->
     send(Config, #presence{}),
     ?recv1(#presence{}),
index 5507900b871224c6cb9b0fdf1589ff07335967bd..96453f77b6f7e57c174b1a4dbffcb53f45a16094 100644 (file)
@@ -197,6 +197,7 @@ Welcome to this XMPP server."
           - "flat"
           - "hometree"
           - "pep"
+      mod_mix: []
       mod_roster: 
         versioning: true
         store_current_id: true
@@ -252,6 +253,7 @@ Welcome to this XMPP server."
           - "flat"
           - "hometree"
           - "pep"
+      mod_mix: []
       mod_roster: 
         versioning: true
         store_current_id: true
index a0e0a48237d854e73df8f59b8695aaabd0337bf9..bc094fa311c038d270617db47eeb97eeaa43c252 100644 (file)
@@ -354,6 +354,14 @@ muc_room_jid(Config) ->
     Server = ?config(server, Config),
     jid:make(<<"test">>, <<"conference.", Server/binary>>, <<>>).
 
+mix_jid(Config) ->
+    Server = ?config(server, Config),
+    jid:make(<<>>, <<"mix.", Server/binary>>, <<>>).
+
+mix_room_jid(Config) ->
+    Server = ?config(server, Config),
+    jid:make(<<"test">>, <<"mix.", Server/binary>>, <<>>).
+
 id() ->
     id(undefined).
 
index 917418f554824897c9cd91bed4ea3d20388ed5fe..01e0676ae8a2355f0917333cde7399f1967110dd 100644 (file)
@@ -15,6 +15,16 @@ decode(_el) -> decode(_el, []).
 decode({xmlel, _name, _attrs, _} = _el, Opts) ->
     IgnoreEls = proplists:get_bool(ignore_els, Opts),
     case {_name, get_attr(<<"xmlns">>, _attrs)} of
+      {<<"participant">>, <<"urn:xmpp:mix:0">>} ->
+         decode_mix_participant(<<"urn:xmpp:mix:0">>, IgnoreEls,
+                                _el);
+      {<<"leave">>, <<"urn:xmpp:mix:0">>} ->
+         decode_mix_leave(<<"urn:xmpp:mix:0">>, IgnoreEls, _el);
+      {<<"join">>, <<"urn:xmpp:mix:0">>} ->
+         decode_mix_join(<<"urn:xmpp:mix:0">>, IgnoreEls, _el);
+      {<<"subscribe">>, <<"urn:xmpp:mix:0">>} ->
+         decode_mix_subscribe(<<"urn:xmpp:mix:0">>, IgnoreEls,
+                              _el);
       {<<"offline">>,
        <<"http://jabber.org/protocol/offline">>} ->
          decode_offline(<<"http://jabber.org/protocol/offline">>,
@@ -1088,6 +1098,10 @@ decode({xmlel, _name, _attrs, _} = _el, Opts) ->
 
 is_known_tag({xmlel, _name, _attrs, _} = _el) ->
     case {_name, get_attr(<<"xmlns">>, _attrs)} of
+      {<<"participant">>, <<"urn:xmpp:mix:0">>} -> true;
+      {<<"leave">>, <<"urn:xmpp:mix:0">>} -> true;
+      {<<"join">>, <<"urn:xmpp:mix:0">>} -> true;
+      {<<"subscribe">>, <<"urn:xmpp:mix:0">>} -> true;
       {<<"offline">>,
        <<"http://jabber.org/protocol/offline">>} ->
          true;
@@ -1987,7 +2001,7 @@ encode({pubsub_items, _, _, _, _} = Items) ->
     encode_pubsub_items(Items,
                        [{<<"xmlns">>,
                          <<"http://jabber.org/protocol/pubsub">>}]);
-encode({pubsub_event_item, _, _, _} = Item) ->
+encode({pubsub_event_item, _, _, _, _} = Item) ->
     encode_pubsub_event_item(Item,
                             [{<<"xmlns">>,
                               <<"http://jabber.org/protocol/pubsub#event">>}]);
@@ -2160,7 +2174,16 @@ encode({offline_item, _, _} = Item) ->
 encode({offline, _, _, _} = Offline) ->
     encode_offline(Offline,
                   [{<<"xmlns">>,
-                    <<"http://jabber.org/protocol/offline">>}]).
+                    <<"http://jabber.org/protocol/offline">>}]);
+encode({mix_join, _, _} = Join) ->
+    encode_mix_join(Join,
+                   [{<<"xmlns">>, <<"urn:xmpp:mix:0">>}]);
+encode({mix_leave} = Leave) ->
+    encode_mix_leave(Leave,
+                    [{<<"xmlns">>, <<"urn:xmpp:mix:0">>}]);
+encode({mix_participant, _, _} = Participant) ->
+    encode_mix_participant(Participant,
+                          [{<<"xmlns">>, <<"urn:xmpp:mix:0">>}]).
 
 get_ns({last, _, _}) -> <<"jabber:iq:last">>;
 get_ns({version, _, _, _}) -> <<"jabber:iq:version">>;
@@ -2286,7 +2309,7 @@ get_ns({pubsub_item, _, _}) ->
     <<"http://jabber.org/protocol/pubsub">>;
 get_ns({pubsub_items, _, _, _, _}) ->
     <<"http://jabber.org/protocol/pubsub">>;
-get_ns({pubsub_event_item, _, _, _}) ->
+get_ns({pubsub_event_item, _, _, _, _}) ->
     <<"http://jabber.org/protocol/pubsub#event">>;
 get_ns({pubsub_event_items, _, _, _}) ->
     <<"http://jabber.org/protocol/pubsub#event">>;
@@ -2359,6 +2382,9 @@ get_ns({offline_item, _, _}) ->
     <<"http://jabber.org/protocol/offline">>;
 get_ns({offline, _, _, _}) ->
     <<"http://jabber.org/protocol/offline">>;
+get_ns({mix_join, _, _}) -> <<"urn:xmpp:mix:0">>;
+get_ns({mix_leave}) -> <<"urn:xmpp:mix:0">>;
+get_ns({mix_participant, _, _}) -> <<"urn:xmpp:mix:0">>;
 get_ns(_) -> <<>>.
 
 dec_int(Val) -> dec_int(Val, infinity, infinity).
@@ -2505,7 +2531,8 @@ pp(pubsub_subscription, 4) -> [jid, node, subid, type];
 pp(pubsub_affiliation, 2) -> [node, type];
 pp(pubsub_item, 2) -> [id, xml_els];
 pp(pubsub_items, 4) -> [node, max_items, subid, items];
-pp(pubsub_event_item, 3) -> [id, node, publisher];
+pp(pubsub_event_item, 4) ->
+    [id, node, publisher, xml_els];
 pp(pubsub_event_items, 3) -> [node, retract, items];
 pp(pubsub_event, 1) -> [items];
 pp(pubsub_subscribe, 2) -> [node, jid];
@@ -2564,6 +2591,9 @@ pp(sm_a, 2) -> [h, xmlns];
 pp(sm_failed, 2) -> [reason, xmlns];
 pp(offline_item, 2) -> [node, action];
 pp(offline, 3) -> [items, purge, fetch];
+pp(mix_join, 2) -> [jid, subscribe];
+pp(mix_leave, 0) -> [];
+pp(mix_participant, 2) -> [jid, nick];
 pp(_, _) -> no.
 
 enc_bool(false) -> <<"false">>;
@@ -2606,6 +2636,170 @@ dec_tzo(Val) ->
     M = jlib:binary_to_integer(M1),
     if H >= -12, H =< 12, M >= 0, M < 60 -> {H, M} end.
 
+decode_mix_participant(__TopXMLNS, __IgnoreEls,
+                      {xmlel, <<"participant">>, _attrs, _els}) ->
+    {Jid, Nick} = decode_mix_participant_attrs(__TopXMLNS,
+                                              _attrs, undefined, undefined),
+    {mix_participant, Jid, Nick}.
+
+decode_mix_participant_attrs(__TopXMLNS,
+                            [{<<"jid">>, _val} | _attrs], _Jid, Nick) ->
+    decode_mix_participant_attrs(__TopXMLNS, _attrs, _val,
+                                Nick);
+decode_mix_participant_attrs(__TopXMLNS,
+                            [{<<"nick">>, _val} | _attrs], Jid, _Nick) ->
+    decode_mix_participant_attrs(__TopXMLNS, _attrs, Jid,
+                                _val);
+decode_mix_participant_attrs(__TopXMLNS, [_ | _attrs],
+                            Jid, Nick) ->
+    decode_mix_participant_attrs(__TopXMLNS, _attrs, Jid,
+                                Nick);
+decode_mix_participant_attrs(__TopXMLNS, [], Jid,
+                            Nick) ->
+    {decode_mix_participant_attr_jid(__TopXMLNS, Jid),
+     decode_mix_participant_attr_nick(__TopXMLNS, Nick)}.
+
+encode_mix_participant({mix_participant, Jid, Nick},
+                      _xmlns_attrs) ->
+    _els = [],
+    _attrs = encode_mix_participant_attr_nick(Nick,
+                                             encode_mix_participant_attr_jid(Jid,
+                                                                             _xmlns_attrs)),
+    {xmlel, <<"participant">>, _attrs, _els}.
+
+decode_mix_participant_attr_jid(__TopXMLNS,
+                               undefined) ->
+    erlang:error({xmpp_codec,
+                 {missing_attr, <<"jid">>, <<"participant">>,
+                  __TopXMLNS}});
+decode_mix_participant_attr_jid(__TopXMLNS, _val) ->
+    case catch dec_jid(_val) of
+      {'EXIT', _} ->
+         erlang:error({xmpp_codec,
+                       {bad_attr_value, <<"jid">>, <<"participant">>,
+                        __TopXMLNS}});
+      _res -> _res
+    end.
+
+encode_mix_participant_attr_jid(_val, _acc) ->
+    [{<<"jid">>, enc_jid(_val)} | _acc].
+
+decode_mix_participant_attr_nick(__TopXMLNS,
+                                undefined) ->
+    undefined;
+decode_mix_participant_attr_nick(__TopXMLNS, _val) ->
+    _val.
+
+encode_mix_participant_attr_nick(undefined, _acc) ->
+    _acc;
+encode_mix_participant_attr_nick(_val, _acc) ->
+    [{<<"nick">>, _val} | _acc].
+
+decode_mix_leave(__TopXMLNS, __IgnoreEls,
+                {xmlel, <<"leave">>, _attrs, _els}) ->
+    {mix_leave}.
+
+encode_mix_leave({mix_leave}, _xmlns_attrs) ->
+    _els = [],
+    _attrs = _xmlns_attrs,
+    {xmlel, <<"leave">>, _attrs, _els}.
+
+decode_mix_join(__TopXMLNS, __IgnoreEls,
+               {xmlel, <<"join">>, _attrs, _els}) ->
+    Subscribe = decode_mix_join_els(__TopXMLNS, __IgnoreEls,
+                                   _els, []),
+    Jid = decode_mix_join_attrs(__TopXMLNS, _attrs,
+                               undefined),
+    {mix_join, Jid, Subscribe}.
+
+decode_mix_join_els(__TopXMLNS, __IgnoreEls, [],
+                   Subscribe) ->
+    lists:reverse(Subscribe);
+decode_mix_join_els(__TopXMLNS, __IgnoreEls,
+                   [{xmlel, <<"subscribe">>, _attrs, _} = _el | _els],
+                   Subscribe) ->
+    _xmlns = get_attr(<<"xmlns">>, _attrs),
+    if _xmlns == <<>>; _xmlns == __TopXMLNS ->
+          decode_mix_join_els(__TopXMLNS, __IgnoreEls, _els,
+                              [decode_mix_subscribe(__TopXMLNS, __IgnoreEls,
+                                                    _el)
+                               | Subscribe]);
+       true ->
+          decode_mix_join_els(__TopXMLNS, __IgnoreEls, _els,
+                              Subscribe)
+    end;
+decode_mix_join_els(__TopXMLNS, __IgnoreEls, [_ | _els],
+                   Subscribe) ->
+    decode_mix_join_els(__TopXMLNS, __IgnoreEls, _els,
+                       Subscribe).
+
+decode_mix_join_attrs(__TopXMLNS,
+                     [{<<"jid">>, _val} | _attrs], _Jid) ->
+    decode_mix_join_attrs(__TopXMLNS, _attrs, _val);
+decode_mix_join_attrs(__TopXMLNS, [_ | _attrs], Jid) ->
+    decode_mix_join_attrs(__TopXMLNS, _attrs, Jid);
+decode_mix_join_attrs(__TopXMLNS, [], Jid) ->
+    decode_mix_join_attr_jid(__TopXMLNS, Jid).
+
+encode_mix_join({mix_join, Jid, Subscribe},
+               _xmlns_attrs) ->
+    _els =
+       lists:reverse('encode_mix_join_$subscribe'(Subscribe,
+                                                  [])),
+    _attrs = encode_mix_join_attr_jid(Jid, _xmlns_attrs),
+    {xmlel, <<"join">>, _attrs, _els}.
+
+'encode_mix_join_$subscribe'([], _acc) -> _acc;
+'encode_mix_join_$subscribe'([Subscribe | _els],
+                            _acc) ->
+    'encode_mix_join_$subscribe'(_els,
+                                [encode_mix_subscribe(Subscribe, []) | _acc]).
+
+decode_mix_join_attr_jid(__TopXMLNS, undefined) ->
+    undefined;
+decode_mix_join_attr_jid(__TopXMLNS, _val) ->
+    case catch dec_jid(_val) of
+      {'EXIT', _} ->
+         erlang:error({xmpp_codec,
+                       {bad_attr_value, <<"jid">>, <<"join">>, __TopXMLNS}});
+      _res -> _res
+    end.
+
+encode_mix_join_attr_jid(undefined, _acc) -> _acc;
+encode_mix_join_attr_jid(_val, _acc) ->
+    [{<<"jid">>, enc_jid(_val)} | _acc].
+
+decode_mix_subscribe(__TopXMLNS, __IgnoreEls,
+                    {xmlel, <<"subscribe">>, _attrs, _els}) ->
+    Node = decode_mix_subscribe_attrs(__TopXMLNS, _attrs,
+                                     undefined),
+    Node.
+
+decode_mix_subscribe_attrs(__TopXMLNS,
+                          [{<<"node">>, _val} | _attrs], _Node) ->
+    decode_mix_subscribe_attrs(__TopXMLNS, _attrs, _val);
+decode_mix_subscribe_attrs(__TopXMLNS, [_ | _attrs],
+                          Node) ->
+    decode_mix_subscribe_attrs(__TopXMLNS, _attrs, Node);
+decode_mix_subscribe_attrs(__TopXMLNS, [], Node) ->
+    decode_mix_subscribe_attr_node(__TopXMLNS, Node).
+
+encode_mix_subscribe(Node, _xmlns_attrs) ->
+    _els = [],
+    _attrs = encode_mix_subscribe_attr_node(Node,
+                                           _xmlns_attrs),
+    {xmlel, <<"subscribe">>, _attrs, _els}.
+
+decode_mix_subscribe_attr_node(__TopXMLNS, undefined) ->
+    erlang:error({xmpp_codec,
+                 {missing_attr, <<"node">>, <<"subscribe">>,
+                  __TopXMLNS}});
+decode_mix_subscribe_attr_node(__TopXMLNS, _val) ->
+    _val.
+
+encode_mix_subscribe_attr_node(_val, _acc) ->
+    [{<<"node">>, _val} | _acc].
+
 decode_offline(__TopXMLNS, __IgnoreEls,
               {xmlel, <<"offline">>, _attrs, _els}) ->
     {Items, Purge, Fetch} = decode_offline_els(__TopXMLNS,
@@ -7883,10 +8077,24 @@ encode_pubsub_event_items_attr_node(_val, _acc) ->
 
 decode_pubsub_event_item(__TopXMLNS, __IgnoreEls,
                         {xmlel, <<"item">>, _attrs, _els}) ->
+    __Xmls = decode_pubsub_event_item_els(__TopXMLNS,
+                                         __IgnoreEls, _els, []),
     {Id, Node, Publisher} =
        decode_pubsub_event_item_attrs(__TopXMLNS, _attrs,
                                       undefined, undefined, undefined),
-    {pubsub_event_item, Id, Node, Publisher}.
+    {pubsub_event_item, Id, Node, Publisher, __Xmls}.
+
+decode_pubsub_event_item_els(__TopXMLNS, __IgnoreEls,
+                            [], __Xmls) ->
+    lists:reverse(__Xmls);
+decode_pubsub_event_item_els(__TopXMLNS, __IgnoreEls,
+                            [{xmlel, _, _, _} = _el | _els], __Xmls) ->
+    decode_pubsub_event_item_els(__TopXMLNS, __IgnoreEls,
+                                _els, [_el | __Xmls]);
+decode_pubsub_event_item_els(__TopXMLNS, __IgnoreEls,
+                            [_ | _els], __Xmls) ->
+    decode_pubsub_event_item_els(__TopXMLNS, __IgnoreEls,
+                                _els, __Xmls).
 
 decode_pubsub_event_item_attrs(__TopXMLNS,
                               [{<<"id">>, _val} | _attrs], _Id, Node,
@@ -7915,9 +8123,9 @@ decode_pubsub_event_item_attrs(__TopXMLNS, [], Id, Node,
                                             Publisher)}.
 
 encode_pubsub_event_item({pubsub_event_item, Id, Node,
-                         Publisher},
+                         Publisher, __Xmls},
                         _xmlns_attrs) ->
-    _els = [],
+    _els = __Xmls,
     _attrs =
        encode_pubsub_event_item_attr_publisher(Publisher,
                                                encode_pubsub_event_item_attr_node(Node,
index 1426287bfc56e3318aa57ea4e36dd1ca08a51e38..b2773a6c1bcea94628274cbd0097541a475ea2b6 100644 (file)
@@ -36,6 +36,8 @@
                              jid :: any(),
                              subid :: binary()}).
 
+-record(mix_leave, {}).
+
 -record(ping, {}).
 
 -record(delay, {stamp :: any(),
 
 -record(pubsub_event_item, {id :: binary(),
                             node :: binary(),
-                            publisher :: binary()}).
+                            publisher :: binary(),
+                            xml_els = [] :: [any()]}).
 
 -record(sm_r, {xmlns :: binary()}).
 
                          notify = false :: any(),
                          items = [] :: [#pubsub_item{}]}).
 
+-record(mix_participant, {jid :: any(),
+                          nick :: binary()}).
+
 -record(vcard_geo, {lat :: binary(),
                     lon :: binary()}).
 
              error :: #error{},
              sub_els = [] :: [any()]}).
 
+-record(mix_join, {jid :: any(),
+                   subscribe = [] :: [binary()]}).
+
 -record(privacy_item, {order :: non_neg_integer(),
                        action :: 'allow' | 'deny',
                        type :: 'group' | 'jid' | 'subscription',
index 129a4efba9a59428f17546244a2fbe52e7676fce..e61b951c5136e6f30d85dc7a5923473175017705 100644 (file)
 -xml(pubsub_event_item,
      #elem{name = <<"item">>,
            xmlns = <<"http://jabber.org/protocol/pubsub#event">>,
-           result = {pubsub_event_item, '$id', '$node', '$publisher'},
+           result = {pubsub_event_item, '$id', '$node', '$publisher', '$_xmls'},
            attrs = [#attr{name = <<"id">>},
                     #attr{name = <<"node">>},
                     #attr{name = <<"publisher">>}]}).
                        label = '$fetch', default = false},
                   #ref{name = offline_item, min = 0, label = '$items'}]}).
 
+-xml(mix_subscribe,
+     #elem{name = <<"subscribe">>,
+           xmlns = <<"urn:xmpp:mix:0">>,
+           result = '$node',
+           attrs = [#attr{name = <<"node">>,
+                          required = true,
+                          label = '$node'}]}).
+
+-xml(mix_join,
+     #elem{name = <<"join">>,
+           xmlns = <<"urn:xmpp:mix:0">>,
+           result = {mix_join, '$jid', '$subscribe'},
+           attrs = [#attr{name = <<"jid">>,
+                          label = '$jid',
+                          dec = {dec_jid, []},
+                          enc = {enc_jid, []}}],
+           refs = [#ref{name = mix_subscribe, min = 0, label = '$subscribe'}]}).
+
+-xml(mix_leave,
+     #elem{name = <<"leave">>,
+           xmlns = <<"urn:xmpp:mix:0">>,
+           result = {mix_leave}}).
+
+-xml(mix_participant,
+     #elem{name = <<"participant">>,
+           xmlns = <<"urn:xmpp:mix:0">>,
+           result = {mix_participant, '$jid', '$nick'},
+           attrs = [#attr{name = <<"jid">>,
+                          required = true,
+                          label = '$jid',
+                          dec = {dec_jid, []},
+                          enc = {enc_jid, []}},
+                    #attr{name = <<"nick">>,
+                          label = '$nick'}]}).
+
 dec_tzo(Val) ->
     [H1, M1] = str:tokens(Val, <<":">>),
     H = jlib:binary_to_integer(H1),