diff options
author | hbono@google.com <hbono@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-17 04:17:53 +0000 |
---|---|---|
committer | hbono@google.com <hbono@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-17 04:17:53 +0000 |
commit | ec880bbf40a7e7e837aaf0ef7be8bd827a24b8be (patch) | |
tree | 05431f361c4b73b337e1488d59cd35e341960cf8 | |
parent | 8814427f49396271ee260e44de0a8482f4ae2b76 (diff) | |
download | chromium_src-ec880bbf40a7e7e837aaf0ef7be8bd827a24b8be.zip chromium_src-ec880bbf40a7e7e837aaf0ef7be8bd827a24b8be.tar.gz chromium_src-ec880bbf40a7e7e837aaf0ef7be8bd827a24b8be.tar.bz2 |
Revert 41790 - Removed auth_task.* and talk_auth_task.* (dead code) because Windows XP doesn't have inet_pton and it crashes XP bots.
Removed GaiaAuth class. Gtalk login will now fail immediately instead of trying (in vain) to gaiaauth.
Removed nowunneeded parameters.
BUG=38034
TEST=manual with and without invalidatesyncxmpplogin
Review URL: http://codereview.chromium.org/885008
TBR=akalin@chromium.org
Review URL: http://codereview.chromium.org/983005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@41808 0039d316-1c4b-4281-b951-d872f2087c98
24 files changed, 1339 insertions, 50 deletions
diff --git a/chrome/browser/sync/notifier/base/async_dns_lookup.cc b/chrome/browser/sync/notifier/base/async_dns_lookup.cc index 267dd50..a8fee43 100644 --- a/chrome/browser/sync/notifier/base/async_dns_lookup.cc +++ b/chrome/browser/sync/notifier/base/async_dns_lookup.cc @@ -14,8 +14,6 @@ #include <netinet/ip.h> #include <sys/socket.h> #include <sys/types.h> -#else -#include <Ws2tcpip.h> #endif // defined(OS_POSIX) #include <vector> @@ -23,6 +21,7 @@ #include "base/basictypes.h" #include "base/logging.h" #include "chrome/browser/sync/notifier/base/nethelpers.h" +#include "chrome/browser/sync/notifier/gaia_auth/inet_aton.h" #include "talk/base/byteorder.h" #include "talk/base/common.h" #include "talk/base/socketaddress.h" @@ -50,7 +49,7 @@ void AsyncDNSLookup::DoWork() { std::string hostname(server_->IPAsString()); in_addr addr; - if (inet_pton(AF_INET, hostname.c_str(), &addr)) { + if (inet_aton(hostname.c_str(), &addr)) { talk_base::CritScope scope(&cs_); ip_list_.push_back(talk_base::NetworkToHost32(addr.s_addr)); } else { diff --git a/chrome/browser/sync/notifier/communicator/auth_task.cc b/chrome/browser/sync/notifier/communicator/auth_task.cc new file mode 100644 index 0000000..ba98fe3 --- /dev/null +++ b/chrome/browser/sync/notifier/communicator/auth_task.cc @@ -0,0 +1,68 @@ +// 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 "chrome/browser/sync/notifier/communicator/auth_task.h" + +#include "chrome/browser/sync/notifier/gaia_auth/gaiaauth.h" +#include "chrome/browser/sync/notifier/communicator/login.h" +#include "chrome/browser/sync/notifier/communicator/login_settings.h" +#include "chrome/browser/sync/notifier/communicator/product_info.h" +#include "talk/base/common.h" +#include "talk/base/urlencode.h" +#include "talk/xmpp/xmppclient.h" + +namespace notifier { +const char kTalkGadgetAuthPath[] = "/auth"; + +AuthTask::AuthTask(talk_base::Task* parent, Login* login, const char* url) + : talk_base::Task(parent), + login_(login), + url_(url), + use_gaia_redirect_(true) { + ASSERT(login && !url_.empty()); +} + +int AuthTask::ProcessStart() { + auth_.reset(new buzz::GaiaAuth(GetUserAgentString(), + GetProductSignature())); + auth_->SignalAuthDone.connect(this, &AuthTask::OnAuthDone); + auth_->StartTokenAuth(login_->xmpp_client()->jid().BareJid(), + login_->login_settings().user_settings().pass(), + use_gaia_redirect_ ? "gaia" : service_); + return STATE_RESPONSE; +} + +int AuthTask::ProcessResponse() { + ASSERT(auth_.get()); + if (!auth_->IsAuthDone()) { + return STATE_BLOCKED; + } + if (!auth_->IsAuthorized()) { + SignalAuthError(!auth_->HadError()); + return STATE_ERROR; + } + + std::string uber_url; + if (use_gaia_redirect_) { + uber_url = auth_->CreateAuthenticatedUrl(url_, service_); + } else { + uber_url = redir_auth_prefix_ + auth_->GetAuthCookie(); + uber_url += redir_continue_; + uber_url += UrlEncodeString(url_); + } + + if (uber_url == "") { + SignalAuthError(true); + return STATE_ERROR; + } + + SignalAuthDone(uber_url); + return STATE_DONE; +} + +void AuthTask::OnAuthDone() { + Wake(); +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/communicator/auth_task.h b/chrome/browser/sync/notifier/communicator/auth_task.h new file mode 100644 index 0000000..b71bd92 --- /dev/null +++ b/chrome/browser/sync/notifier/communicator/auth_task.h @@ -0,0 +1,77 @@ +// 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. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_AUTH_TASK_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_AUTH_TASK_H_ + +#include <string> +#include "talk/base/scoped_ptr.h" +#include "talk/base/sigslot.h" +#include "talk/base/task.h" + +namespace buzz { +class GaiaAuth; +} + +namespace notifier { + +class Login; + +// Create an authenticated talk url from an unauthenticated url. +class AuthTask : public talk_base::Task, public sigslot::has_slots<> { + public: + AuthTask(talk_base::Task* parent, Login* login, const char* url); + + // An abort method which doesn't take any parameters. + // (talk_base::Task::Abort() has a default parameter). + // + // The primary purpose of this method is to allow a signal to be hooked up to + // abort this task. + void Abort() { + talk_base::Task::Abort(); + } + + void set_service(const char* service) { + service_ = service; + } + + void set_use_gaia_redirect(bool use_gaia_redirect) { + use_gaia_redirect_ = use_gaia_redirect; + } + + void set_redir_auth_prefix(const char* redir_auth_prefix) { + redir_auth_prefix_ = redir_auth_prefix; + } + + void set_redir_continue(const char* redir_continue) { + redir_continue_ = redir_continue; + } + + sigslot::signal1<const std::string&> SignalAuthDone; + sigslot::signal1<bool> SignalAuthError; + + protected: + virtual int ProcessStart(); + virtual int ProcessResponse(); + + private: + void OnAuthDone(); + + scoped_ptr<buzz::GaiaAuth> auth_; + Login* login_; + std::string service_; + std::string url_; + + // The following members are used for cases where we don't want to redirect + // through gaia, but rather via the end-site's mechanism. + bool use_gaia_redirect_; + std::string redir_auth_prefix_; + std::string redir_continue_; + + DISALLOW_COPY_AND_ASSIGN(AuthTask); +}; + +} // namespace notifier + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_AUTH_TASK_H_ diff --git a/chrome/browser/sync/notifier/communicator/login.cc b/chrome/browser/sync/notifier/communicator/login.cc index 9da7f56..de7fd3a 100644 --- a/chrome/browser/sync/notifier/communicator/login.cc +++ b/chrome/browser/sync/notifier/communicator/login.cc @@ -41,6 +41,7 @@ Login::Login(talk_base::Task* parent, 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, @@ -49,6 +50,7 @@ Login::Login(talk_base::Task* parent, server_list, server_count, firewall, + no_gaia_auth, proxy_only)), single_attempt_(NULL), successful_connection_(previous_login_successful), diff --git a/chrome/browser/sync/notifier/communicator/login.h b/chrome/browser/sync/notifier/communicator/login.h index de90a1b..284435b 100644 --- a/chrome/browser/sync/notifier/communicator/login.h +++ b/chrome/browser/sync/notifier/communicator/login.h @@ -7,14 +7,15 @@ #include <string> -#include "chrome/browser/sync/notifier/base/sigslotrepeater.h" #include "chrome/browser/sync/notifier/base/time.h" +#include "chrome/browser/sync/notifier/gaia_auth/sigslotrepeater.h" #include "talk/base/proxyinfo.h" #include "talk/base/scoped_ptr.h" #include "talk/base/sigslot.h" #include "talk/xmpp/xmppengine.h" namespace buzz { +class CaptchaChallenge; class XmppClient; class XmppEngine; class XmppClientSettings; @@ -51,6 +52,7 @@ class Login : public sigslot::has_slots<> { int server_count, NetworkStatusDetectorTask* network_status, talk_base::FirewallManager* firewall, + bool no_gaia_auth, bool proxy_only, bool previous_login_successful); ~Login(); diff --git a/chrome/browser/sync/notifier/communicator/login_failure.cc b/chrome/browser/sync/notifier/communicator/login_failure.cc index c10a35b..80d2f74 100644 --- a/chrome/browser/sync/notifier/communicator/login_failure.cc +++ b/chrome/browser/sync/notifier/communicator/login_failure.cc @@ -4,6 +4,8 @@ #include "chrome/browser/sync/notifier/communicator/login_failure.h" +#include "talk/xmpp/prexmppauth.h" + namespace notifier { LoginFailure::LoginFailure(LoginError error) @@ -20,9 +22,25 @@ LoginFailure::LoginFailure(LoginError error, subcode_(subcode) { } +LoginFailure::LoginFailure(LoginError error, + buzz::XmppEngine::Error xmpp_error, + int subcode, + const buzz::CaptchaChallenge& captcha) + : error_(error), + xmpp_error_(xmpp_error), + subcode_(subcode), + captcha_(new buzz::CaptchaChallenge(captcha)) { +} + buzz::XmppEngine::Error LoginFailure::xmpp_error() const { ASSERT(error_ == XMPP_ERROR); return xmpp_error_; } +const buzz::CaptchaChallenge& LoginFailure::captcha() const { + ASSERT(xmpp_error_ == buzz::XmppEngine::ERROR_UNAUTHORIZED || + xmpp_error_ == buzz::XmppEngine::ERROR_MISSING_USERNAME); + return *captcha_.get(); +} + } // namespace notifier diff --git a/chrome/browser/sync/notifier/communicator/login_failure.h b/chrome/browser/sync/notifier/communicator/login_failure.h index 886f8da..21d7d36 100644 --- a/chrome/browser/sync/notifier/communicator/login_failure.h +++ b/chrome/browser/sync/notifier/communicator/login_failure.h @@ -8,6 +8,10 @@ #include "talk/base/common.h" #include "talk/xmpp/xmppengine.h" +namespace buzz { +class CaptchaChallenge; +} + namespace notifier { class LoginFailure { @@ -28,6 +32,10 @@ class LoginFailure { LoginFailure(LoginError error, buzz::XmppEngine::Error xmpp_error, int subcode); + LoginFailure(LoginError error, + buzz::XmppEngine::Error xmpp_error, + int subcode, + const buzz::CaptchaChallenge& captcha); // Used as the first level of error information. LoginError error() const { @@ -40,10 +48,20 @@ class LoginFailure { // (except that the DiagnoseConnectionError has already been done). buzz::XmppEngine::Error xmpp_error() const; + // Returns the captcha challenge. Valid if and only if + // xmpp_error is buzz::XmppEngine::ERROR_UNAUTHORIZED or + // buzz::XmppEngine::ERROR_MISSING_USERNAME + // + // See PhoneWindow::HandleConnectionPasswordError for how to handle this + // (after the if (..) { LoginAccountAndConnectionSetting(); ...} because + // that is done by SingleLoginAttempt. + const buzz::CaptchaChallenge& captcha() const; + private: LoginError error_; buzz::XmppEngine::Error xmpp_error_; int subcode_; + scoped_ptr<buzz::CaptchaChallenge> captcha_; DISALLOW_COPY_AND_ASSIGN(LoginFailure); }; diff --git a/chrome/browser/sync/notifier/communicator/login_settings.cc b/chrome/browser/sync/notifier/communicator/login_settings.cc index 085bf31..bf450bc 100644 --- a/chrome/browser/sync/notifier/communicator/login_settings.cc +++ b/chrome/browser/sync/notifier/communicator/login_settings.cc @@ -20,8 +20,10 @@ LoginSettings::LoginSettings(const buzz::XmppClientSettings& user_settings, ServerInformation* server_list, int server_count, talk_base::FirewallManager* firewall, + bool no_gaia_auth, bool proxy_only) : proxy_only_(proxy_only), + no_gaia_auth_(no_gaia_auth), firewall_(firewall), lang_(lang), server_list_(new ServerInformation[server_count]), diff --git a/chrome/browser/sync/notifier/communicator/login_settings.h b/chrome/browser/sync/notifier/communicator/login_settings.h index 331d341..3e9b971 100644 --- a/chrome/browser/sync/notifier/communicator/login_settings.h +++ b/chrome/browser/sync/notifier/communicator/login_settings.h @@ -30,6 +30,7 @@ class LoginSettings { ServerInformation* server_list, int server_count, talk_base::FirewallManager* firewall, + bool no_gaia_auth, bool proxy_only); ~LoginSettings(); @@ -42,6 +43,10 @@ class LoginSettings { return firewall_; } + bool no_gaia_auth() const { + return no_gaia_auth_; + } + bool proxy_only() const { return proxy_only_; } @@ -75,6 +80,7 @@ class LoginSettings { private: bool proxy_only_; + bool no_gaia_auth_; talk_base::FirewallManager* firewall_; std::string lang_; diff --git a/chrome/browser/sync/notifier/communicator/single_login_attempt.cc b/chrome/browser/sync/notifier/communicator/single_login_attempt.cc index de759a6..80c438b 100644 --- a/chrome/browser/sync/notifier/communicator/single_login_attempt.cc +++ b/chrome/browser/sync/notifier/communicator/single_login_attempt.cc @@ -17,12 +17,14 @@ #include "chrome/browser/sync/notifier/communicator/product_info.h" #include "chrome/browser/sync/notifier/communicator/xmpp_connection_generator.h" #include "chrome/browser/sync/notifier/communicator/xmpp_socket_adapter.h" +#include "chrome/browser/sync/notifier/gaia_auth/gaiaauth.h" #include "talk/base/asynchttprequest.h" #include "talk/base/firewallsocketserver.h" #include "talk/base/signalthread.h" #include "talk/base/taskrunner.h" #include "talk/base/winsock_initializer.h" #include "talk/xmllite/xmlelement.h" +#include "talk/xmpp/prexmppauth.h" #include "talk/xmpp/saslcookiemechanism.h" #include "talk/xmpp/saslhandler.h" #include "talk/xmpp/xmppclient.h" @@ -31,15 +33,29 @@ namespace notifier { +static void FillProxyInfo(const buzz::XmppClientSettings& xcs, + talk_base::ProxyInfo* proxy) { + ASSERT(proxy != NULL); + proxy->type = xcs.proxy(); + proxy->address.SetIP(xcs.proxy_host()); + proxy->address.SetPort(xcs.proxy_port()); + if (xcs.use_proxy_auth()) { + proxy->username = xcs.proxy_user(); + proxy->password = xcs.proxy_pass(); + } +} + static void GetClientErrorInformation( buzz::XmppClient* client, buzz::XmppEngine::Error* error, int* subcode, - buzz::XmlElement** stream_error) { + buzz::XmlElement** stream_error, + buzz::CaptchaChallenge* captcha_challenge) { ASSERT(client != NULL); - ASSERT(error && subcode && stream_error); + ASSERT(error && subcode && stream_error && captcha_challenge); *error = client->GetError(subcode); + *captcha_challenge = client->GetCaptchaChallenge(); *stream_error = NULL; if (*error == buzz::XmppEngine::ERROR_STREAM) { @@ -230,7 +246,7 @@ void SingleLoginAttempt::DoLogin( // Start connecting. client_->Connect(client_settings, login_settings_->lang(), CreateSocket(client_settings), - NULL, + CreatePreXmppAuth(client_settings), CreateSaslHandler(client_settings)); client_->Start(); } @@ -260,6 +276,32 @@ buzz::AsyncSocket* SingleLoginAttempt::CreateSocket( return adapter; } +buzz::PreXmppAuth* SingleLoginAttempt::CreatePreXmppAuth( + const buzz::XmppClientSettings& xcs) { + if (login_settings_->no_gaia_auth()) + return NULL; + + // For GMail, use Gaia preauthentication over HTTP. + buzz::GaiaAuth* auth = new buzz::GaiaAuth(GetUserAgentString(), + GetProductSignature()); + auth->SignalAuthenticationError.connect( + this, + &SingleLoginAttempt::OnAuthenticationError); + auth->SignalCertificateExpired.connect( + this, + &SingleLoginAttempt::OnCertificateExpired); + auth->SignalFreshAuthCookie.connect( + this, + &SingleLoginAttempt::OnFreshAuthCookie); + auth->set_token_service(xcs.token_service()); + + talk_base::ProxyInfo proxy; + FillProxyInfo(xcs, &proxy); + auth->set_proxy(proxy); + auth->set_firewall(login_settings_->firewall()); + return auth; +} + buzz::SaslHandler* SingleLoginAttempt::CreateSaslHandler( const buzz::XmppClientSettings& xcs) { buzz::Jid jid(xcs.user(), xcs.host(), buzz::STR_EMPTY); @@ -449,11 +491,13 @@ void SingleLoginAttempt::OnClientStateChangeClosed( buzz::XmppEngine::State previous_state) { buzz::XmppEngine::Error error = buzz::XmppEngine::ERROR_NONE; int error_subcode = 0; + buzz::CaptchaChallenge captcha_challenge; buzz::XmlElement* stream_error_ptr; GetClientErrorInformation(client_, &error, &error_subcode, - &stream_error_ptr); + &stream_error_ptr, + &captcha_challenge); scoped_ptr<buzz::XmlElement> stream_error(stream_error_ptr); client_->SignalStateChange.disconnect(this); @@ -467,20 +511,36 @@ void SingleLoginAttempt::OnClientStateChangeClosed( SignalUnexpectedDisconnect(); return; } else { - HandleConnectionError(error, error_subcode, stream_error.get()); + HandleConnectionError(error, error_subcode, stream_error.get(), + captcha_challenge); } } -void SingleLoginAttempt::HandleConnectionPasswordError() { +void SingleLoginAttempt::HandleConnectionPasswordError( + const buzz::CaptchaChallenge& captcha_challenge) { LOG(INFO) << "SingleLoginAttempt::HandleConnectionPasswordError"; - LoginFailure failure(LoginFailure::XMPP_ERROR, code_, subcode_); + + // Clear the auth cookie. + std::string current_auth_cookie = + login_settings_->user_settings().auth_cookie(); + login_settings_->modifiable_user_settings()->set_auth_cookie(""); + // If there was an auth cookie and it was the same as the last auth cookie, + // then it is a stale cookie. Retry login. + if (!current_auth_cookie.empty() && !cookie_refreshed_) { + UseCurrentConnection(); + return; + } + + LoginFailure failure(LoginFailure::XMPP_ERROR, code_, subcode_, + captcha_challenge); SignalLoginFailure(failure); } void SingleLoginAttempt::HandleConnectionError( buzz::XmppEngine::Error code, int subcode, - const buzz::XmlElement* stream_error) { + const buzz::XmlElement* stream_error, + const buzz::CaptchaChallenge& captcha_challenge) { LOG(INFO) << "(" << code << ", " << subcode << ")"; // Save off the error code information, so we can use it to tell the user @@ -490,7 +550,7 @@ void SingleLoginAttempt::HandleConnectionError( if ((code_ == buzz::XmppEngine::ERROR_UNAUTHORIZED) || (code_ == buzz::XmppEngine::ERROR_MISSING_USERNAME)) { // There was a problem with credentials (username/password). - HandleConnectionPasswordError(); + HandleConnectionPasswordError(captcha_challenge); return; } diff --git a/chrome/browser/sync/notifier/communicator/single_login_attempt.h b/chrome/browser/sync/notifier/communicator/single_login_attempt.h index 388260c..c798a27 100644 --- a/chrome/browser/sync/notifier/communicator/single_login_attempt.h +++ b/chrome/browser/sync/notifier/communicator/single_login_attempt.h @@ -15,6 +15,8 @@ namespace buzz { class AsyncSocket; +class CaptchaChallenge; +class PreXmppAuth; class SaslHandler; class XmppClient; class XmppClientSettings; @@ -94,6 +96,7 @@ class SingleLoginAttempt : public talk_base::Task, public sigslot::has_slots<> { private: void DoLogin(const ConnectionSettings& connection_settings); buzz::AsyncSocket* CreateSocket(const buzz::XmppClientSettings& xcs); + buzz::PreXmppAuth* CreatePreXmppAuth(const buzz::XmppClientSettings& xcs); static buzz::SaslHandler* CreateSaslHandler( const buzz::XmppClientSettings& xcs); @@ -103,8 +106,10 @@ class SingleLoginAttempt : public talk_base::Task, public sigslot::has_slots<> { void HandleConnectionError( buzz::XmppEngine::Error code, int subcode, - const buzz::XmlElement* stream_error); - void HandleConnectionPasswordError(); + const buzz::XmlElement* stream_error, + const buzz::CaptchaChallenge& captcha_challenge); + void HandleConnectionPasswordError( + const buzz::CaptchaChallenge& captcha_challenge); void DiagnoseConnectionError(); void OnHttpTestDone(talk_base::SignalThread* thread); diff --git a/chrome/browser/sync/notifier/communicator/talk_auth_task.cc b/chrome/browser/sync/notifier/communicator/talk_auth_task.cc new file mode 100644 index 0000000..f085b16 --- /dev/null +++ b/chrome/browser/sync/notifier/communicator/talk_auth_task.cc @@ -0,0 +1,74 @@ +// 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 "chrome/browser/sync/notifier/communicator/talk_auth_task.h" + +#include "chrome/browser/sync/notifier/communicator/login.h" +#include "chrome/browser/sync/notifier/communicator/login_settings.h" +#include "chrome/browser/sync/notifier/communicator/product_info.h" +#include "chrome/browser/sync/notifier/gaia_auth/gaiaauth.h" +#include "talk/base/common.h" +#include "talk/base/urlencode.h" +#include "talk/xmpp/xmppclient.h" + +namespace notifier { +const char kTalkGadgetAuthPath[] = "/auth"; + +TalkAuthTask::TalkAuthTask(talk_base::Task* parent, + Login* login, + const char* url) + : talk_base::Task(parent), + login_(login), + url_(url) { + ASSERT(login && !url_.empty()); +} + +int TalkAuthTask::ProcessStart() { + auth_.reset(new buzz::GaiaAuth(GetUserAgentString(), + GetProductSignature())); + auth_->SignalAuthDone.connect( + this, + &TalkAuthTask::OnAuthDone); + auth_->StartAuth(login_->xmpp_client()->jid().BareJid(), + login_->login_settings().user_settings().pass(), + "talk"); + return STATE_RESPONSE; +} + +int TalkAuthTask::ProcessResponse() { + ASSERT(auth_.get()); + if (!auth_->IsAuthDone()) { + return STATE_BLOCKED; + } + SignalAuthDone(*this); + return STATE_DONE; +} + + +void TalkAuthTask::OnAuthDone() { + Wake(); +} + +bool TalkAuthTask::HadError() const { + return auth_->HadError(); +} + +std::string TalkAuthTask::GetAuthenticatedUrl( + const char* talk_base_url) const { + ASSERT(talk_base_url && *talk_base_url && !auth_->HadError()); + + std::string auth_url(talk_base_url); + auth_url.append(kTalkGadgetAuthPath); + auth_url.append("?silent=true&redirect=true&host="); + auth_url.append(UrlEncodeString(url_)); + auth_url.append("&auth="); + auth_url.append(auth_->GetAuth()); + return auth_url; +} + +std::string TalkAuthTask::GetSID() const { + return auth_->GetSID(); +} + +} // namespace notifier diff --git a/chrome/browser/sync/notifier/communicator/talk_auth_task.h b/chrome/browser/sync/notifier/communicator/talk_auth_task.h new file mode 100644 index 0000000..da00f2a --- /dev/null +++ b/chrome/browser/sync/notifier/communicator/talk_auth_task.h @@ -0,0 +1,64 @@ +// 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. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_TALK_AUTH_TASK_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_TALK_AUTH_TASK_H_ + +#include "talk/base/scoped_ptr.h" +#include "talk/base/sigslot.h" +#include "talk/base/task.h" + +namespace buzz { +class GaiaAuth; +} + +namespace notifier { + +class Login; + +// Create an authenticated talk url from an unauthenticated url. +class TalkAuthTask : public talk_base::Task, public sigslot::has_slots<> { + public: + TalkAuthTask(talk_base::Task* parent, + Login* login, + const char* url); + + // An abort method which doesn't take any parameters. + // (talk_base::Task::Abort() has a default parameter.) + // + // The primary purpose of this method is to allow a signal to be hooked up to + // abort this task. + void Abort() { + talk_base::Task::Abort(); + } + + const std::string& url() { + return url_; + } + + std::string GetAuthenticatedUrl(const char* talk_base_url) const; + std::string GetSID() const; + + sigslot::signal1<const TalkAuthTask&> SignalAuthDone; + + bool HadError() const; + + // TODO(sync): add captcha support. + + protected: + virtual int ProcessStart(); + virtual int ProcessResponse(); + + private: + void OnAuthDone(); + + scoped_ptr<buzz::GaiaAuth> auth_; + Login* login_; + std::string url_; + DISALLOW_COPY_AND_ASSIGN(TalkAuthTask); +}; + +} // namespace notifier + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_COMMUNICATOR_TALK_AUTH_TASK_H_ diff --git a/chrome/browser/sync/notifier/gaia_auth/gaiaauth.cc b/chrome/browser/sync/notifier/gaia_auth/gaiaauth.cc new file mode 100644 index 0000000..1558b06 --- /dev/null +++ b/chrome/browser/sync/notifier/gaia_auth/gaiaauth.cc @@ -0,0 +1,435 @@ +// 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 "base/logging.h" +#include "base/message_loop.h" +#include "base/task.h" +#include "base/thread.h" +#include "chrome/browser/sync/notifier/base/ssl_adapter.h" +#include "chrome/browser/sync/notifier/gaia_auth/gaiaauth.h" +#include "talk/base/asynchttprequest.h" +#include "talk/base/firewallsocketserver.h" +#include "talk/base/httpclient.h" +#include "talk/base/physicalsocketserver.h" +#include "talk/base/socketadapters.h" +#include "talk/base/socketpool.h" +#include "talk/base/stringutils.h" +#include "talk/base/urlencode.h" +#include "talk/xmpp/saslcookiemechanism.h" +#include "talk/xmpp/saslplainmechanism.h" + +namespace buzz { + +static const int kGaiaAuthTimeoutMs = 30 * 1000; // 30 sec + +// Warning, this is externed. +GaiaServer g_gaia_server; + +/////////////////////////////////////////////////////////////////////////////// +// GaiaAuth::WorkerTask +/////////////////////////////////////////////////////////////////////////////// + +// GaiaAuth is NOT invoked during SASL authentication, but it is invoked even +// before XMPP login begins. As a PreXmppAuth object, it is driven by +// XmppClient before the XMPP socket is opened. The job of GaiaAuth is to goes +// out using HTTPS POST to grab cookies from GAIA. +// +// It is used by XmppClient. It grabs a SaslAuthenticator which knows how to +// play the cookie login. + +class GaiaAuth::WorkerTask { + public: + WorkerTask(const std::string& username, + const talk_base::CryptString& pass, + const std::string& service, + const talk_base::ProxyInfo& proxy, + talk_base::FirewallManager* firewall, + const std::string& token, + const CaptchaAnswer& captcha_answer, + bool obtain_auth, + const std::string& user_agent, + const std::string& signature, + const std::string& token_service) : + username_(username), + pass_(pass), + service_(service), + proxy_(proxy), + firewall_(firewall), + success_(false), + error_(true), + error_code_(0), + proxy_auth_required_(false), + certificate_expired_(false), + auth_token_(token), + captcha_answer_(captcha_answer), + fresh_auth_token_(false), + obtain_auth_(obtain_auth), + agent_(user_agent), + signature_(signature), + token_service_(token_service) {} + + ~WorkerTask() {} + + void DoWork(MessageLoop* parent_message_loop, Task* on_work_done_task) { + DCHECK(parent_message_loop); + DCHECK(on_work_done_task); + LOG(INFO) << "GaiaAuth Begin"; + // Maybe we already have an auth token, then there is nothing to do. + if (!auth_token_.empty()) { + LOG(INFO) << "Reusing auth token:" << auth_token_; + success_ = true; + error_ = false; + } else { + // SocketFactory::CreateSSLAdapter() is called on the following + // object somewhere in the depths of libjingle so we wrap it + // with SSLAdapterSocketFactory to make sure it returns the + // right SSLAdapter (see http://crbug.com/30721 ). + notifier::SSLAdapterSocketFactory<talk_base::PhysicalSocketServer> + physical; + talk_base::SocketServer* ss = &physical; + if (firewall_) { + ss = new talk_base::FirewallSocketServer(ss, firewall_); + } + + talk_base::SslSocketFactory factory(ss, agent_); + factory.SetProxy(proxy_); + if (g_gaia_server.use_ssl()) { + factory.SetIgnoreBadCert(true); + factory.UseSSL(g_gaia_server.hostname().c_str()); + } + factory.SetLogging(talk_base::LS_VERBOSE, "GaiaAuth"); + +#if defined(OS_WIN) + talk_base::ReuseSocketPool pool(&factory); +#else + // On non-Windows platforms our SSL socket wrapper + // implementation does not support restartable SSL sockets, so + // we turn it off and use a NewSocketPool instead. This means + // that we create separate connections for each HTTP request but + // this is okay since we do only two (in GaiaRequestSid() and + // GaiaRequestAuthToken()). + factory.SetUseRestartableSSLSockets(false); + talk_base::NewSocketPool pool(&factory); +#endif + talk_base::HttpClient http(agent_, &pool); + + talk_base::HttpMonitor monitor(ss); + monitor.Connect(&http); + + // If we do not already have a SID, let's get one using our password. + if (sid_.empty() || (auth_.empty() && obtain_auth_)) { + GaiaRequestSid(&http, username_, pass_, signature_, + obtain_auth_ ? service_ : "", captcha_answer_, + g_gaia_server); + // TODO(akalin): handle timeouts better; this can cause jank, + // e.g. when we're waiting on this and the user stops syncing, + // which causes the GaiaAuth object to be destroyed, which + // waits on this. (bug: http://crbug.com/31981 ) + ss->Wait(kGaiaAuthTimeoutMs, true); + + error_code_ = monitor.error(); // Save off the error code. + + if (!monitor.done()) { + LOG(INFO) << "GaiaAuth request timed out"; + goto Cleanup; + } else if (monitor.error()) { + LOG(INFO) << "GaiaAuth request error: " << monitor.error(); + if (monitor.error() == talk_base::HE_AUTH) { + success_ = false; + proxy_auth_required_ = true; + } else if (monitor.error() == talk_base::HE_CERTIFICATE_EXPIRED) { + success_ = false; + certificate_expired_ = true; + } + goto Cleanup; + } else { + std::string captcha_token, captcha_url; + switch (GaiaParseSidResponse(http, g_gaia_server, + &captcha_token, &captcha_url, + &sid_, &lsid_, &auth_)) { + case GR_ERROR: + goto Cleanup; + + case GR_UNAUTHORIZED: + if (!captcha_url.empty()) { + captcha_challenge_ = buzz::CaptchaChallenge(captcha_token, + captcha_url); + } + // We had no "error" - we were just unauthorized. + error_ = false; + error_code_ = 0; + goto Cleanup; + + case GR_SUCCESS: + break; + } + } + } + + // If all we need is a SID, then we are done now. + if (service_.empty() || obtain_auth_) { + success_ = true; + error_ = false; + error_code_ = 0; + goto Cleanup; + } + + monitor.reset(); + GaiaRequestAuthToken(&http, sid_, lsid_, service_, g_gaia_server); + ss->Wait(kGaiaAuthTimeoutMs, true); + + error_code_ = monitor.error(); // Save off the error code. + + if (!monitor.done()) { + LOG(INFO) << "GaiaAuth request timed out"; + } else if (monitor.error()) { + LOG(INFO) << "GaiaAuth request error: " << monitor.error(); + if (monitor.error() == talk_base::HE_AUTH) { + success_ = false; + proxy_auth_required_ = true; + } else if (monitor.error() == talk_base::HE_CERTIFICATE_EXPIRED) { + success_ = false; + certificate_expired_ = true; + } + } else { + if (GR_SUCCESS == GaiaParseAuthTokenResponse(http, &auth_token_)) { + fresh_auth_token_ = true; + success_ = true; + error_ = false; + error_code_ = 0; + } + } + } + + // Done authenticating. + + Cleanup: + LOG(INFO) << "GaiaAuth done"; + parent_message_loop->PostTask(FROM_HERE, on_work_done_task); + } + + bool Succeeded() const { return success_; } + bool HadError() const { return error_; } + int GetError() const { return error_code_; } + bool ProxyAuthRequired() const { return proxy_auth_required_; } + bool CertificateExpired() const { return certificate_expired_; } + const buzz::CaptchaChallenge& GetCaptchaChallenge() { + return captcha_challenge_; + } + bool fresh_auth_token() const { return fresh_auth_token_; } + + talk_base::CryptString GetPassword() const { return pass_; } + std::string GetSID() const { return sid_; } + std::string GetAuth() const { return auth_; } + std::string GetToken() const { return auth_token_; } + std::string GetUsername() const { return username_; } + std::string GetTokenService() const { return token_service_; } + + private: + std::string username_; + talk_base::CryptString pass_; + std::string service_; + talk_base::ProxyInfo proxy_; + talk_base::FirewallManager * firewall_; + bool done_; + bool success_; + bool error_; + int error_code_; + bool proxy_auth_required_; + bool certificate_expired_; + std::string sid_; + std::string lsid_; + std::string auth_; + std::string auth_token_; + buzz::CaptchaChallenge captcha_challenge_; + CaptchaAnswer captcha_answer_; + bool fresh_auth_token_; + bool obtain_auth_; + std::string agent_; + std::string signature_; + std::string token_service_; + + DISALLOW_COPY_AND_ASSIGN(WorkerTask); +}; + +} // namespace buzz + +// We outlive any runnable methods we create, so stub out +// RunnableMethodTraits for GaiaAuth and WorkerTask. + +template <> +struct RunnableMethodTraits<buzz::GaiaAuth> { + void RetainCallee(buzz::GaiaAuth* gaia_auth) {} + void ReleaseCallee(buzz::GaiaAuth* gaia_auth) {} +}; + +template <> +struct RunnableMethodTraits<buzz::GaiaAuth::WorkerTask> { + void RetainCallee(buzz::GaiaAuth::WorkerTask* gaia_auth) {} + void ReleaseCallee(buzz::GaiaAuth::WorkerTask* gaia_auth) {} +}; + +namespace buzz { + +/////////////////////////////////////////////////////////////////////////////// +// GaiaAuth +/////////////////////////////////////////////////////////////////////////////// + +GaiaAuth::GaiaAuth(const std::string &user_agent, const std::string &sig) + : agent_(user_agent), signature_(sig), firewall_(0), + worker_task_(NULL), on_work_done_task_(NULL), + worker_thread_("GaiaAuth worker thread"), done_(false), + current_message_loop_(MessageLoop::current()) { + DCHECK(current_message_loop_); +} + +GaiaAuth::~GaiaAuth() { + DCHECK_EQ(MessageLoop::current(), current_message_loop_); + // Wait until worker thread stops running. + worker_thread_.Stop(); + // Then, if the worker thread posted an OnAuthDone() task that is + // still pending, cancel it. + if (on_work_done_task_) { + on_work_done_task_->Cancel(); + } +} + +void GaiaAuth::StartPreXmppAuth(const buzz::Jid& jid, + const talk_base::SocketAddress& server, + const talk_base::CryptString& pass, + const std::string & auth_cookie) { + InternalStartGaiaAuth(jid, server, pass, auth_cookie, "mail", false); +} + +void GaiaAuth::StartTokenAuth(const buzz::Jid& jid, + const talk_base::CryptString& pass, + const std::string& service) { + InternalStartGaiaAuth(jid, talk_base::SocketAddress(), pass, "", service, + false); +} + +void GaiaAuth::StartAuth(const buzz::Jid& jid, + const talk_base::CryptString& pass, + const std::string & service) { + InternalStartGaiaAuth(jid, talk_base::SocketAddress(), pass, "", service, + true); +} + +void GaiaAuth::StartAuthFromSid(const buzz::Jid& jid, + const std::string& sid, + const std::string& service) { + InternalStartGaiaAuth(jid, talk_base::SocketAddress(), + talk_base::CryptString(), sid, service, false); +} + +void GaiaAuth::InternalStartGaiaAuth(const buzz::Jid& jid, + const talk_base::SocketAddress& server, + const talk_base::CryptString& pass, + const std::string& token, + const std::string& service, + bool obtain_auth) { + DCHECK_EQ(MessageLoop::current(), current_message_loop_); + worker_task_.reset( + new WorkerTask(jid.Str(), pass, service, proxy_, firewall_, + token, captcha_answer_, obtain_auth, agent_, + signature_, token_service_)); + on_work_done_task_ = NewRunnableMethod(this, &GaiaAuth::OnAuthDone); + LOG(INFO) << "GaiaAuth begin (async)"; + worker_thread_.Start(); + worker_thread_.message_loop()->PostTask( + FROM_HERE, NewRunnableMethod(worker_task_.get(), + &WorkerTask::DoWork, + current_message_loop_, + on_work_done_task_)); +} + +void GaiaAuth::OnAuthDone() { + DCHECK_EQ(MessageLoop::current(), current_message_loop_); + LOG(INFO) << "GaiaAuth done (async)"; + worker_thread_.Stop(); + on_work_done_task_ = NULL; + done_ = true; + + if (worker_task_->fresh_auth_token()) { + SignalFreshAuthCookie(worker_task_->GetToken()); + } + if (worker_task_->ProxyAuthRequired()) { + SignalAuthenticationError(); + } + if (worker_task_->CertificateExpired()) { + SignalCertificateExpired(); + } + SignalAuthDone(); +} + +std::string GaiaAuth::CreateAuthenticatedUrl( + const std::string & continue_url, const std::string & service) { + if (!done_ || worker_task_->GetToken().empty()) + return ""; + + std::string url; + // Note that http_prefix always ends with a "/". + url += g_gaia_server.http_prefix() + + "accounts/TokenAuth?auth=" + // Do not URL encode - GAIA doesn't like that. + + worker_task_->GetToken(); + url += "&service=" + service; + url += "&continue=" + UrlEncodeString(continue_url); + url += "&source=" + signature_; + return url; +} + +std::string GaiaAuth::GetAuthCookie() { + assert(IsAuthDone() && IsAuthorized()); + if (!done_ || !worker_task_->Succeeded()) { + return ""; + } + return worker_task_->GetToken(); +} + +std::string GaiaAuth::GetAuth() { + assert(IsAuthDone() && IsAuthorized()); + if (!done_ || !worker_task_->Succeeded()) { + return ""; + } + return worker_task_->GetAuth(); +} + +std::string GaiaAuth::GetSID() { + assert(IsAuthDone() && IsAuthorized()); + if (!done_ || !worker_task_->Succeeded()) { + return ""; + } + return worker_task_->GetSID(); +} + +bool GaiaAuth::IsAuthDone() { + return done_; +} + +bool GaiaAuth::IsAuthorized() { + return done_ && worker_task_ != NULL && worker_task_->Succeeded(); +} + +bool GaiaAuth::HadError() { + return done_ && worker_task_ != NULL && worker_task_->HadError(); +} + +int GaiaAuth::GetError() { + if (done_ && worker_task_ != NULL) { + return worker_task_->GetError(); + } + return 0; +} + +buzz::CaptchaChallenge GaiaAuth::GetCaptchaChallenge() { + if (!done_ || worker_task_->Succeeded()) { + return buzz::CaptchaChallenge(); + } + return worker_task_->GetCaptchaChallenge(); +} + +} // namespace buzz diff --git a/chrome/browser/sync/notifier/gaia_auth/gaiaauth.h b/chrome/browser/sync/notifier/gaia_auth/gaiaauth.h new file mode 100644 index 0000000..0de7632 --- /dev/null +++ b/chrome/browser/sync/notifier/gaia_auth/gaiaauth.h @@ -0,0 +1,133 @@ +// 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. +// +// Gaia auth code for XMPP notifier support. This should be merged with the +// other gaia auth file when we have time. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_GAIAAUTH_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_GAIAAUTH_H_ + +#include <string> +#include <vector> + +#include "base/scoped_ptr.h" +#include "base/thread.h" +#include "chrome/browser/sync/notifier/gaia_auth/gaiahelper.h" +#include "talk/base/cryptstring.h" +#include "talk/base/messagequeue.h" +#include "talk/base/proxyinfo.h" +#include "talk/xmpp/prexmppauth.h" + +class MessageLoop; + +namespace talk_base { +class FirewallManager; +} + +namespace buzz { + +/////////////////////////////////////////////////////////////////////////////// +// GaiaAuth +/////////////////////////////////////////////////////////////////////////////// + +class GaiaAuth : public PreXmppAuth { + public: + GaiaAuth(const std::string& user_agent, const std::string& signature); + virtual ~GaiaAuth(); + + void set_proxy(const talk_base::ProxyInfo& proxy) { + proxy_ = proxy; + } + void set_firewall(talk_base::FirewallManager* firewall) { + firewall_ = firewall; + } + void set_captcha_answer(const CaptchaAnswer& captcha_answer) { + captcha_answer_ = captcha_answer; + } + + // From inside XMPP login, this is called. + virtual void StartPreXmppAuth(const buzz::Jid& jid, + const talk_base::SocketAddress& server, + const talk_base::CryptString& pass, + const std::string& auth_cookie); + + void StartTokenAuth(const buzz::Jid& jid, + const talk_base::CryptString& pass, + const std::string& service); + + // This is used when calling GetAuth(). + void StartAuth(const buzz::Jid& jid, + const talk_base::CryptString& pass, + const std::string& service); + + // This is used when bootstrapping from a download page. + void StartAuthFromSid(const buzz::Jid& jid, + const std::string& sid, + const std::string& service); + + virtual bool IsAuthDone(); + virtual bool IsAuthorized(); + virtual bool HadError(); + virtual int GetError(); + virtual buzz::CaptchaChallenge GetCaptchaChallenge(); + // Returns the auth token that can be sent in an url param to gaia in order + // to generate an auth cookie. + virtual std::string GetAuthCookie(); + + // Returns the auth cookie for gaia. + std::string GetAuth(); + std::string GetSID(); + + // Sets / gets the token service to use. + std::string token_service() const { return token_service_; } + void set_token_service(const std::string& token_service) { + token_service_ = token_service; + } + + std::string CreateAuthenticatedUrl(const std::string& continue_url, + const std::string& service); + + sigslot::signal0<> SignalAuthenticationError; + sigslot::signal0<> SignalCertificateExpired; + sigslot::signal1<const std::string&> SignalFreshAuthCookie; + + // Needs to be public for the RunnableMethodTraits specialization in + // gaiaauth.cc. + class WorkerTask; + + private: + void OnAuthDone(); + + void InternalStartGaiaAuth(const buzz::Jid& jid, + const talk_base::SocketAddress& server, + const talk_base::CryptString& pass, + const std::string& sid, + const std::string& service, + bool obtain_auth); + + std::string agent_; + std::string signature_; + talk_base::ProxyInfo proxy_; + talk_base::FirewallManager* firewall_; + scoped_ptr<WorkerTask> worker_task_; + CancelableTask* on_work_done_task_; + base::Thread worker_thread_; + bool done_; + MessageLoop* current_message_loop_; + + CaptchaAnswer captcha_answer_; + std::string token_service_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////////////////////////// + +extern GaiaServer g_gaia_server; + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace buzz + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_GAIAAUTH_H_ diff --git a/chrome/browser/sync/notifier/gaia_auth/gaiahelper.cc b/chrome/browser/sync/notifier/gaia_auth/gaiahelper.cc new file mode 100644 index 0000000..f20f3ed --- /dev/null +++ b/chrome/browser/sync/notifier/gaia_auth/gaiahelper.cc @@ -0,0 +1,237 @@ +// 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 "chrome/browser/sync/notifier/gaia_auth/gaiahelper.h" +#include "base/logging.h" +#include "talk/base/common.h" +#include "talk/base/cryptstring.h" +#include "talk/base/httpclient.h" +#include "talk/base/httpcommon-inl.h" +#include "talk/base/stringutils.h" +#include "talk/base/urlencode.h" +#include "talk/xmpp/jid.h" +#include "talk/xmpp/xmppconstants.h" + +/////////////////////////////////////////////////////////////////////////////// + +namespace { + +std::string GetValueForKey(const std::string& key, const std::string& nvp) { + size_t start_of_line = 0; + size_t end_of_line = 0; + for (;;) { // For each line. + start_of_line = nvp.find_first_not_of("\r\n", end_of_line); + if (start_of_line == std::string::npos) + break; + end_of_line = nvp.find_first_of("\r\n", start_of_line); + if (end_of_line == std::string::npos) { + end_of_line = nvp.length(); + } + size_t equals = nvp.find('=', start_of_line); + if (equals >= end_of_line || + equals == std::string::npos || + equals - start_of_line != key.length()) { + continue; + } + + if (nvp.find(key, start_of_line) == start_of_line) { + return std::string(nvp, equals + 1, end_of_line - equals - 1); + } + } + return ""; +} + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// + +namespace buzz { + +GaiaServer::GaiaServer() + : hostname_("www.google.com"), + port_(443), + use_ssl_(true) { +} + +bool GaiaServer::SetServer(const char* url) { + talk_base::Url<char> parsed(url); + hostname_ = parsed.server(); + port_ = parsed.port(); + use_ssl_ = parsed.secure(); + return true; +} + +bool GaiaServer::SetDebugServer(const char* server) { + const char* colon = strchr(server, ':'); + if (colon) { + hostname_ = std::string(server, colon - server); + port_ = atoi(colon + 1); + use_ssl_ = false; + return true; + } + return false; +} + +std::string GaiaServer::http_prefix() const { + talk_base::Url<char> parsed("", hostname_, port_); + parsed.set_secure(use_ssl_); + return parsed.url(); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool GaiaRequestSid(talk_base::HttpClient* client, + const std::string& username, + const talk_base::CryptString& password, + const std::string& signature, + const std::string& service, + const CaptchaAnswer& captcha_answer, + const GaiaServer& gaia_server) { + buzz::Jid jid(username); + std::string usable_name = username; + if (jid.domain() == buzz::STR_DEFAULT_DOMAIN) { + // The default domain (default.talk.google.com) is not usable for Gaia + // auth. But both gmail.com and googlemain.com will work, because the gaia + // server doesn't check to make sure the appropriate one is being used. So + // we just slam on gmail.com + usable_name = jid.node() + "@" + buzz::STR_GMAIL_COM; + } + + std::string post_data; + post_data += "Email=" + UrlEncodeString(usable_name); + post_data += "&Passwd=" + password.UrlEncode(); + post_data += "&PersistentCookie=false"; + post_data += "&source=" + signature; + // TODO(chron): This behavior is not the same as in the other gaia auth + // loader. We should make it the same. Probably GOOGLE is enough, we don't + // want to auth against hosted accounts. + post_data += "&accountType=HOSTED_OR_GOOGLE"; + post_data += "&skipvpage=true"; + if (!service.empty()) { + post_data += "&service=" + service; + } + + if (!captcha_answer.captcha_token().empty()) { + post_data += "&logintoken=" + captcha_answer.captcha_token(); + post_data += "&logincaptcha=" + + UrlEncodeString(captcha_answer.captcha_answer()); + } + + client->reset(); + client->set_server(talk_base::SocketAddress(gaia_server.hostname(), + gaia_server.port(), false)); + client->request().verb = talk_base::HV_POST; + client->request().path = "/accounts/ClientAuth"; + client->request().setContent("application/x-www-form-urlencoded", + new talk_base::MemoryStream(post_data.data(), post_data.size())); + client->response().document.reset(new talk_base::MemoryStream); + client->start(); + return true; +} + +GaiaResponse GaiaParseSidResponse(const talk_base::HttpClient& client, + const GaiaServer& gaia_server, + std::string* captcha_token, + std::string* captcha_url, + std::string* sid, + std::string* lsid, + std::string* auth) { + uint32 status_code = client.response().scode; + const talk_base::MemoryStream* stream = + static_cast<const talk_base::MemoryStream*>( + client.response().document.get()); + size_t length; + stream->GetPosition(&length); + std::string response; + if (length > 0) { + response.assign(stream->GetBuffer(), length); + } + + LOG(INFO) << "GaiaAuth request to " << client.request().path; + LOG(INFO) << "GaiaAuth Status Code: " << status_code; + LOG(INFO) << response; + + if (status_code == talk_base::HC_FORBIDDEN) { + // The error URL may be the relative path to the captcha jpg. + std::string image_url = GetValueForKey("CaptchaUrl", response); + if (!image_url.empty()) { + // We should activate this "full url code" once we have a better ways + // to crack the URL for later download. Right now we are too dependent + // on what Gaia returns. +#if 0 + if (image_url.find("http://") != 0 && + image_url.find("https://") != 0) { + if (image_url.find("/") == 0) { + *captcha_url = gaia_server.http_prefix() + image_url; + } else { + *captcha_url = Utf8(gaia_server.http_prefix()).AsString() + + "/accounts/" + image_url; + } + } +#else + *captcha_url = "/accounts/" + image_url; +#endif + + *captcha_token = GetValueForKey("CaptchaToken", response); + } + return GR_UNAUTHORIZED; + } + + if (status_code != talk_base::HC_OK) { + return GR_ERROR; + } + + *sid = GetValueForKey("SID", response); + *lsid = GetValueForKey("LSID", response); + if (auth) { + *auth = GetValueForKey("Auth", response); + } + if (sid->empty() || lsid->empty()) { + return GR_ERROR; + } + + return GR_SUCCESS; +} + +bool GaiaRequestAuthToken(talk_base::HttpClient* client, + const std::string& sid, + const std::string& lsid, + const std::string& service, + const GaiaServer& gaia_server) { + std::string post_data; + post_data += "SID=" + UrlEncodeString(sid); + post_data += "&LSID=" + UrlEncodeString(lsid); + post_data += "&service=" + service; + post_data += "&Session=true"; // Creates two week cookie. + + client->reset(); + client->set_server(talk_base::SocketAddress(gaia_server.hostname(), + gaia_server.port(), false)); + client->request().verb = talk_base::HV_POST; + client->request().path = "/accounts/IssueAuthToken"; + client->request().setContent("application/x-www-form-urlencoded", + new talk_base::MemoryStream(post_data.data(), post_data.size())); + client->response().document.reset(new talk_base::MemoryStream); + client->start(); + return true; +} + +GaiaResponse GaiaParseAuthTokenResponse(const talk_base::HttpClient& client, + std::string* auth_token) { + if (client.response().scode != talk_base::HC_OK) { + return GR_ERROR; + } + + const talk_base::MemoryStream* stream = + static_cast<const talk_base::MemoryStream*>( + client.response().document.get()); + size_t length; + stream->GetPosition(&length); + while ((length > 0) && isspace(stream->GetBuffer()[length-1])) + --length; + auth_token->assign(stream->GetBuffer(), length); + return auth_token->empty() ? GR_ERROR : GR_SUCCESS; +} + +} // namespace buzz diff --git a/chrome/browser/sync/notifier/gaia_auth/gaiahelper.h b/chrome/browser/sync/notifier/gaia_auth/gaiahelper.h new file mode 100644 index 0000000..933bc97 --- /dev/null +++ b/chrome/browser/sync/notifier/gaia_auth/gaiahelper.h @@ -0,0 +1,87 @@ +// 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. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_GAIAHELPER_H__ +#define CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_GAIAHELPER_H__ + +#include <string> + +namespace talk_base { +class CryptString; +class HttpClient; +} + +namespace buzz { + +/////////////////////////////////////////////////////////////////////////////// + +class CaptchaAnswer { + public: + CaptchaAnswer() {} + CaptchaAnswer(const std::string& token, const std::string& answer) + : captcha_token_(token), captcha_answer_(answer) { + } + const std::string& captcha_token() const { return captcha_token_; } + const std::string& captcha_answer() const { return captcha_answer_; } + + private: + std::string captcha_token_; + std::string captcha_answer_; +}; + +class GaiaServer { + public: + GaiaServer(); + + bool SetServer(const char* url); // protocol://server:port + bool SetDebugServer(const char* server); // server:port + + const std::string& hostname() const { return hostname_; } + int port() const { return port_; } + bool use_ssl() const { return use_ssl_; } + + std::string http_prefix() const; + + private: + std::string hostname_; + int port_; + bool use_ssl_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Gaia Authentication Helper Functions +/////////////////////////////////////////////////////////////////////////////// + +enum GaiaResponse { GR_ERROR, GR_UNAUTHORIZED, GR_SUCCESS }; + +bool GaiaRequestSid(talk_base::HttpClient* client, + const std::string& username, + const talk_base::CryptString& password, + const std::string& signature, + const std::string& service, + const CaptchaAnswer& captcha_answer, + const GaiaServer& gaia_server); + +GaiaResponse GaiaParseSidResponse(const talk_base::HttpClient& client, + const GaiaServer& gaia_server, + std::string* captcha_token, + std::string* captcha_url, + std::string* sid, + std::string* lsid, + std::string* auth); + +bool GaiaRequestAuthToken(talk_base::HttpClient* client, + const std::string& sid, + const std::string& lsid, + const std::string& service, + const GaiaServer& gaia_server); + +GaiaResponse GaiaParseAuthTokenResponse(const talk_base::HttpClient& client, + std::string* auth_token); + +/////////////////////////////////////////////////////////////////////////////// + +} // namespace buzz + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_GAIAHELPER_H__ diff --git a/chrome/browser/sync/notifier/gaia_auth/inet_aton.h b/chrome/browser/sync/notifier/gaia_auth/inet_aton.h new file mode 100644 index 0000000..29e4dcb --- /dev/null +++ b/chrome/browser/sync/notifier/gaia_auth/inet_aton.h @@ -0,0 +1,14 @@ +// 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. +// +// Define inet_aton alone so it's easier to include. + +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_INET_ATON_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_INET_ATON_H_ + +#if defined(OS_WIN) +int inet_aton(const char* cp, struct in_addr* inp); +#endif + +#endif // CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_INET_ATON_H_ diff --git a/chrome/browser/sync/notifier/base/sigslotrepeater.h b/chrome/browser/sync/notifier/gaia_auth/sigslotrepeater.h index 85ab3e6..3c0941e 100644 --- a/chrome/browser/sync/notifier/base/sigslotrepeater.h +++ b/chrome/browser/sync/notifier/gaia_auth/sigslotrepeater.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_SYNC_NOTIFIER_BASE_SIGSLOTREPEATER_H_ -#define CHROME_BROWSER_SYNC_NOTIFIER_BASE_SIGSLOTREPEATER_H_ +#ifndef CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_INET_ATON_H_SIGSLOTREPEATER_H_ +#define CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_INET_ATON_H_SIGSLOTREPEATER_H_ // Repeaters are both signals and slots, which are designed as intermediate // pass-throughs for signals and slots which don't know about each other (for @@ -80,4 +80,4 @@ class repeater3 : public signal3<arg1_type, arg2_type, arg3_type, mt_policy>, } // namespace sigslot -#endif // CHROME_BROWSER_SYNC_NOTIFIER_BASE_SIGSLOTREPEATER_H_ +#endif // CHROME_BROWSER_SYNC_NOTIFIER_GAIA_AUTH_INET_ATON_H_SIGSLOTREPEATER_H_ diff --git a/third_party/libjingle/files/talk/base/win32window.cc b/chrome/browser/sync/notifier/gaia_auth/win/win32window.cc index 05d639b..720838e 100644 --- a/third_party/libjingle/files/talk/base/win32window.cc +++ b/chrome/browser/sync/notifier/gaia_auth/win/win32window.cc @@ -1,33 +1,12 @@ -/* - * libjingle - * Copyright 2004--2005, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ +// 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. +// +// Originally from libjingle. Minor alterations to compile it in Chrome. #include "talk/base/win32window.h" -#include "talk/base/logging.h" +#include "base/logging.h" #include "talk/base/common.h" namespace talk_base { @@ -64,7 +43,7 @@ bool Win32Window::Create(HWND parent, const wchar_t* title, DWORD style, wcex.lpszClassName = kWindowBaseClassName; window_class_ = ::RegisterClassEx(&wcex); if (!window_class_) { - LOG(LS_ERROR) << "RegisterClassEx failed: " << GetLastError(); + LOG(ERROR) << "RegisterClassEx failed: " << GetLastError(); return false; } } @@ -119,7 +98,7 @@ LRESULT Win32Window::WndProc(HWND hwnd, UINT uMsg, if (WM_DESTROY == uMsg) { for (HWND child = ::GetWindow(hwnd, GW_CHILD); child; child = ::GetWindow(child, GW_HWNDNEXT)) { - LOG(LS_INFO) << "Child window: " << static_cast<void*>(child); + LOG(INFO) << "Child window: " << static_cast<void*>(child); } } if (WM_NCDESTROY == uMsg) { diff --git a/chrome/browser/sync/notifier/listener/mediator_thread_impl.cc b/chrome/browser/sync/notifier/listener/mediator_thread_impl.cc index 4728268..11cf90a 100644 --- a/chrome/browser/sync/notifier/listener/mediator_thread_impl.cc +++ b/chrome/browser/sync/notifier/listener/mediator_thread_impl.cc @@ -190,6 +190,7 @@ void MediatorThreadImpl::DoLogin(LoginData* login_data) { NULL, // talk_base::FirewallManager* is NULL. NULL, + false, // Both the proxy and a non-proxy route // will be attempted. false, diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 2ae4173..98bce6c 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -772,6 +772,8 @@ 'browser/sync/notifier/base/utils.h', 'browser/sync/notifier/base/win/async_network_alive_win32.cc', 'browser/sync/notifier/base/win/time_win32.cc', + 'browser/sync/notifier/communicator/auth_task.cc', + 'browser/sync/notifier/communicator/auth_task.h', 'browser/sync/notifier/communicator/auto_reconnect.cc', 'browser/sync/notifier/communicator/auto_reconnect.h', 'browser/sync/notifier/communicator/connection_options.cc', @@ -791,12 +793,21 @@ 'browser/sync/notifier/communicator/single_login_attempt.h', 'browser/sync/notifier/communicator/ssl_socket_adapter.cc', 'browser/sync/notifier/communicator/ssl_socket_adapter.h', + 'browser/sync/notifier/communicator/talk_auth_task.cc', + 'browser/sync/notifier/communicator/talk_auth_task.h', 'browser/sync/notifier/communicator/xmpp_connection_generator.cc', 'browser/sync/notifier/communicator/xmpp_connection_generator.h', 'browser/sync/notifier/communicator/xmpp_log.cc', 'browser/sync/notifier/communicator/xmpp_log.h', 'browser/sync/notifier/communicator/xmpp_socket_adapter.cc', 'browser/sync/notifier/communicator/xmpp_socket_adapter.h', + 'browser/sync/notifier/gaia_auth/gaiaauth.cc', + 'browser/sync/notifier/gaia_auth/gaiaauth.h', + 'browser/sync/notifier/gaia_auth/gaiahelper.cc', + 'browser/sync/notifier/gaia_auth/gaiahelper.h', + 'browser/sync/notifier/gaia_auth/inet_aton.h', + 'browser/sync/notifier/gaia_auth/sigslotrepeater.h', + 'browser/sync/notifier/gaia_auth/win/win32window.cc', 'browser/sync/notifier/listener/listen_task.cc', 'browser/sync/notifier/listener/listen_task.h', 'browser/sync/notifier/listener/mediator_thread.h', @@ -807,7 +818,6 @@ 'browser/sync/notifier/listener/notification_constants.h', 'browser/sync/notifier/listener/send_update_task.cc', 'browser/sync/notifier/listener/send_update_task.h', - 'browser/sync/notifier/base/sigslotrepeater.h', 'browser/sync/notifier/listener/subscribe_task.cc', 'browser/sync/notifier/listener/subscribe_task.h', 'browser/sync/notifier/listener/talk_mediator.h', diff --git a/third_party/libjingle/libjingle.gyp b/third_party/libjingle/libjingle.gyp index 5b04c2b..180f8a3 100644 --- a/third_party/libjingle/libjingle.gyp +++ b/third_party/libjingle/libjingle.gyp @@ -219,7 +219,6 @@ 'files/talk/base/win32socketserver.cc', 'files/talk/base/win32socketserver.h', 'files/talk/base/win32window.h', - 'files/talk/base/win32window.cc', 'files/talk/base/winfirewall.cc', 'files/talk/base/winfirewall.h', 'files/talk/base/winping.cc', diff --git a/third_party/libjingle/mods-since-v0_4_0.diff b/third_party/libjingle/mods-since-v0_4_0.diff index 7403b4f..307439e4 100644 --- a/third_party/libjingle/mods-since-v0_4_0.diff +++ b/third_party/libjingle/mods-since-v0_4_0.diff @@ -1140,7 +1140,6 @@ diff -r libjingle-0.4.0/talk/base/win32socketserver.cc libjingle/files/talk/base > #include "talk/base/winsock_initializer.h" 269a271 > talk_base::EnsureWinsockInit(); -Only in libjingle/files/talk/base: win32window.cc diff -r libjingle-0.4.0/talk/base/winping.cc libjingle/files/talk/base/winping.cc 133c133 < return sizeof(ICMP_ECHO_REPLY) + max(8UL, data_size); |