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 "webrtc/libjingle/xmpp/xmppclient.h"
12 
13 #include "webrtc/libjingle/xmpp/constants.h"
14 #include "webrtc/libjingle/xmpp/plainsaslhandler.h"
15 #include "webrtc/libjingle/xmpp/prexmppauth.h"
16 #include "webrtc/libjingle/xmpp/saslplainmechanism.h"
17 #include "webrtc/base/logging.h"
18 #include "webrtc/base/scoped_ptr.h"
19 #include "webrtc/base/sigslot.h"
20 #include "webrtc/base/stringutils.h"
21 #include "xmpptask.h"
22 
23 namespace buzz {
24 
25 class XmppClient::Private :
26     public sigslot::has_slots<>,
27     public XmppSessionHandler,
28     public XmppOutputHandler {
29 public:
30 
Private(XmppClient * client)31   explicit Private(XmppClient* client) :
32     client_(client),
33     socket_(),
34     engine_(),
35     proxy_port_(0),
36     pre_engine_error_(XmppEngine::ERROR_NONE),
37     pre_engine_subcode_(0),
38     signal_closed_(false),
39     allow_plain_(false) {}
40 
~Private()41   virtual ~Private() {
42     // We need to disconnect from socket_ before engine_ is destructed (by
43     // the auto-generated destructor code).
44     ResetSocket();
45   }
46 
47   // the owner
48   XmppClient* const client_;
49 
50   // the two main objects
51   rtc::scoped_ptr<AsyncSocket> socket_;
52   rtc::scoped_ptr<XmppEngine> engine_;
53   rtc::scoped_ptr<PreXmppAuth> pre_auth_;
54   rtc::CryptString pass_;
55   std::string auth_mechanism_;
56   std::string auth_token_;
57   rtc::SocketAddress server_;
58   std::string proxy_host_;
59   int proxy_port_;
60   XmppEngine::Error pre_engine_error_;
61   int pre_engine_subcode_;
62   CaptchaChallenge captcha_challenge_;
63   bool signal_closed_;
64   bool allow_plain_;
65 
ResetSocket()66   void ResetSocket() {
67     if (socket_) {
68       socket_->SignalConnected.disconnect(this);
69       socket_->SignalRead.disconnect(this);
70       socket_->SignalClosed.disconnect(this);
71       socket_.reset(NULL);
72     }
73   }
74 
75   // implementations of interfaces
76   void OnStateChange(int state);
77   void WriteOutput(const char* bytes, size_t len);
78   void StartTls(const std::string& domainname);
79   void CloseConnection();
80 
81   // slots for socket signals
82   void OnSocketConnected();
83   void OnSocketRead();
84   void OnSocketClosed();
85 };
86 
IsTestServer(const std::string & server_name,const std::string & test_server_domain)87 bool IsTestServer(const std::string& server_name,
88                   const std::string& test_server_domain) {
89   return (!test_server_domain.empty() &&
90           rtc::ends_with(server_name.c_str(),
91                                test_server_domain.c_str()));
92 }
93 
Connect(const XmppClientSettings & settings,const std::string & lang,AsyncSocket * socket,PreXmppAuth * pre_auth)94 XmppReturnStatus XmppClient::Connect(
95     const XmppClientSettings& settings,
96     const std::string& lang, AsyncSocket* socket, PreXmppAuth* pre_auth) {
97   if (socket == NULL)
98     return XMPP_RETURN_BADARGUMENT;
99   if (d_->socket_)
100     return XMPP_RETURN_BADSTATE;
101 
102   d_->socket_.reset(socket);
103 
104   d_->socket_->SignalConnected.connect(d_.get(), &Private::OnSocketConnected);
105   d_->socket_->SignalRead.connect(d_.get(), &Private::OnSocketRead);
106   d_->socket_->SignalClosed.connect(d_.get(), &Private::OnSocketClosed);
107 
108   d_->engine_.reset(XmppEngine::Create());
109   d_->engine_->SetSessionHandler(d_.get());
110   d_->engine_->SetOutputHandler(d_.get());
111   if (!settings.resource().empty()) {
112     d_->engine_->SetRequestedResource(settings.resource());
113   }
114   d_->engine_->SetTls(settings.use_tls());
115 
116   // The talk.google.com server returns a certificate with common-name:
117   //   CN="gmail.com" for @gmail.com accounts,
118   //   CN="googlemail.com" for @googlemail.com accounts,
119   //   CN="talk.google.com" for other accounts (such as @example.com),
120   // so we tweak the tls server setting for those other accounts to match the
121   // returned certificate CN of "talk.google.com".
122   // For other servers, we leave the strings empty, which causes the jid's
123   // domain to be used.  We do the same for gmail.com and googlemail.com as the
124   // returned CN matches the account domain in those cases.
125   std::string server_name = settings.server().HostAsURIString();
126   if (server_name == buzz::STR_TALK_GOOGLE_COM ||
127       server_name == buzz::STR_TALKX_L_GOOGLE_COM ||
128       server_name == buzz::STR_XMPP_GOOGLE_COM ||
129       server_name == buzz::STR_XMPPX_L_GOOGLE_COM ||
130       IsTestServer(server_name, settings.test_server_domain())) {
131     if (settings.host() != STR_GMAIL_COM &&
132         settings.host() != STR_GOOGLEMAIL_COM) {
133       d_->engine_->SetTlsServer("", STR_TALK_GOOGLE_COM);
134     }
135   }
136 
137   // Set language
138   d_->engine_->SetLanguage(lang);
139 
140   d_->engine_->SetUser(buzz::Jid(settings.user(), settings.host(), STR_EMPTY));
141 
142   d_->pass_ = settings.pass();
143   d_->auth_mechanism_ = settings.auth_mechanism();
144   d_->auth_token_ = settings.auth_token();
145   d_->server_ = settings.server();
146   d_->proxy_host_ = settings.proxy_host();
147   d_->proxy_port_ = settings.proxy_port();
148   d_->allow_plain_ = settings.allow_plain();
149   d_->pre_auth_.reset(pre_auth);
150 
151   return XMPP_RETURN_OK;
152 }
153 
GetState() const154 XmppEngine::State XmppClient::GetState() const {
155   if (!d_->engine_)
156     return XmppEngine::STATE_NONE;
157   return d_->engine_->GetState();
158 }
159 
GetError(int * subcode)160 XmppEngine::Error XmppClient::GetError(int* subcode) {
161   if (subcode) {
162     *subcode = 0;
163   }
164   if (!d_->engine_)
165     return XmppEngine::ERROR_NONE;
166   if (d_->pre_engine_error_ != XmppEngine::ERROR_NONE) {
167     if (subcode) {
168       *subcode = d_->pre_engine_subcode_;
169     }
170     return d_->pre_engine_error_;
171   }
172   return d_->engine_->GetError(subcode);
173 }
174 
GetStreamError()175 const XmlElement* XmppClient::GetStreamError() {
176   if (!d_->engine_) {
177     return NULL;
178   }
179   return d_->engine_->GetStreamError();
180 }
181 
GetCaptchaChallenge()182 CaptchaChallenge XmppClient::GetCaptchaChallenge() {
183   if (!d_->engine_)
184     return CaptchaChallenge();
185   return d_->captcha_challenge_;
186 }
187 
GetAuthMechanism()188 std::string XmppClient::GetAuthMechanism() {
189   if (!d_->engine_)
190     return "";
191   return d_->auth_mechanism_;
192 }
193 
GetAuthToken()194 std::string XmppClient::GetAuthToken() {
195   if (!d_->engine_)
196     return "";
197   return d_->auth_token_;
198 }
199 
ProcessStart()200 int XmppClient::ProcessStart() {
201   // Should not happen, but was observed in crash reports
202   if (!d_->socket_) {
203     LOG(LS_ERROR) << "socket_ already reset";
204     return STATE_DONE;
205   }
206 
207   if (d_->pre_auth_) {
208     d_->pre_auth_->SignalAuthDone.connect(this, &XmppClient::OnAuthDone);
209     d_->pre_auth_->StartPreXmppAuth(
210         d_->engine_->GetUser(), d_->server_, d_->pass_,
211         d_->auth_mechanism_, d_->auth_token_);
212     d_->pass_.Clear(); // done with this;
213     return STATE_PRE_XMPP_LOGIN;
214   }
215   else {
216     d_->engine_->SetSaslHandler(new PlainSaslHandler(
217               d_->engine_->GetUser(), d_->pass_, d_->allow_plain_));
218     d_->pass_.Clear(); // done with this;
219     return STATE_START_XMPP_LOGIN;
220   }
221 }
222 
OnAuthDone()223 void XmppClient::OnAuthDone() {
224   Wake();
225 }
226 
ProcessTokenLogin()227 int XmppClient::ProcessTokenLogin() {
228   // Should not happen, but was observed in crash reports
229   if (!d_->socket_) {
230     LOG(LS_ERROR) << "socket_ already reset";
231     return STATE_DONE;
232   }
233 
234   // Don't know how this could happen, but crash reports show it as NULL
235   if (!d_->pre_auth_) {
236     d_->pre_engine_error_ = XmppEngine::ERROR_AUTH;
237     EnsureClosed();
238     return STATE_ERROR;
239   }
240 
241   // Wait until pre authentication is done is done
242   if (!d_->pre_auth_->IsAuthDone())
243     return STATE_BLOCKED;
244 
245   if (!d_->pre_auth_->IsAuthorized()) {
246     // maybe split out a case when gaia is down?
247     if (d_->pre_auth_->HadError()) {
248       d_->pre_engine_error_ = XmppEngine::ERROR_AUTH;
249       d_->pre_engine_subcode_ = d_->pre_auth_->GetError();
250     }
251     else {
252       d_->pre_engine_error_ = XmppEngine::ERROR_UNAUTHORIZED;
253       d_->pre_engine_subcode_ = 0;
254       d_->captcha_challenge_ = d_->pre_auth_->GetCaptchaChallenge();
255     }
256     d_->pre_auth_.reset(NULL); // done with this
257     EnsureClosed();
258     return STATE_ERROR;
259   }
260 
261   // Save auth token as a result
262 
263   d_->auth_mechanism_ = d_->pre_auth_->GetAuthMechanism();
264   d_->auth_token_ = d_->pre_auth_->GetAuthToken();
265 
266   // transfer ownership of pre_auth_ to engine
267   d_->engine_->SetSaslHandler(d_->pre_auth_.release());
268   return STATE_START_XMPP_LOGIN;
269 }
270 
ProcessStartXmppLogin()271 int XmppClient::ProcessStartXmppLogin() {
272   // Should not happen, but was observed in crash reports
273   if (!d_->socket_) {
274     LOG(LS_ERROR) << "socket_ already reset";
275     return STATE_DONE;
276   }
277 
278   // Done with pre-connect tasks - connect!
279   if (!d_->socket_->Connect(d_->server_)) {
280     EnsureClosed();
281     return STATE_ERROR;
282   }
283 
284   return STATE_RESPONSE;
285 }
286 
ProcessResponse()287 int XmppClient::ProcessResponse() {
288   // Hang around while we are connected.
289   if (!delivering_signal_ &&
290       (!d_->engine_ || d_->engine_->GetState() == XmppEngine::STATE_CLOSED))
291     return STATE_DONE;
292   return STATE_BLOCKED;
293 }
294 
Disconnect()295 XmppReturnStatus XmppClient::Disconnect() {
296   if (!d_->socket_)
297     return XMPP_RETURN_BADSTATE;
298   Abort();
299   d_->engine_->Disconnect();
300   d_->ResetSocket();
301   return XMPP_RETURN_OK;
302 }
303 
XmppClient(TaskParent * parent)304 XmppClient::XmppClient(TaskParent* parent)
305     : XmppTaskParentInterface(parent),
306       delivering_signal_(false),
307       valid_(false) {
308   d_.reset(new Private(this));
309   valid_ = true;
310 }
311 
~XmppClient()312 XmppClient::~XmppClient() {
313   valid_ = false;
314 }
315 
jid() const316 const Jid& XmppClient::jid() const {
317   return d_->engine_->FullJid();
318 }
319 
320 
NextId()321 std::string XmppClient::NextId() {
322   return d_->engine_->NextId();
323 }
324 
SendStanza(const XmlElement * stanza)325 XmppReturnStatus XmppClient::SendStanza(const XmlElement* stanza) {
326   return d_->engine_->SendStanza(stanza);
327 }
328 
SendStanzaError(const XmlElement * old_stanza,XmppStanzaError xse,const std::string & message)329 XmppReturnStatus XmppClient::SendStanzaError(
330     const XmlElement* old_stanza, XmppStanzaError xse,
331     const std::string& message) {
332   return d_->engine_->SendStanzaError(old_stanza, xse, message);
333 }
334 
SendRaw(const std::string & text)335 XmppReturnStatus XmppClient::SendRaw(const std::string& text) {
336   return d_->engine_->SendRaw(text);
337 }
338 
engine()339 XmppEngine* XmppClient::engine() {
340   return d_->engine_.get();
341 }
342 
OnSocketConnected()343 void XmppClient::Private::OnSocketConnected() {
344   engine_->Connect();
345 }
346 
OnSocketRead()347 void XmppClient::Private::OnSocketRead() {
348   char bytes[4096];
349   size_t bytes_read;
350   for (;;) {
351     // Should not happen, but was observed in crash reports
352     if (!socket_) {
353       LOG(LS_ERROR) << "socket_ already reset";
354       return;
355     }
356 
357     if (!socket_->Read(bytes, sizeof(bytes), &bytes_read)) {
358       // TODO: deal with error information
359       return;
360     }
361 
362     if (bytes_read == 0)
363       return;
364 
365 //#if !defined(NDEBUG)
366     client_->SignalLogInput(bytes, static_cast<int>(bytes_read));
367 //#endif
368 
369     engine_->HandleInput(bytes, bytes_read);
370   }
371 }
372 
OnSocketClosed()373 void XmppClient::Private::OnSocketClosed() {
374   int code = socket_->GetError();
375   engine_->ConnectionClosed(code);
376 }
377 
OnStateChange(int state)378 void XmppClient::Private::OnStateChange(int state) {
379   if (state == XmppEngine::STATE_CLOSED) {
380     client_->EnsureClosed();
381   }
382   else {
383     client_->SignalStateChange((XmppEngine::State)state);
384   }
385   client_->Wake();
386 }
387 
WriteOutput(const char * bytes,size_t len)388 void XmppClient::Private::WriteOutput(const char* bytes, size_t len) {
389 //#if !defined(NDEBUG)
390   client_->SignalLogOutput(bytes, static_cast<int>(len));
391 //#endif
392 
393   socket_->Write(bytes, len);
394   // TODO: deal with error information
395 }
396 
StartTls(const std::string & domain)397 void XmppClient::Private::StartTls(const std::string& domain) {
398 #if defined(FEATURE_ENABLE_SSL)
399   socket_->StartTls(domain);
400 #endif
401 }
402 
CloseConnection()403 void XmppClient::Private::CloseConnection() {
404   socket_->Close();
405 }
406 
AddXmppTask(XmppTask * task,XmppEngine::HandlerLevel level)407 void XmppClient::AddXmppTask(XmppTask* task, XmppEngine::HandlerLevel level) {
408   d_->engine_->AddStanzaHandler(task, level);
409 }
410 
RemoveXmppTask(XmppTask * task)411 void XmppClient::RemoveXmppTask(XmppTask* task) {
412   d_->engine_->RemoveStanzaHandler(task);
413 }
414 
EnsureClosed()415 void XmppClient::EnsureClosed() {
416   if (!d_->signal_closed_) {
417     d_->signal_closed_ = true;
418     delivering_signal_ = true;
419     SignalStateChange(XmppEngine::STATE_CLOSED);
420     delivering_signal_ = false;
421   }
422 }
423 
424 }  // namespace buzz
425