Home | History | Annotate | Download | only in xmpp
      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 
     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 
     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 
     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 
     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 
     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 
    154 XmppEngine::State XmppClient::GetState() const {
    155   if (!d_->engine_)
    156     return XmppEngine::STATE_NONE;
    157   return d_->engine_->GetState();
    158 }
    159 
    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 
    175 const XmlElement* XmppClient::GetStreamError() {
    176   if (!d_->engine_) {
    177     return NULL;
    178   }
    179   return d_->engine_->GetStreamError();
    180 }
    181 
    182 CaptchaChallenge XmppClient::GetCaptchaChallenge() {
    183   if (!d_->engine_)
    184     return CaptchaChallenge();
    185   return d_->captcha_challenge_;
    186 }
    187 
    188 std::string XmppClient::GetAuthMechanism() {
    189   if (!d_->engine_)
    190     return "";
    191   return d_->auth_mechanism_;
    192 }
    193 
    194 std::string XmppClient::GetAuthToken() {
    195   if (!d_->engine_)
    196     return "";
    197   return d_->auth_token_;
    198 }
    199 
    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 
    223 void XmppClient::OnAuthDone() {
    224   Wake();
    225 }
    226 
    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 
    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 
    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 
    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 
    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 
    312 XmppClient::~XmppClient() {
    313   valid_ = false;
    314 }
    315 
    316 const Jid& XmppClient::jid() const {
    317   return d_->engine_->FullJid();
    318 }
    319 
    320 
    321 std::string XmppClient::NextId() {
    322   return d_->engine_->NextId();
    323 }
    324 
    325 XmppReturnStatus XmppClient::SendStanza(const XmlElement* stanza) {
    326   return d_->engine_->SendStanza(stanza);
    327 }
    328 
    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 
    335 XmppReturnStatus XmppClient::SendRaw(const std::string& text) {
    336   return d_->engine_->SendRaw(text);
    337 }
    338 
    339 XmppEngine* XmppClient::engine() {
    340   return d_->engine_.get();
    341 }
    342 
    343 void XmppClient::Private::OnSocketConnected() {
    344   engine_->Connect();
    345 }
    346 
    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 
    373 void XmppClient::Private::OnSocketClosed() {
    374   int code = socket_->GetError();
    375   engine_->ConnectionClosed(code);
    376 }
    377 
    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 
    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 
    397 void XmppClient::Private::StartTls(const std::string& domain) {
    398 #if defined(FEATURE_ENABLE_SSL)
    399   socket_->StartTls(domain);
    400 #endif
    401 }
    402 
    403 void XmppClient::Private::CloseConnection() {
    404   socket_->Close();
    405 }
    406 
    407 void XmppClient::AddXmppTask(XmppTask* task, XmppEngine::HandlerLevel level) {
    408   d_->engine_->AddStanzaHandler(task, level);
    409 }
    410 
    411 void XmppClient::RemoveXmppTask(XmppTask* task) {
    412   d_->engine_->RemoveStanzaHandler(task);
    413 }
    414 
    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