<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <algorithm>
#include <cstddef>
#include <string>
#include <vector>

#include "jingle/notifier/communicator/single_login_attempt.h"

#include "base/logging.h"
#include "jingle/notifier/base/chrome_async_socket.h"
#include "jingle/notifier/communicator/connection_options.h"
#include "jingle/notifier/communicator/connection_settings.h"
#include "jingle/notifier/communicator/const_communicator.h"
#include "jingle/notifier/communicator/gaia_token_pre_xmpp_auth.h"
#include "jingle/notifier/communicator/login_failure.h"
#include "jingle/notifier/communicator/login_settings.h"
#include "jingle/notifier/communicator/product_info.h"
#include "jingle/notifier/communicator/xmpp_connection_generator.h"
#include "jingle/notifier/communicator/xmpp_socket_adapter.h"
#include "net/base/ssl_config_service.h"
#include "net/socket/client_socket_factory.h"
#include "talk/base/asynchttprequest.h"
#include "talk/base/firewallsocketserver.h"
#include "talk/base/signalthread.h"
#include "talk/base/taskrunner.h"
#include "talk/base/win32socketinit.h"
#include "talk/xmllite/xmlelement.h"
#include "talk/xmpp/xmppclient.h"
#include "talk/xmpp/xmppclientsettings.h"
#include "talk/xmpp/constants.h"

namespace net {
class NetLog;
}  // namespace net

namespace notifier {

static void GetClientErrorInformation(
    buzz::XmppClient* client,
    buzz::XmppEngine::Error* error,
    int* subcode,
    buzz::XmlElement** stream_error) {
  DCHECK(client);
  DCHECK(error);
  DCHECK(subcode);
  DCHECK(stream_error);

  *error = client->GetError(subcode);

  *stream_error = NULL;
  if (*error == buzz::XmppEngine::ERROR_STREAM) {
    const buzz::XmlElement* error_element = client->GetStreamError();
    if (error_element) {
      *stream_error = new buzz::XmlElement(*error_element);
    }
  }
}

SingleLoginAttempt::SingleLoginAttempt(talk_base::TaskParent* parent,
                                       LoginSettings* login_settings,
                                       bool use_chrome_async_socket,
                                       bool successful_connection)
    : talk_base::Task(parent),
      use_chrome_async_socket_(use_chrome_async_socket),
      state_(buzz::XmppEngine::STATE_NONE),
      code_(buzz::XmppEngine::ERROR_NONE),
      subcode_(0),
      need_authentication_(false),
      certificate_expired_(false),
      cookie_refreshed_(false),
      successful_connection_(successful_connection),
      login_settings_(login_settings),
      client_(NULL) {
#if defined(OS_WIN)
  talk_base::EnsureWinsockInit();
#endif
  connection_generator_.reset(new XmppConnectionGenerator(
                                  this,
                                  login_settings_->host_resolver(),
                                  &login_settings_->connection_options(),
                                  login_settings_->proxy_only(),
                                  login_settings_->server_list(),
                                  login_settings_->server_count()));

  connection_generator_->SignalExhaustedSettings.connect(
      this,
      &SingleLoginAttempt::OnAttemptedAllConnections);
  connection_generator_->SignalNewSettings.connect(
      this,
      &SingleLoginAttempt::DoLogin);
}

SingleLoginAttempt::~SingleLoginAttempt() {
  // If this assertion goes off, it means that "Stop()" didn't get called like
  // it should have been.
  DCHECK(!client_);
}

const talk_base::ProxyInfo& SingleLoginAttempt::proxy() const {
  DCHECK(connection_generator_.get());
  return connection_generator_->proxy();
}

int SingleLoginAttempt::ProcessStart() {
  DCHECK_EQ(GetState(), talk_base::Task::STATE_START);
  connection_generator_->StartGenerating();

  // After being started, this class is callback driven and does signaling from
  // those callbacks (with checks to see if it is done if it may be called back
  // from something that isn't a child task).
  return talk_base::Task::STATE_BLOCKED;
}

void SingleLoginAttempt::Stop() {
  ClearClient();
  talk_base::Task::Stop();

  // No more signals should happen after being stopped. This is needed because
  // some of these signals happen due to other components doing signaling which
  // may continue running even though this task is stopped.
  SignalUnexpectedDisconnect.disconnect_all();
  SignalRedirect.disconnect_all();
  SignalLoginFailure.disconnect_all();
  SignalNeedAutoReconnect.disconnect_all();
  SignalClientStateChange.disconnect_all();
}

void SingleLoginAttempt::OnAttemptedAllConnections(
    bool successfully_resolved_dns,
    int first_dns_error) {

  // Maybe we needed proxy authentication?
  if (need_authentication_) {
    LoginFailure failure(LoginFailure::PROXY_AUTHENTICATION_ERROR);
    SignalLoginFailure(failure);
    return;
  }

  if (certificate_expired_) {
    LoginFailure failure(LoginFailure::CERTIFICATE_EXPIRED_ERROR);
    SignalLoginFailure(failure);
    return;
  }

  if (!successfully_resolved_dns) {
    code_ = buzz::XmppEngine::ERROR_SOCKET;
    subcode_ = first_dns_error;
  }

  LOG(INFO) << "Connection failed with error " << code_;

  // We were connected and we had a problem.
  if (successful_connection_) {
    SignalNeedAutoReconnect();
    // Expect to be deleted at this point.
    return;
  }

  DiagnoseConnectionError();
}

void SingleLoginAttempt::UseNextConnection() {
  DCHECK(connection_generator_.get());
  ClearClient();
  connection_generator_->UseNextConnection();
}

void SingleLoginAttempt::UseCurrentConnection() {
  DCHECK(connection_generator_.get());
  ClearClient();
  connection_generator_->UseCurrentConnection();
}

void SingleLoginAttempt::DoLogin(
    const ConnectionSettings& connection_settings) {
  if (client_) {
    return;
  }

  buzz::XmppClientSettings client_settings;
  // Set the user settings portion.
  *static_cast<buzz::XmppClientSettings*>(&client_settings) =
      login_settings_->user_settings();
  // Fill in the rest of the client settings.
  connection_settings.FillXmppClientSettings(&client_settings);

  client_ = new buzz::XmppClient(this);
  SignalLogInput.repeat(client_->SignalLogInput);
  SignalLogOutput.repeat(client_->SignalLogOutput);

  // Listen for connection progress.
  client_->SignalStateChange.connect(this,
                                     &SingleLoginAttempt::OnClientStateChange);

  // Transition to "start".
  OnClientStateChange(buzz::XmppEngine::STATE_START);
  // Start connecting.<commit_msg>Skip unresolved hosts for sync notifications instead of having libjingle resolve them.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <algorithm>
#include <cstddef>
#include <string>
#include <vector>

#include "jingle/notifier/communicator/single_login_attempt.h"

#include "base/logging.h"
#include "jingle/notifier/base/chrome_async_socket.h"
#include "jingle/notifier/communicator/connection_options.h"
#include "jingle/notifier/communicator/connection_settings.h"
#include "jingle/notifier/communicator/const_communicator.h"
#include "jingle/notifier/communicator/gaia_token_pre_xmpp_auth.h"
#include "jingle/notifier/communicator/login_failure.h"
#include "jingle/notifier/communicator/login_settings.h"
#include "jingle/notifier/communicator/product_info.h"
#include "jingle/notifier/communicator/xmpp_connection_generator.h"
#include "jingle/notifier/communicator/xmpp_socket_adapter.h"
#include "net/base/ssl_config_service.h"
#include "net/socket/client_socket_factory.h"
#include "talk/base/asynchttprequest.h"
#include "talk/base/firewallsocketserver.h"
#include "talk/base/signalthread.h"
#include "talk/base/taskrunner.h"
#include "talk/base/win32socketinit.h"
#include "talk/xmllite/xmlelement.h"
#include "talk/xmpp/xmppclient.h"
#include "talk/xmpp/xmppclientsettings.h"
#include "talk/xmpp/constants.h"

namespace net {
class NetLog;
}  // namespace net

namespace notifier {

static void GetClientErrorInformation(
    buzz::XmppClient* client,
    buzz::XmppEngine::Error* error,
    int* subcode,
    buzz::XmlElement** stream_error) {
  DCHECK(client);
  DCHECK(error);
  DCHECK(subcode);
  DCHECK(stream_error);

  *error = client->GetError(subcode);

  *stream_error = NULL;
  if (*error == buzz::XmppEngine::ERROR_STREAM) {
    const buzz::XmlElement* error_element = client->GetStreamError();
    if (error_element) {
      *stream_error = new buzz::XmlElement(*error_element);
    }
  }
}

SingleLoginAttempt::SingleLoginAttempt(talk_base::TaskParent* parent,
                                       LoginSettings* login_settings,
                                       bool use_chrome_async_socket,
                                       bool successful_connection)
    : talk_base::Task(parent),
      use_chrome_async_socket_(use_chrome_async_socket),
      state_(buzz::XmppEngine::STATE_NONE),
      code_(buzz::XmppEngine::ERROR_NONE),
      subcode_(0),
      need_authentication_(false),
      certificate_expired_(false),
      cookie_refreshed_(false),
      successful_connection_(successful_connection),
      login_settings_(login_settings),
      client_(NULL) {
#if defined(OS_WIN)
  talk_base::EnsureWinsockInit();
#endif
  connection_generator_.reset(new XmppConnectionGenerator(
                                  this,
                                  login_settings_->host_resolver(),
                                  &login_settings_->connection_options(),
                                  login_settings_->proxy_only(),
                                  login_settings_->server_list(),
                                  login_settings_->server_count()));

  connection_generator_->SignalExhaustedSettings.connect(
      this,
      &SingleLoginAttempt::OnAttemptedAllConnections);
  connection_generator_->SignalNewSettings.connect(
      this,
      &SingleLoginAttempt::DoLogin);
}

SingleLoginAttempt::~SingleLoginAttempt() {
  // If this assertion goes off, it means that "Stop()" didn't get called like
  // it should have been.
  DCHECK(!client_);
}

const talk_base::ProxyInfo& SingleLoginAttempt::proxy() const {
  DCHECK(connection_generator_.get());
  return connection_generator_->proxy();
}

int SingleLoginAttempt::ProcessStart() {
  DCHECK_EQ(GetState(), talk_base::Task::STATE_START);
  connection_generator_->StartGenerating();

  // After being started, this class is callback driven and does signaling from
  // those callbacks (with checks to see if it is done if it may be called back
  // from something that isn't a child task).
  return talk_base::Task::STATE_BLOCKED;
}

void SingleLoginAttempt::Stop() {
  ClearClient();
  talk_base::Task::Stop();

  // No more signals should happen after being stopped. This is needed because
  // some of these signals happen due to other components doing signaling which
  // may continue running even though this task is stopped.
  SignalUnexpectedDisconnect.disconnect_all();
  SignalRedirect.disconnect_all();
  SignalLoginFailure.disconnect_all();
  SignalNeedAutoReconnect.disconnect_all();
  SignalClientStateChange.disconnect_all();
}

void SingleLoginAttempt::OnAttemptedAllConnections(
    bool successfully_resolved_dns,
    int first_dns_error) {

  // Maybe we needed proxy authentication?
  if (need_authentication_) {
    LoginFailure failure(LoginFailure::PROXY_AUTHENTICATION_ERROR);
    SignalLoginFailure(failure);
    return;
  }

  if (certificate_expired_) {
    LoginFailure failure(LoginFailure::CERTIFICATE_EXPIRED_ERROR);
    SignalLoginFailure(failure);
    return;
  }

  if (!successfully_resolved_dns) {
    code_ = buzz::XmppEngine::ERROR_SOCKET;
    subcode_ = first_dns_error;
  }

  LOG(INFO) << "Connection failed with error " << code_;

  // We were connected and we had a problem.
  if (successful_connection_) {
    SignalNeedAutoReconnect();
    // Expect to be deleted at this point.
    return;
  }

  DiagnoseConnectionError();
}

void SingleLoginAttempt::UseNextConnection() {
  DCHECK(connection_generator_.get());
  ClearClient();
  connection_generator_->UseNextConnection();
}

void SingleLoginAttempt::UseCurrentConnection() {
  DCHECK(connection_generator_.get());
  ClearClient();
  connection_generator_->UseCurrentConnection();
}

void SingleLoginAttempt::DoLogin(
    const ConnectionSettings& connection_settings) {
  if (client_) {
    return;
  }
  // TODO(akalin): Resolve any unresolved IPs, possibly through a
  // proxy, instead of skipping them.
  if (connection_settings.server().IsUnresolvedIP()) {
    // Handler should attempt reconnect.
    SignalUnexpectedDisconnect();
    return;
  }

  buzz::XmppClientSettings client_settings;
  // Set the user settings portion.
  *static_cast<buzz::XmppClientSettings*>(&client_settings) =
      login_settings_->user_settings();
  // Fill in the rest of the client settings.
  connection_settings.FillXmppClientSettings(&client_settings);

  client_ = new buzz::XmppClient(this);
  SignalLogInput.repeat(client_->SignalLogInput);
  SignalLogOutput.repeat(client_->SignalLogOutput);

  // Listen for connection progress.
  client_->SignalStateChange.connect(this,
                                     &SingleLoginAttempt::OnClientStateChange);

  // Transition to "start".
  OnClientStateChange(buzz::XmppEngine::STATE_START);
  // Start connecting.
{'fraction_non_alphanumeric': 0.10519297725757464, 'fraction_numerical': 0.0010371138602859472, 'mean_word_length': 3.987070557813077, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 47, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 24, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3627063', 'n_tokens_mistral': 4032, 'n_tokens_neox': 3907, 'n_words': 785}