1 // Copyright 2004 Google Inc. All Rights Reserved
7 #include "webrtc/libjingle/xmllite/xmlelement.h"
8 #include "webrtc/libjingle/xmpp/constants.h"
9 #include "webrtc/libjingle/xmpp/plainsaslhandler.h"
10 #include "webrtc/libjingle/xmpp/saslplainmechanism.h"
11 #include "webrtc/libjingle/xmpp/util_unittest.h"
12 #include "webrtc/libjingle/xmpp/xmppengine.h"
13 #include "webrtc/base/common.h"
14 #include "webrtc/base/gunit.h"
18 using buzz::XmlElement;
19 using buzz::XmppEngine;
20 using buzz::XmppIqCookie;
21 using buzz::XmppIqHandler;
22 using buzz::XmppTestHandler;
26 using buzz::QN_ROSTER_QUERY;
27 using buzz::XMPP_RETURN_OK;
28 using buzz::XMPP_RETURN_BADARGUMENT;
30 // XmppEngineTestIqHandler
31 // This class grabs the response to an IQ stanza and stores it in a string.
32 class XmppEngineTestIqHandler : public XmppIqHandler {
34 virtual void IqResponse(XmppIqCookie, const XmlElement * stanza) {
38 std::string IqResponseActivity() {
39 std::string result = ss_.str();
45 std::stringstream ss_;
48 class XmppEngineTest : public testing::Test {
50 XmppEngine* engine() { return engine_.get(); }
51 XmppTestHandler* handler() { return handler_.get(); }
52 virtual void SetUp() {
53 engine_.reset(XmppEngine::Create());
54 handler_.reset(new XmppTestHandler(engine_.get()));
56 Jid jid("david@my-server");
57 rtc::InsecureCryptStringImpl pass;
58 pass.password() = "david";
59 engine_->SetSessionHandler(handler_.get());
60 engine_->SetOutputHandler(handler_.get());
61 engine_->AddStanzaHandler(handler_.get());
62 engine_->SetUser(jid);
63 engine_->SetSaslHandler(
64 new buzz::PlainSaslHandler(jid, rtc::CryptString(pass), true));
66 virtual void TearDown() {
73 rtc::scoped_ptr<XmppEngine> engine_;
74 rtc::scoped_ptr<XmppTestHandler> handler_;
77 void XmppEngineTest::RunLogin() {
79 EXPECT_EQ(XmppEngine::STATE_START, engine()->GetState());
81 EXPECT_EQ(XmppEngine::STATE_OPENING, engine()->GetState());
83 EXPECT_EQ("[OPENING]", handler_->SessionActivity());
85 EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" version=\"1.0\" "
86 "xmlns:stream=\"http://etherx.jabber.org/streams\" "
87 "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity());
90 "<stream:stream id=\"a5f2d8c9\" version=\"1.0\" "
91 "xmlns:stream=\"http://etherx.jabber.org/streams\" "
92 "xmlns=\"jabber:client\">";
93 engine()->HandleInput(input.c_str(), input.length());
97 "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'>"
100 "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
101 "<mechanism>DIGEST-MD5</mechanism>"
102 "<mechanism>PLAIN</mechanism>"
104 "</stream:features>";
105 engine()->HandleInput(input.c_str(), input.length());
106 EXPECT_EQ("<starttls xmlns=\"urn:ietf:params:xml:ns:xmpp-tls\"/>",
107 handler_->OutputActivity());
109 EXPECT_EQ("", handler_->SessionActivity());
110 EXPECT_EQ("", handler_->StanzaActivity());
112 input = "<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>";
113 engine()->HandleInput(input.c_str(), input.length());
114 EXPECT_EQ("[START-TLS my-server]"
115 "<stream:stream to=\"my-server\" xml:lang=\"*\" "
116 "version=\"1.0\" xmlns:stream=\"http://etherx.jabber.org/streams\" "
117 "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity());
119 EXPECT_EQ("", handler_->SessionActivity());
120 EXPECT_EQ("", handler_->StanzaActivity());
122 input = "<stream:stream id=\"01234567\" version=\"1.0\" "
123 "xmlns:stream=\"http://etherx.jabber.org/streams\" "
124 "xmlns=\"jabber:client\">";
125 engine()->HandleInput(input.c_str(), input.length());
129 "<mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>"
130 "<mechanism>DIGEST-MD5</mechanism>"
131 "<mechanism>PLAIN</mechanism>"
133 "</stream:features>";
134 engine()->HandleInput(input.c_str(), input.length());
135 EXPECT_EQ("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
136 "mechanism=\"PLAIN\" "
137 "auth:allow-non-google-login=\"true\" "
138 "auth:client-uses-full-bind-result=\"true\" "
139 "xmlns:auth=\"http://www.google.com/talk/protocol/auth\""
140 ">AGRhdmlkAGRhdmlk</auth>",
141 handler_->OutputActivity());
143 EXPECT_EQ("", handler_->SessionActivity());
144 EXPECT_EQ("", handler_->StanzaActivity());
146 input = "<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>";
147 engine()->HandleInput(input.c_str(), input.length());
148 EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" version=\"1.0\" "
149 "xmlns:stream=\"http://etherx.jabber.org/streams\" "
150 "xmlns=\"jabber:client\">\r\n", handler_->OutputActivity());
152 EXPECT_EQ("", handler_->SessionActivity());
153 EXPECT_EQ("", handler_->StanzaActivity());
155 input = "<stream:stream id=\"01234567\" version=\"1.0\" "
156 "xmlns:stream=\"http://etherx.jabber.org/streams\" "
157 "xmlns=\"jabber:client\">";
158 engine()->HandleInput(input.c_str(), input.length());
160 input = "<stream:features>"
161 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/>"
162 "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>"
163 "</stream:features>";
164 engine()->HandleInput(input.c_str(), input.length());
165 EXPECT_EQ("<iq type=\"set\" id=\"0\">"
166 "<bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/></iq>",
167 handler_->OutputActivity());
169 EXPECT_EQ("", handler_->SessionActivity());
170 EXPECT_EQ("", handler_->StanzaActivity());
172 input = "<iq type='result' id='0'>"
173 "<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>"
174 "david@my-server/test</jid></bind></iq>";
175 engine()->HandleInput(input.c_str(), input.length());
177 EXPECT_EQ("<iq type=\"set\" id=\"1\">"
178 "<session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/></iq>",
179 handler_->OutputActivity());
181 EXPECT_EQ("", handler_->SessionActivity());
182 EXPECT_EQ("", handler_->StanzaActivity());
184 input = "<iq type='result' id='1'/>";
185 engine()->HandleInput(input.c_str(), input.length());
187 EXPECT_EQ("[OPEN]", handler_->SessionActivity());
188 EXPECT_EQ("", handler_->StanzaActivity());
189 EXPECT_EQ(Jid("david@my-server/test"), engine()->FullJid());
192 // TestSuccessfulLogin()
193 // This function simply tests to see if a login works. This includes
194 // encryption and authentication
195 TEST_F(XmppEngineTest, TestSuccessfulLoginAndDisconnect) {
197 engine()->Disconnect();
198 EXPECT_EQ("</stream:stream>[CLOSED]", handler()->OutputActivity());
199 EXPECT_EQ("[CLOSED]", handler()->SessionActivity());
200 EXPECT_EQ("", handler()->StanzaActivity());
203 TEST_F(XmppEngineTest, TestSuccessfulLoginAndConnectionClosed) {
205 engine()->ConnectionClosed(0);
206 EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
207 EXPECT_EQ("[CLOSED][ERROR-CONNECTION-CLOSED]", handler()->SessionActivity());
208 EXPECT_EQ("", handler()->StanzaActivity());
213 // This tests the error case when connecting to a non XMPP service
214 TEST_F(XmppEngineTest, TestNotXmpp) {
217 EXPECT_EQ("<stream:stream to=\"my-server\" xml:lang=\"*\" version=\"1.0\" "
218 "xmlns:stream=\"http://etherx.jabber.org/streams\" "
219 "xmlns=\"jabber:client\">\r\n", handler()->OutputActivity());
221 // Send garbage response (courtesy of apache)
222 std::string input = "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">";
223 engine()->HandleInput(input.c_str(), input.length());
225 EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
226 EXPECT_EQ("[OPENING][CLOSED][ERROR-XML]", handler()->SessionActivity());
227 EXPECT_EQ("", handler()->StanzaActivity());
231 // This tests that arbitrary stanzas can be passed to the server through
233 TEST_F(XmppEngineTest, TestPassthrough) {
234 // Queue up an app stanza
235 XmlElement application_stanza(QName("test", "app-stanza"));
236 application_stanza.AddText("this-is-a-test");
237 engine()->SendStanza(&application_stanza);
239 // Do the whole login handshake
242 EXPECT_EQ("<test:app-stanza xmlns:test=\"test\">this-is-a-test"
243 "</test:app-stanza>", handler()->OutputActivity());
246 XmlElement roster_get(QN_IQ);
247 roster_get.AddAttr(QN_TYPE, "get");
248 roster_get.AddAttr(QN_ID, engine()->NextId());
249 roster_get.AddElement(new XmlElement(QN_ROSTER_QUERY, true));
250 engine()->SendStanza(&roster_get);
251 EXPECT_EQ("<iq type=\"get\" id=\"2\"><query xmlns=\"jabber:iq:roster\"/>"
252 "</iq>", handler()->OutputActivity());
254 // now say the server ends the stream
255 engine()->HandleInput("</stream:stream>", 16);
256 EXPECT_EQ("[CLOSED][ERROR-DOCUMENT-CLOSED]", handler()->SessionActivity());
257 EXPECT_EQ("[CLOSED]", handler()->OutputActivity());
258 EXPECT_EQ("", handler()->StanzaActivity());
262 // This tests the routing of Iq stanzas and responses.
263 TEST_F(XmppEngineTest, TestIqCallback) {
264 XmppEngineTestIqHandler iq_response;
267 // Do the whole login handshake
270 // Build an iq request
271 XmlElement roster_get(QN_IQ);
272 roster_get.AddAttr(QN_TYPE, "get");
273 roster_get.AddAttr(QN_ID, engine()->NextId());
274 roster_get.AddElement(new XmlElement(QN_ROSTER_QUERY, true));
275 engine()->SendIq(&roster_get, &iq_response, &cookie);
276 EXPECT_EQ("<iq type=\"get\" id=\"2\"><query xmlns=\"jabber:iq:roster\"/>"
277 "</iq>", handler()->OutputActivity());
278 EXPECT_EQ("", handler()->SessionActivity());
279 EXPECT_EQ("", handler()->StanzaActivity());
280 EXPECT_EQ("", iq_response.IqResponseActivity());
282 // now say the server responds to the iq
283 std::string input = "<iq type='result' id='2'>"
284 "<query xmlns='jabber:iq:roster'><item>foo</item>"
286 engine()->HandleInput(input.c_str(), input.length());
287 EXPECT_EQ("", handler()->OutputActivity());
288 EXPECT_EQ("", handler()->SessionActivity());
289 EXPECT_EQ("", handler()->StanzaActivity());
290 EXPECT_EQ("<cli:iq type=\"result\" id=\"2\" xmlns:cli=\"jabber:client\">"
291 "<query xmlns=\"jabber:iq:roster\"><item>foo</item></query>"
292 "</cli:iq>", iq_response.IqResponseActivity());
294 EXPECT_EQ(XMPP_RETURN_BADARGUMENT, engine()->RemoveIqHandler(cookie, NULL));
296 // Do it again with another id to test cancel
297 roster_get.SetAttr(QN_ID, engine()->NextId());
298 engine()->SendIq(&roster_get, &iq_response, &cookie);
299 EXPECT_EQ("<iq type=\"get\" id=\"3\"><query xmlns=\"jabber:iq:roster\"/>"
300 "</iq>", handler()->OutputActivity());
301 EXPECT_EQ("", handler()->SessionActivity());
302 EXPECT_EQ("", handler()->StanzaActivity());
303 EXPECT_EQ("", iq_response.IqResponseActivity());
305 // cancel the handler this time
306 EXPECT_EQ(XMPP_RETURN_OK, engine()->RemoveIqHandler(cookie, NULL));
308 // now say the server responds to the iq: the iq handler should not get it.
309 input = "<iq type='result' id='3'><query xmlns='jabber:iq:roster'><item>bar"
310 "</item></query></iq>";
311 engine()->HandleInput(input.c_str(), input.length());
312 EXPECT_EQ("<cli:iq type=\"result\" id=\"3\" xmlns:cli=\"jabber:client\">"
313 "<query xmlns=\"jabber:iq:roster\"><item>bar</item></query>"
314 "</cli:iq>", handler()->StanzaActivity());
315 EXPECT_EQ("", iq_response.IqResponseActivity());
316 EXPECT_EQ("", handler()->OutputActivity());
317 EXPECT_EQ("", handler()->SessionActivity());