]> granicus.if.org Git - ejabberd/commitdiff
Add more tests for C2S
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Tue, 20 Sep 2016 11:04:07 +0000 (14:04 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Tue, 20 Sep 2016 11:04:07 +0000 (14:04 +0300)
src/ejabberd_c2s.erl
test/ejabberd_SUITE.erl
test/suite.erl
test/suite.hrl

index 186f7e9a5906fe190f72d0c7846305dd4185fa65..e98a9eb8fc758cf4ae407407f4352c1a73c9baae 100644 (file)
@@ -323,24 +323,25 @@ get_subscribed(FsmRef) ->
 
 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};
@@ -463,7 +464,7 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, 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(
@@ -492,7 +493,7 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
            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;
@@ -517,13 +518,8 @@ wait_for_auth({xmlstreamelement, #xmlel{} = El}, StateData) ->
     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 = <<>>});
index fbaee781f5696703a728ba4ce3147394c384b411..0af81dd33cc89d8c855b89e8eedded43760a2646 100644 (file)
@@ -19,8 +19,9 @@
                 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").
 
@@ -154,15 +155,26 @@ init_per_testcase(stop_ejabberd, Config) ->
     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,
@@ -172,11 +184,15 @@ init_per_testcase(TestCase, OrigConfig) ->
                 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,
@@ -184,29 +200,35 @@ init_per_testcase(TestCase, OrigConfig) ->
                      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),
@@ -218,30 +240,56 @@ init_per_testcase(TestCase, OrigConfig) ->
 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,
@@ -273,6 +321,7 @@ db_tests(riak) ->
 db_tests(DB) when DB == mnesia; DB == redis ->
     [{single_user, [sequence],
       [test_register,
+       legacy_auth_tests(),
        auth_plain,
        auth_md5,
        presence_broadcast,
@@ -319,6 +368,7 @@ db_tests(_) ->
     %% No support for carboncopy
     [{single_user, [sequence],
       [test_register,
+       legacy_auth_tests(),
        auth_plain,
        auth_md5,
        presence_broadcast,
@@ -361,12 +411,14 @@ db_tests(_) ->
 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() ->
@@ -381,15 +433,15 @@ 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) ->
@@ -398,6 +450,48 @@ 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)).
 
@@ -462,6 +556,28 @@ try_unregister(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
@@ -482,14 +598,37 @@ auth_plain(Config) ->
             {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 = []}]} =
@@ -519,6 +658,21 @@ roster_ver(Config) ->
                               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),
@@ -604,6 +758,18 @@ disco(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, <<"">>),
index d89a49ef8098004ad4d335adc6fce5dea0ecc42d..93141d8460235abbc040e3e087941da4656f4643 100644 (file)
@@ -32,7 +32,7 @@ init_config(Config) ->
     {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">>},
@@ -64,6 +64,12 @@ init_config(Config) ->
      {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!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>},
@@ -118,20 +124,36 @@ process_config_tpl(Content, [{Name, DefaultValue} | Rest]) ->
     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}) ->
@@ -169,24 +191,27 @@ starttls(Config) ->
                   ?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) ->
@@ -198,29 +223,83 @@ 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);
@@ -232,7 +311,9 @@ wait_auth_SASL_result(Config) ->
         #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.
@@ -252,18 +333,23 @@ match_failure(Received, Matches) ->
 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">>}
index fb6b4f3acd79c260bf4a5dad2b9383e61657371a..3095e1bb5c50c397144dc04bc4fbbbe0c4b9c4e4 100644 (file)
@@ -5,12 +5,6 @@
 -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>>).