// Copyright (c) 2011 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 "chrome/browser/chromeos/login/online_attempt.h"
#include <string>
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/login/auth_attempt_state.h"
#include "chrome/browser/chromeos/login/auth_attempt_state_resolver.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/net/gaia/gaia_auth_consumer.h"
#include "chrome/common/net/gaia/gaia_auth_fetcher.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "content/browser/browser_thread.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request_status.h"
#include "third_party/libjingle/source/talk/base/urlencode.h"
namespace chromeos {
// static
const int OnlineAttempt::kClientLoginTimeoutMs = 10000;
OnlineAttempt::OnlineAttempt(AuthAttemptState* current_attempt,
AuthAttemptStateResolver* callback)
: attempt_(current_attempt),
resolver_(callback),
fetch_canceler_(NULL),
try_again_(true) {
CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
}
OnlineAttempt::~OnlineAttempt() {
// Just to be sure.
if (gaia_authenticator_.get())
gaia_authenticator_->CancelRequest();
}
void OnlineAttempt::Initiate(Profile* profile) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
gaia_authenticator_.reset(new GaiaAuthFetcher(this,
GaiaConstants::kChromeOSSource,
profile->GetRequestContext()));
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
NewRunnableMethod(this, &OnlineAttempt::TryClientLogin));
}
void OnlineAttempt::OnClientLoginSuccess(
const GaiaAuthConsumer::ClientLoginResult& credentials) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
VLOG(1) << "Online login successful!";
if (fetch_canceler_) {
fetch_canceler_->Cancel();
fetch_canceler_ = NULL;
}
if (attempt_->hosted_policy() == GaiaAuthFetcher::HostedAccountsAllowed &&
attempt_->is_first_time_user()) {
// First time user, and we don't know if the account is HOSTED or not.
// Since we don't allow HOSTED accounts to log in, we need to try
// again, without allowing HOSTED accounts.
//
// NOTE: we used to do this in the opposite order, so that we'd only
// try the HOSTED pathway if GOOGLE-only failed. This breaks CAPTCHA
// handling, though.
attempt_->DisableHosted();
TryClientLogin();
return;
}
TriggerResolve(credentials, LoginFailure::None());
}
void OnlineAttempt::OnClientLoginFailure(
const GoogleServiceAuthError& error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (fetch_canceler_) {
fetch_canceler_->Cancel();
fetch_canceler_ = NULL;
}
if (error.state() == GoogleServiceAuthError::REQUEST_CANCELED) {
if (try_again_) {
try_again_ = false;
// TODO(cmasone): add UMA tracking for this to see if we can remove it.
LOG(ERROR) << "Login attempt canceled!?!? Trying again.";
TryClientLogin();
return;
}
LOG(ERROR) << "Login attempt canceled again? Already retried...";
}
if (error.state() == GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS &&
attempt_->is_first_time_user() &&
attempt_->hosted_policy() != GaiaAuthFetcher::HostedAccountsAllowed) {
// This was a first-time login, we already tried allowing HOSTED accounts
// and succeeded. That we've failed with INVALID_GAIA_CREDENTIALS now
// indicates that the account is HOSTED.
LOG(WARNING) << "Rejecting valid HOSTED account.";
TriggerResolve(GaiaAuthConsumer::ClientLoginResult(),
LoginFailure::FromNetworkAuthFailure(
GoogleServiceAuthError(
GoogleServiceAuthError::HOSTED_NOT_ALLOWED)));
return;
}
if (error.state() == GoogleServiceAuthError::TWO_FACTOR) {
LOG(WARNING) << "Two factor authenticated. Sync will not work.";
TriggerResolve(GaiaAuthConsumer::ClientLoginResult(),
LoginFailure::None());
return;
}
VLOG(2) << "ClientLogin attempt failed with " << error.state();
TriggerResolve(GaiaAuthConsumer::ClientLoginResult(),
LoginFailure::FromNetworkAuthFailure(error));
}
void OnlineAttempt::TryClientLogin() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
fetch_canceler_ = NewRunnableMethod(this, &OnlineAttempt::CancelClientLogin);
BrowserThread::PostDelayedTask(BrowserThread::IO, FROM_HERE,
fetch_canceler_,
kClientLoginTimeoutMs);
gaia_authenticator_->StartClientLogin(
attempt_->username,
attempt_->password,
GaiaConstants::kContactsService,
attempt_->login_token,
attempt_->login_captcha,
attempt_->hosted_policy());
}
void OnlineAttempt::CancelClientLogin() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (gaia_authenticator_->HasPendingFetch()) {
LOG(WARNING) << "Canceling ClientLogin attempt.";
gaia_authenticator_->CancelRequest();
fetch_canceler_ = NULL;
TriggerResolve(GaiaAuthConsumer::ClientLoginResult(),
LoginFailure(LoginFailure::LOGIN_TIMED_OUT));
}
}
void OnlineAttempt::TriggerResolve(
const GaiaAuthConsumer::ClientLoginResult& credentials,
const LoginFailure& outcome) {
attempt_->RecordOnlineLoginStatus(credentials, outcome);
gaia_authenticator_.reset(NULL);
resolver_->Resolve();
}
} // namespace chromeos