| Index: chrome/browser/sync/notifier/communicator/login.cc
|
| ===================================================================
|
| --- chrome/browser/sync/notifier/communicator/login.cc (revision 0)
|
| +++ chrome/browser/sync/notifier/communicator/login.cc (revision 0)
|
| @@ -0,0 +1,361 @@
|
| +// 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 <string>
|
| +
|
| +#include "chrome/browser/sync/notifier/communicator/login.h"
|
| +
|
| +#include "chrome/browser/sync/notifier/base/network_status_detector_task.h"
|
| +#include "chrome/browser/sync/notifier/base/time.h"
|
| +#include "chrome/browser/sync/notifier/base/timer.h"
|
| +#include "chrome/browser/sync/notifier/communicator/auto_reconnect.h"
|
| +#include "chrome/browser/sync/notifier/communicator/connection_options.h"
|
| +#include "chrome/browser/sync/notifier/communicator/login_settings.h"
|
| +#include "chrome/browser/sync/notifier/communicator/product_info.h"
|
| +#include "chrome/browser/sync/notifier/communicator/single_login_attempt.h"
|
| +#include "talk/base/common.h"
|
| +#include "talk/base/firewallsocketserver.h"
|
| +#include "talk/base/logging.h"
|
| +#include "talk/base/taskrunner.h"
|
| +#include "talk/xmllite/xmlelement.h"
|
| +#include "talk/xmpp/asyncsocket.h"
|
| +#include "talk/xmpp/prexmppauth.h"
|
| +#include "talk/xmpp/xmppclient.h"
|
| +#include "talk/xmpp/xmppclientsettings.h"
|
| +#include "talk/xmpp/xmppengine.h"
|
| +
|
| +namespace notifier {
|
| +
|
| +// redirect valid for 5 minutes
|
| +static const time64 kRedirectTimeoutNs = 5 * kMinsTo100ns;
|
| +
|
| +// Disconnect if network stays down for more than 10 seconds.
|
| +static const int kDisconnectionDelaySecs = 10;
|
| +
|
| +Login::Login(talk_base::Task* parent,
|
| + const buzz::XmppClientSettings& user_settings,
|
| + const ConnectionOptions& options,
|
| + std::string lang,
|
| + ServerInformation* server_list,
|
| + int server_count,
|
| + NetworkStatusDetectorTask* network_status,
|
| + talk_base::FirewallManager* firewall,
|
| + bool no_gaia_auth,
|
| + bool proxy_only,
|
| + bool previous_login_successful)
|
| + : login_settings_(new LoginSettings(user_settings,
|
| + options,
|
| + lang,
|
| + server_list,
|
| + server_count,
|
| + firewall,
|
| + no_gaia_auth,
|
| + proxy_only)),
|
| + single_attempt_(NULL),
|
| + successful_connection_(previous_login_successful),
|
| + parent_(parent),
|
| + state_(STATE_OPENING),
|
| + redirect_time_ns_(0),
|
| + redirect_port_(0),
|
| + unexpected_disconnect_occurred_(false),
|
| + reset_unexpected_timer_(NULL),
|
| + google_host_(user_settings.host()),
|
| + google_user_(user_settings.user()),
|
| + disconnect_timer_(NULL) {
|
| + if (!network_status) {
|
| + network_status = NetworkStatusDetectorTask::Create(parent_);
|
| + if (network_status) {
|
| + // On linux we don't have an implementation of NetworkStatusDetectorTask.
|
| + network_status->Start();
|
| + }
|
| + }
|
| + network_status->SignalNetworkStateDetected.connect(
|
| + this, &Login::OnNetworkStateDetected);
|
| + auto_reconnect_.reset(new AutoReconnect(parent_, network_status));
|
| + auto_reconnect_->SignalStartConnection.connect(this,
|
| + &Login::StartConnection);
|
| + auto_reconnect_->SignalTimerStartStop.connect(
|
| + this,
|
| + &Login::OnAutoReconnectTimerChange);
|
| + SignalClientStateChange.connect(auto_reconnect_.get(),
|
| + &AutoReconnect::OnClientStateChange);
|
| + SignalIdleChange.connect(auto_reconnect_.get(),
|
| + &AutoReconnect::set_idle);
|
| + SignalPowerSuspended.connect(auto_reconnect_.get(),
|
| + &AutoReconnect::OnPowerSuspend);
|
| +}
|
| +
|
| +// defined so that the destructors are executed here (and
|
| +// the corresponding classes don't need to be included in
|
| +// the header file)
|
| +Login::~Login() {
|
| + if (single_attempt_) {
|
| + single_attempt_->Abort();
|
| + single_attempt_ = NULL;
|
| + }
|
| +}
|
| +
|
| +void Login::StartConnection() {
|
| + // If there is a server redirect, use it.
|
| + if (GetCurrent100NSTime() < redirect_time_ns_ + kRedirectTimeoutNs) {
|
| + // Override server/port with redirect values
|
| + talk_base::SocketAddress server_override;
|
| + server_override.SetIP(redirect_server_, false);
|
| + ASSERT(redirect_port_ != 0);
|
| + server_override.SetPort(redirect_port_);
|
| + login_settings_->set_server_override(server_override);
|
| + } else {
|
| + login_settings_->clear_server_override();
|
| + }
|
| +
|
| + if (single_attempt_) {
|
| + single_attempt_->Abort();
|
| + single_attempt_ = NULL;
|
| + }
|
| + single_attempt_ = new SingleLoginAttempt(parent_,
|
| + login_settings_.get(),
|
| + successful_connection_);
|
| +
|
| + // Do the signaling hook-ups.
|
| + single_attempt_->SignalLoginFailure.connect(this, &Login::OnLoginFailure);
|
| + single_attempt_->SignalRedirect.connect(this, &Login::OnRedirect);
|
| + single_attempt_->SignalClientStateChange.connect(
|
| + this,
|
| + &Login::OnClientStateChange) ;
|
| + single_attempt_->SignalUnexpectedDisconnect.connect(
|
| + this,
|
| + &Login::OnUnexpectedDisconnect);
|
| + single_attempt_->SignalLogoff.connect(
|
| + this,
|
| + &Login::OnLogoff);
|
| + single_attempt_->SignalNeedAutoReconnect.connect(
|
| + this,
|
| + &Login::DoAutoReconnect);
|
| + SignalLogInput.repeat(single_attempt_->SignalLogInput);
|
| + SignalLogOutput.repeat(single_attempt_->SignalLogOutput);
|
| +
|
| + single_attempt_->Start();
|
| +}
|
| +
|
| +const std::string& Login::google_host() const {
|
| + return google_host_;
|
| +}
|
| +
|
| +const std::string& Login::google_user() const {
|
| + return google_user_;
|
| +}
|
| +
|
| +const talk_base::ProxyInfo& Login::proxy() const {
|
| + return proxy_info_;
|
| +}
|
| +
|
| +void Login::OnLoginFailure(const LoginFailure& failure) {
|
| + auto_reconnect_->StopReconnectTimer();
|
| + HandleClientStateChange(STATE_CLOSED);
|
| + SignalLoginFailure(failure);
|
| +}
|
| +
|
| +void Login::OnLogoff() {
|
| + HandleClientStateChange(STATE_CLOSED);
|
| +}
|
| +
|
| +void Login::OnClientStateChange(buzz::XmppEngine::State state) {
|
| + ConnectionState new_state = STATE_CLOSED;
|
| +
|
| + switch (state) {
|
| + case buzz::XmppEngine::STATE_NONE:
|
| + case buzz::XmppEngine::STATE_CLOSED:
|
| + // Ignore the closed state (because
|
| + // we may be trying the next dns entry).
|
| + //
|
| + // But we go to this state for other
|
| + // signals when there is no retry happening.
|
| + new_state = state_;
|
| + break;
|
| +
|
| + case buzz::XmppEngine::STATE_START:
|
| + case buzz::XmppEngine::STATE_OPENING:
|
| + new_state = STATE_OPENING;
|
| + break;
|
| +
|
| + case buzz::XmppEngine::STATE_OPEN:
|
| + new_state = STATE_OPENED;
|
| + break;
|
| +
|
| + default:
|
| + ASSERT(false);
|
| + break;
|
| + }
|
| + HandleClientStateChange(new_state);
|
| +}
|
| +
|
| +void Login::HandleClientStateChange(ConnectionState new_state) {
|
| + // Do we need to transition between the retrying and closed states?
|
| + if (auto_reconnect_->is_retrying()) {
|
| + if (new_state == STATE_CLOSED) {
|
| + new_state = STATE_RETRYING;
|
| + }
|
| + } else {
|
| + if (new_state == STATE_RETRYING) {
|
| + new_state = STATE_CLOSED;
|
| + }
|
| + }
|
| +
|
| + if (new_state != state_) {
|
| + state_ = new_state;
|
| + if (reset_unexpected_timer_) {
|
| + reset_unexpected_timer_->Abort();
|
| + reset_unexpected_timer_ = NULL;
|
| + }
|
| +
|
| + if (state_ == STATE_OPENED) {
|
| + successful_connection_ = true;
|
| +
|
| + google_host_ = single_attempt_->xmpp_client()->jid().domain();
|
| + google_user_ = single_attempt_->xmpp_client()->jid().node();
|
| + proxy_info_ = single_attempt_->proxy();
|
| +
|
| + reset_unexpected_timer_ = new Timer(parent_,
|
| + kResetReconnectInfoDelaySec,
|
| + false); // repeat
|
| + reset_unexpected_timer_->SignalTimeout.connect(
|
| + this,
|
| + &Login::ResetUnexpectedDisconnect);
|
| + }
|
| + SignalClientStateChange(state_);
|
| + }
|
| +}
|
| +
|
| +void Login::OnAutoReconnectTimerChange() {
|
| + if (!single_attempt_ || !single_attempt_->xmpp_client()) {
|
| + HandleClientStateChange(STATE_CLOSED);
|
| + return;
|
| + }
|
| + OnClientStateChange(single_attempt_->xmpp_client()->GetState());
|
| +}
|
| +
|
| +buzz::XmppClient* Login::xmpp_client() {
|
| + if (!single_attempt_) {
|
| + return NULL;
|
| + }
|
| + return single_attempt_->xmpp_client();
|
| +}
|
| +
|
| +int Login::seconds_until_reconnect() const {
|
| + return auto_reconnect_->seconds_until();
|
| +}
|
| +
|
| +void Login::UseNextConnection() {
|
| + if (!single_attempt_) {
|
| + // Just in case, there is an obscure case that causes
|
| + // this to get called when there is no single_attempt_.
|
| + return;
|
| + }
|
| + single_attempt_->UseNextConnection();
|
| +}
|
| +
|
| +void Login::UseCurrentConnection() {
|
| + if (!single_attempt_) {
|
| + // Just in case, there is an obscure case that causes
|
| + // this to get called when there is no single_attempt_.
|
| + return;
|
| + }
|
| + single_attempt_->UseCurrentConnection();
|
| +}
|
| +
|
| +void Login::OnRedirect(const std::string& redirect_server, int redirect_port) {
|
| + ASSERT(redirect_port_ != 0);
|
| +
|
| + redirect_time_ns_ = GetCurrent100NSTime();
|
| + redirect_server_ = redirect_server;
|
| + redirect_port_ = redirect_port;
|
| +
|
| + // Drop the current connection, and start the login process again
|
| + StartConnection();
|
| +}
|
| +
|
| +void Login::OnUnexpectedDisconnect() {
|
| + if (reset_unexpected_timer_) {
|
| + reset_unexpected_timer_->Abort();
|
| + reset_unexpected_timer_ = NULL;
|
| + }
|
| +
|
| + // Start the login process again
|
| + if (unexpected_disconnect_occurred_) {
|
| + // If we already have received an unexpected disconnect recently,
|
| + // then our account may have be jailed due to abuse, so we shouldn't
|
| + // make the situation worse by trying really hard to reconnect.
|
| + // Instead, we'll do the autoreconnect route, which has exponential
|
| + // back-off.
|
| + DoAutoReconnect();
|
| + return;
|
| + }
|
| + StartConnection();
|
| + unexpected_disconnect_occurred_ = true;
|
| +}
|
| +
|
| +void Login::ResetUnexpectedDisconnect() {
|
| + reset_unexpected_timer_ = NULL;
|
| + unexpected_disconnect_occurred_ = false;
|
| +}
|
| +
|
| +void Login::DoAutoReconnect() {
|
| + bool allow_auto_reconnect =
|
| + login_settings_->connection_options().auto_reconnect();
|
| + // Start the reconnect time before aborting the connection
|
| + // to ensure that AutoReconnect::is_retrying() is true, so
|
| + // that the Login doesn't transition to the CLOSED state
|
| + // (which would cause the reconnection timer to reset
|
| + // and not double).
|
| + if (allow_auto_reconnect) {
|
| + auto_reconnect_->StartReconnectTimer();
|
| + }
|
| +
|
| + if (single_attempt_) {
|
| + single_attempt_->Abort();
|
| + single_attempt_ = NULL;
|
| + }
|
| +
|
| + if (!allow_auto_reconnect) {
|
| + HandleClientStateChange(STATE_CLOSED);
|
| + return;
|
| + }
|
| +}
|
| +
|
| +void Login::OnNetworkStateDetected(bool was_alive, bool is_alive) {
|
| + if (was_alive && !is_alive) {
|
| + // Our network connection just went down.
|
| + // Setup a timer to disconnect. Don't disconnect immediately to avoid
|
| + // constant connection/disconnection due to flaky network interfaces.
|
| + ASSERT(disconnect_timer_ == NULL);
|
| + disconnect_timer_ = new Timer(parent_, kDisconnectionDelaySecs, false);
|
| + disconnect_timer_->SignalTimeout.connect(this,
|
| + &Login::OnDisconnectTimeout);
|
| + } else if (!was_alive && is_alive) {
|
| + // Our connection has come back up. If we have a disconnect timer going,
|
| + // abort it so we don't disconnect.
|
| + if (disconnect_timer_) {
|
| + disconnect_timer_->Abort();
|
| + // It will free itself.
|
| + disconnect_timer_ = NULL;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void Login::OnDisconnectTimeout() {
|
| + disconnect_timer_ = NULL;
|
| +
|
| + if (state_ != STATE_OPENED) {
|
| + return;
|
| + }
|
| +
|
| + if (single_attempt_) {
|
| + single_attempt_->Abort();
|
| + single_attempt_ = NULL;
|
| + }
|
| +
|
| + StartConnection();
|
| +}
|
| +
|
| +} // namespace notifier
|
|
|
| Property changes on: chrome\browser\sync\notifier\communicator\login.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|