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