// Copyright (c) 2012 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/profile_auth_data.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "net/cookies/cookie_monster.h" #include "net/cookies/cookie_store.h" #include "net/http/http_auth_cache.h" #include "net/http/http_network_session.h" #include "net/http/http_transaction_factory.h" #include "net/ssl/server_bound_cert_service.h" #include "net/ssl/server_bound_cert_store.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_getter.h" using content::BrowserThread; namespace chromeos { namespace { class ProfileAuthDataTransferer { public: ProfileAuthDataTransferer( content::BrowserContext* from_context, content::BrowserContext* to_context, bool transfer_cookies, const base::Closure& completion_callback); void BeginTransfer(); private: void BeginTransferOnIOThread(); void MaybeDoCookieAndCertTransfer(); void Finish(); void OnTransferCookiesIfEmptyJar(const net::CookieList& cookies_in_jar); void OnGetCookiesToTransfer(const net::CookieList& cookies_to_transfer); void RetrieveDefaultCookies(); void OnGetServerBoundCertsToTransfer( const net::ServerBoundCertStore::ServerBoundCertList& certs); void RetrieveDefaultServerBoundCerts(); void TransferDefaultAuthCache(); scoped_refptr from_context_; scoped_refptr to_context_; bool transfer_cookies_; base::Closure completion_callback_; net::CookieList cookies_to_transfer_; net::ServerBoundCertStore::ServerBoundCertList certs_to_transfer_; bool got_cookies_; bool got_server_bound_certs_; }; ProfileAuthDataTransferer::ProfileAuthDataTransferer( content::BrowserContext* from_context, content::BrowserContext* to_context, bool transfer_cookies, const base::Closure& completion_callback) : from_context_(from_context->GetRequestContext()), to_context_(to_context->GetRequestContext()), transfer_cookies_(transfer_cookies), completion_callback_(completion_callback), got_cookies_(false), got_server_bound_certs_(false) { } void ProfileAuthDataTransferer::BeginTransfer() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // If we aren't transferring cookies, post the completion callback // immediately. Otherwise, it will be called when both cookies and channel // ids are finished transferring. if (!transfer_cookies_) { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, completion_callback_); // Null the callback so that when Finish is called the callback won't be // called again. completion_callback_.Reset(); } BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&ProfileAuthDataTransferer::BeginTransferOnIOThread, base::Unretained(this))); } void ProfileAuthDataTransferer::BeginTransferOnIOThread() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); TransferDefaultAuthCache(); if (transfer_cookies_) { RetrieveDefaultCookies(); RetrieveDefaultServerBoundCerts(); } else { Finish(); } } // If both cookies and server bound certs have been retrieved, see if we need to // do the actual transfer. void ProfileAuthDataTransferer::MaybeDoCookieAndCertTransfer() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!(got_cookies_ && got_server_bound_certs_)) return; // Nothing to transfer over? if (!cookies_to_transfer_.size()) { Finish(); return; } // Now let's see if the target cookie monster's jar is even empty. net::CookieStore* to_store = to_context_->GetURLRequestContext()->cookie_store(); net::CookieMonster* to_monster = to_store->GetCookieMonster(); to_monster->GetAllCookiesAsync( base::Bind(&ProfileAuthDataTransferer::OnTransferCookiesIfEmptyJar, base::Unretained(this))); } // Post the |completion_callback_| and delete ourself. void ProfileAuthDataTransferer::Finish() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!completion_callback_.is_null()) BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, completion_callback_); delete this; } // Callback for transferring |cookies_to_transfer_| into |to_context_|'s // CookieMonster if its jar is completely empty. If authentication was // performed by an extension, then the set of cookies that was acquired through // such that process will be automatically transfered into the BrowserContext. void ProfileAuthDataTransferer::OnTransferCookiesIfEmptyJar( const net::CookieList& cookies_in_jar) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); // Transfer only if the existing cookie jar is empty. if (!cookies_in_jar.size()) { net::CookieStore* to_store = to_context_->GetURLRequestContext()->cookie_store(); net::CookieMonster* to_monster = to_store->GetCookieMonster(); to_monster->InitializeFrom(cookies_to_transfer_); net::ServerBoundCertService* to_cert_service = to_context_->GetURLRequestContext()->server_bound_cert_service(); to_cert_service->GetCertStore()->InitializeFrom(certs_to_transfer_); } Finish(); } // Callback for receiving |cookies_to_transfer| from the authentication profile // cookie jar. void ProfileAuthDataTransferer::OnGetCookiesToTransfer( const net::CookieList& cookies_to_transfer) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); got_cookies_ = true; cookies_to_transfer_ = cookies_to_transfer; MaybeDoCookieAndCertTransfer(); } // Retrieves initial set of Profile cookies from the |from_context_|. void ProfileAuthDataTransferer::RetrieveDefaultCookies() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); net::CookieStore* from_store = from_context_->GetURLRequestContext()->cookie_store(); net::CookieMonster* from_monster = from_store->GetCookieMonster(); from_monster->SetKeepExpiredCookies(); from_monster->GetAllCookiesAsync( base::Bind(&ProfileAuthDataTransferer::OnGetCookiesToTransfer, base::Unretained(this))); } // Callback for receiving |cookies_to_transfer| from the authentication profile // cookie jar. void ProfileAuthDataTransferer::OnGetServerBoundCertsToTransfer( const net::ServerBoundCertStore::ServerBoundCertList& certs) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); certs_to_transfer_ = certs; got_server_bound_certs_ = true; MaybeDoCookieAndCertTransfer(); } // Retrieves server bound certs of |from_context_|. void ProfileAuthDataTransferer::RetrieveDefaultServerBoundCerts() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); net::ServerBoundCertService* from_service = from_context_->GetURLRequestContext()->server_bound_cert_service(); from_service->GetCertStore()->GetAllServerBoundCerts( base::Bind(&ProfileAuthDataTransferer::OnGetServerBoundCertsToTransfer, base::Unretained(this))); } // Transfers HTTP authentication cache from the |from_context_| // into the |to_context_|. If user was required to authenticate with a proxy // during the login, this authentication information will be transferred // into the new session. void ProfileAuthDataTransferer::TransferDefaultAuthCache() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); net::HttpAuthCache* new_cache = to_context_->GetURLRequestContext()-> http_transaction_factory()->GetSession()->http_auth_cache(); new_cache->UpdateAllFrom(*from_context_->GetURLRequestContext()-> http_transaction_factory()->GetSession()->http_auth_cache()); } } // namespace void ProfileAuthData::Transfer( content::BrowserContext* from_context, content::BrowserContext* to_context, bool transfer_cookies, const base::Closure& completion_callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); (new ProfileAuthDataTransferer(from_context, to_context, transfer_cookies, completion_callback))->BeginTransfer(); } } // namespace chromeos