diff options
author | gene@chromium.org <gene@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-28 18:21:51 +0000 |
---|---|---|
committer | gene@chromium.org <gene@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-10-28 18:21:51 +0000 |
commit | f0cb5b9f307dfaf5e1540fb753e0a41694b97387 (patch) | |
tree | db9185622a9760a80e6f136feb6e1ab282a681b1 /chrome/service | |
parent | 121352a9a4a7177af585952ff2f3a1e4ec6619f5 (diff) | |
download | chromium_src-f0cb5b9f307dfaf5e1540fb753e0a41694b97387.zip chromium_src-f0cb5b9f307dfaf5e1540fb753e0a41694b97387.tar.gz chromium_src-f0cb5b9f307dfaf5e1540fb753e0a41694b97387.tar.bz2 |
Chrome proxy refactoring.Adding myself to the OWNERS file for handling cloud print proxy issues.Split proxy code from cloud_print_proxy_backend to a separate pieces: - auth code goes to cloud_print_auth.h/cc - connector logic goes to cloud_print_connector.h/cc (printer enumeration, registration and deletion) - wiring backend/frontend threads, notifications with other parts will stay in cloud_print_backend.h/ccMade proxy logic more straightforward.
Review URL: http://codereview.chromium.org/8387011
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@107758 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/service')
22 files changed, 1333 insertions, 922 deletions
diff --git a/chrome/service/OWNERS b/chrome/service/OWNERS index f8fa783..4e9f44d 100644 --- a/chrome/service/OWNERS +++ b/chrome/service/OWNERS @@ -1,5 +1,6 @@ ajwong@chromium.org dmaclach@chromium.org hclam@chromium.org -sanjeevr@chromium.org scottbyer@chromium.org +gene@chromium.org +abodenha@chromium.org diff --git a/chrome/service/cloud_print/cloud_print_auth.cc b/chrome/service/cloud_print/cloud_print_auth.cc new file mode 100644 index 0000000..4e7b056 --- /dev/null +++ b/chrome/service/cloud_print/cloud_print_auth.cc @@ -0,0 +1,205 @@ +// 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/service/cloud_print/cloud_print_auth.h" + +#include "base/string_util.h" +#include "chrome/common/net/gaia/gaia_urls.h" +#include "chrome/service/cloud_print/cloud_print_consts.h" +#include "chrome/service/cloud_print/cloud_print_helpers.h" +#include "chrome/service/cloud_print/cloud_print_token_store.h" +#include "chrome/service/gaia/service_gaia_authenticator.h" +#include "chrome/service/net/service_url_request_context.h" +#include "chrome/service/service_process.h" + +CloudPrintAuth::CloudPrintAuth( + Client* client, + const GURL& cloud_print_server_url, + const base::DictionaryValue* print_sys_settings, + const gaia::OAuthClientInfo& oauth_client_info, + const std::string& proxy_id) + : client_(client), + oauth_client_info_(oauth_client_info), + cloud_print_server_url_(cloud_print_server_url), + proxy_id_(proxy_id) { + DCHECK(client); + if (print_sys_settings) { + // It is possible to have no print settings specified. + print_system_settings_.reset(print_sys_settings->DeepCopy()); + } +} + +CloudPrintAuth::~CloudPrintAuth() { +} + +void CloudPrintAuth::AuthenticateWithLsid( + const std::string& lsid, + const std::string& last_robot_refresh_token, + const std::string& last_robot_email, + const std::string& last_user_email) { + // Keeping VLOGs for Cloud Print proxy logging. It is convinient for finding + // issues with GCP in the field, where only release version is avaialble. + VLOG(1) << "CP_AUTH: Authenticating with LSID"; + scoped_refptr<ServiceGaiaAuthenticator> gaia_auth_for_print( + new ServiceGaiaAuthenticator( + kProxyAuthUserAgent, kCloudPrintGaiaServiceId, + GaiaUrls::GetInstance()->client_login_url(), + g_service_process->io_thread()->message_loop_proxy())); + gaia_auth_for_print->set_message_loop(MessageLoop::current()); + if (gaia_auth_for_print->AuthenticateWithLsid(lsid)) { + // Stash away the user email so we can save it in prefs. + user_email_ = gaia_auth_for_print->email(); + // If the same user is re-enabling Cloud Print and we have stashed robot + // credentials, we will use those. + if ((0 == base::strcasecmp(user_email_.c_str(), last_user_email.c_str())) && + !last_robot_refresh_token.empty() && + !last_robot_email.empty()) { + AuthenticateWithRobotToken(last_robot_refresh_token, + last_robot_email); + } + AuthenticateWithToken(gaia_auth_for_print->auth_token()); + } else { + // Notify client about authentication error. + client_->OnInvalidCredentials(); + } +} + +void CloudPrintAuth::AuthenticateWithToken( + const std::string cloud_print_token) { + VLOG(1) << "CP_AUTH: Authenticating with token"; + + client_login_token_ = cloud_print_token; + + // We need to get the credentials of the robot here. + GURL get_authcode_url = + CloudPrintHelpers::GetUrlForGetAuthCode(cloud_print_server_url_, + oauth_client_info_.client_id, + proxy_id_); + request_ = new CloudPrintURLFetcher; + request_->StartGetRequest(get_authcode_url, + this, + kCloudPrintAuthMaxRetryCount, + std::string()); +} + +void CloudPrintAuth::AuthenticateWithRobotToken( + const std::string& robot_oauth_refresh_token, + const std::string& robot_email) { + VLOG(1) << "CP_AUTH: Authenticating with robot token"; + + robot_email_ = robot_email; + refresh_token_ = robot_oauth_refresh_token; + RefreshAccessToken(); +} + +void CloudPrintAuth::AuthenticateWithRobotAuthCode( + const std::string& robot_oauth_auth_code, + const std::string& robot_email) { + VLOG(1) << "CP_AUTH: Authenticating with robot auth code"; + + robot_email_ = robot_email; + // Now that we have an auth code we need to get the refresh and access tokens. + oauth_client_.reset(new gaia::GaiaOAuthClient( + gaia::kGaiaOAuth2Url, + g_service_process->GetServiceURLRequestContextGetter())); + oauth_client_->GetTokensFromAuthCode(oauth_client_info_, + robot_oauth_auth_code, + kCloudPrintAuthMaxRetryCount, + this); +} + +void CloudPrintAuth::RefreshAccessToken() { + oauth_client_.reset(new gaia::GaiaOAuthClient( + gaia::kGaiaOAuth2Url, + g_service_process->GetServiceURLRequestContextGetter())); + oauth_client_->RefreshToken(oauth_client_info_, + refresh_token_, + kCloudPrintAuthMaxRetryCount, + this); +} + +void CloudPrintAuth::OnGetTokensResponse(const std::string& refresh_token, + const std::string& access_token, + int expires_in_seconds) { + refresh_token_ = refresh_token; + // After saving the refresh token, this is just like having just refreshed + // the access token. Just call OnRefreshTokenResponse. + OnRefreshTokenResponse(access_token, expires_in_seconds); +} + +void CloudPrintAuth::OnRefreshTokenResponse(const std::string& access_token, + int expires_in_seconds) { + client_->OnAuthenticationComplete(access_token, refresh_token_, + robot_email_, user_email_); + + // Schedule a task to refresh the access token again when it is about to + // expire. + DCHECK(expires_in_seconds > kTokenRefreshGracePeriodSecs); + int64 refresh_delay = + (expires_in_seconds - kTokenRefreshGracePeriodSecs)*1000; + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + NewRunnableMethod(this, &CloudPrintAuth::RefreshAccessToken), + refresh_delay); +} + +void CloudPrintAuth::OnOAuthError() { + // Notify client about authentication error. + client_->OnInvalidCredentials(); +} + +void CloudPrintAuth::OnNetworkError(int response_code) { + // Since we specify infinite retries on network errors, this should never + // be called. + NOTREACHED() << + "OnNetworkError invoked when not expected, response code is " << + response_code; +} + +CloudPrintURLFetcher::ResponseAction CloudPrintAuth::HandleJSONData( + const content::URLFetcher* source, + const GURL& url, + base::DictionaryValue* json_data, + bool succeeded) { + if (!succeeded) { + VLOG(1) << "CP_AUTH: Creating robot account failed"; + client_->OnInvalidCredentials(); + return CloudPrintURLFetcher::STOP_PROCESSING; + } + + std::string auth_code; + if (!json_data->GetString(kOAuthCodeValue, &auth_code)) { + VLOG(1) << "CP_AUTH: Creating robot account returned invalid json response"; + client_->OnInvalidCredentials(); + return CloudPrintURLFetcher::STOP_PROCESSING; + } + + json_data->GetString(kXMPPJidValue, &robot_email_); + // Now that we have an auth code we need to get the refresh and access tokens. + oauth_client_.reset(new gaia::GaiaOAuthClient( + gaia::kGaiaOAuth2Url, + g_service_process->GetServiceURLRequestContextGetter())); + oauth_client_->GetTokensFromAuthCode(oauth_client_info_, + auth_code, + kCloudPrintAPIMaxRetryCount, + this); + + return CloudPrintURLFetcher::STOP_PROCESSING; +} + +CloudPrintURLFetcher::ResponseAction CloudPrintAuth::OnRequestAuthError() { + VLOG(1) << "CP_AUTH: Creating robot account authentication error"; + // Notify client about authentication error. + client_->OnInvalidCredentials(); + return CloudPrintURLFetcher::STOP_PROCESSING; +} + +std::string CloudPrintAuth::GetAuthHeader() { + DCHECK(!client_login_token_.empty()); + std::string header; + header = "Authorization: GoogleLogin auth="; + header += client_login_token_; + return header; +} + diff --git a/chrome/service/cloud_print/cloud_print_auth.h b/chrome/service/cloud_print/cloud_print_auth.h new file mode 100644 index 0000000..7eea7f4 --- /dev/null +++ b/chrome/service/cloud_print/cloud_print_auth.h @@ -0,0 +1,114 @@ +// 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. + +#ifndef CHROME_SERVICE_CLOUD_PRINT_CLOUD_PRINT_AUTH_H_ +#define CHROME_SERVICE_CLOUD_PRINT_CLOUD_PRINT_AUTH_H_ +#pragma once + +#include <string> + +#include "base/values.h" +#include "chrome/common/net/gaia/gaia_oauth_client.h" +#include "chrome/service/cloud_print/cloud_print_url_fetcher.h" +#include "googleurl/src/gurl.h" + +// CloudPrintAuth is a class to handle login, token refresh, and other +// authentication tasks for Cloud Print. +// CloudPrintAuth will create new robot account for this proxy if needed. +// CloudPrintAuth will obtain new OAuth token. +// CloudPrintAuth will schedule periodic OAuth token refresh +// It is running in the same thread as CloudPrintProxyBackend::Core. +class CloudPrintAuth + : public base::RefCountedThreadSafe<CloudPrintAuth>, + public CloudPrintURLFetcherDelegate, + public gaia::GaiaOAuthClient::Delegate { + public: + class Client { + public: + virtual void OnAuthenticationComplete( + const std::string& access_token, + const std::string& robot_oauth_refresh_token, + const std::string& robot_email, + const std::string& user_email) = 0; + virtual void OnInvalidCredentials() = 0; + protected: + virtual ~Client() {} + }; + + CloudPrintAuth(Client* client, + const GURL& cloud_print_server_url, + const base::DictionaryValue* print_sys_settings, + const gaia::OAuthClientInfo& oauth_client_info, + const std::string& proxy_id); + virtual ~CloudPrintAuth(); + + // Note: + // + // The Authenticate* methods are the various entry points from + // CloudPrintProxyBackend::Core. It calls us on a dedicated thread to + // actually perform synchronous (and potentially blocking) operations. + // + // When we are passed in an LSID we authenticate using that + // and retrieve new auth tokens. + void AuthenticateWithLsid(const std::string& lsid, + const std::string& last_robot_refresh_token, + const std::string& last_robot_email, + const std::string& last_user_email); + + void AuthenticateWithToken(const std::string cloud_print_token); + void AuthenticateWithRobotToken(const std::string& robot_oauth_refresh_token, + const std::string& robot_email); + void AuthenticateWithRobotAuthCode(const std::string& robot_oauth_auth_code, + const std::string& robot_email); + + void RefreshAccessToken(); + + // gaia::GaiaOAuthClient::Delegate implementation. + virtual void OnGetTokensResponse(const std::string& refresh_token, + const std::string& access_token, + int expires_in_seconds); + virtual void OnRefreshTokenResponse(const std::string& access_token, + int expires_in_seconds); + virtual void OnOAuthError(); + virtual void OnNetworkError(int response_code); + + // CloudPrintURLFetcher::Delegate implementation. + virtual CloudPrintURLFetcher::ResponseAction HandleJSONData( + const content::URLFetcher* source, + const GURL& url, + base::DictionaryValue* json_data, + bool succeeded); + virtual CloudPrintURLFetcher::ResponseAction OnRequestAuthError(); + virtual std::string GetAuthHeader(); + + private: + Client* client_; + gaia::OAuthClientInfo oauth_client_info_; + scoped_ptr<gaia::GaiaOAuthClient> oauth_client_; + scoped_ptr<DictionaryValue> print_system_settings_; + + // The CloudPrintURLFetcher instance for the current request. + scoped_refptr<CloudPrintURLFetcher> request_; + + GURL cloud_print_server_url_; + // Proxy id, need to send to the cloud print server to find and update + // necessary printers during the migration process. + const std::string& proxy_id_; + // The OAuth2 refresh token for the robot. + std::string refresh_token_; + // The email address of the user. This is only used during initial + // authentication with an LSID. This is only used for storing in prefs for + // display purposes. + std::string user_email_; + // The email address of the robot account. + std::string robot_email_; + // client login token used to authenticate request to cloud print server to + // get the robot account. + std::string client_login_token_; + + DISALLOW_COPY_AND_ASSIGN(CloudPrintAuth); +}; + +#endif // CHROME_SERVICE_CLOUD_PRINT_CLOUD_PRINT_AUTH_H_ + diff --git a/chrome/service/cloud_print/cloud_print_connector.cc b/chrome/service/cloud_print/cloud_print_connector.cc new file mode 100644 index 0000000..5432d32 --- /dev/null +++ b/chrome/service/cloud_print/cloud_print_connector.cc @@ -0,0 +1,551 @@ +// 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/service/cloud_print/cloud_print_connector.h" + +#include "base/md5.h" +#include "base/rand_util.h" +#include "base/string_number_conversions.h" +#include "base/string_split.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/service/cloud_print/cloud_print_consts.h" +#include "chrome/service/cloud_print/cloud_print_helpers.h" +#include "grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +CloudPrintConnector::CloudPrintConnector( + Client* client, + const std::string& proxy_id, + const GURL& cloud_print_server_url, + const DictionaryValue* print_system_settings) + : client_(client), + proxy_id_(proxy_id), + cloud_print_server_url_(cloud_print_server_url), + next_response_handler_(NULL) { + if (print_system_settings) { + // It is possible to have no print settings specified. + print_system_settings_.reset(print_system_settings->DeepCopy()); + } +} + +CloudPrintConnector::~CloudPrintConnector() { +} + +bool CloudPrintConnector::Start() { + DCHECK(!print_system_.get()); + VLOG(1) << "CP_CONNECTOR: Starting connector, id: " << proxy_id_; + + pending_tasks_.clear(); + + print_system_ = + cloud_print::PrintSystem::CreateInstance(print_system_settings_.get()); + if (!print_system_.get()) { + NOTREACHED(); + return false; // No print system available, fail initalization. + } + cloud_print::PrintSystem::PrintSystemResult result = print_system_->Init(); + if (!result.succeeded()) { + // We could not initialize the print system. We need to notify the server. + ReportUserMessage(kPrintSystemFailedMessageId, result.message()); + print_system_.release(); + return false; + } + + // Start watching for updates from the print system. + print_server_watcher_ = print_system_->CreatePrintServerWatcher(); + print_server_watcher_->StartWatching(this); + + // Get list of registered printers. + AddPendingAvailableTask(); + return true; +} + +void CloudPrintConnector::Stop() { + VLOG(1) << "CP_CONNECTOR: Stopping connector, id: " << proxy_id_; + DCHECK(print_system_.get()); + if (print_system_.get()) { + // Do uninitialization here. + pending_tasks_.clear(); + print_server_watcher_.release(); + print_system_.release(); + } +} + +bool CloudPrintConnector::IsRunning() { + return print_system_.get() != NULL; +} + +void CloudPrintConnector::RegisterPrinters( + const printing::PrinterList& printers) { + if (!IsRunning()) + return; + printing::PrinterList::const_iterator it; + for (it = printers.begin(); it != printers.end(); ++it) { + AddPendingRegisterTask(*it); + } +} + +// Check for jobs for specific printer +void CloudPrintConnector::CheckForJobs(const std::string& reason, + const std::string& printer_id) { + if (!IsRunning()) + return; + if (!printer_id.empty()) { + JobHandlerMap::iterator index = job_handler_map_.find(printer_id); + if (index != job_handler_map_.end()) + index->second->CheckForJobs(reason); + } else { + for (JobHandlerMap::iterator index = job_handler_map_.begin(); + index != job_handler_map_.end(); index++) { + index->second->CheckForJobs(reason); + } + } +} + +void CloudPrintConnector::OnPrinterAdded() { + AddPendingAvailableTask(); +} + +void CloudPrintConnector::OnPrinterDeleted(const std::string& printer_id) { + AddPendingDeleteTask(printer_id); +} + +void CloudPrintConnector::OnAuthError() { + client_->OnAuthFailed(); +} + +// CloudPrintURLFetcher::Delegate implementation. +CloudPrintURLFetcher::ResponseAction CloudPrintConnector::HandleRawData( + const content::URLFetcher* source, + const GURL& url, + const std::string& data) { + // If this notification came as a result of user message call, stop it. + // Otherwise proceed continue processing. + if (user_message_request_.get() && + user_message_request_->IsSameRequest(source)) + return CloudPrintURLFetcher::STOP_PROCESSING; + return CloudPrintURLFetcher::CONTINUE_PROCESSING; +} + +CloudPrintURLFetcher::ResponseAction CloudPrintConnector::HandleJSONData( + const content::URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + if (!IsRunning()) // Orphant response. Connector has been stopped already. + return CloudPrintURLFetcher::STOP_PROCESSING; + + DCHECK(next_response_handler_); + return (this->*next_response_handler_)(source, url, json_data, succeeded); +} + +CloudPrintURLFetcher::ResponseAction +CloudPrintConnector::HandlePrinterListResponse( + const content::URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + DCHECK(succeeded); + if (!succeeded) + return CloudPrintURLFetcher::RETRY_REQUEST; + + // Now we need to get the list of printers from the print system + // and split printers into 3 categories: + // - existing and registered printers + // - new printers + // - deleted printers + + // Get list of the printers from the print system. + printing::PrinterList local_printers; + cloud_print::PrintSystem::PrintSystemResult result = + print_system_->EnumeratePrinters(&local_printers); + bool full_list = result.succeeded(); + if (!result.succeeded()) { + std::string message = result.message(); + if (message.empty()) + message = l10n_util::GetStringUTF8(IDS_CLOUD_PRINT_ENUM_FAILED); + // There was a failure enumerating printers. Send a message to the server. + ReportUserMessage(kEnumPrintersFailedMessageId, message); + } + + // Go through the list of the cloud printers and init print job handlers. + ListValue* printer_list = NULL; + // There may be no "printers" value in the JSON + if (json_data->GetList(kPrinterListValue, &printer_list) && printer_list) { + for (size_t index = 0; index < printer_list->GetSize(); index++) { + DictionaryValue* printer_data = NULL; + if (printer_list->GetDictionary(index, &printer_data)) { + std::string printer_name; + printer_data->GetString(kNameValue, &printer_name); + if (RemovePrinterFromList(printer_name, &local_printers)) { + InitJobHandlerForPrinter(printer_data); + } else { + // Cloud printer is not found on the local system. + if (full_list) { // Delete only if we get the full list of printer. + std::string printer_id; + printer_data->GetString(kIdValue, &printer_id); + AddPendingDeleteTask(printer_id); + } + } + } else { + NOTREACHED(); + } + } + } + + request_ = NULL; + if (!local_printers.empty()) { + // Notify client that we have a list of printers available. + // Client will call us back to finish registration + client_->OnPrintersAvailable(local_printers); + } + ContinuePendingTaskProcessing(); // Continue processing background tasks. + return CloudPrintURLFetcher::STOP_PROCESSING; +} + +CloudPrintURLFetcher::ResponseAction +CloudPrintConnector::HandlePrinterDeleteResponse( + const content::URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + VLOG(1) << "CP_CONNECTOR: Handler printer delete response, succeeded:" + << succeeded << " url: " << url; + ContinuePendingTaskProcessing(); // Continue processing background tasks. + return CloudPrintURLFetcher::STOP_PROCESSING; +} + +CloudPrintURLFetcher::ResponseAction +CloudPrintConnector::HandleRegisterPrinterResponse( + const content::URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded) { + VLOG(1) << "CP_CONNECTOR: Handler printer register response, succeeded:" + << succeeded << " url: " << url; + if (succeeded) { + ListValue* printer_list = NULL; + // There should be a "printers" value in the JSON + if (json_data->GetList(kPrinterListValue, &printer_list)) { + DictionaryValue* printer_data = NULL; + if (printer_list->GetDictionary(0, &printer_data)) + InitJobHandlerForPrinter(printer_data); + } + } + ContinuePendingTaskProcessing(); // Continue processing background tasks. + return CloudPrintURLFetcher::STOP_PROCESSING; +} + + +CloudPrintURLFetcher::ResponseAction CloudPrintConnector::OnRequestAuthError() { + OnAuthError(); + return CloudPrintURLFetcher::STOP_PROCESSING; +} + +std::string CloudPrintConnector::GetAuthHeader() { + return CloudPrintHelpers::GetCloudPrintAuthHeader(); +} + +void CloudPrintConnector::StartGetRequest(const GURL& url, + int max_retries, + ResponseHandler handler) { + next_response_handler_ = handler; + request_ = new CloudPrintURLFetcher; + request_->StartGetRequest(url, this, max_retries, std::string()); +} + +void CloudPrintConnector::StartPostRequest(const GURL& url, + int max_retries, + const std::string& mime_type, + const std::string& post_data, + ResponseHandler handler) { + next_response_handler_ = handler; + request_ = new CloudPrintURLFetcher; + request_->StartPostRequest( + url, this, max_retries, mime_type, post_data, std::string()); +} + +void CloudPrintConnector::ReportUserMessage(const std::string& message_id, + const std::string& failure_msg) { + // This is a fire and forget type of function. + // Result of this request will be ignored. + std::string mime_boundary; + CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary); + GURL url = CloudPrintHelpers::GetUrlForUserMessage(cloud_print_server_url_, + message_id); + std::string post_data; + CloudPrintHelpers::AddMultipartValueForUpload(kMessageTextValue, + failure_msg, + mime_boundary, + std::string(), + &post_data); + // Terminate the request body + post_data.append("--" + mime_boundary + "--\r\n"); + std::string mime_type("multipart/form-data; boundary="); + mime_type += mime_boundary; + user_message_request_ = new CloudPrintURLFetcher; + user_message_request_->StartPostRequest(url, this, 1, mime_type, post_data, + std::string()); +} + +bool CloudPrintConnector::RemovePrinterFromList( + const std::string& printer_name, + printing::PrinterList* printer_list) { + for (printing::PrinterList::iterator index = printer_list->begin(); + index != printer_list->end(); index++) { + if (IsSamePrinter(index->printer_name, printer_name)) { + index = printer_list->erase(index); + return true; + } + } + return false; +} + +void CloudPrintConnector::InitJobHandlerForPrinter( + DictionaryValue* printer_data) { + DCHECK(printer_data); + PrinterJobHandler::PrinterInfoFromCloud printer_info_cloud; + printer_data->GetString(kIdValue, &printer_info_cloud.printer_id); + DCHECK(!printer_info_cloud.printer_id.empty()); + VLOG(1) << "CP_CONNECTOR: Init job handler for printer id: " + << printer_info_cloud.printer_id; + JobHandlerMap::iterator index = job_handler_map_.find( + printer_info_cloud.printer_id); + if (index != job_handler_map_.end()) + return; // Nothing to do if we already have a job handler for this printer. + + printing::PrinterBasicInfo printer_info; + printer_data->GetString(kNameValue, &printer_info.printer_name); + DCHECK(!printer_info.printer_name.empty()); + printer_data->GetString(kPrinterDescValue, + &printer_info.printer_description); + // Printer status is a string value which actually contains an integer. + std::string printer_status; + if (printer_data->GetString(kPrinterStatusValue, &printer_status)) { + base::StringToInt(printer_status, &printer_info.printer_status); + } + printer_data->GetString(kPrinterCapsHashValue, + &printer_info_cloud.caps_hash); + ListValue* tags_list = NULL; + if (printer_data->GetList(kTagsValue, &tags_list) && tags_list) { + for (size_t index = 0; index < tags_list->GetSize(); index++) { + std::string tag; + if (tags_list->GetString(index, &tag) && + StartsWithASCII(tag, kTagsHashTagName, false)) { + std::vector<std::string> tag_parts; + base::SplitStringDontTrim(tag, '=', &tag_parts); + DCHECK_EQ(tag_parts.size(), 2U); + if (tag_parts.size() == 2) + printer_info_cloud.tags_hash = tag_parts[1]; + } + } + } + scoped_refptr<PrinterJobHandler> job_handler; + job_handler = new PrinterJobHandler(printer_info, + printer_info_cloud, + cloud_print_server_url_, + print_system_.get(), + this); + job_handler_map_[printer_info_cloud.printer_id] = job_handler; + job_handler->Initialize(); +} + +void CloudPrintConnector::AddPendingAvailableTask() { + PendingTask task; + task.type = PENDING_PRINTERS_AVAILABLE; + AddPendingTask(task); +} + +void CloudPrintConnector::AddPendingDeleteTask(const std::string& id) { + PendingTask task; + task.type = PENDING_PRINTER_DELETE; + task.printer_id = id; + AddPendingTask(task); +} + +void CloudPrintConnector::AddPendingRegisterTask( + const printing::PrinterBasicInfo& info) { + PendingTask task; + task.type = PENDING_PRINTER_REGISTER; + task.printer_info = info; + AddPendingTask(task); +} + +void CloudPrintConnector::AddPendingTask(const PendingTask& task) { + pending_tasks_.push_back(task); + // If this is the only pending task, we need to start the process. + if (pending_tasks_.size() == 1) { + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableMethod( + this, &CloudPrintConnector::ProcessPendingTask)); + } +} + +void CloudPrintConnector::ProcessPendingTask() { + if (!IsRunning()) + return; // Orphant call. + if (pending_tasks_.size() == 0) + return; // No peding tasks. + + PendingTask task = pending_tasks_.front(); + + switch (task.type) { + case PENDING_PRINTERS_AVAILABLE : + OnPrintersAvailable(); + break; + case PENDING_PRINTER_REGISTER : + OnPrinterRegister(task.printer_info); + break; + case PENDING_PRINTER_DELETE : + OnPrinterDelete(task.printer_id); + break; + default: + NOTREACHED(); + } +} + +void CloudPrintConnector::OnPrintersAvailable() { + GURL printer_list_url = + CloudPrintHelpers::GetUrlForPrinterList(cloud_print_server_url_, + proxy_id_); + StartGetRequest(printer_list_url, + kCloudPrintRegisterMaxRetryCount, + &CloudPrintConnector::HandlePrinterListResponse); +} + +void CloudPrintConnector::OnPrinterRegister( + const printing::PrinterBasicInfo& info) { + for (JobHandlerMap::iterator it = job_handler_map_.begin(); + it != job_handler_map_.end(); ++it) { + if (IsSamePrinter(it->second->GetPrinterName(), info.printer_name)) { + // Printer already registered, continue to the next task. + ContinuePendingTaskProcessing(); + return; + } + } + + cloud_print::PrintSystem::PrinterCapsAndDefaultsCallback* callback = + NewCallback(this, &CloudPrintConnector::OnReceivePrinterCaps); + // Asnchronously fetch the printer caps and defaults. The story will + // continue in OnReceivePrinterCaps. + print_system_->GetPrinterCapsAndDefaults( + info.printer_name.c_str(), callback); +} + +void CloudPrintConnector::OnPrinterDelete(const std::string& printer_id) { + // Remove corresponding printer job handler. + JobHandlerMap::iterator it = job_handler_map_.find(printer_id); + if (it != job_handler_map_.end()) { + it->second->Shutdown(); + job_handler_map_.erase(it); + } + + // TODO(gene): We probably should not try indefinitely here. Just once or + // twice should be enough. + // Bug: http://code.google.com/p/chromium/issues/detail?id=101850 + GURL url = CloudPrintHelpers::GetUrlForPrinterDelete(cloud_print_server_url_, + printer_id); + StartGetRequest(url, + kCloudPrintAPIMaxRetryCount, + &CloudPrintConnector::HandlePrinterDeleteResponse); +} + +void CloudPrintConnector::ContinuePendingTaskProcessing() { + if (pending_tasks_.size() == 0) + return; // No peding tasks. + + // Delete current task and repost if we have more task avaialble. + pending_tasks_.pop_front(); + if (pending_tasks_.size() != 0) { + MessageLoop::current()->PostTask( + FROM_HERE, NewRunnableMethod( + this, &CloudPrintConnector::ProcessPendingTask)); + } +} + +void CloudPrintConnector::OnReceivePrinterCaps( + bool succeeded, + const std::string& printer_name, + const printing::PrinterCapsAndDefaults& caps_and_defaults) { + if (!IsRunning()) + return; // Orphant call. + DCHECK(pending_tasks_.size() > 0 && + pending_tasks_.front().type == PENDING_PRINTER_REGISTER); + + if (!succeeded) { + LOG(ERROR) << "CP_CONNECTOR: Failed to get printer info for: " << + printer_name; + // This printer failed to register, notify the server of this failure. + string16 printer_name_utf16 = UTF8ToUTF16(printer_name); + std::string status_message = l10n_util::GetStringFUTF8( + IDS_CLOUD_PRINT_REGISTER_PRINTER_FAILED, + printer_name_utf16); + ReportUserMessage(kGetPrinterCapsFailedMessageId, status_message); + + ContinuePendingTaskProcessing(); // Skip this printer registration. + return; + } + + const printing::PrinterBasicInfo& info = pending_tasks_.front().printer_info; + DCHECK(IsSamePrinter(info.printer_name, printer_name)); + + std::string mime_boundary; + CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary); + std::string post_data; + + CloudPrintHelpers::AddMultipartValueForUpload(kProxyIdValue, proxy_id_, + mime_boundary, + std::string(), &post_data); + CloudPrintHelpers::AddMultipartValueForUpload(kPrinterNameValue, + info.printer_name, + mime_boundary, + std::string(), &post_data); + CloudPrintHelpers::AddMultipartValueForUpload(kPrinterDescValue, + info.printer_description, + mime_boundary, + std::string() , &post_data); + CloudPrintHelpers::AddMultipartValueForUpload( + kPrinterStatusValue, base::StringPrintf("%d", info.printer_status), + mime_boundary, std::string(), &post_data); + // Add printer options as tags. + CloudPrintHelpers::GenerateMultipartPostDataForPrinterTags(info.options, + mime_boundary, + &post_data); + + CloudPrintHelpers::AddMultipartValueForUpload( + kPrinterCapsValue, caps_and_defaults.printer_capabilities, + mime_boundary, caps_and_defaults.caps_mime_type, + &post_data); + CloudPrintHelpers::AddMultipartValueForUpload( + kPrinterDefaultsValue, caps_and_defaults.printer_defaults, + mime_boundary, caps_and_defaults.defaults_mime_type, + &post_data); + // Send a hash of the printer capabilities to the server. We will use this + // later to check if the capabilities have changed + CloudPrintHelpers::AddMultipartValueForUpload( + kPrinterCapsHashValue, + base::MD5String(caps_and_defaults.printer_capabilities), + mime_boundary, std::string(), &post_data); + + // Terminate the request body + post_data.append("--" + mime_boundary + "--\r\n"); + std::string mime_type("multipart/form-data; boundary="); + mime_type += mime_boundary; + + GURL post_url = CloudPrintHelpers::GetUrlForPrinterRegistration( + cloud_print_server_url_); + StartPostRequest(post_url, + kCloudPrintAPIMaxRetryCount, + mime_type, + post_data, + &CloudPrintConnector::HandleRegisterPrinterResponse); +} + +bool CloudPrintConnector::IsSamePrinter(const std::string& name1, + const std::string& name2) const { + return (0 == base::strcasecmp(name1.c_str(), name2.c_str())); +} + diff --git a/chrome/service/cloud_print/cloud_print_connector.h b/chrome/service/cloud_print/cloud_print_connector.h new file mode 100644 index 0000000..8394b23 --- /dev/null +++ b/chrome/service/cloud_print/cloud_print_connector.h @@ -0,0 +1,187 @@ +// 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. + +#ifndef CHROME_SERVICE_CLOUD_PRINT_CLOUD_PRINT_CONNECTOR_H_ +#define CHROME_SERVICE_CLOUD_PRINT_CLOUD_PRINT_CONNECTOR_H_ +#pragma once + +#include <list> +#include <map> +#include <string> + +#include "base/threading/thread.h" +#include "base/values.h" +#include "chrome/service/cloud_print/print_system.h" +#include "chrome/service/cloud_print/printer_job_handler.h" + +// CloudPrintConnector handles top printer management tasks. +// - Matching local and cloud printers +// - Registration of local printers +// - Deleting cloud printers +// All tasks are posted to the commond queue (PendingTasks) and executed +// one-by-one in FIFO order. +// CloudPrintConnector will notify client over Client interface. +class CloudPrintConnector + : public base::RefCountedThreadSafe<CloudPrintConnector>, + public cloud_print::PrintServerWatcherDelegate, + public PrinterJobHandlerDelegate, + public CloudPrintURLFetcherDelegate { + public: + class Client { + public: + virtual void OnPrintersAvailable(const printing::PrinterList& printers) = 0; + virtual void OnAuthFailed() = 0; + protected: + virtual ~Client() {} + }; + + CloudPrintConnector(Client* client, + const std::string& proxy_id, + const GURL& cloud_print_server_url, + const DictionaryValue* print_system_settings); + virtual ~CloudPrintConnector(); + + bool Start(); + void Stop(); + bool IsRunning(); + + // Register printer from the list. + void RegisterPrinters(const printing::PrinterList& printers); + + // Check for jobs for specific printer. If printer id is empty + // jobs will be checked for all available printers. + void CheckForJobs(const std::string& reason, const std::string& printer_id); + + // cloud_print::PrintServerWatcherDelegate implementation + virtual void OnPrinterAdded(); + // PrinterJobHandler::Delegate implementation + virtual void OnPrinterDeleted(const std::string& printer_name); + virtual void OnAuthError(); + + // CloudPrintURLFetcher::Delegate implementation. + virtual CloudPrintURLFetcher::ResponseAction HandleRawData( + const content::URLFetcher* source, + const GURL& url, + const std::string& data); + + virtual CloudPrintURLFetcher::ResponseAction HandleJSONData( + const content::URLFetcher* source, + const GURL& url, + base::DictionaryValue* json_data, + bool succeeded); + virtual CloudPrintURLFetcher::ResponseAction OnRequestAuthError(); + virtual std::string GetAuthHeader(); + + private: + // Prototype for a response handler. + typedef CloudPrintURLFetcher::ResponseAction + (CloudPrintConnector::*ResponseHandler)( + const content::URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + + // Begin response handlers + CloudPrintURLFetcher::ResponseAction HandlePrinterListResponse( + const content::URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + + CloudPrintURLFetcher::ResponseAction HandlePrinterDeleteResponse( + const content::URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + + CloudPrintURLFetcher::ResponseAction HandleRegisterPrinterResponse( + const content::URLFetcher* source, + const GURL& url, + DictionaryValue* json_data, + bool succeeded); + // End response handlers + + // Helper functions for network requests. + void StartGetRequest(const GURL& url, + int max_retries, + ResponseHandler handler); + void StartPostRequest(const GURL& url, + int max_retries, + const std::string& mime_type, + const std::string& post_data, + ResponseHandler handler); + + // Reports a diagnostic message to the server. + void ReportUserMessage(const std::string& message_id, + const std::string& failure_message); + + bool RemovePrinterFromList(const std::string& printer_name, + printing::PrinterList* printer_list); + + void InitJobHandlerForPrinter(DictionaryValue* printer_data); + + enum PendingTaskType { + PENDING_PRINTERS_AVAILABLE, + PENDING_PRINTER_REGISTER, + PENDING_PRINTER_DELETE + }; + + struct PendingTask { + PendingTaskType type; + // Optional members, depending on type. + std::string printer_id; // For pending delete. + printing::PrinterBasicInfo printer_info; // For pending registration. + + PendingTask() {} + ~PendingTask() {} + }; + + void AddPendingAvailableTask(); + void AddPendingDeleteTask(const std::string& id); + void AddPendingRegisterTask(const printing::PrinterBasicInfo& info); + void AddPendingTask(const PendingTask& task); + void ProcessPendingTask(); + void ContinuePendingTaskProcessing(); + void OnPrintersAvailable(); + void OnPrinterRegister(const printing::PrinterBasicInfo& info); + void OnPrinterDelete(const std::string& name); + + void OnReceivePrinterCaps( + bool succeeded, + const std::string& printer_name, + const printing::PrinterCapsAndDefaults& caps_and_defaults); + + bool IsSamePrinter(const std::string& name1, const std::string& name2) const; + + // CloudPrintConnector client. + Client* client_; + // Print system settings. + scoped_ptr<DictionaryValue> print_system_settings_; + // Pointer to current print system. + scoped_refptr<cloud_print::PrintSystem> print_system_; + // Watcher for print system updates. + scoped_refptr<cloud_print::PrintSystem::PrintServerWatcher> + print_server_watcher_; + // Id of the Cloud Print proxy. + std::string proxy_id_; + // Cloud Print server url. + GURL cloud_print_server_url_; + // A map of printer id to job handler. + typedef std::map<std::string, scoped_refptr<PrinterJobHandler> > + JobHandlerMap; + JobHandlerMap job_handler_map_; + // Next response handler. + ResponseHandler next_response_handler_; + // The list of peding tasks to be done in the background. + std::list<PendingTask> pending_tasks_; + // The CloudPrintURLFetcher instance for the current request. + scoped_refptr<CloudPrintURLFetcher> request_; + // The CloudPrintURLFetcher instance for the user message request. + scoped_refptr<CloudPrintURLFetcher> user_message_request_; + + DISALLOW_COPY_AND_ASSIGN(CloudPrintConnector); +}; + +#endif // CHROME_SERVICE_CLOUD_PRINT_CLOUD_PRINT_CONNECTOR_H_ + diff --git a/chrome/service/cloud_print/cloud_print_consts.cc b/chrome/service/cloud_print/cloud_print_consts.cc index 41fbb85..39275b9 100644 --- a/chrome/service/cloud_print/cloud_print_consts.cc +++ b/chrome/service/cloud_print/cloud_print_consts.cc @@ -38,6 +38,7 @@ const char kTagDryRunFlag[] = "__cp__dry_run"; const char kDefaultCloudPrintServerUrl[] = "https://www.google.com/cloudprint"; const char kCloudPrintGaiaServiceId[] = "cloudprint"; const char kSyncGaiaServiceId[] = "chromiumsync"; +const char kProxyAuthUserAgent[] = "ChromiumBrowser"; const char kCloudPrintPushNotificationsSource[] = "cloudprint.google.com"; // The cloud print server expects the X-Google-CloudPrint-Proxy header for // certain requests. diff --git a/chrome/service/cloud_print/cloud_print_consts.h b/chrome/service/cloud_print/cloud_print_consts.h index 1ff6ef7..319e2f4 100644 --- a/chrome/service/cloud_print/cloud_print_consts.h +++ b/chrome/service/cloud_print/cloud_print_consts.h @@ -18,6 +18,7 @@ extern const char kPrinterStatusValue[]; extern const char kPrinterTagValue[]; extern const char kPrinterRemoveTagValue[]; extern const char kMessageTextValue[]; + // Values in the respone JSON from the cloud print server extern const char kPrinterListValue[]; extern const char kSuccessValue[]; @@ -38,6 +39,7 @@ extern const char kTagDryRunFlag[]; extern const char kDefaultCloudPrintServerUrl[]; extern const char kCloudPrintGaiaServiceId[]; extern const char kSyncGaiaServiceId[]; +extern const char kProxyAuthUserAgent[]; extern const char kCloudPrintPushNotificationsSource[]; extern const char kChromeCloudPrintProxyHeader[]; extern const char kCloudPrintUserAgent[]; @@ -56,6 +58,10 @@ extern const char kDefaultCloudPrintOAuthClientSecret[]; const int kJobDataMaxRetryCount = 5; // Max retry count (infinity) for API fetch requests. const int kCloudPrintAPIMaxRetryCount = -1; +// Max retry count (infinity) for Registration requests. +const int kCloudPrintRegisterMaxRetryCount = -1; +// Max retry count (infinity) for authentication requests. +const int kCloudPrintAuthMaxRetryCount = -1; // When we don't have XMPP notifications available, we resort to polling for // print jobs. We choose a random interval in seconds between these 2 values. diff --git a/chrome/service/cloud_print/cloud_print_helpers.cc b/chrome/service/cloud_print/cloud_print_helpers.cc index 492c46a..1a441d7 100644 --- a/chrome/service/cloud_print/cloud_print_helpers.cc +++ b/chrome/service/cloud_print/cloud_print_helpers.cc @@ -14,6 +14,7 @@ #include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/service/cloud_print/cloud_print_consts.h" +#include "chrome/service/cloud_print/cloud_print_token_store.h" #include "chrome/service/service_process.h" std::string StringFromJobStatus(cloud_print::PrintJobStatus status) { @@ -266,3 +267,20 @@ bool CloudPrintHelpers::IsDryRunJob(const std::vector<std::string>& tags) { } return false; } + +std::string CloudPrintHelpers::GetCloudPrintAuthHeader() { + std::string header; + CloudPrintTokenStore* token_store = CloudPrintTokenStore::current(); + if (!token_store || token_store->token().empty()) { + // Using LOG here for critical errors. GCP connector may run in the headless + // mode and error indication might be useful for user in that case. + LOG(ERROR) << "CP_PROXY: Missing OAuth token for request"; + } + + if (token_store) { + header = "Authorization: OAuth "; + header += token_store->token(); + } + return header; +} + diff --git a/chrome/service/cloud_print/cloud_print_helpers.h b/chrome/service/cloud_print/cloud_print_helpers.h index 5fb73c7..dfe5ded 100644 --- a/chrome/service/cloud_print/cloud_print_helpers.h +++ b/chrome/service/cloud_print/cloud_print_helpers.h @@ -70,6 +70,8 @@ class CloudPrintHelpers { // Returns true is tags indicate a dry run (test) job. static bool IsDryRunJob(const std::vector<std::string>& tags); + + static std::string GetCloudPrintAuthHeader(); private: CloudPrintHelpers() { } diff --git a/chrome/service/cloud_print/cloud_print_proxy.cc b/chrome/service/cloud_print/cloud_print_proxy.cc index 802a973..6dc7b1b 100644 --- a/chrome/service/cloud_print/cloud_print_proxy.cc +++ b/chrome/service/cloud_print/cloud_print_proxy.cc @@ -158,7 +158,8 @@ bool CloudPrintProxy::CreateBackend() { GURL cloud_print_server_url(cloud_print_server_url_str.c_str()); DCHECK(cloud_print_server_url.is_valid()); - backend_.reset(new CloudPrintProxyBackend(this, cloud_print_server_url, + backend_.reset(new CloudPrintProxyBackend(this, proxy_id_, + cloud_print_server_url, print_system_settings, oauth_client_info, enable_job_poll)); diff --git a/chrome/service/cloud_print/cloud_print_proxy_backend.cc b/chrome/service/cloud_print/cloud_print_proxy_backend.cc index 5483405..9f4327a 100644 --- a/chrome/service/cloud_print/cloud_print_proxy_backend.cc +++ b/chrome/service/cloud_print/cloud_print_proxy_backend.cc @@ -8,20 +8,15 @@ #include <vector> #include "base/file_util.h" -#include "base/md5.h" #include "base/rand_util.h" -#include "base/string_number_conversions.h" -#include "base/string_split.h" -#include "base/stringprintf.h" -#include "base/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/net/gaia/gaia_oauth_client.h" #include "chrome/common/net/gaia/gaia_urls.h" +#include "chrome/service/cloud_print/cloud_print_auth.h" +#include "chrome/service/cloud_print/cloud_print_connector.h" #include "chrome/service/cloud_print/cloud_print_consts.h" #include "chrome/service/cloud_print/cloud_print_helpers.h" #include "chrome/service/cloud_print/cloud_print_token_store.h" -#include "chrome/service/cloud_print/cloud_print_url_fetcher.h" -#include "chrome/service/cloud_print/printer_job_handler.h" #include "chrome/service/gaia/service_gaia_authenticator.h" #include "chrome/service/net/service_url_request_context.h" #include "chrome/service/service_process.h" @@ -30,21 +25,19 @@ #include "jingle/notifier/base/notifier_options.h" #include "jingle/notifier/listener/mediator_thread_impl.h" #include "jingle/notifier/listener/talk_mediator_impl.h" -#include "net/url_request/url_request_status.h" #include "ui/base/l10n/l10n_util.h" // The real guts of CloudPrintProxyBackend, to keep the public client API clean. class CloudPrintProxyBackend::Core : public base::RefCountedThreadSafe<CloudPrintProxyBackend::Core>, - public CloudPrintURLFetcherDelegate, - public cloud_print::PrintServerWatcherDelegate, - public PrinterJobHandlerDelegate, - public notifier::TalkMediator::Delegate, - public gaia::GaiaOAuthClient::Delegate { + public CloudPrintAuth::Client, + public CloudPrintConnector::Client, + public notifier::TalkMediator::Delegate { public: // It is OK for print_server_url to be empty. In this case system should // use system default (local) print server. Core(CloudPrintProxyBackend* backend, + const std::string& proxy_id, const GURL& cloud_print_server_url, const DictionaryValue* print_system_settings, const gaia::OAuthClientInfo& oauth_client_info, @@ -80,23 +73,17 @@ class CloudPrintProxyBackend::Core void DoRegisterSelectedPrinters( const printing::PrinterList& printer_list); - // CloudPrintURLFetcher::Delegate implementation. - virtual CloudPrintURLFetcher::ResponseAction HandleJSONData( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded); + // CloudPrintAuth::Client implementation. + virtual void OnAuthenticationComplete( + const std::string& access_token, + const std::string& robot_oauth_refresh_token, + const std::string& robot_email, + const std::string& user_email); + virtual void OnInvalidCredentials(); - virtual void OnRequestAuthError(); - - // cloud_print::PrintServerWatcherDelegate implementation - virtual void OnPrinterAdded(); - // PrinterJobHandler::Delegate implementation - virtual void OnPrinterJobHandlerShutdown(PrinterJobHandler* job_handler, - const std::string& printer_id); - virtual void OnAuthError(); - virtual void OnPrinterNotFound(const std::string& printer_name, - bool* delete_from_server); + // CloudPrintConnector::Client implementation. + virtual void OnPrintersAvailable(const printing::PrinterList& printers); + virtual void OnAuthFailed(); // notifier::TalkMediator::Delegate implementation. virtual void OnNotificationStateChange( @@ -105,62 +92,7 @@ class CloudPrintProxyBackend::Core const notifier::Notification& notification); virtual void OnOutgoingNotification(); - // gaia::GaiaOAuthClient::Delegate implementation. - virtual void OnGetTokensResponse(const std::string& refresh_token, - const std::string& access_token, - int expires_in_seconds); - virtual void OnRefreshTokenResponse(const std::string& access_token, - int expires_in_seconds); - virtual void OnOAuthError(); - virtual void OnNetworkError(int response_code); - private: - // Prototype for a response handler. - typedef CloudPrintURLFetcher::ResponseAction - (CloudPrintProxyBackend::Core::*ResponseHandler)( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded); - // Begin response handlers - CloudPrintURLFetcher::ResponseAction HandlePrinterListResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded); - - CloudPrintURLFetcher::ResponseAction HandleRegisterPrinterResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded); - - CloudPrintURLFetcher::ResponseAction HandleRegisterFailedStatusResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded); - - CloudPrintURLFetcher::ResponseAction HandlePrintSystemUnavailableResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded); - - CloudPrintURLFetcher::ResponseAction HandleEnumPrintersFailedResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded); - - CloudPrintURLFetcher::ResponseAction HandleGetAuthCodeResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded); - - // End response handlers - // NotifyXXX is how the Core communicates with the frontend across // threads. void NotifyPrinterListAvailable( @@ -172,37 +104,9 @@ class CloudPrintProxyBackend::Core void NotifyAuthenticationFailed(); void NotifyPrintSystemUnavailable(); - // Once we have robot credentials, this method gets the ball rolling. - void PostAuthInitialization(); - // Starts a new printer registration process. - void StartRegistration(); - // Ends the printer registration process. - void EndRegistration(); - // Registers printer capabilities and defaults for the next printer in the - // list with the cloud print server. - void RegisterNextPrinter(); - // Retrieves the list of registered printers for this user/proxy combination - // from the cloud print server. - void GetRegisteredPrinters(); - // Removes the given printer from the list. Returns false if the printer - // did not exist in the list. - bool RemovePrinterFromList(const std::string& printer_name); - // Initializes a job handler object for the specified printer. The job - // handler is responsible for checking for pending print jobs for this - // printer and print them. - void InitJobHandlerForPrinter(DictionaryValue* printer_data); - // Reports a diagnostic message to the server. - void ReportUserMessage(const std::string& message_id, - const std::string& failure_message, - ResponseHandler handler); - // Make a GAIA request to refresh the access token. - void RefreshAccessToken(); - - // Callback method for GetPrinterCapsAndDefaults. - void OnReceivePrinterCaps( - bool succeeded, - const std::string& printer_name, - const printing::PrinterCapsAndDefaults& caps_and_defaults); + // Init XMPP channel + void InitNotifications(const std::string& robot_email, + const std::string& access_token); void HandlePrinterNotification(const std::string& printer_id); void PollForJobs(); @@ -214,50 +118,16 @@ class CloudPrintProxyBackend::Core // Our parent CloudPrintProxyBackend CloudPrintProxyBackend* backend_; + // Cloud Print authenticator. + scoped_refptr<CloudPrintAuth> auth_; + + // Cloud Print connector. + scoped_refptr<CloudPrintConnector> connector_; + + // Server URL. GURL cloud_print_server_url_; - gaia::OAuthClientInfo oauth_client_info_; - scoped_ptr<DictionaryValue> print_system_settings_; - // Pointer to current print system. - scoped_refptr<cloud_print::PrintSystem> print_system_; - // The list of printers to be registered with the cloud print server. - // To begin with,this list is initialized with the list of local and network - // printers available. Then we query the server for the list of printers - // already registered. We trim this list to remove the printers already - // registered. We then pass a copy of this list to the frontend to give the - // user a chance to further trim the list. When the frontend gives us the - // final list we make a copy into this so that we can start registering. - printing::PrinterList printer_list_; - // Indicates whether the printers in printer_list_ is the complete set of - // printers to be registered for this proxy. - bool complete_list_available_; - // The CloudPrintURLFetcher instance for the current request. - scoped_refptr<CloudPrintURLFetcher> request_; - // The index of the nex printer to be uploaded. - size_t next_upload_index_; - // The unique id for this proxy + // Proxy Id. std::string proxy_id_; - // The OAuth2 refresh token for the robot. - std::string refresh_token_; - // The email address of the user. This is only used during initial - // authentication with an LSID. This is only used for storing in prefs for - // display purposes. - std::string user_email_; - // The email address of the robot account. - std::string robot_email_; - // Cached info about the last printer that we tried to upload. We cache this - // so we won't have to requery the printer if the upload fails and we need - // to retry. - std::string last_uploaded_printer_name_; - printing::PrinterCapsAndDefaults last_uploaded_printer_info_; - // A map of printer id to job handler. - typedef std::map<std::string, scoped_refptr<PrinterJobHandler> > - JobHandlerMap; - JobHandlerMap job_handler_map_; - ResponseHandler next_response_handler_; - scoped_refptr<cloud_print::PrintSystem::PrintServerWatcher> - print_server_watcher_; - bool new_printers_available_; - bool registration_in_progress_; // Notification (xmpp) handler. scoped_ptr<notifier::TalkMediator> talk_mediator_; // Indicates whether XMPP notifications are currently enabled. @@ -269,7 +139,6 @@ class CloudPrintProxyBackend::Core bool job_poll_scheduled_; // Indicates whether we should poll for jobs when we lose XMPP connection. bool enable_job_poll_; - scoped_ptr<gaia::GaiaOAuthClient> oauth_client_; scoped_ptr<CloudPrintTokenStore> token_store_; DISALLOW_COPY_AND_ASSIGN(Core); @@ -277,6 +146,7 @@ class CloudPrintProxyBackend::Core CloudPrintProxyBackend::CloudPrintProxyBackend( CloudPrintProxyFrontend* frontend, + const std::string& proxy_id, const GURL& cloud_print_server_url, const DictionaryValue* print_system_settings, const gaia::OAuthClientInfo& oauth_client_info, @@ -286,6 +156,7 @@ CloudPrintProxyBackend::CloudPrintProxyBackend( frontend_(frontend) { DCHECK(frontend_); core_ = new Core(this, + proxy_id, cloud_print_server_url, print_system_settings, oauth_client_info, @@ -379,25 +250,25 @@ void CloudPrintProxyBackend::RegisterPrinters( CloudPrintProxyBackend::Core::Core( CloudPrintProxyBackend* backend, + const std::string& proxy_id, const GURL& cloud_print_server_url, const DictionaryValue* print_system_settings, const gaia::OAuthClientInfo& oauth_client_info, bool enable_job_poll) : backend_(backend), cloud_print_server_url_(cloud_print_server_url), - oauth_client_info_(oauth_client_info), - complete_list_available_(false), - next_upload_index_(0), - next_response_handler_(NULL), - new_printers_available_(false), - registration_in_progress_(false), - notifications_enabled_(false), + proxy_id_(proxy_id), job_poll_scheduled_(false), enable_job_poll_(enable_job_poll) { - if (print_system_settings) { - // It is possible to have no print settings specified. - print_system_settings_.reset(print_system_settings->DeepCopy()); - } + auth_ = new CloudPrintAuth(this, + cloud_print_server_url, + print_system_settings, + oauth_client_info, + proxy_id); + connector_ = new CloudPrintConnector(this, + proxy_id, + cloud_print_server_url, + print_system_settings); } void CloudPrintProxyBackend::Core::DoInitializeWithLsid( @@ -410,333 +281,150 @@ void CloudPrintProxyBackend::Core::DoInitializeWithLsid( // Note: The GAIA login is synchronous but that should be OK because we are in // the CloudPrintProxyCoreThread and we cannot really do anything else until // the GAIA signin is successful. - std::string user_agent = "ChromiumBrowser"; - scoped_refptr<ServiceGaiaAuthenticator> gaia_auth_for_print( - new ServiceGaiaAuthenticator( - user_agent, kCloudPrintGaiaServiceId, - GaiaUrls::GetInstance()->client_login_url(), - g_service_process->io_thread()->message_loop_proxy())); - gaia_auth_for_print->set_message_loop(MessageLoop::current()); - if (gaia_auth_for_print->AuthenticateWithLsid(lsid)) { - // Stash away the user email so we can save it in prefs. - user_email_ = gaia_auth_for_print->email(); - // If the same user is re-enabling Cloud Print and we have stashed robot - // credentials, we will use those. - if ((0 == base::strcasecmp(user_email_.c_str(), last_user_email.c_str())) && - !last_robot_refresh_token.empty() && - !last_robot_email.empty()) { - DoInitializeWithRobotToken(last_robot_refresh_token, - last_robot_email, - proxy_id); - } - DoInitializeWithToken(gaia_auth_for_print->auth_token(), - proxy_id); - } else { - // Let the frontend know the of authentication failure. - OnAuthError(); - } + auth_->AuthenticateWithLsid(lsid, last_robot_refresh_token, + last_robot_email, last_user_email); } void CloudPrintProxyBackend::Core::DoInitializeWithToken( const std::string cloud_print_token, const std::string& proxy_id) { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - VLOG(1) << "CP_PROXY: Starting proxy, id: " << proxy_id; - proxy_id_ = proxy_id; - GetTokenStore()->SetToken(cloud_print_token, false); - - // We need to get the credentials of the robot here. - GURL get_authcode_url = - CloudPrintHelpers::GetUrlForGetAuthCode(cloud_print_server_url_, - oauth_client_info_.client_id, - proxy_id_); - next_response_handler_ = - &CloudPrintProxyBackend::Core::HandleGetAuthCodeResponse; - request_ = new CloudPrintURLFetcher; - request_->StartGetRequest(get_authcode_url, - this, - kCloudPrintAPIMaxRetryCount, - std::string()); + auth_->AuthenticateWithToken(cloud_print_token); } void CloudPrintProxyBackend::Core::DoInitializeWithRobotToken( const std::string& robot_oauth_refresh_token, const std::string& robot_email, const std::string& proxy_id) { - robot_email_ = robot_email; - proxy_id_ = proxy_id; - refresh_token_ = robot_oauth_refresh_token; - RefreshAccessToken(); + DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); + auth_->AuthenticateWithRobotToken(robot_oauth_refresh_token, robot_email); } void CloudPrintProxyBackend::Core::DoInitializeWithRobotAuthCode( const std::string& robot_oauth_auth_code, const std::string& robot_email, const std::string& proxy_id) { - robot_email_ = robot_email; - proxy_id_ = proxy_id; - // Now that we have an auth code we need to get the refresh and access tokens. - oauth_client_.reset(new gaia::GaiaOAuthClient( - gaia::kGaiaOAuth2Url, - g_service_process->GetServiceURLRequestContextGetter())); - oauth_client_->GetTokensFromAuthCode(oauth_client_info_, - robot_oauth_auth_code, - kCloudPrintAPIMaxRetryCount, - this); -} - -void CloudPrintProxyBackend::Core::PostAuthInitialization() { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - // Now we can get down to registering printers. - print_system_ = - cloud_print::PrintSystem::CreateInstance(print_system_settings_.get()); - if (!print_system_.get()) { - NOTREACHED(); - return; // No print system available, fail initalization. - } - cloud_print::PrintSystem::PrintSystemResult result = print_system_->Init(); - - if (result.succeeded()) { - notifier::NotifierOptions notifier_options; - notifier_options.request_context_getter = - g_service_process->GetServiceURLRequestContextGetter(); - notifier_options.auth_mechanism = "X-OAUTH2"; - talk_mediator_.reset(new notifier::TalkMediatorImpl( - new notifier::MediatorThreadImpl(notifier_options), - notifier_options)); - notifier::Subscription subscription; - subscription.channel = kCloudPrintPushNotificationsSource; - subscription.from = kCloudPrintPushNotificationsSource; - talk_mediator_->AddSubscription(subscription); - talk_mediator_->SetDelegate(this); - talk_mediator_->SetAuthToken( - robot_email_, - CloudPrintTokenStore::current()->token(), - kSyncGaiaServiceId); - talk_mediator_->Login(); - - print_server_watcher_ = print_system_->CreatePrintServerWatcher(); - print_server_watcher_->StartWatching(this); + auth_->AuthenticateWithRobotAuthCode(robot_oauth_auth_code, robot_email); +} - StartRegistration(); +void CloudPrintProxyBackend::Core::OnAuthenticationComplete( + const std::string& access_token, + const std::string& robot_oauth_refresh_token, + const std::string& robot_email, + const std::string& user_email) { + CloudPrintTokenStore* token_store = GetTokenStore(); + bool first_time = token_store->token().empty(); + token_store->SetToken(access_token); + // Let the frontend know that we have authenticated. + backend_->frontend_loop_->PostTask( + FROM_HERE, NewRunnableMethod(this, + &Core::NotifyAuthenticated, + robot_oauth_refresh_token, + robot_email, + user_email)); + if (first_time) { + InitNotifications(robot_email, access_token); } else { - // We could not initialize the print system. We need to notify the server. - ReportUserMessage( - kPrintSystemFailedMessageId, - result.message(), - &CloudPrintProxyBackend::Core::HandlePrintSystemUnavailableResponse); + // If we are refreshing a token, update the XMPP token too. + DCHECK(talk_mediator_.get()); + talk_mediator_->SetAuthToken(robot_email, + access_token, + kSyncGaiaServiceId); + } + // Start cloud print connector if needed. + if (!connector_->IsRunning()) { + if (!connector_->Start()) { + // Let the frontend know that we do not have a print system. + backend_->frontend_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, + &Core::NotifyPrintSystemUnavailable)); + } } } -void CloudPrintProxyBackend::Core::StartRegistration() { +void CloudPrintProxyBackend::Core::OnInvalidCredentials() { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - printer_list_.clear(); - cloud_print::PrintSystem::PrintSystemResult result = - print_system_->EnumeratePrinters(&printer_list_); - complete_list_available_ = result.succeeded(); - registration_in_progress_ = true; - if (!result.succeeded()) { - std::string message = result.message(); - if (message.empty()) - message = l10n_util::GetStringUTF8(IDS_CLOUD_PRINT_ENUM_FAILED); - // There was a failure enumerating printers. Send a message to the server. - ReportUserMessage( - kEnumPrintersFailedMessageId, - message, - &CloudPrintProxyBackend::Core::HandleEnumPrintersFailedResponse); - } else { - // Now we need to ask the server about printers that were registered on the - // server so that we can trim this list. - GetRegisteredPrinters(); - } + VLOG(1) << "CP_CONNECTOR: Auth Error"; + backend_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, + &Core::NotifyAuthenticationFailed)); +} + +void CloudPrintProxyBackend::Core::OnPrintersAvailable( + const printing::PrinterList& printers) { + // Let the frontend know that we have a list of printers available. + backend_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, + &Core::NotifyPrinterListAvailable, printers)); +} + +void CloudPrintProxyBackend::Core::OnAuthFailed() { + VLOG(1) << "CP_CONNECTOR: Authentication failed in connector."; + // Let's stop connecter and refresh token. We'll restart connecter once + // new token available. + if (connector_->IsRunning()) + connector_->Stop(); + + // Refresh Auth token. + auth_->RefreshAccessToken(); } -void CloudPrintProxyBackend::Core::EndRegistration() { +void CloudPrintProxyBackend::Core::InitNotifications( + const std::string& robot_email, + const std::string& access_token) { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - request_ = NULL; - registration_in_progress_ = false; - if (new_printers_available_) { - new_printers_available_ = false; - StartRegistration(); - } + + notifier::NotifierOptions notifier_options; + notifier_options.request_context_getter = + g_service_process->GetServiceURLRequestContextGetter(); + notifier_options.auth_mechanism = "X-OAUTH2"; + talk_mediator_.reset(new notifier::TalkMediatorImpl( + new notifier::MediatorThreadImpl(notifier_options), + notifier_options)); + notifier::Subscription subscription; + subscription.channel = kCloudPrintPushNotificationsSource; + subscription.from = kCloudPrintPushNotificationsSource; + talk_mediator_->AddSubscription(subscription); + talk_mediator_->SetDelegate(this); + talk_mediator_->SetAuthToken(robot_email, access_token, kSyncGaiaServiceId); + talk_mediator_->Login(); } void CloudPrintProxyBackend::Core::DoShutdown() { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - VLOG(1) << "CP_PROXY: Shutdown proxy, id: " << proxy_id_; - if (print_server_watcher_ != NULL) - print_server_watcher_->StopWatching(); + VLOG(1) << "CP_CONNECTOR: Shutdown connector, id: " << proxy_id_; + + if (connector_->IsRunning()) + connector_->Stop(); - // Need to kill all running jobs. - while (!job_handler_map_.empty()) { - JobHandlerMap::iterator index = job_handler_map_.begin(); - // Shutdown will call our OnPrinterJobHandlerShutdown method which will - // remove this from the map. - index->second->Shutdown(); - } // Important to delete the TalkMediator on this thread. if (talk_mediator_.get()) talk_mediator_->Logout(); talk_mediator_.reset(); notifications_enabled_ = false; notifications_enabled_since_ = base::TimeTicks(); - request_ = NULL; token_store_.reset(); } void CloudPrintProxyBackend::Core::DoRegisterSelectedPrinters( const printing::PrinterList& printer_list) { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - if (!print_system_.get()) - return; // No print system available. - printer_list_.assign(printer_list.begin(), printer_list.end()); - next_upload_index_ = 0; - RegisterNextPrinter(); -} - -void CloudPrintProxyBackend::Core::GetRegisteredPrinters() { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - GURL printer_list_url = - CloudPrintHelpers::GetUrlForPrinterList(cloud_print_server_url_, - proxy_id_); - next_response_handler_ = - &CloudPrintProxyBackend::Core::HandlePrinterListResponse; - request_ = new CloudPrintURLFetcher; - request_->StartGetRequest(printer_list_url, - this, - kCloudPrintAPIMaxRetryCount, - std::string()); -} - -void CloudPrintProxyBackend::Core::RegisterNextPrinter() { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - // For the next printer to be uploaded, create a multi-part post request to - // upload the printer capabilities and the printer defaults. - if (next_upload_index_ < printer_list_.size()) { - const printing::PrinterBasicInfo& info = - printer_list_.at(next_upload_index_); - // If we are retrying a previous upload, we don't need to fetch the caps - // and defaults again. - if (info.printer_name != last_uploaded_printer_name_) { - cloud_print::PrintSystem::PrinterCapsAndDefaultsCallback* callback = - NewCallback(this, - &CloudPrintProxyBackend::Core::OnReceivePrinterCaps); - // Asnchronously fetch the printer caps and defaults. The story will - // continue in OnReceivePrinterCaps. - print_system_->GetPrinterCapsAndDefaults( - info.printer_name.c_str(), callback); - } else { - OnReceivePrinterCaps(true, - last_uploaded_printer_name_, - last_uploaded_printer_info_); - } - } else { - EndRegistration(); - } -} - -void CloudPrintProxyBackend::Core::OnReceivePrinterCaps( - bool succeeded, - const std::string& printer_name, - const printing::PrinterCapsAndDefaults& caps_and_defaults) { - DCHECK(next_upload_index_ < printer_list_.size()); - if (succeeded) { - const printing::PrinterBasicInfo& info = - printer_list_.at(next_upload_index_); - - last_uploaded_printer_name_ = info.printer_name; - last_uploaded_printer_info_ = caps_and_defaults; - - std::string mime_boundary; - CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary); - std::string post_data; - - CloudPrintHelpers::AddMultipartValueForUpload(kProxyIdValue, proxy_id_, - mime_boundary, - std::string(), &post_data); - CloudPrintHelpers::AddMultipartValueForUpload(kPrinterNameValue, - info.printer_name, - mime_boundary, - std::string(), &post_data); - CloudPrintHelpers::AddMultipartValueForUpload(kPrinterDescValue, - info.printer_description, - mime_boundary, - std::string() , &post_data); - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterStatusValue, base::StringPrintf("%d", info.printer_status), - mime_boundary, std::string(), &post_data); - // Add printer options as tags. - CloudPrintHelpers::GenerateMultipartPostDataForPrinterTags(info.options, - mime_boundary, - &post_data); - - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterCapsValue, last_uploaded_printer_info_.printer_capabilities, - mime_boundary, last_uploaded_printer_info_.caps_mime_type, - &post_data); - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterDefaultsValue, last_uploaded_printer_info_.printer_defaults, - mime_boundary, last_uploaded_printer_info_.defaults_mime_type, - &post_data); - // Send a hash of the printer capabilities to the server. We will use this - // later to check if the capabilities have changed - CloudPrintHelpers::AddMultipartValueForUpload( - kPrinterCapsHashValue, - base::MD5String(last_uploaded_printer_info_.printer_capabilities), - mime_boundary, std::string(), &post_data); - GURL post_url = CloudPrintHelpers::GetUrlForPrinterRegistration( - cloud_print_server_url_); - - next_response_handler_ = - &CloudPrintProxyBackend::Core::HandleRegisterPrinterResponse; - // Terminate the request body - post_data.append("--" + mime_boundary + "--\r\n"); - std::string mime_type("multipart/form-data; boundary="); - mime_type += mime_boundary; - request_ = new CloudPrintURLFetcher; - request_->StartPostRequest(post_url, - this, - kCloudPrintAPIMaxRetryCount, - mime_type, - post_data, - std::string()); - } else { - LOG(ERROR) << "CP_PROXY: Failed to get printer info for: " << - printer_name; - // This printer failed to register, notify the server of this failure. - string16 printer_name_utf16 = UTF8ToUTF16(printer_name); - std::string status_message = l10n_util::GetStringFUTF8( - IDS_CLOUD_PRINT_REGISTER_PRINTER_FAILED, - printer_name_utf16); - ReportUserMessage( - kGetPrinterCapsFailedMessageId, - status_message, - &CloudPrintProxyBackend::Core::HandleRegisterFailedStatusResponse); - } + connector_->RegisterPrinters(printer_list); } void CloudPrintProxyBackend::Core::HandlePrinterNotification( const std::string& printer_id) { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - VLOG(1) << "CP_PROXY: Handle printer notification, id: " << printer_id; - JobHandlerMap::iterator index = job_handler_map_.find(printer_id); - if (index != job_handler_map_.end()) - index->second->CheckForJobs(kJobFetchReasonNotified); + VLOG(1) << "CP_CONNECTOR: Handle printer notification, id: " << printer_id; + connector_->CheckForJobs(kJobFetchReasonNotified, printer_id); } void CloudPrintProxyBackend::Core::PollForJobs() { - VLOG(1) << "CP_PROXY: Polling for jobs."; + VLOG(1) << "CP_CONNECTOR: Polling for jobs."; DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - for (JobHandlerMap::iterator index = job_handler_map_.begin(); - index != job_handler_map_.end(); index++) { - // If notifications are on, then we should poll for this printer only if - // the last time it fetched jobs was before notifications were last enabled. - bool should_poll = - !notifications_enabled_ || - (index->second->last_job_fetch_time() <= notifications_enabled_since_); - if (should_poll) - index->second->CheckForJobs(kJobFetchReasonPoll); - } + // Check all printers for jobs. + connector_->CheckForJobs(kJobFetchReasonPoll, std::string()); + job_poll_scheduled_ = false; // If we don't have notifications and job polling is enabled, poll again // after a while. @@ -763,21 +451,6 @@ CloudPrintTokenStore* CloudPrintProxyBackend::Core::GetTokenStore() { return token_store_.get(); } -// CloudPrintURLFetcher::Delegate implementation. -CloudPrintURLFetcher::ResponseAction -CloudPrintProxyBackend::Core::HandleJSONData( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded) { - DCHECK(next_response_handler_); - return (this->*next_response_handler_)(source, url, json_data, succeeded); -} - -void CloudPrintProxyBackend::Core::OnRequestAuthError() { - OnAuthError(); -} - void CloudPrintProxyBackend::Core::NotifyPrinterListAvailable( const printing::PrinterList& printer_list) { DCHECK(MessageLoop::current() == backend_->frontend_loop_); @@ -804,256 +477,17 @@ void CloudPrintProxyBackend::Core::NotifyPrintSystemUnavailable() { backend_->frontend_->OnPrintSystemUnavailable(); } -CloudPrintURLFetcher::ResponseAction -CloudPrintProxyBackend::Core::HandleGetAuthCodeResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - if (!succeeded) { - OnAuthError(); - return CloudPrintURLFetcher::STOP_PROCESSING; - } - std::string auth_code; - if (!json_data->GetString(kOAuthCodeValue, &auth_code)) { - OnAuthError(); - return CloudPrintURLFetcher::STOP_PROCESSING; - } - json_data->GetString(kXMPPJidValue, &robot_email_); - // Now that we have an auth code we need to get the refresh and access tokens. - oauth_client_.reset(new gaia::GaiaOAuthClient( - gaia::kGaiaOAuth2Url, - g_service_process->GetServiceURLRequestContextGetter())); - oauth_client_->GetTokensFromAuthCode(oauth_client_info_, - auth_code, - kCloudPrintAPIMaxRetryCount, - this); - - return CloudPrintURLFetcher::STOP_PROCESSING; -} - -CloudPrintURLFetcher::ResponseAction -CloudPrintProxyBackend::Core::HandlePrinterListResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - if (!succeeded) { - NOTREACHED(); - return CloudPrintURLFetcher::RETRY_REQUEST; - } - ListValue* printer_list = NULL; - // There may be no "printers" value in the JSON - if (json_data->GetList(kPrinterListValue, &printer_list) && printer_list) { - for (size_t index = 0; index < printer_list->GetSize(); index++) { - DictionaryValue* printer_data = NULL; - if (printer_list->GetDictionary(index, &printer_data)) { - std::string printer_name; - printer_data->GetString(kNameValue, &printer_name); - RemovePrinterFromList(printer_name); - InitJobHandlerForPrinter(printer_data); - } else { - NOTREACHED(); - } - } - } - request_ = NULL; - if (!printer_list_.empty()) { - // Let the frontend know that we have a list of printers available. - backend_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, - &Core::NotifyPrinterListAvailable, printer_list_)); - } else { - // No more work to be done here. - MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &Core::EndRegistration)); - } - return CloudPrintURLFetcher::STOP_PROCESSING; -} - -void CloudPrintProxyBackend::Core::InitJobHandlerForPrinter( - DictionaryValue* printer_data) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - DCHECK(printer_data); - PrinterJobHandler::PrinterInfoFromCloud printer_info_cloud; - printer_data->GetString(kIdValue, &printer_info_cloud.printer_id); - DCHECK(!printer_info_cloud.printer_id.empty()); - VLOG(1) << "CP_PROXY: Init job handler for printer id: " - << printer_info_cloud.printer_id; - JobHandlerMap::iterator index = job_handler_map_.find( - printer_info_cloud.printer_id); - // We might already have a job handler for this printer - if (index == job_handler_map_.end()) { - printing::PrinterBasicInfo printer_info; - printer_data->GetString(kNameValue, &printer_info.printer_name); - DCHECK(!printer_info.printer_name.empty()); - printer_data->GetString(kPrinterDescValue, - &printer_info.printer_description); - // Printer status is a string value which actually contains an integer. - std::string printer_status; - if (printer_data->GetString(kPrinterStatusValue, &printer_status)) { - base::StringToInt(printer_status, &printer_info.printer_status); - } - printer_data->GetString(kPrinterCapsHashValue, - &printer_info_cloud.caps_hash); - ListValue* tags_list = NULL; - if (printer_data->GetList(kTagsValue, &tags_list) && tags_list) { - for (size_t index = 0; index < tags_list->GetSize(); index++) { - std::string tag; - if (tags_list->GetString(index, &tag) && - StartsWithASCII(tag, kTagsHashTagName, false)) { - std::vector<std::string> tag_parts; - base::SplitStringDontTrim(tag, '=', &tag_parts); - DCHECK_EQ(tag_parts.size(), 2U); - if (tag_parts.size() == 2) - printer_info_cloud.tags_hash = tag_parts[1]; - } - } - } - scoped_refptr<PrinterJobHandler> job_handler; - job_handler = new PrinterJobHandler(printer_info, - printer_info_cloud, - cloud_print_server_url_, - print_system_.get(), - this); - job_handler_map_[printer_info_cloud.printer_id] = job_handler; - job_handler->Initialize(); - } -} - -void CloudPrintProxyBackend::Core::ReportUserMessage( - const std::string& message_id, - const std::string& failure_message, - ResponseHandler handler) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - std::string mime_boundary; - CloudPrintHelpers::CreateMimeBoundaryForUpload(&mime_boundary); - GURL post_url = CloudPrintHelpers::GetUrlForUserMessage( - cloud_print_server_url_, - message_id); - std::string post_data; - CloudPrintHelpers::AddMultipartValueForUpload(kMessageTextValue, - failure_message, - mime_boundary, - std::string(), - &post_data); - next_response_handler_ = handler; - // Terminate the request body - post_data.append("--" + mime_boundary + "--\r\n"); - std::string mime_type("multipart/form-data; boundary="); - mime_type += mime_boundary; - request_ = new CloudPrintURLFetcher; - request_->StartPostRequest(post_url, - this, - kCloudPrintAPIMaxRetryCount, - mime_type, - post_data, - std::string()); -} - -CloudPrintURLFetcher::ResponseAction -CloudPrintProxyBackend::Core::HandleRegisterPrinterResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - if (succeeded) { - ListValue* printer_list = NULL; - // There should be a "printers" value in the JSON - if (json_data->GetList(kPrinterListValue, &printer_list)) { - DictionaryValue* printer_data = NULL; - if (printer_list->GetDictionary(0, &printer_data)) - InitJobHandlerForPrinter(printer_data); - } - } - next_upload_index_++; - MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, - &CloudPrintProxyBackend::Core::RegisterNextPrinter)); - return CloudPrintURLFetcher::STOP_PROCESSING; -} - -CloudPrintURLFetcher::ResponseAction -CloudPrintProxyBackend::Core::HandleRegisterFailedStatusResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - next_upload_index_++; - MessageLoop::current()->PostTask( - FROM_HERE, - NewRunnableMethod(this, - &CloudPrintProxyBackend::Core::RegisterNextPrinter)); - return CloudPrintURLFetcher::STOP_PROCESSING; -} - -CloudPrintURLFetcher::ResponseAction -CloudPrintProxyBackend::Core::HandlePrintSystemUnavailableResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - // Let the frontend know that we do not have a print system. - backend_->frontend_loop_->PostTask( - FROM_HERE, - NewRunnableMethod(this, - &Core::NotifyPrintSystemUnavailable)); - return CloudPrintURLFetcher::STOP_PROCESSING; -} - -CloudPrintURLFetcher::ResponseAction -CloudPrintProxyBackend::Core::HandleEnumPrintersFailedResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - // Now proceed with printer registration. - GetRegisteredPrinters(); - return CloudPrintURLFetcher::STOP_PROCESSING; -} - - -bool CloudPrintProxyBackend::Core::RemovePrinterFromList( - const std::string& printer_name) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - for (printing::PrinterList::iterator index = printer_list_.begin(); - index != printer_list_.end(); index++) { - if (0 == base::strcasecmp(index->printer_name.c_str(), - printer_name.c_str())) { - index = printer_list_.erase(index); - return true; - } - } - return false; -} - -void CloudPrintProxyBackend::Core::RefreshAccessToken() { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - oauth_client_.reset(new gaia::GaiaOAuthClient( - gaia::kGaiaOAuth2Url, - g_service_process->GetServiceURLRequestContextGetter())); - oauth_client_->RefreshToken(oauth_client_info_, - refresh_token_, - kCloudPrintAPIMaxRetryCount, - this); -} - void CloudPrintProxyBackend::Core::OnNotificationStateChange( bool notification_enabled) { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); notifications_enabled_ = notification_enabled; if (notifications_enabled_) { notifications_enabled_since_ = base::TimeTicks::Now(); - VLOG(1) << "Notifications for proxy " << proxy_id_ << " were enabled at " + VLOG(1) << "Notifications for connector " << proxy_id_ + << " were enabled at " << notifications_enabled_since_.ToInternalValue(); } else { - LOG(ERROR) << "Notifications for proxy " << proxy_id_ << " disabled."; + LOG(ERROR) << "Notifications for connector " << proxy_id_ << " disabled."; notifications_enabled_since_ = base::TimeTicks(); } // A state change means one of two cases. @@ -1071,98 +505,10 @@ void CloudPrintProxyBackend::Core::OnNotificationStateChange( void CloudPrintProxyBackend::Core::OnIncomingNotification( const notifier::Notification& notification) { DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - VLOG(1) << "CP_PROXY: Incoming notification."; + VLOG(1) << "CP_CONNECTOR: Incoming notification."; if (0 == base::strcasecmp(kCloudPrintPushNotificationsSource, notification.channel.c_str())) HandlePrinterNotification(notification.data); } void CloudPrintProxyBackend::Core::OnOutgoingNotification() {} - -// cloud_print::PrinterChangeNotifier::Delegate implementation -void CloudPrintProxyBackend::Core::OnPrinterAdded() { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - if (registration_in_progress_) - new_printers_available_ = true; - else - StartRegistration(); -} - -// PrinterJobHandler::Delegate implementation -void CloudPrintProxyBackend::Core::OnPrinterJobHandlerShutdown( - PrinterJobHandler* job_handler, const std::string& printer_id) { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - VLOG(1) << "CP_PROXY: Printer job handle shutdown, id " << printer_id; - job_handler_map_.erase(printer_id); -} - -void CloudPrintProxyBackend::Core::OnAuthError() { - DCHECK(MessageLoop::current() == backend_->core_thread_.message_loop()); - VLOG(1) << "CP_PROXY: Auth Error"; - backend_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, - &Core::NotifyAuthenticationFailed)); -} - -void CloudPrintProxyBackend::Core::OnPrinterNotFound( - const std::string& printer_name, - bool* delete_from_server) { - // If we have a complete list of local printers, then this needs to be deleted - // from the server. - *delete_from_server = complete_list_available_; -} - - // gaia::GaiaOAuthClient::Delegate implementation. -void CloudPrintProxyBackend::Core::OnGetTokensResponse( - const std::string& refresh_token, - const std::string& access_token, - int expires_in_seconds) { - refresh_token_ = refresh_token; - // After saving the refresh token, this is just like having just refreshed - // the access token. Just call OnRefreshTokenResponse. - OnRefreshTokenResponse(access_token, expires_in_seconds); -} - -void CloudPrintProxyBackend::Core::OnRefreshTokenResponse( - const std::string& access_token, int expires_in_seconds) { - // If our current token is not OAuth, we either have no token at all or we - // have a ClientLogin token which we just exchanged for an OAuth token. - // In this case we need to do the startup initialiazation. - // TODO(sanjeevr): Use an enum for state instead of using this as a signal. - // I will do this in a follow-up change. - CloudPrintTokenStore* token_store = GetTokenStore(); - bool first_time = !token_store->token_is_oauth(); - token_store->SetToken(access_token, true); - // Let the frontend know that we have authenticated. - backend_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, - &Core::NotifyAuthenticated, refresh_token_, robot_email_, user_email_)); - if (first_time) { - PostAuthInitialization(); - } else { - // If we are refreshing a token, update the XMPP token too. - DCHECK(talk_mediator_.get()); - talk_mediator_->SetAuthToken(robot_email_, - access_token, - kSyncGaiaServiceId); - } - // Schedule a task to refresh the access token again when it is about to - // expire. - DCHECK(expires_in_seconds > kTokenRefreshGracePeriodSecs); - int64 refresh_delay = - (expires_in_seconds - kTokenRefreshGracePeriodSecs)*1000; - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - NewRunnableMethod(this, &Core::RefreshAccessToken), - refresh_delay); -} - -void CloudPrintProxyBackend::Core::OnOAuthError() { - OnAuthError(); -} - -void CloudPrintProxyBackend::Core::OnNetworkError(int response_code) { - // Since we specify inifinite retries on network errors, this should never - // be called. - NOTREACHED() << - "OnNetworkError invoked when not expected, response code is " << - response_code; -} diff --git a/chrome/service/cloud_print/cloud_print_proxy_backend.h b/chrome/service/cloud_print/cloud_print_proxy_backend.h index 8841ade..d66152e 100644 --- a/chrome/service/cloud_print/cloud_print_proxy_backend.h +++ b/chrome/service/cloud_print/cloud_print_proxy_backend.h @@ -58,6 +58,7 @@ class CloudPrintProxyBackend { // use system default settings. CloudPrintProxyBackend( CloudPrintProxyFrontend* frontend, + const std::string& proxy_id, const GURL& cloud_print_server_url, const base::DictionaryValue* print_sys_settings, const gaia::OAuthClientInfo& oauth_client_info, diff --git a/chrome/service/cloud_print/cloud_print_token_store.cc b/chrome/service/cloud_print/cloud_print_token_store.cc index 5c38314..e0bb0b0 100644 --- a/chrome/service/cloud_print/cloud_print_token_store.cc +++ b/chrome/service/cloud_print/cloud_print_token_store.cc @@ -16,7 +16,7 @@ CloudPrintTokenStore* CloudPrintTokenStore::current() { return lazy_tls.Pointer()->Get(); } -CloudPrintTokenStore::CloudPrintTokenStore() : token_is_oauth_(false) { +CloudPrintTokenStore::CloudPrintTokenStore() { lazy_tls.Pointer()->Set(this); } @@ -24,8 +24,7 @@ CloudPrintTokenStore::~CloudPrintTokenStore() { lazy_tls.Pointer()->Set(NULL); } -void CloudPrintTokenStore::SetToken(const std::string& token, bool is_oauth) { +void CloudPrintTokenStore::SetToken(const std::string& token) { DCHECK(CalledOnValidThread()); token_ = token; - token_is_oauth_ = is_oauth; } diff --git a/chrome/service/cloud_print/cloud_print_token_store.h b/chrome/service/cloud_print/cloud_print_token_store.h index b136983..d6b4a4a 100644 --- a/chrome/service/cloud_print/cloud_print_token_store.h +++ b/chrome/service/cloud_print/cloud_print_token_store.h @@ -23,19 +23,14 @@ class CloudPrintTokenStore : public base::NonThreadSafe { CloudPrintTokenStore(); ~CloudPrintTokenStore(); - void SetToken(const std::string& token, bool is_oauth); + void SetToken(const std::string& token); std::string token() const { DCHECK(CalledOnValidThread()); return token_; } - bool token_is_oauth() const { - DCHECK(CalledOnValidThread()); - return token_is_oauth_; - } private: std::string token_; - bool token_is_oauth_; DISALLOW_COPY_AND_ASSIGN(CloudPrintTokenStore); }; diff --git a/chrome/service/cloud_print/cloud_print_token_store_unittest.cc b/chrome/service/cloud_print/cloud_print_token_store_unittest.cc index 9cf9339..1dff537 100644 --- a/chrome/service/cloud_print/cloud_print_token_store_unittest.cc +++ b/chrome/service/cloud_print/cloud_print_token_store_unittest.cc @@ -10,12 +10,8 @@ TEST(CloudPrintTokenStoreTest, Basic) { EXPECT_EQ(NULL, CloudPrintTokenStore::current()); CloudPrintTokenStore* store = new CloudPrintTokenStore; EXPECT_EQ(store, CloudPrintTokenStore::current()); - CloudPrintTokenStore::current()->SetToken("myclientlogintoken", false); + CloudPrintTokenStore::current()->SetToken("myclientlogintoken"); EXPECT_EQ(CloudPrintTokenStore::current()->token(), "myclientlogintoken"); - EXPECT_FALSE(CloudPrintTokenStore::current()->token_is_oauth()); - CloudPrintTokenStore::current()->SetToken("myoauth2token", true); - EXPECT_EQ(CloudPrintTokenStore::current()->token(), "myoauth2token"); - EXPECT_TRUE(CloudPrintTokenStore::current()->token_is_oauth()); delete store; EXPECT_EQ(NULL, CloudPrintTokenStore::current()); } diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher.cc b/chrome/service/cloud_print/cloud_print_url_fetcher.cc index e686685..8cc0700 100644 --- a/chrome/service/cloud_print/cloud_print_url_fetcher.cc +++ b/chrome/service/cloud_print/cloud_print_url_fetcher.cc @@ -20,6 +20,10 @@ CloudPrintURLFetcher::CloudPrintURLFetcher() num_retries_(0) { } +bool CloudPrintURLFetcher::IsSameRequest(const content::URLFetcher* source) { + return (request_.get() == source); +} + void CloudPrintURLFetcher::StartGetRequest( const GURL& url, Delegate* delegate, @@ -65,15 +69,14 @@ void CloudPrintURLFetcher::OnURLFetchComplete( source->GetResponseCode(), source->GetCookies(), data); + + // If we get auth error, notify delegate and check if it wants to proceed. + if (action == CONTINUE_PROCESSING && + source->GetResponseCode() == RC_FORBIDDEN) { + action = delegate_->OnRequestAuthError(); + } + if (action == CONTINUE_PROCESSING) { - // If we are not using an OAuth token, and we got an auth error, we are - // done. Else, the token may have been refreshed. Let us try again. - if ((RC_FORBIDDEN == source->GetResponseCode()) && - (!CloudPrintTokenStore::current() || - !CloudPrintTokenStore::current()->token_is_oauth())) { - delegate_->OnRequestAuthError(); - return; - } // We need to retry on all network errors. if (!source->GetStatus().is_success() || (source->GetResponseCode() != 200)) action = RETRY_REQUEST; @@ -105,6 +108,12 @@ void CloudPrintURLFetcher::OnURLFetchComplete( // be ignored. request_->ReceivedContentWasMalformed(); + // If we receive error code from the server "Media Type Not Supported", + // there is no reason to retry, request will never succeed. + // In that case we should call OnRequestGiveUp() right away. + if (source->GetResponseCode() == RC_UNSUPPORTED_MEDIA_TYPE) + num_retries_ = source->GetMaxRetries(); + ++num_retries_; if ((-1 != source->GetMaxRetries()) && (num_retries_ > source->GetMaxRetries())) { @@ -136,8 +145,8 @@ void CloudPrintURLFetcher::StartRequestHelper( // Since we implement our own retry logic, disable the retry in URLFetcher. request_->SetAutomaticallyRetryOn5xx(false); request_->SetMaxRetries(max_retries); - SetupRequestHeaders(); delegate_ = delegate; + SetupRequestHeaders(); if (request_type == content::URLFetcher::POST) { request_->SetUploadData(post_data_mime_type, post_data); } @@ -146,14 +155,9 @@ void CloudPrintURLFetcher::StartRequestHelper( } void CloudPrintURLFetcher::SetupRequestHeaders() { - std::string headers; - CloudPrintTokenStore* token_store = CloudPrintTokenStore::current(); - if (token_store) { - headers = token_store->token_is_oauth() ? - "Authorization: OAuth " : "Authorization: GoogleLogin auth="; - headers += token_store->token(); + std::string headers = delegate_->GetAuthHeader(); + if (!headers.empty()) headers += "\r\n"; - } headers += kChromeCloudPrintProxyHeader; if (!additional_headers_.empty()) { headers += "\r\n"; diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher.h b/chrome/service/cloud_print/cloud_print_url_fetcher.h index 17e538d..2d26bb8 100644 --- a/chrome/service/cloud_print/cloud_print_url_fetcher.h +++ b/chrome/service/cloud_print/cloud_print_url_fetcher.h @@ -38,6 +38,7 @@ class CloudPrintURLFetcher STOP_PROCESSING, RETRY_REQUEST, }; + class Delegate { public: virtual ~Delegate() { } @@ -80,10 +81,20 @@ class CloudPrintURLFetcher virtual void OnRequestGiveUp() { } // Invoked when the request returns a 403 error (applicable only when // HandleRawResponse returns CONTINUE_PROCESSING). - virtual void OnRequestAuthError() = 0; + // Returning RETRY_REQUEST will retry current request. (auth information + // may have been updated and new info is available through the + // Authenticator interface). + // Returning CONTINUE_PROCESSING will treat auth error as a network error. + virtual ResponseAction OnRequestAuthError() = 0; + + // Authentication information may change between retries. + // CloudPrintURLFetcher will request auth info before sending any request. + virtual std::string GetAuthHeader() = 0; }; CloudPrintURLFetcher(); + bool IsSameRequest(const content::URLFetcher* source); + void StartGetRequest(const GURL& url, Delegate* delegate, int max_retries, diff --git a/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc b/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc index ac2aeab..319ac8c 100644 --- a/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc +++ b/chrome/service/cloud_print/cloud_print_url_fetcher_unittest.cc @@ -84,8 +84,13 @@ class CloudPrintURLFetcherTest : public testing::Test, const net::ResponseCookies& cookies, const std::string& data); - virtual void OnRequestAuthError() { + virtual CloudPrintURLFetcher::ResponseAction OnRequestAuthError() { ADD_FAILURE(); + return CloudPrintURLFetcher::STOP_PROCESSING; + } + + virtual std::string GetAuthHeader() { + return std::string(); } scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy() { diff --git a/chrome/service/cloud_print/job_status_updater.cc b/chrome/service/cloud_print/job_status_updater.cc index 9fffc95..c2d7fc1 100644 --- a/chrome/service/cloud_print/job_status_updater.cc +++ b/chrome/service/cloud_print/job_status_updater.cc @@ -89,7 +89,17 @@ CloudPrintURLFetcher::ResponseAction JobStatusUpdater::HandleJSONData( return CloudPrintURLFetcher::STOP_PROCESSING; } -void JobStatusUpdater::OnRequestAuthError() { +CloudPrintURLFetcher::ResponseAction JobStatusUpdater::OnRequestAuthError() { + // We got an Auth error and have no idea how long it will take to refresh + // auth information (may take forever). We'll drop current request and + // propagate this error to the upper level. After auth issues will be + // resolved, GCP connector will restart. if (delegate_) delegate_->OnAuthError(); + return CloudPrintURLFetcher::STOP_PROCESSING; +} + +std::string JobStatusUpdater::GetAuthHeader() { + return CloudPrintHelpers::GetCloudPrintAuthHeader(); } + diff --git a/chrome/service/cloud_print/job_status_updater.h b/chrome/service/cloud_print/job_status_updater.h index 49b106e..aaacc2c 100644 --- a/chrome/service/cloud_print/job_status_updater.h +++ b/chrome/service/cloud_print/job_status_updater.h @@ -50,7 +50,8 @@ class JobStatusUpdater : public base::RefCountedThreadSafe<JobStatusUpdater>, const GURL& url, base::DictionaryValue* json_data, bool succeeded); - virtual void OnRequestAuthError(); + virtual CloudPrintURLFetcher::ResponseAction OnRequestAuthError(); + virtual std::string GetAuthHeader(); private: std::string printer_name_; diff --git a/chrome/service/cloud_print/printer_job_handler.cc b/chrome/service/cloud_print/printer_job_handler.cc index 3128b2a..36fd51a 100644 --- a/chrome/service/cloud_print/printer_job_handler.cc +++ b/chrome/service/cloud_print/printer_job_handler.cc @@ -51,26 +51,17 @@ PrinterJobHandler::PrinterJobHandler( shutting_down_(false), job_check_pending_(false), printer_update_pending_(true), - printer_delete_pending_(false), task_in_progress_(false) { } bool PrinterJobHandler::Initialize() { - if (print_system_->IsValidPrinter( - printer_info_.printer_name)) { - printer_watcher_ = print_system_->CreatePrinterWatcher( - printer_info_.printer_name); - printer_watcher_->StartWatching(this); - CheckForJobs(kJobFetchReasonStartup); - } else { - // This printer does not exist any more. Check if we should delete it from - // the server. - bool delete_from_server = false; - delegate_->OnPrinterNotFound(printer_info_.printer_name, - &delete_from_server); - if (delete_from_server) - OnPrinterDeleted(); - } + if (!print_system_->IsValidPrinter(printer_info_.printer_name)) + return false; + + printer_watcher_ = print_system_->CreatePrinterWatcher( + printer_info_.printer_name); + printer_watcher_->StartWatching(this); + CheckForJobs(kJobFetchReasonStartup); return true; } @@ -86,8 +77,12 @@ void PrinterJobHandler::Reset() { print_thread_.Stop(); } +std::string PrinterJobHandler::GetPrinterName() const { + return printer_info_.printer_name; +} + void PrinterJobHandler::Start() { - VLOG(1) << "CP_PROXY: Start printer job handler, id: " + VLOG(1) << "CP_CONNECTOR: Start printer job handler, id: " << printer_info_cloud_.printer_id << ", task in progress: " << task_in_progress_; if (task_in_progress_) { @@ -99,18 +94,6 @@ void PrinterJobHandler::Start() { if (!shutting_down_) { // Check if we have work to do. if (HavePendingTasks()) { - if (printer_delete_pending_) { - printer_delete_pending_ = false; - task_in_progress_ = true; - SetNextJSONHandler(&PrinterJobHandler::HandlePrinterDeleteResponse); - request_ = new CloudPrintURLFetcher; - request_->StartGetRequest( - CloudPrintHelpers::GetUrlForPrinterDelete( - cloud_print_server_url_, printer_info_cloud_.printer_id), - this, - kCloudPrintAPIMaxRetryCount, - std::string()); - } if (!task_in_progress_ && printer_update_pending_) { printer_update_pending_ = false; task_in_progress_ = UpdatePrinterInfo(); @@ -139,7 +122,7 @@ void PrinterJobHandler::Start() { } void PrinterJobHandler::Stop() { - VLOG(1) << "CP_PROXY: Stop printer job handler, id: " + VLOG(1) << "CP_CONNECTOR: Stop printer job handler, id: " << printer_info_cloud_.printer_id; task_in_progress_ = false; Reset(); @@ -150,7 +133,7 @@ void PrinterJobHandler::Stop() { } void PrinterJobHandler::CheckForJobs(const std::string& reason) { - VLOG(1) << "CP_PROXY: CheckForJobs, id: " + VLOG(1) << "CP_CONNECTOR: CheckForJobs, id: " << printer_info_cloud_.printer_id << ", reason: " << reason << ", task in progress: " << task_in_progress_; @@ -164,13 +147,13 @@ void PrinterJobHandler::CheckForJobs(const std::string& reason) { bool PrinterJobHandler::UpdatePrinterInfo() { if (!printer_watcher_) { - LOG(ERROR) << "CP_PROXY: Printer watcher is missing." + LOG(ERROR) << "CP_CONNECTOR: Printer watcher is missing." << "Check printer server url for printer id: " << printer_info_cloud_.printer_id; return false; } - VLOG(1) << "CP_PROXY: Update printer info, id: " + VLOG(1) << "CP_CONNECTOR: Update printer info, id: " << printer_info_cloud_.printer_id; // We need to update the parts of the printer info that have changed // (could be printer name, description, status or capabilities). @@ -316,10 +299,7 @@ CloudPrintURLFetcher::ResponseAction PrinterJobHandler::HandleJSONData( DictionaryValue* json_data, bool succeeded) { DCHECK(next_json_data_handler_); - return (this->*next_json_data_handler_)(source, - url, - json_data, - succeeded); + return (this->*next_json_data_handler_)(source, url, json_data, succeeded); } void PrinterJobHandler::OnRequestGiveUp() { @@ -333,8 +313,15 @@ void PrinterJobHandler::OnRequestGiveUp() { JOB_DOWNLOAD_FAILED)); } -void PrinterJobHandler::OnRequestAuthError() { +CloudPrintURLFetcher::ResponseAction PrinterJobHandler::OnRequestAuthError() { + // TODO(gene): We might consider stop processing if we get auth error here. OnAuthError(); + // Continue processing as a network error. + return CloudPrintURLFetcher::CONTINUE_PROCESSING; +} + +std::string PrinterJobHandler::GetAuthHeader() { + return CloudPrintHelpers::GetCloudPrintAuthHeader(); } // JobStatusUpdater::Delegate implementation @@ -357,11 +344,8 @@ void PrinterJobHandler::OnAuthError() { } void PrinterJobHandler::OnPrinterDeleted() { - printer_delete_pending_ = true; - if (!task_in_progress_) { - MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Start)); - } + if (delegate_) + delegate_->OnPrinterDeleted(printer_info_cloud_.printer_id); } void PrinterJobHandler::OnPrinterChanged() { @@ -390,7 +374,7 @@ PrinterJobHandler::HandlePrinterUpdateResponse( const GURL& url, DictionaryValue* json_data, bool succeeded) { - VLOG(1) << "CP_PROXY: Handle printer update response, id: " + VLOG(1) << "CP_CONNECTOR: Handle printer update response, id: " << printer_info_cloud_.printer_id; // We are done here. Go to the Stop state MessageLoop::current()->PostTask( @@ -399,26 +383,12 @@ PrinterJobHandler::HandlePrinterUpdateResponse( } CloudPrintURLFetcher::ResponseAction -PrinterJobHandler::HandlePrinterDeleteResponse( - const content::URLFetcher* source, - const GURL& url, - DictionaryValue* json_data, - bool succeeded) { - VLOG(1) << "CP_PROXY: Handler printer delete response, id: " - << printer_info_cloud_.printer_id; - // The printer has been deleted. Shutdown the handler class. - MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Shutdown)); - return CloudPrintURLFetcher::STOP_PROCESSING; -} - -CloudPrintURLFetcher::ResponseAction PrinterJobHandler::HandleJobMetadataResponse( const content::URLFetcher* source, const GURL& url, DictionaryValue* json_data, bool succeeded) { - VLOG(1) << "CP_PROXY: Handle job metadata response, id: " + VLOG(1) << "CP_CONNECTOR: Handle job metadata response, id: " << printer_info_cloud_.printer_id; bool job_available = false; if (succeeded) { @@ -465,7 +435,7 @@ CloudPrintURLFetcher::ResponseAction PrinterJobHandler::HandlePrintTicketResponse(const content::URLFetcher* source, const GURL& url, const std::string& data) { - VLOG(1) << "CP_PROXY: Handle print ticket response, id: " + VLOG(1) << "CP_CONNECTOR: Handle print ticket response, id: " << printer_info_cloud_.printer_id; if (print_system_->ValidatePrintTicket(printer_info_.printer_name, data)) { job_details_.print_ticket_ = data; @@ -488,7 +458,7 @@ CloudPrintURLFetcher::ResponseAction PrinterJobHandler::HandlePrintDataResponse(const content::URLFetcher* source, const GURL& url, const std::string& data) { - VLOG(1) << "CP_PROXY: Handle print data response, id: " + VLOG(1) << "CP_CONNECTOR: Handle print data response, id: " << printer_info_cloud_.printer_id; Task* next_task = NULL; if (file_util::CreateTemporaryFile(&job_details_.print_data_file_path_)) { @@ -518,7 +488,7 @@ PrinterJobHandler::HandleSuccessStatusUpdateResponse( const GURL& url, DictionaryValue* json_data, bool succeeded) { - VLOG(1) << "CP_PROXY: Handle success status update response, id: " + VLOG(1) << "CP_CONNECTOR: Handle success status update response, id: " << printer_info_cloud_.printer_id; // The print job has been spooled locally. We now need to create an object // that monitors the status of the job and updates the server. @@ -545,7 +515,7 @@ PrinterJobHandler::HandleFailureStatusUpdateResponse( const GURL& url, DictionaryValue* json_data, bool succeeded) { - VLOG(1) << "CP_PROXY: Handle failure status update response, id: " + VLOG(1) << "CP_CONNECTOR: Handle failure status update response, id: " << printer_info_cloud_.printer_id; MessageLoop::current()->PostTask( FROM_HERE, NewRunnableMethod(this, &PrinterJobHandler::Stop)); @@ -554,7 +524,8 @@ PrinterJobHandler::HandleFailureStatusUpdateResponse( // End Response handlers void PrinterJobHandler::StartPrinting() { - VLOG(1) << "CP_PROXY: Start printing, id: " << printer_info_cloud_.printer_id; + VLOG(1) << "CP_CONNECTOR: Start printing, id: " + << printer_info_cloud_.printer_id; // We are done with the request object for now. request_ = NULL; if (!shutting_down_) { @@ -570,14 +541,14 @@ void PrinterJobHandler::StartPrinting() { } void PrinterJobHandler::JobFailed(PrintJobError error) { - VLOG(1) << "CP_PROXY: Job failed, id: " << printer_info_cloud_.printer_id; + VLOG(1) << "CP_CONNECTOR: Job failed, id: " << printer_info_cloud_.printer_id; if (!shutting_down_) { UpdateJobStatus(cloud_print::PRINT_JOB_STATUS_ERROR, error); } } void PrinterJobHandler::JobSpooled(cloud_print::PlatformJobId local_job_id) { - VLOG(1) << "CP_PROXY: Job spooled, printer id: " + VLOG(1) << "CP_CONNECTOR: Job spooled, printer id: " << printer_info_cloud_.printer_id << ", job id: " << local_job_id; if (!shutting_down_) { local_job_id_ = local_job_id; @@ -587,7 +558,7 @@ void PrinterJobHandler::JobSpooled(cloud_print::PlatformJobId local_job_id) { } void PrinterJobHandler::Shutdown() { - VLOG(1) << "CP_PROXY: Printer job handler shutdown, id: " + VLOG(1) << "CP_CONNECTOR: Printer job handler shutdown, id: " << printer_info_cloud_.printer_id; Reset(); shutting_down_ = true; @@ -596,20 +567,16 @@ void PrinterJobHandler::Shutdown() { // remove the updater object from the list. job_status_updater_list_.front()->Stop(); } - if (delegate_) { - delegate_->OnPrinterJobHandlerShutdown(this, - printer_info_cloud_.printer_id); - } } void PrinterJobHandler::UpdateJobStatus(cloud_print::PrintJobStatus status, PrintJobError error) { - VLOG(1) << "CP_PROXY: Update job status, id: " + VLOG(1) << "CP_CONNECTOR: Update job status, id: " << printer_info_cloud_.printer_id; if (!shutting_down_) { if (!job_details_.job_id_.empty()) { - VLOG(1) << "CP_PROXY: Updating status, job id: " << job_details_.job_id_ - << ", status: " << status; + VLOG(1) << "CP_CONNECTOR: Updating status, job id: " + << job_details_.job_id_ << ", status: " << status; if (error == SUCCESS) { SetNextJSONHandler( &PrinterJobHandler::HandleSuccessStatusUpdateResponse); @@ -640,13 +607,12 @@ void PrinterJobHandler::SetNextDataHandler(DataHandler handler) { } bool PrinterJobHandler::HavePendingTasks() { - return (job_check_pending_ || printer_update_pending_ || - printer_delete_pending_); + return (job_check_pending_ || printer_update_pending_); } void PrinterJobHandler::FailedFetchingJobData() { if (!shutting_down_) { - LOG(ERROR) << "CP_PROXY: Failed fetching job data for printer: " << + LOG(ERROR) << "CP_CONNECTOR: Failed fetching job data for printer: " << printer_info_.printer_name << ", job id: " << job_details_.job_id_; JobFailed(INVALID_JOB_DATA); } diff --git a/chrome/service/cloud_print/printer_job_handler.h b/chrome/service/cloud_print/printer_job_handler.h index 1db7171..03b50f7 100644 --- a/chrome/service/cloud_print/printer_job_handler.h +++ b/chrome/service/cloud_print/printer_job_handler.h @@ -33,12 +33,12 @@ class URLFetcher; // | Have Pending tasks // | // | -// <----Delete Pending -- | ---Update Pending-----> -// | | | -// | | | -// | | | -// Delete Printer from server | Update Printer info on server -// Shutdown | Go to Stop +// | ---Update Pending-----> +// | | +// | | +// | | +// | Update Printer info on server +// | Go to Stop // | // | Job Available // | @@ -88,14 +88,10 @@ class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, public: class Delegate { public: - virtual void OnPrinterJobHandlerShutdown( - PrinterJobHandler* job_handler, const std::string& printer_id) = 0; + // Notify delegate about authentication error. virtual void OnAuthError() = 0; - // Called when the PrinterJobHandler cannot find the printer locally. The - // delegate returns |delete_from_server| to true if the printer should be - // deleted from the server,false otherwise. - virtual void OnPrinterNotFound(const std::string& printer_name, - bool* delete_from_server) = 0; + // Notify delegate that printer has been deleted. + virtual void OnPrinterDeleted(const std::string& printer_name) = 0; protected: virtual ~Delegate() {} @@ -115,6 +111,7 @@ class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, Delegate* delegate); virtual ~PrinterJobHandler(); bool Initialize(); + std::string GetPrinterName() const; // Requests a job check. |reason| is the reason for fetching the job. Used // for logging and diagnostc purposes. void CheckForJobs(const std::string& reason); @@ -143,7 +140,8 @@ class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, base::DictionaryValue* json_data, bool succeeded); virtual void OnRequestGiveUp(); - virtual void OnRequestAuthError(); + virtual CloudPrintURLFetcher::ResponseAction OnRequestAuthError(); + virtual std::string GetAuthHeader(); // JobStatusUpdater::Delegate implementation virtual bool OnJobCompleted(JobStatusUpdater* updater); @@ -180,12 +178,6 @@ class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, base::DictionaryValue* json_data, bool succeeded); - CloudPrintURLFetcher::ResponseAction HandlePrinterDeleteResponse( - const content::URLFetcher* source, - const GURL& url, - base::DictionaryValue* json_data, - bool succeeded); - CloudPrintURLFetcher::ResponseAction HandleJobMetadataResponse( const content::URLFetcher* source, const GURL& url, @@ -288,7 +280,6 @@ class PrinterJobHandler : public base::RefCountedThreadSafe<PrinterJobHandler>, // Flags that specify various pending server updates bool job_check_pending_; bool printer_update_pending_; - bool printer_delete_pending_; // Some task in the state machine is in progress. bool task_in_progress_; |