| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 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 <memory> | 
|  | 13 #include <sstream> | 
|  | 14 #include <string> | 
|  | 15 | 
|  | 16 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h" | 
|  | 17 #include "third_party/libjingle_xmpp/xmpp/constants.h" | 
|  | 18 #include "third_party/libjingle_xmpp/xmpp/plainsaslhandler.h" | 
|  | 19 #include "third_party/libjingle_xmpp/xmpp/saslplainmechanism.h" | 
|  | 20 #include "third_party/libjingle_xmpp/xmpp/util_unittest.h" | 
|  | 21 #include "third_party/libjingle_xmpp/xmpp/xmppengine.h" | 
|  | 22 #include "third_party/webrtc/base/common.h" | 
|  | 23 #include "third_party/webrtc/base/gunit.h" | 
|  | 24 | 
|  | 25 using buzz::Jid; | 
|  | 26 using buzz::QName; | 
|  | 27 using buzz::XmlElement; | 
|  | 28 using buzz::XmppEngine; | 
|  | 29 using buzz::XmppIqCookie; | 
|  | 30 using buzz::XmppIqHandler; | 
|  | 31 using buzz::XmppTestHandler; | 
|  | 32 using buzz::QN_ID; | 
|  | 33 using buzz::QN_IQ; | 
|  | 34 using buzz::QN_TYPE; | 
|  | 35 using buzz::QN_ROSTER_QUERY; | 
|  | 36 using buzz::XMPP_RETURN_OK; | 
|  | 37 using buzz::XMPP_RETURN_BADARGUMENT; | 
|  | 38 | 
|  | 39 // XmppEngineTestIqHandler | 
|  | 40 //    This class grabs the response to an IQ stanza and stores it in a string. | 
|  | 41 class XmppEngineTestIqHandler : public XmppIqHandler { | 
|  | 42  public: | 
|  | 43   virtual void IqResponse(XmppIqCookie, const XmlElement * stanza) { | 
|  | 44     ss_ << stanza->Str(); | 
|  | 45   } | 
|  | 46 | 
|  | 47   std::string IqResponseActivity() { | 
|  | 48     std::string result = ss_.str(); | 
|  | 49     ss_.str(""); | 
|  | 50     return result; | 
|  | 51   } | 
|  | 52 | 
|  | 53  private: | 
|  | 54   std::stringstream ss_; | 
|  | 55 }; | 
|  | 56 | 
|  | 57 class XmppEngineTest : public testing::Test { | 
|  | 58  public: | 
|  | 59   XmppEngine* engine() { return engine_.get(); } | 
|  | 60   XmppTestHandler* handler() { return handler_.get(); } | 
|  | 61   virtual void SetUp() { | 
|  | 62     engine_.reset(XmppEngine::Create()); | 
|  | 63     handler_.reset(new XmppTestHandler(engine_.get())); | 
|  | 64 | 
|  | 65     Jid jid("david@my-server"); | 
|  | 66     rtc::InsecureCryptStringImpl pass; | 
|  | 67     pass.password() = "david"; | 
|  | 68     engine_->SetSessionHandler(handler_.get()); | 
|  | 69     engine_->SetOutputHandler(handler_.get()); | 
|  | 70     engine_->AddStanzaHandler(handler_.get()); | 
|  | 71     engine_->SetUser(jid); | 
|  | 72     engine_->SetSaslHandler( | 
|  | 73         new buzz::PlainSaslHandler(jid, rtc::CryptString(pass), true)); | 
|  | 74   } | 
|  | 75   virtual void TearDown() { | 
|  | 76     handler_.reset(); | 
|  | 77     engine_.reset(); | 
|  | 78   } | 
|  | 79   void RunLogin(); | 
|  | 80 | 
|  | 81  private: | 
|  | 82   std::unique_ptr<XmppEngine> engine_; | 
|  | 83   std::unique_ptr<XmppTestHandler> handler_; | 
|  | 84 }; | 
|  | 85 | 
|  | 86 void XmppEngineTest::RunLogin() { | 
|  | 87   // Connect | 
|  | 88   EXPECT_EQ(XmppEngine::STATE_START, engine()->GetState()); | 
|  | 89   engine()->Connect(); | 
|  | 90   EXPECT_EQ(XmppEngine::STATE_OPENING, engine()->GetState()); | 
|  | 91 | 
|  | 92   EXPECT_EQ("[OPENING]", handler_->SessionActivity()); | 
|  | 93 | 
|  | 94   EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" version=\"1.0\" " | 
|  | 95            "xmlns:stream=\"http://etherx.jabber.org/streams\" " | 
|  | 96            "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity()); | 
|  | 97 | 
|  | 98   std::string input = | 
|  | 99     "<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 | 
|  | 104   input = | 
|  | 105     "<stream:features>" | 
|  | 106       "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>" | 
|  | 107         "<required/>" | 
|  | 108       "</starttls>" | 
|  | 109       "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" | 
|  | 110         "<mechanism>DIGEST-MD5</mechanism>" | 
|  | 111         "<mechanism>PLAIN</mechanism>" | 
|  | 112       "</mechanisms>" | 
|  | 113     "</stream:features>"; | 
|  | 114   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 115   EXPECT_EQ("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>", | 
|  | 116       handler_->OutputActivity()); | 
|  | 117 | 
|  | 118   EXPECT_EQ("", handler_->SessionActivity()); | 
|  | 119   EXPECT_EQ("", handler_->StanzaActivity()); | 
|  | 120 | 
|  | 121   input = "<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"; | 
|  | 122   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 123   EXPECT_EQ("[START-TLS my-server]" | 
|  | 124            "<stream:stream to=\"my-server\" xml:lang=\"*\" " | 
|  | 125            "version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" " | 
|  | 126            "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity()); | 
|  | 127 | 
|  | 128   EXPECT_EQ("", handler_->SessionActivity()); | 
|  | 129   EXPECT_EQ("", handler_->StanzaActivity()); | 
|  | 130 | 
|  | 131   input = "<stream:stream id=\"01234567\" version=\"1.0\" " | 
|  | 132           "xmlns:stream=\"http://etherx.jabber.org/streams\" " | 
|  | 133           "xmlns=\"jabber:client\">"; | 
|  | 134   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 135 | 
|  | 136   input = | 
|  | 137     "<stream:features>" | 
|  | 138       "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" | 
|  | 139         "<mechanism>DIGEST-MD5</mechanism>" | 
|  | 140         "<mechanism>PLAIN</mechanism>" | 
|  | 141       "</mechanisms>" | 
|  | 142     "</stream:features>"; | 
|  | 143   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 144   EXPECT_EQ("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" " | 
|  | 145       "mechanism=\"PLAIN\" " | 
|  | 146       "auth:allow-non-google-login=\"true\" " | 
|  | 147       "auth:client-uses-full-bind-result=\"true\" " | 
|  | 148       "xmlns:auth=\"http://www.google.com/talk/protocol/auth\"" | 
|  | 149       ">AGRhdmlkAGRhdmlk</auth>", | 
|  | 150       handler_->OutputActivity()); | 
|  | 151 | 
|  | 152   EXPECT_EQ("", handler_->SessionActivity()); | 
|  | 153   EXPECT_EQ("", handler_->StanzaActivity()); | 
|  | 154 | 
|  | 155   input = "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>"; | 
|  | 156   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 157   EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" version=\"1.0\" " | 
|  | 158       "xmlns:stream=\"http://etherx.jabber.org/streams\" " | 
|  | 159       "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity()); | 
|  | 160 | 
|  | 161   EXPECT_EQ("", handler_->SessionActivity()); | 
|  | 162   EXPECT_EQ("", handler_->StanzaActivity()); | 
|  | 163 | 
|  | 164   input = "<stream:stream id=\"01234567\" version=\"1.0\" " | 
|  | 165       "xmlns:stream=\"http://etherx.jabber.org/streams\" " | 
|  | 166       "xmlns=\"jabber:client\">"; | 
|  | 167   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 168 | 
|  | 169   input = "<stream:features>" | 
|  | 170           "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>" | 
|  | 171           "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>" | 
|  | 172           "</stream:features>"; | 
|  | 173   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 174   EXPECT_EQ("<iq type=\"set\" id=\"0\">" | 
|  | 175            "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/></iq>", | 
|  | 176            handler_->OutputActivity()); | 
|  | 177 | 
|  | 178   EXPECT_EQ("", handler_->SessionActivity()); | 
|  | 179   EXPECT_EQ("", handler_->StanzaActivity()); | 
|  | 180 | 
|  | 181   input = "<iq type='result' id='0'>" | 
|  | 182           "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>" | 
|  | 183           "david@my-server/test</jid></bind></iq>"; | 
|  | 184   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 185 | 
|  | 186   EXPECT_EQ("<iq type=\"set\" id=\"1\">" | 
|  | 187            "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/></iq>", | 
|  | 188            handler_->OutputActivity()); | 
|  | 189 | 
|  | 190   EXPECT_EQ("", handler_->SessionActivity()); | 
|  | 191   EXPECT_EQ("", handler_->StanzaActivity()); | 
|  | 192 | 
|  | 193   input = "<iq type='result' id='1'/>"; | 
|  | 194   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 195 | 
|  | 196   EXPECT_EQ("[OPEN]", handler_->SessionActivity()); | 
|  | 197   EXPECT_EQ("", handler_->StanzaActivity()); | 
|  | 198   EXPECT_EQ(Jid("david@my-server/test"), engine()->FullJid()); | 
|  | 199 } | 
|  | 200 | 
|  | 201 // TestSuccessfulLogin() | 
|  | 202 //    This function simply tests to see if a login works.  This includes | 
|  | 203 //    encryption and authentication | 
|  | 204 TEST_F(XmppEngineTest, TestSuccessfulLoginAndDisconnect) { | 
|  | 205   RunLogin(); | 
|  | 206   engine()->Disconnect(); | 
|  | 207   EXPECT_EQ("</stream:stream>[CLOSED]", handler()->OutputActivity()); | 
|  | 208   EXPECT_EQ("[CLOSED]", handler()->SessionActivity()); | 
|  | 209   EXPECT_EQ("", handler()->StanzaActivity()); | 
|  | 210 } | 
|  | 211 | 
|  | 212 TEST_F(XmppEngineTest, TestSuccessfulLoginAndConnectionClosed) { | 
|  | 213   RunLogin(); | 
|  | 214   engine()->ConnectionClosed(0); | 
|  | 215   EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); | 
|  | 216   EXPECT_EQ("[CLOSED][ERROR-CONNECTION-CLOSED]", handler()->SessionActivity()); | 
|  | 217   EXPECT_EQ("", handler()->StanzaActivity()); | 
|  | 218 } | 
|  | 219 | 
|  | 220 | 
|  | 221 // TestNotXmpp() | 
|  | 222 //    This tests the error case when connecting to a non XMPP service | 
|  | 223 TEST_F(XmppEngineTest, TestNotXmpp) { | 
|  | 224   // Connect | 
|  | 225   engine()->Connect(); | 
|  | 226   EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" version=\"1.0\" " | 
|  | 227           "xmlns:stream=\"http://etherx.jabber.org/streams\" " | 
|  | 228           "xmlns=\"jabber:client\">\r\n", handler()->OutputActivity()); | 
|  | 229 | 
|  | 230   // Send garbage response (courtesy of apache) | 
|  | 231   std::string input = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">"; | 
|  | 232   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 233 | 
|  | 234   EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); | 
|  | 235   EXPECT_EQ("[OPENING][CLOSED][ERROR-XML]", handler()->SessionActivity()); | 
|  | 236   EXPECT_EQ("", handler()->StanzaActivity()); | 
|  | 237 } | 
|  | 238 | 
|  | 239 // TestPassthrough() | 
|  | 240 //    This tests that arbitrary stanzas can be passed to the server through | 
|  | 241 //    the engine. | 
|  | 242 TEST_F(XmppEngineTest, TestPassthrough) { | 
|  | 243   // Queue up an app stanza | 
|  | 244   XmlElement application_stanza(QName("test", "app-stanza")); | 
|  | 245   application_stanza.AddText("this-is-a-test"); | 
|  | 246   engine()->SendStanza(&application_stanza); | 
|  | 247 | 
|  | 248   // Do the whole login handshake | 
|  | 249   RunLogin(); | 
|  | 250 | 
|  | 251   EXPECT_EQ("<test:app-stanza xmlns:test=\"test\">this-is-a-test" | 
|  | 252           "</test:app-stanza>", handler()->OutputActivity()); | 
|  | 253 | 
|  | 254   // do another stanza | 
|  | 255   XmlElement roster_get(QN_IQ); | 
|  | 256   roster_get.AddAttr(QN_TYPE, "get"); | 
|  | 257   roster_get.AddAttr(QN_ID, engine()->NextId()); | 
|  | 258   roster_get.AddElement(new XmlElement(QN_ROSTER_QUERY, true)); | 
|  | 259   engine()->SendStanza(&roster_get); | 
|  | 260   EXPECT_EQ("<iq type=\"get\" id=\"2\"><query xmlns=\"jabber:iq:roster\"/>" | 
|  | 261           "</iq>", handler()->OutputActivity()); | 
|  | 262 | 
|  | 263   // now say the server ends the stream | 
|  | 264   engine()->HandleInput("</stream:stream>", 16); | 
|  | 265   EXPECT_EQ("[CLOSED][ERROR-DOCUMENT-CLOSED]", handler()->SessionActivity()); | 
|  | 266   EXPECT_EQ("[CLOSED]", handler()->OutputActivity()); | 
|  | 267   EXPECT_EQ("", handler()->StanzaActivity()); | 
|  | 268 } | 
|  | 269 | 
|  | 270 // TestIqCallback() | 
|  | 271 //    This tests the routing of Iq stanzas and responses. | 
|  | 272 TEST_F(XmppEngineTest, TestIqCallback) { | 
|  | 273   XmppEngineTestIqHandler iq_response; | 
|  | 274   XmppIqCookie cookie; | 
|  | 275 | 
|  | 276   // Do the whole login handshake | 
|  | 277   RunLogin(); | 
|  | 278 | 
|  | 279   // Build an iq request | 
|  | 280   XmlElement roster_get(QN_IQ); | 
|  | 281   roster_get.AddAttr(QN_TYPE, "get"); | 
|  | 282   roster_get.AddAttr(QN_ID, engine()->NextId()); | 
|  | 283   roster_get.AddElement(new XmlElement(QN_ROSTER_QUERY, true)); | 
|  | 284   engine()->SendIq(&roster_get, &iq_response, &cookie); | 
|  | 285   EXPECT_EQ("<iq type=\"get\" id=\"2\"><query xmlns=\"jabber:iq:roster\"/>" | 
|  | 286           "</iq>", handler()->OutputActivity()); | 
|  | 287   EXPECT_EQ("", handler()->SessionActivity()); | 
|  | 288   EXPECT_EQ("", handler()->StanzaActivity()); | 
|  | 289   EXPECT_EQ("", iq_response.IqResponseActivity()); | 
|  | 290 | 
|  | 291   // now say the server responds to the iq | 
|  | 292   std::string input = "<iq type='result' id='2'>" | 
|  | 293                       "<query xmlns='jabber:iq:roster'><item>foo</item>" | 
|  | 294                       "</query></iq>"; | 
|  | 295   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 296   EXPECT_EQ("", handler()->OutputActivity()); | 
|  | 297   EXPECT_EQ("", handler()->SessionActivity()); | 
|  | 298   EXPECT_EQ("", handler()->StanzaActivity()); | 
|  | 299   EXPECT_EQ("<cli:iq type=\"result\" id=\"2\" xmlns:cli=\"jabber:client\">" | 
|  | 300           "<query xmlns=\"jabber:iq:roster\"><item>foo</item></query>" | 
|  | 301           "</cli:iq>", iq_response.IqResponseActivity()); | 
|  | 302 | 
|  | 303   EXPECT_EQ(XMPP_RETURN_BADARGUMENT, engine()->RemoveIqHandler(cookie, NULL)); | 
|  | 304 | 
|  | 305   // Do it again with another id to test cancel | 
|  | 306   roster_get.SetAttr(QN_ID, engine()->NextId()); | 
|  | 307   engine()->SendIq(&roster_get, &iq_response, &cookie); | 
|  | 308   EXPECT_EQ("<iq type=\"get\" id=\"3\"><query xmlns=\"jabber:iq:roster\"/>" | 
|  | 309           "</iq>", handler()->OutputActivity()); | 
|  | 310   EXPECT_EQ("", handler()->SessionActivity()); | 
|  | 311   EXPECT_EQ("", handler()->StanzaActivity()); | 
|  | 312   EXPECT_EQ("", iq_response.IqResponseActivity()); | 
|  | 313 | 
|  | 314   // cancel the handler this time | 
|  | 315   EXPECT_EQ(XMPP_RETURN_OK, engine()->RemoveIqHandler(cookie, NULL)); | 
|  | 316 | 
|  | 317   // now say the server responds to the iq: the iq handler should not get it. | 
|  | 318   input = "<iq type='result' id='3'><query xmlns='jabber:iq:roster'><item>bar" | 
|  | 319           "</item></query></iq>"; | 
|  | 320   engine()->HandleInput(input.c_str(), input.length()); | 
|  | 321   EXPECT_EQ("<cli:iq type=\"result\" id=\"3\" xmlns:cli=\"jabber:client\">" | 
|  | 322           "<query xmlns=\"jabber:iq:roster\"><item>bar</item></query>" | 
|  | 323           "</cli:iq>", handler()->StanzaActivity()); | 
|  | 324   EXPECT_EQ("", iq_response.IqResponseActivity()); | 
|  | 325   EXPECT_EQ("", handler()->OutputActivity()); | 
|  | 326   EXPECT_EQ("", handler()->SessionActivity()); | 
|  | 327 } | 
| OLD | NEW | 
|---|