privacy,
blocking,
vcard,
- muc_single,
pubsub,
test_unregister]},
{test_roster_subscribe, [parallel],
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]}].
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]}];
privacy,
blocking,
vcard,
- muc_single,
pubsub,
test_unregister]},
{test_roster_subscribe, [parallel],
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]}].
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")
%% 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,
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{}],
[<<"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,
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) ->
{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">>},
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);
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;
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">>,
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">>,
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.
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),
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(),
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">>,