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