wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
try xmpp:decode(#xmlel{name = Name, attrs = Attrs}) of
- #stream_start{xmlns = NS_CLIENT, stream_xmlns = NS_STREAM, lang = Lang}
+ #stream_start{xmlns = NS_CLIENT, stream_xmlns = NS_STREAM,
+ version = Version, lang = Lang}
when NS_CLIENT /= ?NS_CLIENT; NS_STREAM /= ?NS_STREAM ->
- send_header(StateData, ?MYNAME, <<"">>, Lang),
+ send_header(StateData, ?MYNAME, Version, Lang),
send_element(StateData, xmpp:serr_invalid_namespace()),
{stop, normal, StateData};
- #stream_start{lang = Lang} when byte_size(Lang) > 35 ->
+ #stream_start{lang = Lang, version = Version} when byte_size(Lang) > 35 ->
%% As stated in BCP47, 4.4.1:
%% Protocols or specifications that specify limited buffer sizes for
%% language tags MUST allow for language tags of at least 35 characters.
%% Do not store long language tag to avoid possible DoS/flood attacks
- send_header(StateData, ?MYNAME, <<"">>, ?MYLANG),
+ send_header(StateData, ?MYNAME, Version, ?MYLANG),
Txt = <<"Too long value of 'xml:lang' attribute">>,
send_element(StateData,
xmpp:serr_policy_violation(Txt, ?MYLANG)),
{stop, normal, StateData};
- #stream_start{to = undefined, lang = Lang} ->
+ #stream_start{to = undefined, lang = Lang, version = Version} ->
Txt = <<"Missing 'to' attribute">>,
- send_header(StateData, ?MYNAME, <<"">>, Lang),
+ send_header(StateData, ?MYNAME, Version, Lang),
send_element(StateData,
xmpp:serr_improper_addressing(Txt, Lang)),
{stop, normal, StateData};
end
end;
_ ->
- send_header(StateData, Server, <<"">>, ?MYLANG),
+ send_header(StateData, Server, StreamVersion, ?MYLANG),
if not StateData#state.tls_enabled and
StateData#state.tls_required ->
send_element(
end
catch _:{xmpp_codec, Why} ->
Txt = xmpp:format_error(Why),
- send_header(StateData, ?MYNAME, <<"">>, ?MYLANG),
+ send_header(StateData, ?MYNAME, <<"1.0">>, ?MYLANG),
send_element(StateData, xmpp:serr_not_well_formed(Txt, ?MYLANG)),
{stop, normal, StateData}
end;
decode_element(El, wait_for_auth, StateData);
wait_for_auth(Pkt, StateData) when ?IS_STREAM_MGMT_PACKET(Pkt) ->
fsm_next_state(wait_for_auth, dispatch_stream_mgmt(Pkt, StateData));
-wait_for_auth(#iq{type = get,
- sub_els = [#legacy_auth{username = U}]} = IQ, StateData) ->
- Username = case U of
- undefined -> <<"">>;
- _ -> U
- end,
- Auth = #legacy_auth{username = Username, password = <<>>, resource = <<>>},
+wait_for_auth(#iq{type = get, sub_els = [#legacy_auth{}]} = IQ, StateData) ->
+ Auth = #legacy_auth{username = <<>>, password = <<>>, resource = <<>>},
Res = case ejabberd_auth:plain_password_required(StateData#state.server) of
false ->
xmpp:make_iq_result(IQ, Auth#legacy_auth{digest = <<>>});
wait_for_master/1, wait_for_slave/1,
make_iq_result/1, start_event_relay/0,
stop_event_relay/1, put_event/2, get_event/1,
- bind/1, auth/1, open_session/1, zlib/1, starttls/1,
- close_socket/1]).
+ bind/1, auth/1, auth/2, open_session/1, open_session/2,
+ zlib/1, starttls/1, close_socket/1, init_stream/1,
+ auth_legacy/2, auth_legacy/3]).
-include("suite.hrl").
open_session(bind(auth(connect(Config))));
init_per_testcase(TestCase, OrigConfig) ->
subscribe_to_events(OrigConfig),
+ TestGroup = proplists:get_value(
+ name, ?config(tc_group_properties, OrigConfig)),
Server = ?config(server, OrigConfig),
- Resource = ?config(resource, OrigConfig),
+ Resource = case TestGroup of
+ generic ->
+ randoms:get_string();
+ legacy_auth ->
+ randoms:get_string();
+ _ ->
+ ?config(resource, OrigConfig)
+ end,
MasterResource = ?config(master_resource, OrigConfig),
SlaveResource = ?config(slave_resource, OrigConfig),
Test = atom_to_list(TestCase),
IsMaster = lists:suffix("_master", Test),
IsSlave = lists:suffix("_slave", Test),
IsCarbons = lists:prefix("carbons_", Test),
- User = if IsMaster or IsCarbons -> <<"test_master!#$%^*()`~+-;_=[]{}|\\">>;
+ IsReplaced = lists:prefix("replaced_", Test),
+ User = if IsReplaced -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>;
+ IsMaster or IsCarbons -> <<"test_master!#$%^*()`~+-;_=[]{}|\\">>;
IsSlave -> <<"test_slave!#$%^*()`~+-;_=[]{}|\\">>;
true -> <<"test_single!#$%^*()`~+-;_=[]{}|\\">>
end,
end,
Slave = if IsCarbons ->
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, SlaveResource);
+ IsReplaced ->
+ jid:make(User, Server, Resource);
true ->
jid:make(<<"test_slave!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource)
end,
Master = if IsCarbons ->
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, MasterResource);
+ IsReplaced ->
+ jid:make(User, Server, Resource);
true ->
jid:make(<<"test_master!#$%^*()`~+-;_=[]{}|\\">>, Server, Resource)
end,
set_opt(slave, Slave,
set_opt(master, Master,
set_opt(resource, MyResource, OrigConfig)))),
- case TestCase of
- test_connect ->
+ case Test of
+ "test_connect" ++ _ ->
Config;
- test_auth ->
+ "test_legacy_auth" ++ _ ->
+ init_stream(set_opt(stream_version, <<"">>, Config));
+ "test_auth" ++ _ ->
connect(Config);
- test_starttls ->
+ "test_starttls" ++ _ ->
connect(Config);
- test_zlib ->
+ "test_zlib" ->
connect(Config);
- test_register ->
+ "test_register" ->
connect(Config);
- auth_md5 ->
+ "auth_md5" ->
connect(Config);
- auth_plain ->
+ "auth_plain" ->
connect(Config);
- test_bind ->
+ "unauthenticated_" ++ _ ->
+ connect(Config);
+ "test_bind" ->
auth(connect(Config));
- sm_resume ->
+ "sm_resume" ->
auth(connect(Config));
- sm_resume_failed ->
+ "sm_resume_failed" ->
auth(connect(Config));
- test_open_session ->
+ "test_open_session" ->
bind(auth(connect(Config)));
+ "replaced" ++ _ ->
+ auth(connect(Config));
_ when IsMaster or IsSlave ->
Password = ?config(password, Config),
ejabberd_auth:try_register(User, Server, Password),
end_per_testcase(_TestCase, _Config) ->
ok.
+legacy_auth_tests() ->
+ {legacy_auth, [parallel],
+ [test_legacy_auth,
+ test_legacy_auth_digest,
+ test_legacy_auth_no_resource,
+ test_legacy_auth_bad_jid,
+ test_legacy_auth_fail]}.
+
no_db_tests() ->
- [{generic, [sequence],
- [test_connect,
+ [{generic, [parallel],
+ [test_connect_bad_xml,
+ test_connect_unknown_ns,
+ test_connect_bad_ns_client,
+ test_connect_bad_ns_stream,
+ test_connect_bad_lang,
+ test_connect_bad_to,
+ test_connect_missing_to,
+ test_connect,
+ unauthenticated_iq,
+ unauthenticated_stanza,
test_starttls,
test_zlib,
test_auth,
+ test_auth_fail,
test_bind,
test_open_session,
- presence,
+ codec_failure,
+ unsupported_query,
+ bad_nonza,
+ invalid_from,
ping,
version,
time,
stats,
- sm,
- sm_resume,
- sm_resume_failed,
disco]},
+ {presence, [sequence], [presence]},
+ {sm, [sequence],
+ [sm,
+ sm_resume,
+ sm_resume_failed]},
{test_proxy65, [parallel],
- [proxy65_master, proxy65_slave]}].
+ [proxy65_master, proxy65_slave]},
+ {replaced, [parallel],
+ [replaced_master, replaced_slave]}].
db_tests(riak) ->
%% No support for mod_pubsub
[{single_user, [sequence],
[test_register,
+ legacy_auth_tests(),
auth_plain,
auth_md5,
presence_broadcast,
db_tests(DB) when DB == mnesia; DB == redis ->
[{single_user, [sequence],
[test_register,
+ legacy_auth_tests(),
auth_plain,
auth_md5,
presence_broadcast,
%% No support for carboncopy
[{single_user, [sequence],
[test_register,
+ legacy_auth_tests(),
auth_plain,
auth_md5,
presence_broadcast,
ldap_tests() ->
[{ldap_tests, [sequence],
[test_auth,
+ test_auth_fail,
vcard_get,
ldap_shared_roster_get]}].
extauth_tests() ->
[{extauth_tests, [sequence],
[test_auth,
+ test_auth_fail,
test_unregister]}].
groups() ->
{riak, [sequence], db_tests(riak)}].
all() ->
- [{group, ldap},
+ [%%{group, ldap},
{group, no_db},
{group, mnesia},
- {group, redis},
- {group, mysql},
- {group, pgsql},
- {group, sqlite},
- {group, extauth},
- {group, riak},
+ %%{group, redis},
+ %%{group, mysql},
+ %%{group, pgsql},
+ %%{group, sqlite},
+ %%{group, extauth},
+ %%{group, riak},
stop_ejabberd].
stop_ejabberd(Config) ->
?recv1({xmlstreamend, <<"stream:stream">>}),
Config.
+test_connect_bad_xml(Config) ->
+ Config0 = init_stream(set_opt(ns_client, <<"'">>, Config)),
+ ?recv1(#stream_error{reason = 'not-well-formed'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_unknown_ns(Config) ->
+ Config0 = init_stream(set_opt(ns_client, <<"wrong">>, Config)),
+ ?recv1(#stream_error{reason = 'not-well-formed'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_bad_ns_client(Config) ->
+ Config0 = init_stream(set_opt(ns_client, ?NS_SERVER, Config)),
+ ?recv1(#stream_error{reason = 'invalid-namespace'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_bad_ns_stream(Config) ->
+ Config0 = init_stream(set_opt(ns_stream, <<"wrong">>, Config)),
+ ?recv1(#stream_error{reason = 'invalid-namespace'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_bad_lang(Config) ->
+ Config0 = init_stream(set_opt(lang, lists:duplicate(36, $x), Config)),
+ ?recv1(#stream_error{reason = 'policy-violation'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_bad_to(Config) ->
+ Config0 = init_stream(set_opt(server, <<"wrong.com">>, Config)),
+ ?recv1(#stream_error{reason = 'host-unknown'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
+test_connect_missing_to(Config) ->
+ Config0 = init_stream(set_opt(server, <<"">>, Config)),
+ ?recv1(#stream_error{reason = 'improper-addressing'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config0).
+
test_connect(Config) ->
disconnect(connect(Config)).
?recv1(#stream_error{reason = conflict}),
Config.
+unauthenticated_stanza(Config) ->
+ %% Unauthenticated stanza should be silently dropped.
+ send(Config, #message{to = server_jid(Config)}),
+ disconnect(Config).
+
+unauthenticated_iq(Config) ->
+ #iq{type = error} =
+ send_recv(Config, #iq{type = get, sub_els = [#disco_info{}]}),
+ disconnect(Config).
+
+bad_nonza(Config) ->
+ %% Unsupported and invalid nonza should be silently dropped.
+ send(Config, #caps{}),
+ send(Config, #stanza_error{type = wrong}),
+ disconnect(Config).
+
+invalid_from(Config) ->
+ send(Config, #message{from = jid:make(randoms:get_string())}),
+ ?recv1(#stream_error{reason = 'invalid-from'}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config).
+
auth_md5(Config) ->
Mechs = ?config(mechs, Config),
case lists:member(<<"DIGEST-MD5">>, Mechs) of
{skipped, 'PLAIN_not_available'}
end.
+test_legacy_auth(Config) ->
+ disconnect(auth_legacy(Config, _Digest = false)).
+
+test_legacy_auth_digest(Config) ->
+ ServerJID = server_jid(Config),
+ disconnect(auth_legacy(Config, _Digest = true)).
+
+test_legacy_auth_no_resource(Config0) ->
+ Config = set_opt(resource, <<"">>, Config0),
+ disconnect(auth_legacy(Config, _Digest = false, _ShouldFail = true)).
+
+test_legacy_auth_bad_jid(Config0) ->
+ Config = set_opt(user, <<"@">>, Config0),
+ disconnect(auth_legacy(Config, _Digest = false, _ShouldFail = true)).
+
+test_legacy_auth_fail(Config0) ->
+ Config = set_opt(user, <<"wrong">>, Config0),
+ disconnect(auth_legacy(Config, _Digest = false, _ShouldFail = true)).
+
test_auth(Config) ->
disconnect(auth(Config)).
+test_auth_fail(Config0) ->
+ Config = set_opt(user, <<"wrong">>, Config0),
+ disconnect(auth(Config, _ShouldFail = true)).
+
test_bind(Config) ->
disconnect(bind(Config)).
test_open_session(Config) ->
- disconnect(open_session(Config)).
+ disconnect(open_session(Config, true)).
roster_get(Config) ->
#iq{type = result, sub_els = [#roster_query{items = []}]} =
sub_els = [#roster_query{ver = Ver2}]}),
disconnect(Config).
+codec_failure(Config) ->
+ #iq{type = error} = send_recv(Config, #iq{type = wrong}),
+ disconnect(Config).
+
+unsupported_query(Config) ->
+ ServerJID = server_jid(Config),
+ #iq{type = error} = send_recv(Config, #iq{type = get, to = ServerJID}),
+ #iq{type = error} = send_recv(Config, #iq{type = get, to = ServerJID,
+ sub_els = [#caps{}]}),
+ #iq{type = error} = send_recv(Config, #iq{type = get, to = ServerJID,
+ sub_els = [#roster_query{},
+ #disco_info{},
+ #privacy_query{}]}),
+ disconnect(Config).
+
presence(Config) ->
send(Config, #presence{}),
JID = my_jid(Config),
end, Items),
disconnect(Config).
+replaced_master(Config0) ->
+ Config = bind(Config0),
+ wait_for_slave(Config),
+ ?recv1(#stream_error{reason = conflict}),
+ ?recv1({xmlstreamend, <<"stream:stream">>}),
+ close_socket(Config).
+
+replaced_slave(Config0) ->
+ wait_for_master(Config0),
+ Config = bind(Config0),
+ disconnect(Config).
+
sm(Config) ->
Server = ?config(server, Config),
ServerJID = jid:make(<<"">>, Server, <<"">>),
{ok, CfgContentTpl} = file:read_file(ConfigPathTpl),
CfgContent = process_config_tpl(CfgContentTpl, [
{c2s_port, 5222},
- {loglevel, 4},
+ {loglevel, 5},
{s2s_port, 5269},
{web_port, 5280},
{mysql_server, <<"localhost">>},
{slave_nick, <<"slave_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
{room_subject, <<"hello, world!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
{certfile, CertFile},
+ {ns_client, ?NS_CLIENT},
+ {ns_stream, ?NS_STREAM},
+ {stream_version, <<"1.0">>},
+ {stream_id, <<"">>},
+ {mechs, []},
+ {lang, <<"en">>},
{base_dir, BaseDir},
{resource, <<"resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
{master_resource, <<"master_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
NewContent = binary:replace(Content, <<"@@",(atom_to_binary(Name, latin1))/binary, "@@">>, Val),
process_config_tpl(NewContent, Rest).
+stream_header(Config) ->
+ NSStream = ?config(ns_stream, Config),
+ NSClient = ?config(ns_client, Config),
+ Lang = ?config(lang, Config),
+ To = case ?config(server, Config) of
+ <<"">> -> <<"">>;
+ Server -> <<"to='", Server/binary, "'">>
+ end,
+ Version = ?config(stream_version, Config),
+ io_lib:format("<?xml version='1.0'?><stream:stream "
+ "xmlns:stream='~s' xmlns='~s' ~s "
+ "version='~s' xml:lang='~s'>",
+ [NSStream, NSClient, To, Version, Lang]).
connect(Config) ->
+ process_stream_features(init_stream(Config)).
+
+init_stream(Config) ->
+ Version = ?config(stream_version, Config),
{ok, Sock} = ejabberd_socket:connect(
?config(server_host, Config),
?config(server_port, Config),
[binary, {packet, 0}, {active, false}]),
- init_stream(set_opt(socket, Sock, Config)).
+ NewConfig = set_opt(socket, Sock, Config),
+ ok = send_text(NewConfig, stream_header(NewConfig)),
+ #stream_start{id = ID, xmlns = ?NS_CLIENT,
+ version = Version} = recv(),
+ set_opt(stream_id, ID, NewConfig).
-init_stream(Config) ->
- ok = send_text(Config, io_lib:format(?STREAM_HEADER,
- [?config(server, Config)])),
- {xmlstreamstart, <<"stream:stream">>, Attrs} = recv(),
- <<"jabber:client">> = fxml:get_attr_s(<<"xmlns">>, Attrs),
- <<"1.0">> = fxml:get_attr_s(<<"version">>, Attrs),
+process_stream_features(Config) ->
#stream_features{sub_els = Fs} = recv(),
Mechs = lists:flatmap(
fun(#sasl_mechanisms{list = Ms}) ->
?config(socket, Config),
[{certfile, ?config(certfile, Config)},
connect]),
- init_stream(set_opt(socket, TLSSocket, Config)).
+ process_stream_features(init_stream(set_opt(socket, TLSSocket, Config))).
zlib(Config) ->
send(Config, #compress{methods = [<<"zlib">>]}),
#compressed{} = recv(),
ZlibSocket = ejabberd_socket:compress(?config(socket, Config)),
- init_stream(set_opt(socket, ZlibSocket, Config)).
+ process_stream_features(init_stream(set_opt(socket, ZlibSocket, Config))).
auth(Config) ->
+ auth(Config, false).
+
+auth(Config, ShouldFail) ->
Mechs = ?config(mechs, Config),
HaveMD5 = lists:member(<<"DIGEST-MD5">>, Mechs),
HavePLAIN = lists:member(<<"PLAIN">>, Mechs),
if HavePLAIN ->
- auth_SASL(<<"PLAIN">>, Config);
+ auth_SASL(<<"PLAIN">>, Config, ShouldFail);
HaveMD5 ->
- auth_SASL(<<"DIGEST-MD5">>, Config);
+ auth_SASL(<<"DIGEST-MD5">>, Config, ShouldFail);
true ->
- ct:fail(no_sasl_mechanisms_available)
+ auth_legacy(Config, false, ShouldFail)
end.
bind(Config) ->
Config.
open_session(Config) ->
- #iq{type = result, sub_els = []} =
- send_recv(Config, #iq{type = set, sub_els = [#xmpp_session{}]}),
+ open_session(Config, false).
+
+open_session(Config, Force) ->
+ if Force ->
+ #iq{type = result, sub_els = []} =
+ send_recv(Config, #iq{type = set, sub_els = [#xmpp_session{}]});
+ true ->
+ ok
+ end,
Config.
+auth_legacy(Config, IsDigest) ->
+ auth_legacy(Config, IsDigest, false).
+
+auth_legacy(Config, IsDigest, ShouldFail) ->
+ ServerJID = server_jid(Config),
+ U = ?config(user, Config),
+ R = ?config(resource, Config),
+ P = ?config(password, Config),
+ #iq{type = result,
+ from = ServerJID,
+ sub_els = [#legacy_auth{username = <<"">>,
+ password = <<"">>,
+ resource = <<"">>} = Auth]} =
+ send_recv(Config,
+ #iq{to = ServerJID, type = get,
+ sub_els = [#legacy_auth{}]}),
+ Res = case Auth#legacy_auth.digest of
+ <<"">> when IsDigest ->
+ StreamID = ?config(stream_id, Config),
+ D = p1_sha:sha(<<StreamID/binary, P/binary>>),
+ send_recv(Config, #iq{to = ServerJID, type = set,
+ sub_els = [#legacy_auth{username = U,
+ resource = R,
+ digest = D}]});
+ _ when not IsDigest ->
+ send_recv(Config, #iq{to = ServerJID, type = set,
+ sub_els = [#legacy_auth{username = U,
+ resource = R,
+ password = P}]})
+ end,
+ case Res of
+ #iq{from = ServerJID, type = result, sub_els = []} ->
+ if ShouldFail ->
+ ct:fail(legacy_auth_should_have_failed);
+ true ->
+ Config
+ end;
+ #iq{from = ServerJID, type = error} ->
+ if ShouldFail ->
+ Config;
+ true ->
+ ct:fail(legacy_auth_failed)
+ end
+ end.
+
auth_SASL(Mech, Config) ->
+ auth_SASL(Mech, Config, false).
+
+auth_SASL(Mech, Config, ShouldFail) ->
{Response, SASL} = sasl_new(Mech,
?config(user, Config),
?config(server, Config),
?config(password, Config)),
send(Config, #sasl_auth{mechanism = Mech, text = Response}),
- wait_auth_SASL_result(set_opt(sasl, SASL, Config)).
+ wait_auth_SASL_result(set_opt(sasl, SASL, Config), ShouldFail).
-wait_auth_SASL_result(Config) ->
+wait_auth_SASL_result(Config, ShouldFail) ->
case recv() of
+ #sasl_success{} when ShouldFail ->
+ ct:fail(sasl_auth_should_have_failed);
#sasl_success{} ->
ejabberd_socket:reset_stream(?config(socket, Config)),
- send_text(Config,
- io_lib:format(?STREAM_HEADER,
- [?config(server, Config)])),
- {xmlstreamstart, <<"stream:stream">>, Attrs} = recv(),
- <<"jabber:client">> = fxml:get_attr_s(<<"xmlns">>, Attrs),
- <<"1.0">> = fxml:get_attr_s(<<"version">>, Attrs),
+ send_text(Config, stream_header(Config)),
+ #stream_start{xmlns = ?NS_CLIENT, version = <<"1.0">>} = recv(),
#stream_features{sub_els = Fs} = recv(),
+ #xmpp_session{optional = true} = lists:keyfind(xmpp_session, 1, Fs),
lists:foldl(
fun(#feature_sm{}, ConfigAcc) ->
set_opt(sm, true, ConfigAcc);
#sasl_challenge{text = ClientIn} ->
{Response, SASL} = (?config(sasl, Config))(ClientIn),
send(Config, #sasl_response{text = Response}),
- wait_auth_SASL_result(set_opt(sasl, SASL, Config));
+ wait_auth_SASL_result(set_opt(sasl, SASL, Config), ShouldFail);
+ #sasl_failure{} when ShouldFail ->
+ Config;
#sasl_failure{} ->
ct:fail(sasl_auth_failed)
end.
recv() ->
receive
{'$gen_event', {xmlstreamelement, El}} ->
- try
- Pkt = xmpp:decode(El),
- ct:pal("recv: ~p ->~n~s", [El, xmpp_codec:pp(Pkt)]),
- Pkt
- catch _:{xmpp_codec, Why} ->
- ct:fail("recv failed: ~p->~n~s",
- [El, xmpp:format_error(Why)])
- end;
+ decode(El);
+ {'$gen_event', {xmlstreamstart, Name, Attrs}} ->
+ decode(#xmlel{name = Name, attrs = Attrs});
{'$gen_event', Event} ->
Event
end.
+decode(El) ->
+ try
+ Pkt = xmpp:decode(El),
+ ct:pal("recv: ~p ->~n~s", [El, xmpp_codec:pp(Pkt)]),
+ Pkt
+ catch _:{xmpp_codec, Why} ->
+ ct:fail("recv failed: ~p->~n~s",
+ [El, xmpp:format_error(Why)])
+ end.
+
fix_ns(#xmlel{name = Tag, attrs = Attrs} = El)
when Tag == <<"stream:features">>; Tag == <<"stream:error">> ->
NewAttrs = [{<<"xmlns">>, <<"http://etherx.jabber.org/streams">>}
-include("mod_proxy65.hrl").
-include("xmpp_codec.hrl").
--define(STREAM_HEADER,
- <<"<?xml version='1.0'?><stream:stream "
- "xmlns:stream='http://etherx.jabber.org/stream"
- "s' xmlns='jabber:client' to='~s' version='1.0"
- "'>">>).
-
-define(STREAM_TRAILER, <<"</stream:stream>">>).
-define(PUBSUB(Node), <<(?NS_PUBSUB)/binary, "#", Node>>).