// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/local_discovery/privet_http_impl.h" #include #include #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/rand_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/local_discovery/privet_constants.h" #include "components/cloud_devices/common/printer_description.h" #include "net/base/url_util.h" #include "printing/pwg_raster_settings.h" #include "printing/units.h" #include "ui/gfx/text_elider.h" #include "url/gurl.h" #if defined(ENABLE_PRINT_PREVIEW) #include "chrome/browser/local_discovery/pwg_raster_converter.h" #endif // ENABLE_PRINT_PREVIEW using namespace cloud_devices::printer; namespace cloud_print { extern const char kContentTypeJSON[]; } namespace local_discovery { namespace { const char kUrlPlaceHolder[] = "http://host/"; const char kPrivetRegisterActionArgName[] = "action"; const char kPrivetRegisterUserArgName[] = "user"; const int kPrivetCancelationTimeoutSeconds = 3; #if defined(ENABLE_PRINT_PREVIEW) const char kPrivetURLKeyUserName[] = "user_name"; const char kPrivetURLKeyClientName[] = "client_name"; const char kPrivetURLKeyJobname[] = "job_name"; const char kPrivetURLKeyOffline[] = "offline"; const char kPrivetURLValueOffline[] = "1"; const char kPrivetURLValueClientName[] = "Chrome"; const char kPrivetContentTypePDF[] = "application/pdf"; const char kPrivetContentTypePWGRaster[] = "image/pwg-raster"; const char kPrivetContentTypeAny[] = "*/*"; const char kPrivetKeyJobID[] = "job_id"; const int kPrivetLocalPrintMaxRetries = 2; const int kPrivetLocalPrintDefaultTimeout = 5; const size_t kPrivetLocalPrintMaxJobNameLength = 64; #endif // ENABLE_PRINT_PREVIEW GURL CreatePrivetURL(const std::string& path) { GURL url(kUrlPlaceHolder); GURL::Replacements replacements; replacements.SetPathStr(path); return url.ReplaceComponents(replacements); } GURL CreatePrivetRegisterURL(const std::string& action, const std::string& user) { GURL url = CreatePrivetURL(kPrivetRegisterPath); url = net::AppendQueryParameter(url, kPrivetRegisterActionArgName, action); return net::AppendQueryParameter(url, kPrivetRegisterUserArgName, user); } GURL CreatePrivetParamURL(const std::string& path, const std::string& query_params) { GURL url(kUrlPlaceHolder); GURL::Replacements replacements; replacements.SetPathStr(path); if (!query_params.empty()) { replacements.SetQueryStr(query_params); } return url.ReplaceComponents(replacements); } } // namespace PrivetInfoOperationImpl::PrivetInfoOperationImpl( PrivetHTTPClient* privet_client, const PrivetJSONOperation::ResultCallback& callback) : privet_client_(privet_client), callback_(callback) { } PrivetInfoOperationImpl::~PrivetInfoOperationImpl() { } void PrivetInfoOperationImpl::Start() { url_fetcher_ = privet_client_->CreateURLFetcher( CreatePrivetURL(kPrivetInfoPath), net::URLFetcher::GET, this); url_fetcher_->DoNotRetryOnTransientError(); url_fetcher_->SendEmptyPrivetToken(); url_fetcher_->Start(); } PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() { return privet_client_; } void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher, PrivetURLFetcher::ErrorType error) { callback_.Run(NULL); } void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher, const base::DictionaryValue& value, bool has_error) { callback_.Run(&value); } PrivetRegisterOperationImpl::PrivetRegisterOperationImpl( PrivetHTTPClient* privet_client, const std::string& user, PrivetRegisterOperation::Delegate* delegate) : user_(user), delegate_(delegate), privet_client_(privet_client), ongoing_(false) { } PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() { } void PrivetRegisterOperationImpl::Start() { ongoing_ = true; next_response_handler_ = base::Bind(&PrivetRegisterOperationImpl::StartResponse, base::Unretained(this)); SendRequest(kPrivetActionStart); } void PrivetRegisterOperationImpl::Cancel() { url_fetcher_.reset(); if (ongoing_) { // Owned by the message loop. Cancelation* cancelation = new Cancelation(privet_client_, user_); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&PrivetRegisterOperationImpl::Cancelation::Cleanup, base::Owned(cancelation)), base::TimeDelta::FromSeconds(kPrivetCancelationTimeoutSeconds)); ongoing_ = false; } } void PrivetRegisterOperationImpl::CompleteRegistration() { next_response_handler_ = base::Bind(&PrivetRegisterOperationImpl::CompleteResponse, base::Unretained(this)); SendRequest(kPrivetActionComplete); } PrivetHTTPClient* PrivetRegisterOperationImpl::GetHTTPClient() { return privet_client_; } void PrivetRegisterOperationImpl::OnError(PrivetURLFetcher* fetcher, PrivetURLFetcher::ErrorType error) { ongoing_ = false; int visible_http_code = -1; FailureReason reason = FAILURE_NETWORK; if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) { visible_http_code = fetcher->response_code(); reason = FAILURE_HTTP_ERROR; } else if (error == PrivetURLFetcher::JSON_PARSE_ERROR) { reason = FAILURE_MALFORMED_RESPONSE; } else if (error == PrivetURLFetcher::TOKEN_ERROR) { reason = FAILURE_TOKEN; } else if (error == PrivetURLFetcher::RETRY_ERROR) { reason = FAILURE_RETRY; } delegate_->OnPrivetRegisterError(this, current_action_, reason, visible_http_code, NULL); } void PrivetRegisterOperationImpl::OnParsedJson( PrivetURLFetcher* fetcher, const base::DictionaryValue& value, bool has_error) { if (has_error) { std::string error; value.GetString(kPrivetKeyError, &error); ongoing_ = false; delegate_->OnPrivetRegisterError(this, current_action_, FAILURE_JSON_ERROR, fetcher->response_code(), &value); return; } // TODO(noamsml): Match the user&action with the user&action in the object, // and fail if different. next_response_handler_.Run(value); } void PrivetRegisterOperationImpl::OnNeedPrivetToken( PrivetURLFetcher* fetcher, const PrivetURLFetcher::TokenCallback& callback) { privet_client_->RefreshPrivetToken(callback); } void PrivetRegisterOperationImpl::SendRequest(const std::string& action) { current_action_ = action; url_fetcher_ = privet_client_->CreateURLFetcher( CreatePrivetRegisterURL(action, user_), net::URLFetcher::POST, this); url_fetcher_->Start(); } void PrivetRegisterOperationImpl::StartResponse( const base::DictionaryValue& value) { next_response_handler_ = base::Bind(&PrivetRegisterOperationImpl::GetClaimTokenResponse, base::Unretained(this)); SendRequest(kPrivetActionGetClaimToken); } void PrivetRegisterOperationImpl::GetClaimTokenResponse( const base::DictionaryValue& value) { std::string claimUrl; std::string claimToken; bool got_url = value.GetString(kPrivetKeyClaimURL, &claimUrl); bool got_token = value.GetString(kPrivetKeyClaimToken, &claimToken); if (got_url || got_token) { delegate_->OnPrivetRegisterClaimToken(this, claimToken, GURL(claimUrl)); } else { delegate_->OnPrivetRegisterError(this, current_action_, FAILURE_MALFORMED_RESPONSE, -1, NULL); } } void PrivetRegisterOperationImpl::CompleteResponse( const base::DictionaryValue& value) { std::string id; value.GetString(kPrivetKeyDeviceID, &id); ongoing_ = false; expected_id_ = id; StartInfoOperation(); } void PrivetRegisterOperationImpl::OnPrivetInfoDone( const base::DictionaryValue* value) { // TODO(noamsml): Simplify error case and depracate HTTP error value in // OnPrivetRegisterError. if (!value) { delegate_->OnPrivetRegisterError(this, kPrivetActionNameInfo, FAILURE_NETWORK, -1, NULL); return; } if (!value->HasKey(kPrivetInfoKeyID)) { if (value->HasKey(kPrivetKeyError)) { delegate_->OnPrivetRegisterError(this, kPrivetActionNameInfo, FAILURE_JSON_ERROR, -1, value); } else { delegate_->OnPrivetRegisterError(this, kPrivetActionNameInfo, FAILURE_MALFORMED_RESPONSE, -1, NULL); } return; } std::string id; if (!value->GetString(kPrivetInfoKeyID, &id) || id != expected_id_) { delegate_->OnPrivetRegisterError(this, kPrivetActionNameInfo, FAILURE_MALFORMED_RESPONSE, -1, NULL); } else { delegate_->OnPrivetRegisterDone(this, id); } } void PrivetRegisterOperationImpl::StartInfoOperation() { info_operation_ = privet_client_->CreateInfoOperation( base::Bind(&PrivetRegisterOperationImpl::OnPrivetInfoDone, base::Unretained(this))); info_operation_->Start(); } PrivetRegisterOperationImpl::Cancelation::Cancelation( PrivetHTTPClient* privet_client, const std::string& user) { url_fetcher_ = privet_client->CreateURLFetcher( CreatePrivetRegisterURL(kPrivetActionCancel, user), net::URLFetcher::POST, this); url_fetcher_->DoNotRetryOnTransientError(); url_fetcher_->Start(); } PrivetRegisterOperationImpl::Cancelation::~Cancelation() { } void PrivetRegisterOperationImpl::Cancelation::OnError( PrivetURLFetcher* fetcher, PrivetURLFetcher::ErrorType error) { } void PrivetRegisterOperationImpl::Cancelation::OnParsedJson( PrivetURLFetcher* fetcher, const base::DictionaryValue& value, bool has_error) { } void PrivetRegisterOperationImpl::Cancelation::Cleanup() { // Nothing needs to be done, as base::Owned will delete this object, // this callback is just here to pass ownership of the Cancelation to // the message loop. } PrivetJSONOperationImpl::PrivetJSONOperationImpl( PrivetHTTPClient* privet_client, const std::string& path, const std::string& query_params, const PrivetJSONOperation::ResultCallback& callback) : privet_client_(privet_client), path_(path), query_params_(query_params), callback_(callback) { } PrivetJSONOperationImpl::~PrivetJSONOperationImpl() { } void PrivetJSONOperationImpl::Start() { url_fetcher_ = privet_client_->CreateURLFetcher( CreatePrivetParamURL(path_, query_params_), net::URLFetcher::GET, this); url_fetcher_->DoNotRetryOnTransientError(); url_fetcher_->Start(); } PrivetHTTPClient* PrivetJSONOperationImpl::GetHTTPClient() { return privet_client_; } void PrivetJSONOperationImpl::OnError( PrivetURLFetcher* fetcher, PrivetURLFetcher::ErrorType error) { callback_.Run(NULL); } void PrivetJSONOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher, const base::DictionaryValue& value, bool has_error) { callback_.Run(&value); } void PrivetJSONOperationImpl::OnNeedPrivetToken( PrivetURLFetcher* fetcher, const PrivetURLFetcher::TokenCallback& callback) { privet_client_->RefreshPrivetToken(callback); } PrivetDataReadOperationImpl::PrivetDataReadOperationImpl( PrivetHTTPClient* privet_client, const std::string& path, const std::string& query_params, const PrivetDataReadOperation::ResultCallback& callback) : privet_client_(privet_client), path_(path), query_params_(query_params), callback_(callback), has_range_(false), save_to_file_(false) { } PrivetDataReadOperationImpl::~PrivetDataReadOperationImpl() { } void PrivetDataReadOperationImpl::Start() { url_fetcher_ = privet_client_->CreateURLFetcher( CreatePrivetParamURL(path_, query_params_), net::URLFetcher::GET, this); url_fetcher_->DoNotRetryOnTransientError(); if (has_range_) { url_fetcher_->SetByteRange(range_start_, range_end_); } if (save_to_file_) { url_fetcher_->SaveResponseToFile(); } url_fetcher_->Start(); } void PrivetDataReadOperationImpl::SetDataRange(int range_start, int range_end) { has_range_ = true; range_start_ = range_start; range_end_ = range_end; } void PrivetDataReadOperationImpl::SaveDataToFile() { save_to_file_ = false; } PrivetHTTPClient* PrivetDataReadOperationImpl::GetHTTPClient() { return privet_client_; } void PrivetDataReadOperationImpl::OnError( PrivetURLFetcher* fetcher, PrivetURLFetcher::ErrorType error) { callback_.Run(RESPONSE_TYPE_ERROR, std::string(), base::FilePath()); } void PrivetDataReadOperationImpl::OnParsedJson( PrivetURLFetcher* fetcher, const base::DictionaryValue& value, bool has_error) { NOTREACHED(); } void PrivetDataReadOperationImpl::OnNeedPrivetToken( PrivetURLFetcher* fetcher, const PrivetURLFetcher::TokenCallback& callback) { privet_client_->RefreshPrivetToken(callback); } bool PrivetDataReadOperationImpl::OnRawData(PrivetURLFetcher* fetcher, bool is_file, const std::string& data_str, const base::FilePath& file_path) { ResponseType type = (is_file) ? RESPONSE_TYPE_FILE : RESPONSE_TYPE_STRING; callback_.Run(type, data_str, file_path); return true; } #if defined(ENABLE_PRINT_PREVIEW) PrivetLocalPrintOperationImpl::PrivetLocalPrintOperationImpl( PrivetHTTPClient* privet_client, PrivetLocalPrintOperation::Delegate* delegate) : privet_client_(privet_client), delegate_(delegate), use_pdf_(false), has_extended_workflow_(false), started_(false), offline_(false), dpi_(printing::kDefaultPdfDpi), invalid_job_retries_(0), weak_factory_(this) { } PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() { } void PrivetLocalPrintOperationImpl::Start() { DCHECK(!started_); // We need to get the /info response so we can know which APIs are available. // TODO(noamsml): Use cached info when available. info_operation_ = privet_client_->CreateInfoOperation( base::Bind(&PrivetLocalPrintOperationImpl::OnPrivetInfoDone, base::Unretained(this))); info_operation_->Start(); started_ = true; } void PrivetLocalPrintOperationImpl::OnPrivetInfoDone( const base::DictionaryValue* value) { if (value && !value->HasKey(kPrivetKeyError)) { has_extended_workflow_ = false; bool has_printing = false; const base::ListValue* api_list; if (value->GetList(kPrivetInfoKeyAPIList, &api_list)) { for (size_t i = 0; i < api_list->GetSize(); i++) { std::string api; api_list->GetString(i, &api); if (api == kPrivetSubmitdocPath) { has_printing = true; } else if (api == kPrivetCreatejobPath) { has_extended_workflow_ = true; } } } if (!has_printing) { delegate_->OnPrivetPrintingError(this, -1); return; } StartInitialRequest(); } else { delegate_->OnPrivetPrintingError(this, -1); } } void PrivetLocalPrintOperationImpl::StartInitialRequest() { use_pdf_ = false; ContentTypesCapability content_types; if (content_types.LoadFrom(capabilities_)) { use_pdf_ = content_types.Contains(kPrivetContentTypePDF) || content_types.Contains(kPrivetContentTypeAny); } if (use_pdf_) { StartPrinting(); } else { DpiCapability dpis; if (dpis.LoadFrom(capabilities_)) { dpi_ = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical); } StartConvertToPWG(); } } void PrivetLocalPrintOperationImpl::DoCreatejob() { current_response_ = base::Bind( &PrivetLocalPrintOperationImpl::OnCreatejobResponse, base::Unretained(this)); url_fetcher_= privet_client_->CreateURLFetcher( CreatePrivetURL(kPrivetCreatejobPath), net::URLFetcher::POST, this); url_fetcher_->SetUploadData(cloud_print::kContentTypeJSON, ticket_.ToString()); url_fetcher_->Start(); } void PrivetLocalPrintOperationImpl::DoSubmitdoc() { current_response_ = base::Bind( &PrivetLocalPrintOperationImpl::OnSubmitdocResponse, base::Unretained(this)); GURL url = CreatePrivetURL(kPrivetSubmitdocPath); url = net::AppendQueryParameter(url, kPrivetURLKeyClientName, kPrivetURLValueClientName); if (!user_.empty()) { url = net::AppendQueryParameter(url, kPrivetURLKeyUserName, user_); } base::string16 shortened_jobname; gfx::ElideString(base::UTF8ToUTF16(jobname_), kPrivetLocalPrintMaxJobNameLength, &shortened_jobname); if (!jobname_.empty()) { url = net::AppendQueryParameter( url, kPrivetURLKeyJobname, base::UTF16ToUTF8(shortened_jobname)); } if (!jobid_.empty()) { url = net::AppendQueryParameter(url, kPrivetKeyJobID, jobid_); } if (offline_) { url = net::AppendQueryParameter(url, kPrivetURLKeyOffline, kPrivetURLValueOffline); } url_fetcher_= privet_client_->CreateURLFetcher( url, net::URLFetcher::POST, this); if (!use_pdf_) { url_fetcher_->SetUploadFilePath(kPrivetContentTypePWGRaster, pwg_file_path_); } else { // TODO(noamsml): Move to file-based upload data? std::string data_str((const char*)data_->front(), data_->size()); url_fetcher_->SetUploadData(kPrivetContentTypePDF, data_str); } url_fetcher_->Start(); } void PrivetLocalPrintOperationImpl::StartPrinting() { if (has_extended_workflow_ && jobid_.empty()) { DoCreatejob(); } else { DoSubmitdoc(); } } void PrivetLocalPrintOperationImpl::FillPwgRasterSettings( printing::PwgRasterSettings* transform_settings) { PwgRasterConfigCapability raster_capability; // If the raster capability fails to load, raster_capability will contain // the default value. raster_capability.LoadFrom(capabilities_); DuplexTicketItem duplex_item; DuplexType duplex_value = NO_DUPLEX; DocumentSheetBack document_sheet_back = raster_capability.value().document_sheet_back; if (duplex_item.LoadFrom(ticket_)) { duplex_value = duplex_item.value(); } transform_settings->odd_page_transform = printing::TRANSFORM_NORMAL; switch (duplex_value) { case NO_DUPLEX: transform_settings->odd_page_transform = printing::TRANSFORM_NORMAL; break; case LONG_EDGE: if (document_sheet_back == ROTATED) { transform_settings->odd_page_transform = printing::TRANSFORM_ROTATE_180; } else if (document_sheet_back == FLIPPED) { transform_settings->odd_page_transform = printing::TRANSFORM_FLIP_VERTICAL; } break; case SHORT_EDGE: if (document_sheet_back == MANUAL_TUMBLE) { transform_settings->odd_page_transform = printing::TRANSFORM_ROTATE_180; } else if (document_sheet_back == FLIPPED) { transform_settings->odd_page_transform = printing::TRANSFORM_FLIP_HORIZONTAL; } } transform_settings->rotate_all_pages = raster_capability.value().rotate_all_pages; transform_settings->reverse_page_order = raster_capability.value().reverse_order_streaming; } void PrivetLocalPrintOperationImpl::StartConvertToPWG() { printing::PwgRasterSettings transform_settings; FillPwgRasterSettings(&transform_settings); if (!pwg_raster_converter_) pwg_raster_converter_ = PWGRasterConverter::CreateDefault(); double scale = dpi_; scale /= printing::kPointsPerInch; // Make vertical rectangle to optimize streaming to printer. Fix orientation // by autorotate. gfx::Rect area(std::min(page_size_.width(), page_size_.height()) * scale, std::max(page_size_.width(), page_size_.height()) * scale); pwg_raster_converter_->Start( data_.get(), printing::PdfRenderSettings(area, dpi_, true), transform_settings, base::Bind(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted, base::Unretained(this))); } void PrivetLocalPrintOperationImpl::OnSubmitdocResponse( bool has_error, const base::DictionaryValue* value) { std::string error; // This error is only relevant in the case of extended workflow: // If the print job ID is invalid, retry createjob and submitdoc, // rather than simply retrying the current request. if (has_error && value->GetString(kPrivetKeyError, &error)) { if (has_extended_workflow_ && error == kPrivetErrorInvalidPrintJob && invalid_job_retries_ < kPrivetLocalPrintMaxRetries) { invalid_job_retries_++; int timeout = kPrivetLocalPrintDefaultTimeout; value->GetInteger(kPrivetKeyTimeout, &timeout); double random_scaling_factor = 1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition; timeout = static_cast(timeout * random_scaling_factor); timeout = std::max(timeout, kPrivetMinimumTimeout); base::MessageLoop::current()->PostDelayedTask( FROM_HERE, base::Bind(&PrivetLocalPrintOperationImpl::DoCreatejob, weak_factory_.GetWeakPtr()), base::TimeDelta::FromSeconds(timeout)); } else if (use_pdf_ && error == kPrivetErrorInvalidDocumentType) { use_pdf_ = false; StartConvertToPWG(); } else { delegate_->OnPrivetPrintingError(this, 200); } return; } // If we've gotten this far, there are no errors, so we've effectively // succeeded. delegate_->OnPrivetPrintingDone(this); } void PrivetLocalPrintOperationImpl::OnCreatejobResponse( bool has_error, const base::DictionaryValue* value) { if (has_error) { delegate_->OnPrivetPrintingError(this, 200); return; } // Try to get job ID from value. If not, jobid_ will be empty and we will use // simple printing. value->GetString(kPrivetKeyJobID, &jobid_); DoSubmitdoc(); } void PrivetLocalPrintOperationImpl::OnPWGRasterConverted( bool success, const base::FilePath& pwg_file_path) { if (!success) { delegate_->OnPrivetPrintingError(this, -1); return; } DCHECK(!pwg_file_path.empty()); pwg_file_path_ = pwg_file_path; StartPrinting(); } PrivetHTTPClient* PrivetLocalPrintOperationImpl::GetHTTPClient() { return privet_client_; } void PrivetLocalPrintOperationImpl::OnError( PrivetURLFetcher* fetcher, PrivetURLFetcher::ErrorType error) { delegate_->OnPrivetPrintingError(this, -1); } void PrivetLocalPrintOperationImpl::OnParsedJson( PrivetURLFetcher* fetcher, const base::DictionaryValue& value, bool has_error) { DCHECK(!current_response_.is_null()); current_response_.Run(has_error, &value); } void PrivetLocalPrintOperationImpl::OnNeedPrivetToken( PrivetURLFetcher* fetcher, const PrivetURLFetcher::TokenCallback& callback) { privet_client_->RefreshPrivetToken(callback); } void PrivetLocalPrintOperationImpl::SetData( const scoped_refptr& data) { DCHECK(!started_); data_ = data; } void PrivetLocalPrintOperationImpl::SetTicket(const std::string& ticket) { DCHECK(!started_); ticket_.InitFromString(ticket); } void PrivetLocalPrintOperationImpl::SetCapabilities( const std::string& capabilities) { DCHECK(!started_); capabilities_.InitFromString(capabilities); } void PrivetLocalPrintOperationImpl::SetUsername(const std::string& user) { DCHECK(!started_); user_= user; } void PrivetLocalPrintOperationImpl::SetJobname(const std::string& jobname) { DCHECK(!started_); jobname_ = jobname; } void PrivetLocalPrintOperationImpl::SetOffline(bool offline) { DCHECK(!started_); offline_ = offline; } void PrivetLocalPrintOperationImpl::SetPageSize(const gfx::Size& page_size) { DCHECK(!started_); page_size_ = page_size; } void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting( scoped_ptr pwg_raster_converter) { pwg_raster_converter_ = pwg_raster_converter.Pass(); } #endif // ENABLE_PRINT_PREVIEW PrivetHTTPClientImpl::PrivetHTTPClientImpl( const std::string& name, const net::HostPortPair& host_port, net::URLRequestContextGetter* request_context) : name_(name), request_context_(request_context), host_port_(host_port) {} PrivetHTTPClientImpl::~PrivetHTTPClientImpl() { } const std::string& PrivetHTTPClientImpl::GetName() { return name_; } scoped_ptr PrivetHTTPClientImpl::CreateInfoOperation( const PrivetJSONOperation::ResultCallback& callback) { return scoped_ptr( new PrivetInfoOperationImpl(this, callback)); } scoped_ptr PrivetHTTPClientImpl::CreateURLFetcher( const GURL& url, net::URLFetcher::RequestType request_type, PrivetURLFetcher::Delegate* delegate) { GURL::Replacements replacements; replacements.SetHostStr(host_port_.host()); std::string port(base::IntToString(host_port_.port())); // Keep string alive. replacements.SetPortStr(port); return scoped_ptr( new PrivetURLFetcher(url.ReplaceComponents(replacements), request_type, request_context_.get(), delegate)); } void PrivetHTTPClientImpl::RefreshPrivetToken( const PrivetURLFetcher::TokenCallback& callback) { token_callbacks_.push_back(callback); if (!info_operation_) { info_operation_ = CreateInfoOperation( base::Bind(&PrivetHTTPClientImpl::OnPrivetInfoDone, base::Unretained(this))); info_operation_->Start(); } } void PrivetHTTPClientImpl::OnPrivetInfoDone( const base::DictionaryValue* value) { info_operation_.reset(); std::string token; // If this does not succeed, token will be empty, and an empty string // is our sentinel value, since empty X-Privet-Tokens are not allowed. if (value) { value->GetString(kPrivetInfoKeyToken, &token); } TokenCallbackVector token_callbacks; token_callbacks_.swap(token_callbacks); for (TokenCallbackVector::iterator i = token_callbacks.begin(); i != token_callbacks.end(); i++) { i->Run(token); } } PrivetV1HTTPClientImpl::PrivetV1HTTPClientImpl( scoped_ptr info_client) : info_client_(info_client.Pass()) { } PrivetV1HTTPClientImpl::~PrivetV1HTTPClientImpl() { } const std::string& PrivetV1HTTPClientImpl::GetName() { return info_client()->GetName(); } scoped_ptr PrivetV1HTTPClientImpl::CreateInfoOperation( const PrivetJSONOperation::ResultCallback& callback) { return info_client()->CreateInfoOperation(callback); } scoped_ptr PrivetV1HTTPClientImpl::CreateRegisterOperation( const std::string& user, PrivetRegisterOperation::Delegate* delegate) { return scoped_ptr( new PrivetRegisterOperationImpl(info_client(), user, delegate)); } scoped_ptr PrivetV1HTTPClientImpl::CreateCapabilitiesOperation( const PrivetJSONOperation::ResultCallback& callback) { return scoped_ptr(new PrivetJSONOperationImpl( info_client(), kPrivetCapabilitiesPath, "", callback)); } scoped_ptr PrivetV1HTTPClientImpl::CreateLocalPrintOperation( PrivetLocalPrintOperation::Delegate* delegate) { #if defined(ENABLE_PRINT_PREVIEW) return scoped_ptr( new PrivetLocalPrintOperationImpl(info_client(), delegate)); #else return scoped_ptr(); #endif // ENABLE_PRINT_PREVIEW } } // namespace local_discovery