Home | History | Annotate | Download | only in xmpp
      1 /*
      2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include <iostream>
     12 #include <sstream>
     13 #include <string>
     14 #include "webrtc/libjingle/xmllite/xmlelement.h"
     15 #include "webrtc/libjingle/xmpp/constants.h"
     16 #include "webrtc/libjingle/xmpp/plainsaslhandler.h"
     17 #include "webrtc/libjingle/xmpp/saslplainmechanism.h"
     18 #include "webrtc/libjingle/xmpp/util_unittest.h"
     19 #include "webrtc/libjingle/xmpp/xmppengine.h"
     20 #include "webrtc/base/common.h"
     21 #include "webrtc/base/cryptstring.h"
     22 #include "webrtc/base/gunit.h"
     23 
     24 using buzz::Jid;
     25 using buzz::QName;
     26 using buzz::XmlElement;
     27 using buzz::XmppEngine;
     28 using buzz::XmppTestHandler;
     29 
     30 enum XlttStage {
     31   XLTT_STAGE_CONNECT = 0,
     32   XLTT_STAGE_STREAMSTART,
     33   XLTT_STAGE_TLS_FEATURES,
     34   XLTT_STAGE_TLS_PROCEED,
     35   XLTT_STAGE_ENCRYPTED_START,
     36   XLTT_STAGE_AUTH_FEATURES,
     37   XLTT_STAGE_AUTH_SUCCESS,
     38   XLTT_STAGE_AUTHENTICATED_START,
     39   XLTT_STAGE_BIND_FEATURES,
     40   XLTT_STAGE_BIND_SUCCESS,
     41   XLTT_STAGE_SESSION_SUCCESS,
     42 };
     43 
     44 class XmppLoginTaskTest : public testing::Test {
     45  public:
     46   XmppEngine* engine() { return engine_.get(); }
     47   XmppTestHandler* handler() { return handler_.get(); }
     48   virtual void SetUp() {
     49     engine_.reset(XmppEngine::Create());
     50     handler_.reset(new XmppTestHandler(engine_.get()));
     51 
     52     Jid jid("david@my-server");
     53     rtc::InsecureCryptStringImpl pass;
     54     pass.password() = "david";
     55     engine_->SetSessionHandler(handler_.get());
     56     engine_->SetOutputHandler(handler_.get());
     57     engine_->AddStanzaHandler(handler_.get());
     58     engine_->SetUser(jid);
     59     engine_->SetSaslHandler(
     60         new buzz::PlainSaslHandler(jid, rtc::CryptString(pass), true));
     61   }
     62   virtual void TearDown() {
     63     handler_.reset();
     64     engine_.reset();
     65   }
     66   void RunPartialLogin(XlttStage startstage, XlttStage endstage);
     67   void SetTlsOptions(buzz::TlsOptions option);
     68 
     69  private:
     70   rtc::scoped_ptr<XmppEngine> engine_;
     71   rtc::scoped_ptr<XmppTestHandler> handler_;
     72 };
     73 
     74 void XmppLoginTaskTest::SetTlsOptions(buzz::TlsOptions option) {
     75   engine_->SetTls(option);
     76 }
     77 void XmppLoginTaskTest::RunPartialLogin(XlttStage startstage,
     78                                         XlttStage endstage) {
     79   std::string input;
     80 
     81   switch (startstage) {
     82     case XLTT_STAGE_CONNECT: {
     83       engine_->Connect();
     84       XmlElement appStanza(QName("test", "app-stanza"));
     85       appStanza.AddText("this-is-a-test");
     86       engine_->SendStanza(&appStanza);
     87 
     88       EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" "
     89           "version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" "
     90           "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity());
     91       EXPECT_EQ("[OPENING]", handler_->SessionActivity());
     92       EXPECT_EQ("", handler_->StanzaActivity());
     93       if (endstage == XLTT_STAGE_CONNECT)
     94         return;
     95       FALLTHROUGH();
     96     }
     97 
     98     case XLTT_STAGE_STREAMSTART: {
     99       input = "<stream:stream id=\"a5f2d8c9\" version=\"1.0\" "
    100           "xmlns:stream=\"http://etherx.jabber.org/streams\" "
    101           "xmlns=\"jabber:client\">";
    102       engine_->HandleInput(input.c_str(), input.length());
    103       EXPECT_EQ("", handler_->StanzaActivity());
    104       EXPECT_EQ("", handler_->SessionActivity());
    105       EXPECT_EQ("", handler_->OutputActivity());
    106       if (endstage == XLTT_STAGE_STREAMSTART)
    107         return;
    108       FALLTHROUGH();
    109     }
    110 
    111     case XLTT_STAGE_TLS_FEATURES: {
    112       input = "<stream:features>"
    113         "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
    114        "</stream:features>";
    115       engine_->HandleInput(input.c_str(), input.length());
    116       EXPECT_EQ("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>",
    117           handler_->OutputActivity());
    118       EXPECT_EQ("", handler_->StanzaActivity());
    119       EXPECT_EQ("", handler_->SessionActivity());
    120       if (endstage == XLTT_STAGE_TLS_FEATURES)
    121         return;
    122       FALLTHROUGH();
    123     }
    124 
    125     case XLTT_STAGE_TLS_PROCEED: {
    126       input = std::string("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
    127       engine_->HandleInput(input.c_str(), input.length());
    128       EXPECT_EQ("[START-TLS my-server]"
    129           "<stream:stream to=\"my-server\" xml:lang=\"*\" "
    130           "version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" "
    131           "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity());
    132       EXPECT_EQ("", handler_->StanzaActivity());
    133       EXPECT_EQ("", handler_->SessionActivity());
    134       if (endstage == XLTT_STAGE_TLS_PROCEED)
    135         return;
    136       FALLTHROUGH();
    137     }
    138 
    139     case XLTT_STAGE_ENCRYPTED_START: {
    140       input = std::string("<stream:stream id=\"01234567\" version=\"1.0\" "
    141           "xmlns:stream=\"http://etherx.jabber.org/streams\" "
    142           "xmlns=\"jabber:client\">");
    143       engine_->HandleInput(input.c_str(), input.length());
    144       EXPECT_EQ("", handler_->StanzaActivity());
    145       EXPECT_EQ("", handler_->SessionActivity());
    146       EXPECT_EQ("", handler_->OutputActivity());
    147       if (endstage == XLTT_STAGE_ENCRYPTED_START)
    148         return;
    149       FALLTHROUGH();
    150     }
    151 
    152     case XLTT_STAGE_AUTH_FEATURES: {
    153       input = "<stream:features>"
    154         "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
    155           "<mechanism>DIGEST-MD5</mechanism>"
    156           "<mechanism>PLAIN</mechanism>"
    157         "</mechanisms>"
    158        "</stream:features>";
    159       engine_->HandleInput(input.c_str(), input.length());
    160       EXPECT_EQ("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
    161           "mechanism=\"PLAIN\" "
    162           "auth:allow-non-google-login=\"true\" "
    163           "auth:client-uses-full-bind-result=\"true\" "
    164           "xmlns:auth=\"http://www.google.com/talk/protocol/auth\""
    165           ">AGRhdmlkAGRhdmlk</auth>",
    166           handler_->OutputActivity());
    167       EXPECT_EQ("", handler_->StanzaActivity());
    168       EXPECT_EQ("", handler_->SessionActivity());
    169       if (endstage == XLTT_STAGE_AUTH_FEATURES)
    170         return;
    171       FALLTHROUGH();
    172     }
    173 
    174     case XLTT_STAGE_AUTH_SUCCESS: {
    175       input = "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>";
    176       engine_->HandleInput(input.c_str(), input.length());
    177       EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" "
    178           "version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" "
    179           "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity());
    180       EXPECT_EQ("", handler_->StanzaActivity());
    181       EXPECT_EQ("", handler_->SessionActivity());
    182       if (endstage == XLTT_STAGE_AUTH_SUCCESS)
    183         return;
    184       FALLTHROUGH();
    185     }
    186 
    187     case XLTT_STAGE_AUTHENTICATED_START: {
    188       input = std::string("<stream:stream id=\"01234567\" version=\"1.0\" "
    189           "xmlns:stream=\"http://etherx.jabber.org/streams\" "
    190           "xmlns=\"jabber:client\">");
    191       engine_->HandleInput(input.c_str(), input.length());
    192       EXPECT_EQ("", handler_->StanzaActivity());
    193       EXPECT_EQ("", handler_->SessionActivity());
    194       EXPECT_EQ("", handler_->OutputActivity());
    195       if (endstage == XLTT_STAGE_AUTHENTICATED_START)
    196         return;
    197       FALLTHROUGH();
    198     }
    199 
    200     case XLTT_STAGE_BIND_FEATURES: {
    201       input = "<stream:features>"
    202           "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>"
    203           "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
    204         "</stream:features>";
    205       engine_->HandleInput(input.c_str(), input.length());
    206       EXPECT_EQ("<iq type=\"set\" id=\"0\">"
    207           "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/></iq>",
    208           handler_->OutputActivity());
    209       EXPECT_EQ("", handler_->StanzaActivity());
    210       EXPECT_EQ("", handler_->SessionActivity());
    211       if (endstage == XLTT_STAGE_BIND_FEATURES)
    212         return;
    213       FALLTHROUGH();
    214     }
    215 
    216     case XLTT_STAGE_BIND_SUCCESS: {
    217       input = "<iq type='result' id='0'>"
    218           "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
    219           "<jid>david@my-server/test</jid></bind></iq>";
    220       engine_->HandleInput(input.c_str(), input.length());
    221       EXPECT_EQ("<iq type=\"set\" id=\"1\">"
    222           "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/></iq>",
    223           handler_->OutputActivity());
    224       EXPECT_EQ("", handler_->StanzaActivity());
    225       EXPECT_EQ("", handler_->SessionActivity());
    226       if (endstage == XLTT_STAGE_BIND_SUCCESS)
    227         return;
    228       FALLTHROUGH();
    229     }
    230 
    231     case XLTT_STAGE_SESSION_SUCCESS: {
    232       input = "<iq type='result' id='1'/>";
    233       engine_->HandleInput(input.c_str(), input.length());
    234       EXPECT_EQ("<test:app-stanza xmlns:test=\"test\">this-is-a-test"
    235           "</test:app-stanza>", handler_->OutputActivity());
    236       EXPECT_EQ("[OPEN]", handler_->SessionActivity());
    237       EXPECT_EQ("", handler_->StanzaActivity());
    238       if (endstage == XLTT_STAGE_SESSION_SUCCESS)
    239         return;
    240       FALLTHROUGH();
    241     }
    242     default:
    243       break;
    244   }
    245 }
    246 
    247 TEST_F(XmppLoginTaskTest, TestUtf8Good) {
    248   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_CONNECT);
    249 
    250   std::string input = "<?xml version='1.0' encoding='UTF-8'?>"
    251       "<stream:stream id=\"a5f2d8c9\" version=\"1.0\" "
    252       "xmlns:stream=\"http://etherx.jabber.org/streams\" "
    253       "xmlns=\"jabber:client\">";
    254   engine()->HandleInput(input.c_str(), input.length());
    255   EXPECT_EQ("", handler()->OutputActivity());
    256   EXPECT_EQ("", handler()->SessionActivity());
    257   EXPECT_EQ("", handler()->StanzaActivity());
    258 }
    259 
    260 TEST_F(XmppLoginTaskTest, TestNonUtf8Bad) {
    261   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_CONNECT);
    262 
    263   std::string input = "<?xml version='1.0' encoding='ISO-8859-1'?>"
    264       "<stream:stream id=\"a5f2d8c9\" version=\"1.0\" "
    265       "xmlns:stream=\"http://etherx.jabber.org/streams\" "
    266       "xmlns=\"jabber:client\">";
    267   engine()->HandleInput(input.c_str(), input.length());
    268   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    269   EXPECT_EQ("[CLOSED][ERROR-XML]", handler()->SessionActivity());
    270   EXPECT_EQ("", handler()->StanzaActivity());
    271 }
    272 
    273 TEST_F(XmppLoginTaskTest, TestNoFeatures) {
    274   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART);
    275 
    276   std::string input = "<iq type='get' id='1'/>";
    277   engine()->HandleInput(input.c_str(), input.length());
    278 
    279   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    280   EXPECT_EQ("[CLOSED][ERROR-VERSION]", handler()->SessionActivity());
    281   EXPECT_EQ("", handler()->StanzaActivity());
    282 }
    283 
    284 TEST_F(XmppLoginTaskTest, TestTlsRequiredNotPresent) {
    285   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART);
    286 
    287   std::string input = "<stream:features>"
    288       "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
    289         "<mechanism>DIGEST-MD5</mechanism>"
    290         "<mechanism>PLAIN</mechanism>"
    291       "</mechanisms>"
    292      "</stream:features>";
    293   engine()->HandleInput(input.c_str(), input.length());
    294 
    295   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    296   EXPECT_EQ("[CLOSED][ERROR-TLS]", handler()->SessionActivity());
    297   EXPECT_EQ("", handler()->StanzaActivity());
    298 }
    299 
    300 TEST_F(XmppLoginTaskTest, TestTlsRequeiredAndPresent) {
    301   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART);
    302 
    303   std::string input = "<stream:features>"
    304       "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>"
    305         "<required/>"
    306       "</starttls>"
    307       "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
    308         "<mechanism>X-GOOGLE-TOKEN</mechanism>"
    309         "<mechanism>PLAIN</mechanism>"
    310         "<mechanism>X-OAUTH2</mechanism>"
    311       "</mechanisms>"
    312      "</stream:features>";
    313   engine()->HandleInput(input.c_str(), input.length());
    314 
    315   EXPECT_EQ("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>",
    316       handler()->OutputActivity());
    317   EXPECT_EQ("", handler()->SessionActivity());
    318   EXPECT_EQ("", handler()->StanzaActivity());
    319 }
    320 
    321 TEST_F(XmppLoginTaskTest, TestTlsEnabledNotPresent) {
    322   SetTlsOptions(buzz::TLS_ENABLED);
    323   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART);
    324 
    325   std::string input = "<stream:features>"
    326       "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
    327         "<mechanism>DIGEST-MD5</mechanism>"
    328         "<mechanism>PLAIN</mechanism>"
    329       "</mechanisms>"
    330      "</stream:features>";
    331   engine()->HandleInput(input.c_str(), input.length());
    332 
    333   EXPECT_EQ("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
    334       "mechanism=\"PLAIN\" auth:allow-non-google-login=\"true\" "
    335       "auth:client-uses-full-bind-result=\"true\" "
    336       "xmlns:auth=\"http://www.google.com/talk/protocol/auth\""
    337       ">AGRhdmlkAGRhdmlk</auth>", handler()->OutputActivity());
    338   EXPECT_EQ("", handler()->SessionActivity());
    339   EXPECT_EQ("", handler()->StanzaActivity());
    340 }
    341 
    342 TEST_F(XmppLoginTaskTest, TestTlsEnabledAndPresent) {
    343   SetTlsOptions(buzz::TLS_ENABLED);
    344   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART);
    345 
    346   std::string input = "<stream:features>"
    347       "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
    348         "<mechanism>X-GOOGLE-TOKEN</mechanism>"
    349         "<mechanism>PLAIN</mechanism>"
    350         "<mechanism>X-OAUTH2</mechanism>"
    351       "</mechanisms>"
    352       "</stream:features>";
    353   engine()->HandleInput(input.c_str(), input.length());
    354 
    355   EXPECT_EQ("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
    356       "mechanism=\"PLAIN\" auth:allow-non-google-login=\"true\" "
    357       "auth:client-uses-full-bind-result=\"true\" "
    358       "xmlns:auth=\"http://www.google.com/talk/protocol/auth\""
    359       ">AGRhdmlkAGRhdmlk</auth>", handler()->OutputActivity());
    360   EXPECT_EQ("", handler()->SessionActivity());
    361   EXPECT_EQ("", handler()->StanzaActivity());
    362 }
    363 
    364 TEST_F(XmppLoginTaskTest, TestTlsDisabledNotPresent) {
    365   SetTlsOptions(buzz::TLS_DISABLED);
    366   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART);
    367 
    368     std::string input = "<stream:features>"
    369       "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
    370         "<mechanism>DIGEST-MD5</mechanism>"
    371         "<mechanism>PLAIN</mechanism>"
    372       "</mechanisms>"
    373      "</stream:features>";
    374   engine()->HandleInput(input.c_str(), input.length());
    375 
    376   EXPECT_EQ("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
    377       "mechanism=\"PLAIN\" auth:allow-non-google-login=\"true\" "
    378       "auth:client-uses-full-bind-result=\"true\" "
    379       "xmlns:auth=\"http://www.google.com/talk/protocol/auth\""
    380       ">AGRhdmlkAGRhdmlk</auth>", handler()->OutputActivity());
    381   EXPECT_EQ("", handler()->SessionActivity());
    382   EXPECT_EQ("", handler()->StanzaActivity());
    383 }
    384 
    385 TEST_F(XmppLoginTaskTest, TestTlsDisabledAndPresent) {
    386   SetTlsOptions(buzz::TLS_DISABLED);
    387   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_STREAMSTART);
    388 
    389   std::string input = "<stream:features>"
    390       "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
    391         "<mechanism>X-GOOGLE-TOKEN</mechanism>"
    392         "<mechanism>PLAIN</mechanism>"
    393         "<mechanism>X-OAUTH2</mechanism>"
    394       "</mechanisms>"
    395       "</stream:features>";
    396   engine()->HandleInput(input.c_str(), input.length());
    397 
    398   EXPECT_EQ("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
    399       "mechanism=\"PLAIN\" auth:allow-non-google-login=\"true\" "
    400       "auth:client-uses-full-bind-result=\"true\" "
    401       "xmlns:auth=\"http://www.google.com/talk/protocol/auth\""
    402       ">AGRhdmlkAGRhdmlk</auth>", handler()->OutputActivity());
    403   EXPECT_EQ("", handler()->SessionActivity());
    404   EXPECT_EQ("", handler()->StanzaActivity());
    405 }
    406 
    407 TEST_F(XmppLoginTaskTest, TestTlsFailure) {
    408   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_TLS_FEATURES);
    409 
    410   std::string input = "<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
    411   engine()->HandleInput(input.c_str(), input.length());
    412 
    413   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    414   EXPECT_EQ("[CLOSED][ERROR-TLS]", handler()->SessionActivity());
    415   EXPECT_EQ("", handler()->StanzaActivity());
    416 }
    417 
    418 TEST_F(XmppLoginTaskTest, TestTlsBadStream) {
    419   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_TLS_PROCEED);
    420 
    421   std::string input = "<wrongtag>";
    422   engine()->HandleInput(input.c_str(), input.length());
    423 
    424   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    425   EXPECT_EQ("[CLOSED][ERROR-VERSION]", handler()->SessionActivity());
    426   EXPECT_EQ("", handler()->StanzaActivity());
    427 }
    428 
    429 TEST_F(XmppLoginTaskTest, TestMissingSaslPlain) {
    430   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_ENCRYPTED_START);
    431 
    432   std::string input = "<stream:features>"
    433         "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
    434           "<mechanism>DIGEST-MD5</mechanism>"
    435         "</mechanisms>"
    436        "</stream:features>";
    437   engine()->HandleInput(input.c_str(), input.length());
    438 
    439   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    440   EXPECT_EQ("[CLOSED][ERROR-AUTH]", handler()->SessionActivity());
    441   EXPECT_EQ("", handler()->StanzaActivity());
    442 }
    443 
    444 TEST_F(XmppLoginTaskTest, TestWrongPassword) {
    445   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_AUTH_FEATURES);
    446 
    447   std::string input = "<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>";
    448   engine()->HandleInput(input.c_str(), input.length());
    449 
    450   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    451   EXPECT_EQ("[CLOSED][ERROR-UNAUTHORIZED]", handler()->SessionActivity());
    452   EXPECT_EQ("", handler()->StanzaActivity());
    453 }
    454 
    455 TEST_F(XmppLoginTaskTest, TestAuthBadStream) {
    456   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_AUTH_SUCCESS);
    457 
    458   std::string input = "<wrongtag>";
    459   engine()->HandleInput(input.c_str(), input.length());
    460 
    461   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    462   EXPECT_EQ("[CLOSED][ERROR-VERSION]", handler()->SessionActivity());
    463   EXPECT_EQ("", handler()->StanzaActivity());
    464 }
    465 
    466 TEST_F(XmppLoginTaskTest, TestMissingBindFeature) {
    467   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_AUTHENTICATED_START);
    468 
    469   std::string input = "<stream:features>"
    470           "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
    471         "</stream:features>";
    472   engine()->HandleInput(input.c_str(), input.length());
    473 
    474   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    475   EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity());
    476 }
    477 
    478 TEST_F(XmppLoginTaskTest, TestMissingSessionFeature) {
    479   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_AUTHENTICATED_START);
    480 
    481   std::string input = "<stream:features>"
    482           "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>"
    483         "</stream:features>";
    484   engine()->HandleInput(input.c_str(), input.length());
    485 
    486   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    487   EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity());
    488   EXPECT_EQ("", handler()->StanzaActivity());
    489 }
    490 
    491 /* TODO: Handle this case properly inside XmppLoginTask.
    492 TEST_F(XmppLoginTaskTest, TestBindFailure1) {
    493   // check wrong JID
    494   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_FEATURES);
    495 
    496   std::string input = "<iq type='result' id='0'>"
    497       "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>"
    498       "<jid>davey@my-server/test</jid></bind></iq>";
    499   engine()->HandleInput(input.c_str(), input.length());
    500 
    501   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    502   EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity());
    503   EXPECT_EQ("", handler()->StanzaActivity());
    504 }
    505 */
    506 
    507 TEST_F(XmppLoginTaskTest, TestBindFailure2) {
    508   // check missing JID
    509   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_FEATURES);
    510 
    511   std::string input = "<iq type='result' id='0'>"
    512       "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>";
    513   engine()->HandleInput(input.c_str(), input.length());
    514 
    515   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    516   EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity());
    517   EXPECT_EQ("", handler()->StanzaActivity());
    518 }
    519 
    520 TEST_F(XmppLoginTaskTest, TestBindFailure3) {
    521   // check plain failure
    522   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_FEATURES);
    523 
    524   std::string input = "<iq type='error' id='0'/>";
    525   engine()->HandleInput(input.c_str(), input.length());
    526 
    527   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    528   EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity());
    529   EXPECT_EQ("", handler()->StanzaActivity());
    530 }
    531 
    532 TEST_F(XmppLoginTaskTest, TestBindFailure4) {
    533   // check wrong id to ignore
    534   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_FEATURES);
    535 
    536   std::string input = "<iq type='error' id='1'/>";
    537   engine()->HandleInput(input.c_str(), input.length());
    538 
    539   // continue after an ignored iq
    540   RunPartialLogin(XLTT_STAGE_BIND_SUCCESS, XLTT_STAGE_SESSION_SUCCESS);
    541 }
    542 
    543 TEST_F(XmppLoginTaskTest, TestSessionFailurePlain1) {
    544   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_SUCCESS);
    545 
    546   std::string input = "<iq type='error' id='1'/>";
    547   engine()->HandleInput(input.c_str(), input.length());
    548 
    549   EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    550   EXPECT_EQ("[CLOSED][ERROR-BIND]", handler()->SessionActivity());
    551 }
    552 
    553 TEST_F(XmppLoginTaskTest, TestSessionFailurePlain2) {
    554   RunPartialLogin(XLTT_STAGE_CONNECT, XLTT_STAGE_BIND_SUCCESS);
    555 
    556   // check reverse iq to ignore
    557   // TODO: consider queueing or passing through?
    558   std::string input = "<iq type='get' id='1'/>";
    559   engine()->HandleInput(input.c_str(), input.length());
    560 
    561   EXPECT_EQ("", handler()->OutputActivity());
    562   EXPECT_EQ("", handler()->SessionActivity());
    563 
    564   // continue after an ignored iq
    565   RunPartialLogin(XLTT_STAGE_SESSION_SUCCESS, XLTT_STAGE_SESSION_SUCCESS);
    566 }
    567 
    568 TEST_F(XmppLoginTaskTest, TestBadXml) {
    569   int errorKind = 0;
    570   for (XlttStage stage = XLTT_STAGE_CONNECT;
    571       stage <= XLTT_STAGE_SESSION_SUCCESS;
    572       stage = static_cast<XlttStage>(stage + 1)) {
    573     RunPartialLogin(XLTT_STAGE_CONNECT, stage);
    574 
    575     std::string input;
    576     switch (errorKind++ % 5) {
    577       case 0: input = "&syntax;"; break;
    578       case 1: input = "<nons:iq/>"; break;
    579       case 2: input = "<iq a='b' a='dupe'/>"; break;
    580       case 3: input = "<>"; break;
    581       case 4: input = "<iq a='<wrong>'>"; break;
    582     }
    583 
    584     engine()->HandleInput(input.c_str(), input.length());
    585 
    586     EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    587     EXPECT_EQ("[CLOSED][ERROR-XML]", handler()->SessionActivity());
    588 
    589     TearDown();
    590     SetUp();
    591   }
    592 }
    593 
    594 TEST_F(XmppLoginTaskTest, TestStreamError) {
    595   for (XlttStage stage = XLTT_STAGE_CONNECT;
    596       stage <= XLTT_STAGE_SESSION_SUCCESS;
    597       stage = static_cast<XlttStage>(stage + 1)) {
    598     switch (stage) {
    599       case XLTT_STAGE_CONNECT:
    600       case XLTT_STAGE_TLS_PROCEED:
    601       case XLTT_STAGE_AUTH_SUCCESS:
    602         continue;
    603       default:
    604         break;
    605     }
    606 
    607     RunPartialLogin(XLTT_STAGE_CONNECT, stage);
    608 
    609     std::string input = "<stream:error>"
    610         "<xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>"
    611         "<text xml:lang='en' xmlns='urn:ietf:params:xml:ns:xmpp-streams'>"
    612         "Some special application diagnostic information!"
    613         "</text>"
    614         "<escape-your-data xmlns='application-ns'/>"
    615         "</stream:error>";
    616 
    617     engine()->HandleInput(input.c_str(), input.length());
    618 
    619     EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
    620     EXPECT_EQ("[CLOSED][ERROR-STREAM]", handler()->SessionActivity());
    621 
    622     EXPECT_EQ("<str:error xmlns:str=\"http://etherx.jabber.org/streams\">"
    623         "<xml-not-well-formed xmlns=\"urn:ietf:params:xml:ns:xmpp-streams\"/>"
    624         "<text xml:lang=\"en\" xmlns=\"urn:ietf:params:xml:ns:xmpp-streams\">"
    625         "Some special application diagnostic information!"
    626         "</text>"
    627         "<escape-your-data xmlns=\"application-ns\"/>"
    628         "</str:error>", engine()->GetStreamError()->Str());
    629 
    630     TearDown();
    631     SetUp();
    632   }
    633 }
    634 
    635