25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-24 16:23:40 +01:00

Add some MUC checks. Several cleanups

This commit is contained in:
Evgeniy Khramtsov 2013-06-17 04:00:19 +10:00 committed by Alexey Shchepin
parent 45edf337cf
commit 96b94e3ebb
4 changed files with 2370 additions and 545 deletions

View File

@ -102,7 +102,7 @@ end_per_group(_GroupName, Config) ->
stop_event_relay(Config), stop_event_relay(Config),
ok. ok.
init_per_testcase(stop_ejabberd = TestCase, OrigConfig) -> init_per_testcase(stop_ejabberd, OrigConfig) ->
User = <<"test_stop">>, User = <<"test_stop">>,
Config = set_opt(user, User, OrigConfig), Config = set_opt(user, User, OrigConfig),
ejabberd_auth:try_register(User, ejabberd_auth:try_register(User,
@ -179,14 +179,22 @@ groups() ->
blocking, blocking,
vcard, vcard,
pubsub, pubsub,
muc_single,
test_unregister]}, test_unregister]},
{test_roster, [parallel], [roster_master, roster_slave]}, {test_roster_subscribe, [parallel],
{test_proxy65, [parallel], [proxy65_master, proxy65_slave]}]. [roster_subscribe_master,
roster_subscribe_slave]},
{test_proxy65, [parallel],
[proxy65_master, proxy65_slave]},
{test_roster_remove, [parallel],
[roster_remove_master,
roster_remove_slave]}].
all() -> all() ->
[{group, single_user}, [{group, single_user},
{group, test_roster}, {group, test_roster_subscribe},
{group, test_proxy65}, {group, test_proxy65},
{group, test_roster_remove},
stop_ejabberd]. stop_ejabberd].
stop_ejabberd(Config) -> stop_ejabberd(Config) ->
@ -281,17 +289,17 @@ test_register(Config) ->
end. end.
register(Config) -> register(Config) ->
I1 = send(Config, #iq{type = result,
#iq{type = get, to = server_jid(Config),
sub_els = [#register{}]}),
#iq{type = result, id = I1,
sub_els = [#register{username = none, sub_els = [#register{username = none,
password = none}]} = recv(), password = none}]} =
I2 = send(Config, send_recv(Config, #iq{type = get, to = server_jid(Config),
sub_els = [#register{}]}),
#iq{type = result, sub_els = []} =
send_recv(
Config,
#iq{type = set, #iq{type = set,
sub_els = [#register{username = ?config(user, Config), sub_els = [#register{username = ?config(user, Config),
password = ?config(password, Config)}]}), password = ?config(password, Config)}]}),
#iq{type = result, id = I2, sub_els = []} = recv(),
Config. Config.
test_unregister(Config) -> test_unregister(Config) ->
@ -304,10 +312,11 @@ test_unregister(Config) ->
try_unregister(Config) -> try_unregister(Config) ->
true = is_feature_advertised(Config, ?NS_REGISTER), true = is_feature_advertised(Config, ?NS_REGISTER),
I = send(Config, #iq{type = result, sub_els = []} =
send_recv(
Config,
#iq{type = set, #iq{type = set,
sub_els = [#register{remove = true}]}), sub_els = [#register{remove = true}]}),
#iq{type = result, id = I, sub_els = []} = recv(),
#stream_error{reason = conflict} = recv(), #stream_error{reason = conflict} = recv(),
Config. Config.
@ -330,75 +339,74 @@ test_bind(Config) ->
disconnect(bind(Config)). disconnect(bind(Config)).
bind(Config) -> bind(Config) ->
ID = send(Config, #iq{type = result, sub_els = [#bind{}]} =
send_recv(
Config,
#iq{type = set, #iq{type = set,
sub_els = [#bind{resource = ?config(resource, Config)}]}), sub_els = [#bind{resource = ?config(resource, Config)}]}),
#iq{type = result, id = ID, sub_els = [#bind{}]} = recv(),
Config. Config.
test_open_session(Config) -> test_open_session(Config) ->
disconnect(open_session(Config)). disconnect(open_session(Config)).
open_session(Config) -> open_session(Config) ->
ID = send(Config, #iq{type = set, sub_els = [#session{}]}), #iq{type = result, sub_els = []} =
#iq{type = result, id = ID, sub_els = []} = recv(), send_recv(Config, #iq{type = set, sub_els = [#session{}]}),
Config. Config.
roster_get(Config) -> roster_get(Config) ->
ID = send(Config, #iq{type = get, sub_els = [#roster{}]}), #iq{type = result, sub_els = [#roster{items = []}]} =
#iq{type = result, id = ID, send_recv(Config, #iq{type = get, sub_els = [#roster{}]}),
sub_els = [#roster{items = []}]} = recv(),
disconnect(Config). disconnect(Config).
presence_broadcast(Config) -> presence_broadcast(Config) ->
send(Config, #presence{}), send(Config, #presence{}),
JID = my_jid(Config), JID = my_jid(Config),
%% We receive the welcome message first %% We receive the welcome message and the presence broadcast
#message{type = normal} = recv(), ?recv2(#message{type = normal},
%% Then we receive back our presence #presence{from = JID, to = JID}),
#presence{from = JID, to = JID} = recv(),
disconnect(Config). disconnect(Config).
ping(Config) -> ping(Config) ->
true = is_feature_advertised(Config, ?NS_PING), true = is_feature_advertised(Config, ?NS_PING),
ID = send(Config, #iq{type = result, sub_els = []} =
send_recv(
Config,
#iq{type = get, sub_els = [#ping{}], to = server_jid(Config)}), #iq{type = get, sub_els = [#ping{}], to = server_jid(Config)}),
#iq{type = result, id = ID, sub_els = []} = recv(),
disconnect(Config). disconnect(Config).
version(Config) -> version(Config) ->
true = is_feature_advertised(Config, ?NS_VERSION), true = is_feature_advertised(Config, ?NS_VERSION),
ID = send(Config, #iq{type = get, sub_els = [#version{}], #iq{type = result, sub_els = [#version{}]} =
send_recv(
Config, #iq{type = get, sub_els = [#version{}],
to = server_jid(Config)}), to = server_jid(Config)}),
#iq{type = result, id = ID, sub_els = [#version{}]} = recv(),
disconnect(Config). disconnect(Config).
time(Config) -> time(Config) ->
true = is_feature_advertised(Config, ?NS_TIME), true = is_feature_advertised(Config, ?NS_TIME),
ID = send(Config, #iq{type = get, sub_els = [#time{}], #iq{type = result, sub_els = [#time{}]} =
send_recv(Config, #iq{type = get, sub_els = [#time{}],
to = server_jid(Config)}), to = server_jid(Config)}),
#iq{type = result, id = ID, sub_els = [#time{}]} = recv(),
disconnect(Config). disconnect(Config).
disco(Config) -> disco(Config) ->
true = is_feature_advertised(Config, ?NS_DISCO_INFO), true = is_feature_advertised(Config, ?NS_DISCO_INFO),
true = is_feature_advertised(Config, ?NS_DISCO_ITEMS), true = is_feature_advertised(Config, ?NS_DISCO_ITEMS),
I1 = send(Config, #iq{type = get, sub_els = [#disco_items{}], #iq{type = result, sub_els = [#disco_items{items = Items}]} =
send_recv(
Config, #iq{type = get, sub_els = [#disco_items{}],
to = server_jid(Config)}), to = server_jid(Config)}),
#iq{type = result, id = I1, sub_els = [#disco_items{items = Items}]} = recv(),
lists:foreach( lists:foreach(
fun(#disco_item{jid = JID, node = Node}) -> fun(#disco_item{jid = JID, node = Node}) ->
I = send(Config, #iq{type = result} =
send_recv(Config,
#iq{type = get, to = JID, #iq{type = get, to = JID,
sub_els = [#disco_info{node = Node}]}), sub_els = [#disco_info{node = Node}]})
#iq{type = result, id = I, sub_els = _} = recv()
end, Items), end, Items),
disconnect(Config). disconnect(Config).
private(Config) -> private(Config) ->
I1 = send(Config, #iq{type = get, sub_els = [#private{}],
to = server_jid(Config)}),
#iq{type = error, id = I1} = recv(),
Conference = #bookmark_conference{name = <<"Some name">>, Conference = #bookmark_conference{name = <<"Some name">>,
autojoin = true, autojoin = true,
jid = jlib:make_jid( jid = jlib:make_jid(
@ -406,29 +414,34 @@ private(Config) ->
<<"some.conference.org">>, <<"some.conference.org">>,
<<>>)}, <<>>)},
Storage = #bookmark_storage{conference = [Conference]}, Storage = #bookmark_storage{conference = [Conference]},
I2 = send(Config, #iq{type = set, #iq{type = error} =
send_recv(Config, #iq{type = get, sub_els = [#private{}],
to = server_jid(Config)}),
#iq{type = result, sub_els = []} =
send_recv(
Config, #iq{type = set,
sub_els = [#private{sub_els = [Storage]}]}), sub_els = [#private{sub_els = [Storage]}]}),
#iq{type = result, id = I2, sub_els = []} = recv(), #iq{type = result,
I3 = send(Config, sub_els = [#private{sub_els = [Storage]}]} =
send_recv(
Config,
#iq{type = get, #iq{type = get,
sub_els = [#private{sub_els = [#bookmark_storage{}]}]}), sub_els = [#private{sub_els = [#bookmark_storage{}]}]}),
#iq{type = result, id = I3,
sub_els = [#private{sub_els = [Storage]}]} = recv(),
disconnect(Config). disconnect(Config).
last(Config) -> last(Config) ->
true = is_feature_advertised(Config, ?NS_LAST), true = is_feature_advertised(Config, ?NS_LAST),
ID = send(Config, #iq{type = get, sub_els = [#last{}], #iq{type = result, sub_els = [#last{}]} =
send_recv(Config, #iq{type = get, sub_els = [#last{}],
to = server_jid(Config)}), to = server_jid(Config)}),
#iq{type = result, id = ID, sub_els = [#last{}]} = recv(),
disconnect(Config). disconnect(Config).
privacy(Config) -> privacy(Config) ->
true = is_feature_advertised(Config, ?NS_PRIVACY), true = is_feature_advertised(Config, ?NS_PRIVACY),
I1 = send(Config, #iq{type = get, sub_els = [#privacy{}]}), #iq{type = result, sub_els = [#privacy{}]} =
#iq{type = result, id = I1, sub_els = [#privacy{}]} = recv(), send_recv(Config, #iq{type = get, sub_els = [#privacy{}]}),
JID = <<"tybalt@example.com">>, JID = <<"tybalt@example.com">>,
I2 = send(Config, I1 = send(Config,
#iq{type = set, #iq{type = set,
sub_els = [#privacy{ sub_els = [#privacy{
lists = [#privacy_list{ lists = [#privacy_list{
@ -438,65 +451,74 @@ privacy(Config) ->
type = jid, type = jid,
order = 3, order = 3,
action = deny, action = deny,
stanza = 'presence-in', kinds = ['presence-in'],
value = JID}]}]}]}), value = JID}]}]}]}),
#iq{type = result, id = I2, sub_els = []} = recv(), {Push1, _} =
Push1 = #iq{type = set, ?recv2(
#iq{type = set,
sub_els = [#privacy{ sub_els = [#privacy{
lists = [#privacy_list{ lists = [#privacy_list{
name = <<"public">>}]}]} = recv(), name = <<"public">>}]}]},
#iq{type = result, id = I1, sub_els = []}),
send(Config, make_iq_result(Push1)), send(Config, make_iq_result(Push1)),
I3 = send(Config, #iq{type = set, #iq{type = result, sub_els = []} =
send_recv(Config, #iq{type = set,
sub_els = [#privacy{active = <<"public">>}]}), sub_els = [#privacy{active = <<"public">>}]}),
#iq{type = result, id = I3, sub_els = []} = recv(), #iq{type = result, sub_els = []} =
I4 = send(Config, #iq{type = set, send_recv(Config, #iq{type = set,
sub_els = [#privacy{default = <<"public">>}]}), sub_els = [#privacy{default = <<"public">>}]}),
#iq{type = result, id = I4, sub_els = []} = recv(), #iq{type = result,
I5 = send(Config, #iq{type = get, sub_els = [#privacy{}]}),
#iq{type = result, id = I5,
sub_els = [#privacy{default = <<"public">>, sub_els = [#privacy{default = <<"public">>,
active = <<"public">>, active = <<"public">>,
lists = [#privacy_list{name = <<"public">>}]}]} = recv(), lists = [#privacy_list{name = <<"public">>}]}]} =
I6 = send(Config, send_recv(Config, #iq{type = get, sub_els = [#privacy{}]}),
#iq{type = result, sub_els = []} =
send_recv(Config,
#iq{type = set, sub_els = [#privacy{default = none}]}), #iq{type = set, sub_els = [#privacy{default = none}]}),
#iq{type = result, id = I6, sub_els = []} = recv(), #iq{type = result, sub_els = []} =
I7 = send(Config, #iq{type = set, sub_els = [#privacy{active = none}]}), send_recv(Config, #iq{type = set, sub_els = [#privacy{active = none}]}),
#iq{type = result, id = I7, sub_els = []} = recv(), I2 = send(Config, #iq{type = set,
I8 = send(Config, #iq{type = set,
sub_els = [#privacy{ sub_els = [#privacy{
lists = lists =
[#privacy_list{ [#privacy_list{
name = <<"public">>}]}]}), name = <<"public">>}]}]}),
#iq{type = result, id = I8, sub_els = []} = recv(), {Push2, _} =
%% BUG: We should receive this: ?recv2(
%% _Push2 = #iq{type = set, id = PushI2, sub_els = []} = recv(), #iq{type = set,
%% TODO: this should be fixed in ejabberd
Push2 = #iq{type = set,
sub_els = [#privacy{ sub_els = [#privacy{
lists = [#privacy_list{ lists = [#privacy_list{
name = <<"public">>}]}]} = recv(), name = <<"public">>}]}]},
#iq{type = result, id = I2, sub_els = []}),
send(Config, make_iq_result(Push2)), send(Config, make_iq_result(Push2)),
disconnect(Config). disconnect(Config).
blocking(Config) -> blocking(Config) ->
true = is_feature_advertised(Config, ?NS_BLOCKING), true = is_feature_advertised(Config, ?NS_BLOCKING),
JID = jlib:make_jid(<<"romeo">>, <<"montague.net">>, <<>>), JID = jlib:make_jid(<<"romeo">>, <<"montague.net">>, <<>>),
I1 = send(Config, #iq{type = get, sub_els = [#block_list{}]}), #iq{type = result, sub_els = [#block_list{}]} =
#iq{type = result, id = I1, sub_els = [#block_list{}]} = recv(), send_recv(Config, #iq{type = get, sub_els = [#block_list{}]}),
I2 = send(Config, #iq{type = set, I1 = send(Config, #iq{type = set,
sub_els = [#block{items = [JID]}]}), sub_els = [#block{items = [JID]}]}),
#iq{type = result, id = I2, sub_els = []} = recv(), {Push1, Push2, _} =
#iq{type = set, id = _, ?recv3(
sub_els = [#privacy{lists = [#privacy_list{}]}]} = recv(), #iq{type = set,
#iq{type = set, id = _, sub_els = [#privacy{lists = [#privacy_list{}]}]},
sub_els = [#block{items = [JID]}]} = recv(), #iq{type = set,
I3 = send(Config, #iq{type = set, sub_els = [#block{items = [JID]}]},
#iq{type = result, id = I1, sub_els = []}),
send(Config, make_iq_result(Push1)),
send(Config, make_iq_result(Push2)),
I2 = send(Config, #iq{type = set,
sub_els = [#unblock{items = [JID]}]}), sub_els = [#unblock{items = [JID]}]}),
#iq{type = result, id = I3, sub_els = []} = recv(), {Push3, Push4, _} =
#iq{type = set, id = _, ?recv3(
sub_els = [#privacy{lists = [#privacy_list{}]}]} = recv(), #iq{type = set,
#iq{type = set, id = _, sub_els = [#privacy{lists = [#privacy_list{}]}]},
sub_els = [#unblock{items = [JID]}]} = recv(), #iq{type = set,
sub_els = [#unblock{items = [JID]}]},
#iq{type = result, id = I2, sub_els = []}),
send(Config, make_iq_result(Push3)),
send(Config, make_iq_result(Push4)),
disconnect(Config). disconnect(Config).
vcard(Config) -> vcard(Config) ->
@ -531,23 +553,23 @@ vcard(Config) ->
url = <<"http://www.xmpp.org/xsf/people/stpeter.shtml">>, url = <<"http://www.xmpp.org/xsf/people/stpeter.shtml">>,
desc = <<"More information about me is located on my " desc = <<"More information about me is located on my "
"personal website: http://www.saint-andre.com/">>}, "personal website: http://www.saint-andre.com/">>},
I1 = send(Config, #iq{type = set, sub_els = [VCard]}), #iq{type = result, sub_els = []} =
#iq{type = result, id = I1, sub_els = []} = recv(), send_recv(Config, #iq{type = set, sub_els = [VCard]}),
I2 = send(Config, #iq{type = get, sub_els = [#vcard{}]}),
%% TODO: check if VCard == VCard1. %% TODO: check if VCard == VCard1.
#iq{type = result, id = I2, sub_els = [_VCard1]} = recv(), #iq{type = result, sub_els = [_VCard1]} =
send_recv(Config, #iq{type = get, sub_els = [#vcard{}]}),
disconnect(Config). disconnect(Config).
stats(Config) -> stats(Config) ->
ID = send(Config, #iq{type = get, sub_els = [#stats{}], #iq{type = result, sub_els = [#stats{stat = Stats}]} =
send_recv(Config, #iq{type = get, sub_els = [#stats{}],
to = server_jid(Config)}), to = server_jid(Config)}),
#iq{type = result, id = ID, sub_els = [#stats{stat = Stats}]} = recv(),
lists:foreach( lists:foreach(
fun(#stat{} = Stat) -> fun(#stat{} = Stat) ->
I = send(Config, #iq{type = get, #iq{type = result, sub_els = [_|_]} =
send_recv(Config, #iq{type = get,
sub_els = [#stats{stat = [Stat]}], sub_els = [#stats{stat = [Stat]}],
to = server_jid(Config)}), to = server_jid(Config)})
#iq{type = result, id = I, sub_els = [_|_]} = recv()
end, Stats), end, Stats),
disconnect(Config). disconnect(Config).
@ -557,35 +579,35 @@ pubsub(Config) ->
ItemID = randoms:get_string(), ItemID = randoms:get_string(),
Node = <<"presence">>, Node = <<"presence">>,
Item = #pubsub_item{id = ItemID, sub_els = [#presence{}]}, Item = #pubsub_item{id = ItemID, sub_els = [#presence{}]},
I1 = send(Config, #iq{type = result,
sub_els = [#pubsub{publish = {<<"presence">>,
[#pubsub_item{id = ItemID}]}}]} =
send_recv(Config,
#iq{type = set, to = pubsub_jid(Config), #iq{type = set, to = pubsub_jid(Config),
sub_els = [#pubsub{publish = {Node, [Item]}}]}), sub_els = [#pubsub{publish = {Node, [Item]}}]}),
#iq{type = result, id = I1,
sub_els = [#pubsub{publish = {<<"presence">>,
[#pubsub_item{id = ItemID}]}}]} = recv(),
%% Subscribe to node "presence" %% Subscribe to node "presence"
I2 = send(Config, I = send(Config,
#iq{type = set, to = pubsub_jid(Config), #iq{type = set, to = pubsub_jid(Config),
sub_els = [#pubsub{subscribe = {Node, my_jid(Config)}}]}), sub_els = [#pubsub{subscribe = {Node, my_jid(Config)}}]}),
#message{sub_els = [#pubsub_event{}, #delay{}]} = recv(), ?recv2(
#iq{type = result, id = I2} = recv(), #message{sub_els = [#pubsub_event{}, #delay{}]},
#iq{type = result, id = I}),
%% Get subscriptions %% Get subscriptions
true = is_feature_advertised(Config, ?PUBSUB("retrieve-subscriptions")), true = is_feature_advertised(Config, ?PUBSUB("retrieve-subscriptions")),
I3 = send(Config, #iq{type = get, to = pubsub_jid(Config), #iq{type = result,
sub_els = [#pubsub{subscriptions = {none, []}}]}),
#iq{type = result, id = I3,
sub_els = sub_els =
[#pubsub{subscriptions = [#pubsub{subscriptions =
{none, [#pubsub_subscription{node = Node}]}}]} = recv(), {none, [#pubsub_subscription{node = Node}]}}]} =
send_recv(Config, #iq{type = get, to = pubsub_jid(Config),
sub_els = [#pubsub{subscriptions = {none, []}}]}),
%% Get affiliations %% Get affiliations
true = is_feature_advertised(Config, ?PUBSUB("retrieve-affiliations")), true = is_feature_advertised(Config, ?PUBSUB("retrieve-affiliations")),
I4 = send(Config, #iq{type = get, to = pubsub_jid(Config), #iq{type = result,
sub_els = [#pubsub{affiliations = []}]}),
#iq{type = result, id = I4,
sub_els = [#pubsub{ sub_els = [#pubsub{
affiliations = affiliations =
[#pubsub_affiliation{node = Node, type = owner}]}]} [#pubsub_affiliation{node = Node, type = owner}]}]} =
= recv(), send_recv(Config, #iq{type = get, to = pubsub_jid(Config),
sub_els = [#pubsub{affiliations = []}]}),
disconnect(Config). disconnect(Config).
auth_md5(Config) -> auth_md5(Config) ->
@ -608,7 +630,7 @@ auth_plain(Config) ->
{skipped, 'PLAIN_not_available'} {skipped, 'PLAIN_not_available'}
end. end.
roster_master(Config) -> roster_subscribe_master(Config) ->
send(Config, #presence{}), send(Config, #presence{}),
#presence{} = recv(), #presence{} = recv(),
wait_for_slave(Config), wait_for_slave(Config),
@ -654,35 +676,13 @@ roster_master(Config) ->
subscription = both}]}]}, subscription = both}]}]},
#iq{type = result, id = I1, sub_els = []}), #iq{type = result, id = I1, sub_els = []}),
send(Config, make_iq_result(Push5)), send(Config, make_iq_result(Push5)),
#iq{sub_els = [#roster{items = [#roster_item{groups = G1}]}]} = Push5,
Groups = lists:sort(G1),
wait_for_slave(Config), wait_for_slave(Config),
#presence{type = unavailable, from = Peer} = recv(), #presence{type = unavailable, from = Peer} = recv(),
%% The peer removed us from.
%% {Push6, Push7, _, _, _} =
%% ?recv5(
%% %% TODO: I guess this can be optimized, we don't need
%% %% to send transient roster push with subscription = 'to'.
%% #iq{type = set,
%% sub_els =
%% [#roster{items = [#roster_item{
%% jid = LPeer,
%% subscription = to}]}]},
%% #iq{type = set,
%% sub_els =
%% [#roster{items = [#roster_item{
%% jid = LPeer,
%% subscription = none}]}]},
%% #presence{type = unsubscribe, from = LPeer},
%% #presence{type = unsubscribed, from = LPeer},
%% #presence{type = unavailable, from = Peer}),
%% send(Config, make_iq_result(Push6)),
%% send(Config, make_iq_result(Push7)),
%% #iq{sub_els = [#roster{items = [#roster_item{groups = G1}]}]} = Push5,
%% #iq{sub_els = [#roster{items = [#roster_item{groups = G2}]}]} = Push6,
%% #iq{sub_els = [#roster{items = [#roster_item{groups = G3}]}]} = Push7,
%% Groups = lists:sort(G1), Groups = lists:sort(G2), Groups = lists:sort(G3),
disconnect(Config). disconnect(Config).
roster_slave(Config) -> roster_subscribe_slave(Config) ->
send(Config, #presence{}), send(Config, #presence{}),
#presence{} = recv(), #presence{} = recv(),
wait_for_master(Config), wait_for_master(Config),
@ -711,39 +711,86 @@ roster_slave(Config) ->
send(Config, make_iq_result(Push3)), send(Config, make_iq_result(Push3)),
#presence{type = undefined, from = Peer} = recv(), #presence{type = undefined, from = Peer} = recv(),
wait_for_master(Config), wait_for_master(Config),
disconnect(Config).
roster_remove_master(Config) ->
MyJID = my_jid(Config),
Peer = ?config(slave, Config),
LPeer = jlib:jid_remove_resource(Peer),
Groups = [<<"A">>, <<"B">>],
wait_for_slave(Config),
send(Config, #presence{}),
?recv2(#presence{from = MyJID, type = undefined},
#presence{from = Peer, type = undefined}),
%% The peer removed us from its roster.
{Push1, Push2, _, _, _} =
?recv5(
%% TODO: I guess this can be optimized, we don't need
%% to send transient roster push with subscription = 'to'.
#iq{type = set,
sub_els =
[#roster{items = [#roster_item{
jid = LPeer,
subscription = to}]}]},
#iq{type = set,
sub_els =
[#roster{items = [#roster_item{
jid = LPeer,
subscription = none}]}]},
#presence{type = unsubscribe, from = LPeer},
#presence{type = unsubscribed, from = LPeer},
#presence{type = unavailable, from = Peer}),
send(Config, make_iq_result(Push1)),
send(Config, make_iq_result(Push2)),
#iq{sub_els = [#roster{items = [#roster_item{groups = G1}]}]} = Push1,
#iq{sub_els = [#roster{items = [#roster_item{groups = G2}]}]} = Push2,
Groups = lists:sort(G1), Groups = lists:sort(G2),
disconnect(Config).
roster_remove_slave(Config) ->
MyJID = my_jid(Config),
Peer = ?config(master, Config),
LPeer = jlib:jid_remove_resource(Peer),
send(Config, #presence{}),
#presence{from = MyJID, type = undefined} = recv(),
wait_for_master(Config),
#presence{from = Peer, type = undefined} = recv(),
%% Remove the peer from roster. %% Remove the peer from roster.
%% Item = #roster_item{jid = LPeer, subscription = remove}, Item = #roster_item{jid = LPeer, subscription = remove},
%% I1 = send(Config, #iq{type = set, sub_els = [#roster{items = [Item]}]}), I = send(Config, #iq{type = set, sub_els = [#roster{items = [Item]}]}),
%% {Push4, _} = ?recv2( {Push, _} = ?recv2(
%% #iq{type = set, #iq{type = set,
%% sub_els = sub_els =
%% [#roster{items = [#roster_item{ [#roster{items = [#roster_item{
%% jid = LPeer, jid = LPeer,
%% subscription = remove}]}]}, subscription = remove}]}]},
%% #iq{type = result, id = I1, sub_els = []}), #iq{type = result, id = I, sub_els = []}),
%% send(Config, make_iq_result(Push4)), send(Config, make_iq_result(Push)),
%% #presence{type = unavailable, from = Peer} = recv(), #presence{type = unavailable, from = Peer} = recv(),
disconnect(Config). disconnect(Config).
proxy65_master(Config) -> proxy65_master(Config) ->
Proxy = proxy_jid(Config), Proxy = proxy_jid(Config),
MyJID = my_jid(Config), MyJID = my_jid(Config),
Peer = ?config(slave, Config), Peer = ?config(slave, Config),
wait_for_slave(Config),
send(Config, #presence{}), send(Config, #presence{}),
?recv2(#presence{from = MyJID, type = undefined}, ?recv2(#presence{from = MyJID, type = undefined},
#presence{from = Peer, type = undefined}), #presence{from = Peer, type = undefined}),
true = is_feature_advertised(Config, ?NS_BYTESTREAMS, Proxy), true = is_feature_advertised(Config, ?NS_BYTESTREAMS, Proxy),
I1 = send(Config, #iq{type = get, sub_els = [#bytestreams{}], to = Proxy}), #iq{type = result, sub_els = [#bytestreams{hosts = [StreamHost]}]} =
#iq{type = result, id = I1, send_recv(
sub_els = [#bytestreams{hosts = [StreamHost]}]} = recv(), Config,
#iq{type = get, sub_els = [#bytestreams{}], to = Proxy}),
SID = randoms:get_string(), SID = randoms:get_string(),
Data = crypto:rand_bytes(1024), Data = crypto:rand_bytes(1024),
put_event(Config, {StreamHost, SID, Data}), put_event(Config, {StreamHost, SID, Data}),
Socks5 = socks5_connect(StreamHost, {SID, MyJID, Peer}), Socks5 = socks5_connect(StreamHost, {SID, MyJID, Peer}),
I2 = send(Config, wait_for_slave(Config),
#iq{type = result, sub_els = []} =
send_recv(Config,
#iq{type = set, to = Proxy, #iq{type = set, to = Proxy,
sub_els = [#bytestreams{activate = Peer, sid = SID}]}), sub_els = [#bytestreams{activate = Peer, sid = SID}]}),
#iq{type = result, id = I2, sub_els = []} = recv(),
socks5_send(Socks5, Data), socks5_send(Socks5, Data),
#presence{type = unavailable, from = Peer} = recv(), #presence{type = unavailable, from = Peer} = recv(),
disconnect(Config). disconnect(Config).
@ -752,13 +799,89 @@ proxy65_slave(Config) ->
MyJID = my_jid(Config), MyJID = my_jid(Config),
Peer = ?config(master, Config), Peer = ?config(master, Config),
send(Config, #presence{}), send(Config, #presence{}),
?recv2(#presence{from = MyJID, type = undefined}, #presence{from = MyJID, type = undefined} = recv(),
#presence{from = Peer, type = undefined}), wait_for_master(Config),
#presence{from = Peer, type = undefined} = recv(),
{StreamHost, SID, Data} = get_event(Config), {StreamHost, SID, Data} = get_event(Config),
Socks5 = socks5_connect(StreamHost, {SID, Peer, MyJID}), Socks5 = socks5_connect(StreamHost, {SID, Peer, MyJID}),
wait_for_master(Config),
socks5_recv(Socks5, Data), socks5_recv(Socks5, Data),
disconnect(Config). disconnect(Config).
muc_single(Config) ->
MyJID = my_jid(Config),
MUC = muc_jid(Config),
Room = muc_room_jid(Config),
Nick = ?config(user, Config),
NickJID = jlib:jid_replace_resource(Room, Nick),
true = is_feature_advertised(Config, ?NS_MUC, MUC),
%% Joining
send(Config, #presence{to = NickJID, 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")
%% 3. Room history (if any)
%% 4. The room subject
%% 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,
sub_els = [#muc_user{
status_codes = Codes,
items = [#muc_item{role = moderator,
jid = MyJID,
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),
%% Request the configuration
#iq{type = result, sub_els = [#muc_owner{config = #xdata{} = RoomCfg}]} =
send_recv(Config, #iq{type = get, sub_els = [#muc_owner{}],
to = Room}),
NewFields =
lists:flatmap(
fun(#xdata_field{var = Var, values = OrigVals}) ->
Vals = case Var of
<<"FORM_TYPE">> ->
OrigVals;
<<"muc#roomconfig_roomname">> ->
[<<"Test room">>];
<<"muc#roomconfig_roomdesc">> ->
[<<"Trying to break the server">>];
<<"muc#roomconfig_persistentroom">> ->
[<<"1">>];
<<"muc#roomconfig_changesubject">> ->
[<<"0">>];
<<"muc#roomconfig_allowinvites">> ->
[<<"1">>];
_ ->
[]
end,
if Vals /= [] ->
[#xdata_field{values = Vals, var = Var}];
true ->
[]
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),
disconnect(Config).
auth_SASL(Mech, Config) -> auth_SASL(Mech, Config) ->
{Response, SASL} = sasl_new(Mech, {Response, SASL} = sasl_new(Mech,
?config(user, Config), ?config(user, Config),
@ -800,8 +923,9 @@ re_register(Config) ->
recv() -> recv() ->
receive receive
{'$gen_event', {xmlstreamelement, El}} -> {'$gen_event', {xmlstreamelement, El}} ->
ct:pal("recv: ~p", [El]), Pkt = xmpp_codec:decode(fix_ns(El)),
xmpp_codec:decode(fix_ns(El)); ct:pal("recv: ~p ->~n~s", [El, xmpp_codec:pp(Pkt)]),
Pkt;
{'$gen_event', Event} -> {'$gen_event', Event} ->
Event Event
end. end.
@ -837,10 +961,14 @@ send(State, Pkt) ->
{undefined, Pkt} {undefined, Pkt}
end, end,
El = xmpp_codec:encode(NewPkt), El = xmpp_codec:encode(NewPkt),
ct:pal("sent: ~p", [El]), ct:pal("sent: ~p <-~n~s", [El, xmpp_codec:pp(NewPkt)]),
ok = send_text(State, xml:element_to_binary(El)), ok = send_text(State, xml:element_to_binary(El)),
NewID. NewID.
send_recv(State, IQ) ->
ID = send(State, IQ),
#iq{id = ID} = recv().
sasl_new(<<"PLAIN">>, User, Server, Password) -> sasl_new(<<"PLAIN">>, User, Server, Password) ->
{<<User/binary, $@, Server/binary, 0, User/binary, 0, Password/binary>>, {<<User/binary, $@, Server/binary, 0, User/binary, 0, Password/binary>>,
fun (_) -> {error, <<"Invalid SASL challenge">>} end}; fun (_) -> {error, <<"Invalid SASL challenge">>} end};
@ -934,6 +1062,14 @@ proxy_jid(Config) ->
Server = ?config(server, Config), Server = ?config(server, Config),
jlib:make_jid(<<>>, <<"proxy.", Server/binary>>, <<>>). jlib:make_jid(<<>>, <<"proxy.", Server/binary>>, <<>>).
muc_jid(Config) ->
Server = ?config(server, Config),
jlib:make_jid(<<>>, <<"conference.", Server/binary>>, <<>>).
muc_room_jid(Config) ->
Server = ?config(server, Config),
jlib:make_jid(<<"test">>, <<"conference.", Server/binary>>, <<>>).
id() -> id() ->
id(undefined). id(undefined).

File diff suppressed because it is too large Load Diff

View File

@ -1,31 +1,33 @@
%% Created automatically by XML generator (xml_gen.erl) %% Created automatically by XML generator (xml_gen.erl)
%% Source: xmpp_codec.spec %% Source: xmpp_codec.spec
%% Date: Sat, 15 Jun 2013 16:36:04 GMT
-record(last, {seconds, text}). -record(bind, {jid, resource}).
-record(version,
{version_name, version_ver, version_os}).
-record(roster_item,
{jid, name, groups = [], subscription = none, ask}).
-record(roster, {items = [], ver}).
-record(privacy_item,
{order, action, type, value, stanza}).
-record(privacy_list, {name, items = []}).
-record(privacy, {lists = [], default, active}).
-record(block, {items = []}). -record(block, {items = []}).
-record(unblock, {items = []}).
-record(block_list, {}). -record(block_list, {}).
-record(identity, {category, type, name}). -record(bookmark_conference,
{name, jid, autojoin = false, nick, password}).
-record(bookmark_storage, {conference = [], url = []}).
-record(bookmark_url, {name, url}).
-record(bytestreams,
{hosts = [], used, activate, dstaddr, mode = tcp, sid}).
-record(caps, {hash, node, ver}).
-record(compress, {methods = []}).
-record(compress_failure, {reason}).
-record(compressed, {}).
-record(compression, {methods = []}).
-record(delay, {stamp, from}).
-record(disco_info, -record(disco_info,
{node, identity = [], feature = [], xdata = []}). {node, identity = [], feature = [], xdata = []}).
@ -34,77 +36,91 @@
-record(disco_items, {node, items = []}). -record(disco_items, {node, items = []}).
-record(private, {sub_els = []}). -record(error, {type, by, reason, text}).
-record(bookmark_conference, -record(feature_register, {}).
{name, jid, autojoin = false, nick, password}).
-record(bookmark_url, {name, url}). -record(gone, {uri}).
-record(bookmark_storage, {conference = [], url = []}). -record(identity, {category, type, name}).
-record(stat, {name, units, value, error = []}).
-record(stats, {stat = []}).
-record(iq, -record(iq,
{id, type, lang, from, to, error, sub_els = []}). {id, type, lang, from, to, error, sub_els = []}).
-record(last, {seconds, text}).
-record(legacy_delay, {stamp, from}).
-record(message, -record(message,
{id, type = normal, lang, from, to, subject = [], {id, type = normal, lang, from, to, subject = [],
body = [], thread, error, sub_els = []}). body = [], thread, error, sub_els = []}).
-record(presence, -record(muc, {history, password}).
{id, type, lang, from, to, show, status = [], priority,
error, sub_els = []}).
-record(gone, {uri}). -record(muc_actor, {jid, nick}).
-record(redirect, {uri}). -record(muc_decline, {reason, from, to}).
-record(error, {type, by, reason, text}). -record(muc_history,
{maxchars, maxstanzas, seconds, since}).
-record(bind, {jid, resource}). -record(muc_invite, {reason, from, to}).
-record(sasl_auth, {mechanism, text}). -record(muc_item,
{actor, continue, reason, affiliation, role, jid,
nick}).
-record(sasl_abort, {}). -record(muc_owner, {destroy, config}).
-record(sasl_challenge, {text}). -record(muc_owner_destroy, {jid, reason, password}).
-record(sasl_response, {text}). -record(muc_user,
{decline, destroy, invites = [], items = [],
status_codes = [], password}).
-record(sasl_success, {text}). -record(muc_user_destroy, {reason, jid}).
-record(sasl_failure, {reason, text = []}). -record(p1_ack, {}).
-record(sasl_mechanisms, {list = []}).
-record(starttls, {required = false}).
-record(starttls_proceed, {}).
-record(starttls_failure, {}).
-record(compress_failure, {reason}).
-record(compress, {methods = []}).
-record(compressed, {}).
-record(compression, {methods = []}).
-record(stream_features, {sub_els = []}).
-record(p1_push, {}). -record(p1_push, {}).
-record(p1_rebind, {}). -record(p1_rebind, {}).
-record(p1_ack, {}). -record(ping, {}).
-record(caps, {hash, node, ver}). -record(presence,
{id, type, lang, from, to, show, status = [], priority,
error, sub_els = []}).
-record(feature_register, {}). -record(privacy, {lists = [], default, active}).
-record(privacy_item,
{order, action, type, value, kinds = []}).
-record(privacy_list, {name, items = []}).
-record(private, {sub_els = []}).
-record(pubsub,
{subscriptions, affiliations, publish, subscribe}).
-record(pubsub_affiliation, {node, type}).
-record(pubsub_event, {items = []}).
-record(pubsub_event_item, {id, node, publisher}).
-record(pubsub_event_items,
{node, retract = [], items = []}).
-record(pubsub_item, {id, sub_els = []}).
-record(pubsub_items,
{node, max_items, subid, items = []}).
-record(pubsub_subscription, {jid, node, subid, type}).
-record(redirect, {uri}).
-record(register, -record(register,
{registered = false, remove = false, instructions, {registered = false, remove = false, instructions,
@ -112,52 +128,50 @@
address, city, state, zip, phone, url, date, misc, text, address, city, state, zip, phone, url, date, misc, text,
key}). key}).
-record(session, {}). -record(roster, {items = [], ver}).
-record(ping, {}). -record(roster_item,
{jid, name, groups = [], subscription = none, ask}).
-record(time, {tzo, utc}). -record(sasl_abort, {}).
-record(sasl_auth, {mechanism, text}).
-record(sasl_challenge, {text}).
-record(sasl_failure, {reason, text = []}).
-record(sasl_mechanisms, {list = []}).
-record(sasl_response, {text}).
-record(sasl_success, {text}).
-record('see-other-host', {host}). -record('see-other-host', {host}).
-record(session, {}).
-record(starttls, {required = false}).
-record(starttls_failure, {}).
-record(starttls_proceed, {}).
-record(stat, {name, units, value, error = []}).
-record(stats, {stat = []}).
-record(stream_error, {reason, text}). -record(stream_error, {reason, text}).
-record(vcard_name, -record(stream_features, {sub_els = []}).
{family, given, middle, prefix, suffix}).
-record(vcard_adr, -record(streamhost, {jid, host, port = 1080}).
{home = false, work = false, postal = false,
parcel = false, dom = false, intl = false, pref = false,
pobox, extadd, street, locality, region, pcode, ctry}).
-record(vcard_label, -record(text, {lang, data}).
{home = false, work = false, postal = false,
parcel = false, dom = false, intl = false, pref = false,
line = []}).
-record(vcard_tel, -record(time, {tzo, utc}).
{home = false, work = false, voice = false, fax = false,
pager = false, msg = false, cell = false, video = false,
bbs = false, modem = false, isdn = false, pcs = false,
pref = false, number}).
-record(vcard_email, -record(unblock, {items = []}).
{home = false, work = false, internet = false,
pref = false, x400 = false, userid}).
-record(vcard_geo, {lat, lon}).
-record(vcard_logo, {type, binval, extval}).
-record(vcard_photo, {type, binval, extval}).
-record(vcard_org, {name, units = []}).
-record(vcard_sound, {phonetic, binval, extval}).
-record(vcard_key, {type, cred}).
-record(vcard_agent, {vcard, extval}).
-record(vcard, -record(vcard,
{version, fn, n, nickname, photo, bday, adr = [], {version, fn, n, nickname, photo, bday, adr = [],
@ -166,38 +180,50 @@
prodid, agent, rev, sort_string, sound, uid, url, class, prodid, agent, rev, sort_string, sound, uid, url, class,
key, desc}). key, desc}).
-record(xdata_field, -record(vcard_adr,
{label, type, var, required = false, desc, values = [], {home = false, work = false, postal = false,
options = []}). parcel = false, dom = false, intl = false, pref = false,
pobox, extadd, street, locality, region, pcode, ctry}).
-record(vcard_agent, {vcard, extval}).
-record(vcard_email,
{home = false, work = false, internet = false,
pref = false, x400 = false, userid}).
-record(vcard_geo, {lat, lon}).
-record(vcard_key, {type, cred}).
-record(vcard_label,
{home = false, work = false, postal = false,
parcel = false, dom = false, intl = false, pref = false,
line = []}).
-record(vcard_logo, {type, binval, extval}).
-record(vcard_name,
{family, given, middle, prefix, suffix}).
-record(vcard_org, {name, units = []}).
-record(vcard_photo, {type, binval, extval}).
-record(vcard_sound, {phonetic, binval, extval}).
-record(vcard_tel,
{home = false, work = false, voice = false, fax = false,
pager = false, msg = false, cell = false, video = false,
bbs = false, modem = false, isdn = false, pcs = false,
pref = false, number}).
-record(version,
{version_name, version_ver, version_os}).
-record(xdata, -record(xdata,
{type, instructions = [], title, reported, items = [], {type, instructions = [], title, reported, items = [],
fields = []}). fields = []}).
-record(pubsub_subscription, {jid, node, subid, type}). -record(xdata_field,
{label, type, var, required = false, desc, values = [],
-record(pubsub_affiliation, {node, type}). options = []}).
-record(pubsub_item, {id, sub_els = []}).
-record(pubsub_items,
{node, max_items, subid, items = []}).
-record(pubsub_event_item, {id, node, publisher}).
-record(pubsub_event_items,
{node, retract = [], items = []}).
-record(pubsub_event, {items = []}).
-record(pubsub,
{subscriptions, affiliations, publish, subscribe}).
-record(delay, {stamp, from}).
-record(legacy_delay, {stamp, from}).
-record(streamhost, {jid, host, port = 1080}).
-record(bytestreams,
{hosts = [], used, activate, dstaddr, mode = tcp, sid}).

View File

@ -85,7 +85,7 @@
#elem{name = <<"item">>, #elem{name = <<"item">>,
xmlns = <<"jabber:iq:privacy">>, xmlns = <<"jabber:iq:privacy">>,
result = {privacy_item, '$order', '$action', '$type', result = {privacy_item, '$order', '$action', '$type',
'$value', '$stanza'}, '$value', '$kinds'},
attrs = [#attr{name = <<"action">>, attrs = [#attr{name = <<"action">>,
required = true, required = true,
dec = {dec_enum, [[allow, deny]]}, dec = {dec_enum, [[allow, deny]]},
@ -99,17 +99,13 @@
enc = {enc_enum, []}}, enc = {enc_enum, []}},
#attr{name = <<"value">>}], #attr{name = <<"value">>}],
refs = [#ref{name = privacy_message, refs = [#ref{name = privacy_message,
min = 0, max = 1, label = '$kinds'},
label = '$stanza'},
#ref{name = privacy_iq, #ref{name = privacy_iq,
min = 0, max = 1, label = '$kinds'},
label = '$stanza'},
#ref{name = privacy_presence_in, #ref{name = privacy_presence_in,
min = 0, max = 1, label = '$kinds'},
label = '$stanza'},
#ref{name = privacy_presence_out, #ref{name = privacy_presence_out,
min = 0, max = 1, label = '$kinds'}]}}.
label = '$stanza'}]}}.
{privacy_list, {privacy_list,
#elem{name = <<"list">>, #elem{name = <<"list">>,
@ -328,13 +324,15 @@
{message_subject, {message_subject,
#elem{name = <<"subject">>, #elem{name = <<"subject">>,
xmlns = <<"jabber:client">>, xmlns = <<"jabber:client">>,
result = {'$lang', '$cdata'}, result = {text, '$lang', '$data'},
cdata = #cdata{label = '$data'},
attrs = [#attr{name = <<"xml:lang">>, label = '$lang'}]}}. attrs = [#attr{name = <<"xml:lang">>, label = '$lang'}]}}.
{message_body, {message_body,
#elem{name = <<"body">>, #elem{name = <<"body">>,
xmlns = <<"jabber:client">>, xmlns = <<"jabber:client">>,
result = {'$lang', '$cdata'}, result = {text, '$lang', '$data'},
cdata = #cdata{label = '$data'},
attrs = [#attr{name = <<"xml:lang">>, label = '$lang'}]}}. attrs = [#attr{name = <<"xml:lang">>, label = '$lang'}]}}.
{message_thread, {message_thread,
@ -376,7 +374,8 @@
{presence_status, {presence_status,
#elem{name = <<"status">>, #elem{name = <<"status">>,
xmlns = <<"jabber:client">>, xmlns = <<"jabber:client">>,
result = {'$lang', '$cdata'}, result = {text, '$lang', '$data'},
cdata = #cdata{label = '$data'},
attrs = [#attr{name = <<"xml:lang">>, attrs = [#attr{name = <<"xml:lang">>,
label = '$lang'}]}}. label = '$lang'}]}}.
@ -505,7 +504,8 @@
{error_text, {error_text,
#elem{name = <<"text">>, #elem{name = <<"text">>,
result = {'$lang', '$cdata'}, result = {text, '$lang', '$data'},
cdata = #cdata{label = '$data'},
xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>, xmlns = <<"urn:ietf:params:xml:ns:xmpp-stanzas">>,
attrs = [#attr{name = <<"xml:lang">>, attrs = [#attr{name = <<"xml:lang">>,
label = '$lang'}]}}. label = '$lang'}]}}.
@ -634,7 +634,8 @@
{sasl_failure_text, {sasl_failure_text,
#elem{name = <<"text">>, #elem{name = <<"text">>,
xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>, xmlns = <<"urn:ietf:params:xml:ns:xmpp-sasl">>,
result = {'$lang', '$cdata'}, result = {text, '$lang', '$data'},
cdata = #cdata{label = '$data'},
attrs = [#attr{name = <<"xml:lang">>, attrs = [#attr{name = <<"xml:lang">>,
label = '$lang'}]}}. label = '$lang'}]}}.
@ -1019,8 +1020,8 @@
{stream_error_text, {stream_error_text,
#elem{name = <<"text">>, #elem{name = <<"text">>,
result = {'$lang', '$text'}, result = {text, '$lang', '$data'},
cdata = #cdata{label = '$text'}, cdata = #cdata{label = '$data'},
xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>, xmlns = <<"urn:ietf:params:xml:ns:xmpp-streams">>,
attrs = [#attr{name = <<"xml:lang">>, attrs = [#attr{name = <<"xml:lang">>,
label = '$lang'}]}}. label = '$lang'}]}}.
@ -1762,6 +1763,164 @@
#ref{name = bytestreams_activate, #ref{name = bytestreams_activate,
min = 0, max = 1, label = '$activate'}]}}. min = 0, max = 1, label = '$activate'}]}}.
{muc_history,
#elem{name = <<"history">>,
xmlns = <<"http://jabber.org/protocol/muc">>,
result = {muc_history, '$maxchars', '$maxstanzas',
'$seconds', '$since'},
attrs = [#attr{name = <<"maxchars">>,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}},
#attr{name = <<"maxstanzas">>,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}},
#attr{name = <<"seconds">>,
dec = {dec_int, [0, infinity]},
enc = {enc_int, []}},
#attr{name = <<"since">>,
dec = {dec_utc, []},
enc = {enc_utc, []}}]}}.
{muc_user_reason,
#elem{name = <<"reason">>,
xmlns = <<"http://jabber.org/protocol/muc#user">>,
result = '$cdata'}}.
{muc_user_decline,
#elem{name = <<"decline">>,
xmlns = <<"http://jabber.org/protocol/muc#user">>,
result = {muc_decline, '$reason', '$from', '$to'},
attrs = [#attr{name = <<"to">>,
dec = {dec_jid, []},
enc = {enc_jid, []}},
#attr{name = <<"from">>,
dec = {dec_jid, []},
enc = {enc_jid, []}}],
refs = [#ref{name = muc_user_reason, min = 0,
max = 1, label = '$reason'}]}}.
{muc_user_destroy,
#elem{name = <<"destroy">>,
xmlns = <<"http://jabber.org/protocol/muc#user">>,
result = {muc_user_destroy, '$reason', '$jid'},
attrs = [#attr{name = <<"jid">>,
dec = {dec_jid, []},
enc = {enc_jid, []}}],
refs = [#ref{name = muc_user_reason, min = 0,
max = 1, label = '$reason'}]}}.
{muc_user_invite,
#elem{name = <<"invite">>,
xmlns = <<"http://jabber.org/protocol/muc#user">>,
result = {muc_invite, '$reason', '$from', '$to'},
attrs = [#attr{name = <<"to">>,
dec = {dec_jid, []},
enc = {enc_jid, []}},
#attr{name = <<"from">>,
dec = {dec_jid, []},
enc = {enc_jid, []}}],
refs = [#ref{name = muc_user_reason, min = 0,
max = 1, label = '$reason'}]}}.
{muc_user_actor,
#elem{name = <<"actor">>,
xmlns = <<"http://jabber.org/protocol/muc#user">>,
result = {muc_actor, '$jid', '$nick'},
attrs = [#attr{name = <<"jid">>,
dec = {dec_jid, []},
enc = {enc_jid, []}},
#attr{name = <<"nick">>}]}}.
{muc_user_continue,
#elem{name = <<"continue">>,
xmlns = <<"http://jabber.org/protocol/muc#user">>,
result = '$thread',
attrs = [#attr{name = <<"thread">>}]}}.
{muc_user_status,
#elem{name = <<"status">>,
xmlns = <<"http://jabber.org/protocol/muc#user">>,
result = '$code',
attrs = [#attr{name = <<"code">>,
dec = {dec_int, [100, 999]},
enc = {enc_int, []}}]}}.
{muc_user_item,
#elem{name = <<"item">>,
xmlns = <<"http://jabber.org/protocol/muc#user">>,
result = {muc_item, '$actor', '$continue', '$reason',
'$affiliation', '$role', '$jid', '$nick'},
refs = [#ref{name = muc_user_actor,
min = 0, max = 1, label = '$actor'},
#ref{name = muc_user_continue,
min = 0, max = 1, label = '$continue'},
#ref{name = muc_user_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">>}]}}.
{muc_user,
#elem{name = <<"x">>,
xmlns = <<"http://jabber.org/protocol/muc#user">>,
result = {muc_user, '$decline', '$destroy', '$invites',
'$items', '$status_codes', '$password'},
attrs = [#attr{name = <<"password">>}],
refs = [#ref{name = muc_user_decline, min = 0,
max = 1, label = '$decline'},
#ref{name = muc_user_destroy, min = 0, max = 1,
label = '$destroy'},
#ref{name = muc_user_invite, label = '$invites'},
#ref{name = muc_user_item, label = '$items'},
#ref{name = muc_user_status, label = '$status_codes'}]}}.
{muc_owner_password,
#elem{name = <<"password">>,
xmlns = <<"http://jabber.org/protocol/muc#owner">>,
result = '$cdata'}}.
{muc_owner_reason,
#elem{name = <<"reason">>,
xmlns = <<"http://jabber.org/protocol/muc#owner">>,
result = '$cdata'}}.
{muc_owner_destroy,
#elem{name = <<"destroy">>,
xmlns = <<"http://jabber.org/protocol/muc#owner">>,
result = {muc_owner_destroy, '$jid', '$reason', '$password'},
attrs = [#attr{name = <<"jid">>,
dec = {dec_jid, []},
enc = {enc_jid, []}}],
refs = [#ref{name = muc_owner_password, min = 0, max = 1,
label = '$password'},
#ref{name = muc_owner_reason, min = 0, max = 1,
label = '$reason'}]}}.
{muc_owner,
#elem{name = <<"query">>,
xmlns = <<"http://jabber.org/protocol/muc#owner">>,
result = {muc_owner, '$destroy', '$config'},
refs = [#ref{name = muc_owner_destroy, min = 0, max = 1,
label = '$destroy'},
#ref{name = xdata, min = 0, max = 1, label = '$config'}]}}.
{muc,
#elem{name = <<"x">>,
xmlns = <<"http://jabber.org/protocol/muc">>,
result = {muc, '$history', '$password'},
attrs = [#attr{name = <<"password">>}],
refs = [#ref{name = muc_history, min = 0, max = 1,
label = '$history'}]}}.
dec_tzo(Val) -> dec_tzo(Val) ->
[H1, M1] = str:tokens(Val, <<":">>), [H1, M1] = str:tokens(Val, <<":">>),
H = erlang:binary_to_integer(H1), H = erlang:binary_to_integer(H1),