privet_http_impl.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/local_discovery/privet_http_impl.h" 6 7#include <algorithm> 8#include <vector> 9 10#include "base/bind.h" 11#include "base/message_loop/message_loop.h" 12#include "base/rand_util.h" 13#include "base/strings/string_number_conversions.h" 14#include "base/strings/stringprintf.h" 15#include "chrome/browser/local_discovery/privet_constants.h" 16#include "components/cloud_devices/printer_description.h" 17#include "net/base/url_util.h" 18#include "printing/units.h" 19#include "url/gurl.h" 20 21namespace local_discovery { 22 23namespace { 24const char kUrlPlaceHolder[] = "http://host/"; 25const char kPrivetRegisterActionArgName[] = "action"; 26const char kPrivetRegisterUserArgName[] = "user"; 27 28const char kPrivetURLKeyUserName[] = "user_name"; 29const char kPrivetURLKeyClientName[] = "client_name"; 30const char kPrivetURLKeyJobname[] = "job_name"; 31const char kPrivetURLKeyOffline[] = "offline"; 32const char kPrivetURLValueOffline[] = "1"; 33const char kPrivetURLValueClientName[] = "Chrome"; 34 35const char kPrivetContentTypePDF[] = "application/pdf"; 36const char kPrivetContentTypePWGRaster[] = "image/pwg-raster"; 37const char kPrivetContentTypeAny[] = "*/*"; 38const char kPrivetContentTypeCJT[] = "application/json"; 39 40const char kPrivetStorageListPath[] = "/privet/storage/list"; 41const char kPrivetStorageContentPath[] = "/privet/storage/content"; 42const char kPrivetStorageParamPathFormat[] = "path=%s"; 43 44const char kPrivetKeyJobID[] = "job_id"; 45 46const int kPrivetCancelationTimeoutSeconds = 3; 47 48const int kPrivetLocalPrintMaxRetries = 2; 49 50const int kPrivetLocalPrintDefaultTimeout = 5; 51 52GURL CreatePrivetURL(const std::string& path) { 53 GURL url(kUrlPlaceHolder); 54 GURL::Replacements replacements; 55 replacements.SetPathStr(path); 56 return url.ReplaceComponents(replacements); 57} 58 59GURL CreatePrivetRegisterURL(const std::string& action, 60 const std::string& user) { 61 GURL url = CreatePrivetURL(kPrivetRegisterPath); 62 url = net::AppendQueryParameter(url, kPrivetRegisterActionArgName, action); 63 return net::AppendQueryParameter(url, kPrivetRegisterUserArgName, user); 64} 65 66GURL CreatePrivetParamURL(const std::string& path, 67 const std::string& query_params) { 68 GURL url(kUrlPlaceHolder); 69 GURL::Replacements replacements; 70 replacements.SetPathStr(path); 71 if (!query_params.empty()) { 72 replacements.SetQueryStr(query_params); 73 } 74 return url.ReplaceComponents(replacements); 75} 76 77} // namespace 78 79PrivetInfoOperationImpl::PrivetInfoOperationImpl( 80 PrivetHTTPClientImpl* privet_client, 81 const PrivetJSONOperation::ResultCallback& callback) 82 : privet_client_(privet_client), callback_(callback) { 83} 84 85PrivetInfoOperationImpl::~PrivetInfoOperationImpl() { 86} 87 88void PrivetInfoOperationImpl::Start() { 89 url_fetcher_ = privet_client_->CreateURLFetcher( 90 CreatePrivetURL(kPrivetInfoPath), net::URLFetcher::GET, this); 91 92 url_fetcher_->DoNotRetryOnTransientError(); 93 url_fetcher_->AllowEmptyPrivetToken(); 94 95 url_fetcher_->Start(); 96} 97 98PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() { 99 return privet_client_; 100} 101 102void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher, 103 PrivetURLFetcher::ErrorType error) { 104 callback_.Run(NULL); 105} 106 107void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher, 108 const base::DictionaryValue* value, 109 bool has_error) { 110 if (!has_error) 111 privet_client_->CacheInfo(value); 112 callback_.Run(value); 113} 114 115PrivetRegisterOperationImpl::PrivetRegisterOperationImpl( 116 PrivetHTTPClientImpl* privet_client, 117 const std::string& user, 118 PrivetRegisterOperation::Delegate* delegate) 119 : user_(user), delegate_(delegate), privet_client_(privet_client), 120 ongoing_(false) { 121} 122 123PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() { 124} 125 126void PrivetRegisterOperationImpl::Start() { 127 ongoing_ = true; 128 next_response_handler_ = 129 base::Bind(&PrivetRegisterOperationImpl::StartResponse, 130 base::Unretained(this)); 131 SendRequest(kPrivetActionStart); 132} 133 134void PrivetRegisterOperationImpl::Cancel() { 135 url_fetcher_.reset(); 136 137 if (ongoing_) { 138 // Owned by the message loop. 139 Cancelation* cancelation = new Cancelation(privet_client_, user_); 140 141 base::MessageLoop::current()->PostDelayedTask( 142 FROM_HERE, 143 base::Bind(&PrivetRegisterOperationImpl::Cancelation::Cleanup, 144 base::Owned(cancelation)), 145 base::TimeDelta::FromSeconds(kPrivetCancelationTimeoutSeconds)); 146 147 ongoing_ = false; 148 } 149} 150 151void PrivetRegisterOperationImpl::CompleteRegistration() { 152 next_response_handler_ = 153 base::Bind(&PrivetRegisterOperationImpl::CompleteResponse, 154 base::Unretained(this)); 155 SendRequest(kPrivetActionComplete); 156} 157 158PrivetHTTPClient* PrivetRegisterOperationImpl::GetHTTPClient() { 159 return privet_client_; 160} 161 162void PrivetRegisterOperationImpl::OnError(PrivetURLFetcher* fetcher, 163 PrivetURLFetcher::ErrorType error) { 164 ongoing_ = false; 165 int visible_http_code = -1; 166 FailureReason reason = FAILURE_NETWORK; 167 168 if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) { 169 visible_http_code = fetcher->response_code(); 170 reason = FAILURE_HTTP_ERROR; 171 } else if (error == PrivetURLFetcher::JSON_PARSE_ERROR) { 172 reason = FAILURE_MALFORMED_RESPONSE; 173 } else if (error == PrivetURLFetcher::TOKEN_ERROR) { 174 reason = FAILURE_TOKEN; 175 } else if (error == PrivetURLFetcher::RETRY_ERROR) { 176 reason = FAILURE_RETRY; 177 } 178 179 delegate_->OnPrivetRegisterError(this, 180 current_action_, 181 reason, 182 visible_http_code, 183 NULL); 184} 185 186void PrivetRegisterOperationImpl::OnParsedJson( 187 PrivetURLFetcher* fetcher, 188 const base::DictionaryValue* value, 189 bool has_error) { 190 if (has_error) { 191 std::string error; 192 value->GetString(kPrivetKeyError, &error); 193 194 ongoing_ = false; 195 delegate_->OnPrivetRegisterError(this, 196 current_action_, 197 FAILURE_JSON_ERROR, 198 fetcher->response_code(), 199 value); 200 return; 201 } 202 203 // TODO(noamsml): Match the user&action with the user&action in the object, 204 // and fail if different. 205 206 next_response_handler_.Run(*value); 207} 208 209void PrivetRegisterOperationImpl::OnNeedPrivetToken( 210 PrivetURLFetcher* fetcher, 211 const PrivetURLFetcher::TokenCallback& callback) { 212 privet_client_->RefreshPrivetToken(callback); 213} 214 215void PrivetRegisterOperationImpl::SendRequest(const std::string& action) { 216 current_action_ = action; 217 url_fetcher_ = privet_client_->CreateURLFetcher( 218 CreatePrivetRegisterURL(action, user_), net::URLFetcher::POST, this); 219 url_fetcher_->Start(); 220} 221 222void PrivetRegisterOperationImpl::StartResponse( 223 const base::DictionaryValue& value) { 224 next_response_handler_ = 225 base::Bind(&PrivetRegisterOperationImpl::GetClaimTokenResponse, 226 base::Unretained(this)); 227 228 SendRequest(kPrivetActionGetClaimToken); 229} 230 231void PrivetRegisterOperationImpl::GetClaimTokenResponse( 232 const base::DictionaryValue& value) { 233 std::string claimUrl; 234 std::string claimToken; 235 bool got_url = value.GetString(kPrivetKeyClaimURL, &claimUrl); 236 bool got_token = value.GetString(kPrivetKeyClaimToken, &claimToken); 237 if (got_url || got_token) { 238 delegate_->OnPrivetRegisterClaimToken(this, claimToken, GURL(claimUrl)); 239 } else { 240 delegate_->OnPrivetRegisterError(this, 241 current_action_, 242 FAILURE_MALFORMED_RESPONSE, 243 -1, 244 NULL); 245 } 246} 247 248void PrivetRegisterOperationImpl::CompleteResponse( 249 const base::DictionaryValue& value) { 250 std::string id; 251 value.GetString(kPrivetKeyDeviceID, &id); 252 ongoing_ = false; 253 expected_id_ = id; 254 StartInfoOperation(); 255} 256 257void PrivetRegisterOperationImpl::OnPrivetInfoDone( 258 const base::DictionaryValue* value) { 259 // TODO(noamsml): Simplify error case and depracate HTTP error value in 260 // OnPrivetRegisterError. 261 if (!value) { 262 delegate_->OnPrivetRegisterError(this, 263 kPrivetActionNameInfo, 264 FAILURE_NETWORK, 265 -1, 266 NULL); 267 return; 268 } 269 270 if (!value->HasKey(kPrivetInfoKeyID)) { 271 if (value->HasKey(kPrivetKeyError)) { 272 delegate_->OnPrivetRegisterError(this, 273 kPrivetActionNameInfo, 274 FAILURE_JSON_ERROR, 275 -1, 276 value); 277 } else { 278 delegate_->OnPrivetRegisterError(this, 279 kPrivetActionNameInfo, 280 FAILURE_MALFORMED_RESPONSE, 281 -1, 282 NULL); 283 } 284 return; 285 } 286 287 std::string id; 288 289 if (!value->GetString(kPrivetInfoKeyID, &id) || 290 id != expected_id_) { 291 delegate_->OnPrivetRegisterError(this, 292 kPrivetActionNameInfo, 293 FAILURE_MALFORMED_RESPONSE, 294 -1, 295 NULL); 296 } else { 297 delegate_->OnPrivetRegisterDone(this, id); 298 } 299} 300 301void PrivetRegisterOperationImpl::StartInfoOperation() { 302 info_operation_ = privet_client_->CreateInfoOperation( 303 base::Bind(&PrivetRegisterOperationImpl::OnPrivetInfoDone, 304 base::Unretained(this))); 305 info_operation_->Start(); 306} 307 308PrivetRegisterOperationImpl::Cancelation::Cancelation( 309 PrivetHTTPClientImpl* privet_client, 310 const std::string& user) { 311 url_fetcher_ = 312 privet_client->CreateURLFetcher( 313 CreatePrivetRegisterURL(kPrivetActionCancel, user), 314 net::URLFetcher::POST, this); 315 url_fetcher_->DoNotRetryOnTransientError(); 316 url_fetcher_->Start(); 317} 318 319PrivetRegisterOperationImpl::Cancelation::~Cancelation() { 320} 321 322void PrivetRegisterOperationImpl::Cancelation::OnError( 323 PrivetURLFetcher* fetcher, 324 PrivetURLFetcher::ErrorType error) { 325} 326 327void PrivetRegisterOperationImpl::Cancelation::OnParsedJson( 328 PrivetURLFetcher* fetcher, 329 const base::DictionaryValue* value, 330 bool has_error) { 331} 332 333void PrivetRegisterOperationImpl::Cancelation::Cleanup() { 334 // Nothing needs to be done, as base::Owned will delete this object, 335 // this callback is just here to pass ownership of the Cancelation to 336 // the message loop. 337} 338 339PrivetJSONOperationImpl::PrivetJSONOperationImpl( 340 PrivetHTTPClientImpl* privet_client, 341 const std::string& path, 342 const std::string& query_params, 343 const PrivetJSONOperation::ResultCallback& callback) 344 : privet_client_(privet_client), path_(path), query_params_(query_params), 345 callback_(callback) { 346} 347 348PrivetJSONOperationImpl::~PrivetJSONOperationImpl() { 349} 350 351void PrivetJSONOperationImpl::Start() { 352 url_fetcher_ = privet_client_->CreateURLFetcher( 353 CreatePrivetParamURL(path_, query_params_), net::URLFetcher::GET, this); 354 url_fetcher_->DoNotRetryOnTransientError(); 355 url_fetcher_->Start(); 356} 357 358PrivetHTTPClient* PrivetJSONOperationImpl::GetHTTPClient() { 359 return privet_client_; 360} 361 362void PrivetJSONOperationImpl::OnError( 363 PrivetURLFetcher* fetcher, 364 PrivetURLFetcher::ErrorType error) { 365 callback_.Run(NULL); 366} 367 368void PrivetJSONOperationImpl::OnParsedJson( 369 PrivetURLFetcher* fetcher, 370 const base::DictionaryValue* value, 371 bool has_error) { 372 callback_.Run(value); 373} 374 375void PrivetJSONOperationImpl::OnNeedPrivetToken( 376 PrivetURLFetcher* fetcher, 377 const PrivetURLFetcher::TokenCallback& callback) { 378 privet_client_->RefreshPrivetToken(callback); 379} 380 381PrivetDataReadOperationImpl::PrivetDataReadOperationImpl( 382 PrivetHTTPClientImpl* privet_client, 383 const std::string& path, 384 const std::string& query_params, 385 const PrivetDataReadOperation::ResultCallback& callback) 386 : privet_client_(privet_client), path_(path), query_params_(query_params), 387 callback_(callback), has_range_(false), save_to_file_(false) { 388} 389 390PrivetDataReadOperationImpl::~PrivetDataReadOperationImpl() { 391} 392 393 394void PrivetDataReadOperationImpl::Start() { 395 url_fetcher_ = privet_client_->CreateURLFetcher( 396 CreatePrivetParamURL(path_, query_params_), net::URLFetcher::GET, this); 397 url_fetcher_->DoNotRetryOnTransientError(); 398 399 if (has_range_) { 400 url_fetcher_->SetByteRange(range_start_, range_end_); 401 } 402 403 if (save_to_file_) { 404 url_fetcher_->SaveResponseToFile(); 405 } 406 407 url_fetcher_->Start(); 408} 409 410void PrivetDataReadOperationImpl::SetDataRange(int range_start, int range_end) { 411 has_range_ = true; 412 range_start_ = range_start; 413 range_end_ = range_end; 414} 415 416void PrivetDataReadOperationImpl::SaveDataToFile() { 417 save_to_file_ = false; 418} 419 420PrivetHTTPClient* PrivetDataReadOperationImpl::GetHTTPClient() { 421 return privet_client_; 422} 423 424void PrivetDataReadOperationImpl::OnError( 425 PrivetURLFetcher* fetcher, 426 PrivetURLFetcher::ErrorType error) { 427 callback_.Run(RESPONSE_TYPE_ERROR, std::string(), base::FilePath()); 428} 429 430void PrivetDataReadOperationImpl::OnParsedJson( 431 PrivetURLFetcher* fetcher, 432 const base::DictionaryValue* value, 433 bool has_error) { 434 NOTREACHED(); 435} 436 437void PrivetDataReadOperationImpl::OnNeedPrivetToken( 438 PrivetURLFetcher* fetcher, 439 const PrivetURLFetcher::TokenCallback& callback) { 440 privet_client_->RefreshPrivetToken(callback); 441} 442 443bool PrivetDataReadOperationImpl::OnRawData(PrivetURLFetcher* fetcher, 444 bool is_file, 445 const std::string& data_str, 446 const base::FilePath& file_path) { 447 ResponseType type = (is_file) ? RESPONSE_TYPE_FILE : RESPONSE_TYPE_STRING; 448 callback_.Run(type, data_str, file_path); 449 return true; 450} 451 452PrivetLocalPrintOperationImpl::PrivetLocalPrintOperationImpl( 453 PrivetHTTPClientImpl* privet_client, 454 PrivetLocalPrintOperation::Delegate* delegate) 455 : privet_client_(privet_client), 456 delegate_(delegate), 457 use_pdf_(false), 458 has_capabilities_(false), 459 has_extended_workflow_(false), 460 started_(false), 461 offline_(false), 462 dpi_(printing::kDefaultPdfDpi), 463 invalid_job_retries_(0), 464 weak_factory_(this) { 465} 466 467PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() { 468} 469 470void PrivetLocalPrintOperationImpl::Start() { 471 DCHECK(!started_); 472 473 // We need to get the /info response so we can know which APIs are available. 474 // TODO(noamsml): Use cached info when available. 475 info_operation_ = privet_client_->CreateInfoOperation( 476 base::Bind(&PrivetLocalPrintOperationImpl::OnPrivetInfoDone, 477 base::Unretained(this))); 478 info_operation_->Start(); 479 480 started_ = true; 481} 482 483void PrivetLocalPrintOperationImpl::OnPrivetInfoDone( 484 const base::DictionaryValue* value) { 485 if (value && !value->HasKey(kPrivetKeyError)) { 486 has_capabilities_ = false; 487 has_extended_workflow_ = false; 488 bool has_printing = false; 489 490 const base::ListValue* api_list; 491 if (value->GetList(kPrivetInfoKeyAPIList, &api_list)) { 492 for (size_t i = 0; i < api_list->GetSize(); i++) { 493 std::string api; 494 api_list->GetString(i, &api); 495 if (api == kPrivetCapabilitiesPath) { 496 has_capabilities_ = true; 497 } else if (api == kPrivetSubmitdocPath) { 498 has_printing = true; 499 } else if (api == kPrivetCreatejobPath) { 500 has_extended_workflow_ = true; 501 } 502 } 503 } 504 505 if (!has_printing) { 506 delegate_->OnPrivetPrintingError(this, -1); 507 return; 508 } 509 510 StartInitialRequest(); 511 } else { 512 delegate_->OnPrivetPrintingError(this, -1); 513 } 514} 515 516void PrivetLocalPrintOperationImpl::StartInitialRequest() { 517 if (has_capabilities_) { 518 GetCapabilities(); 519 } else { 520 // Since we have no capabilities, the only reasonable format we can 521 // request is PWG Raster. 522 use_pdf_ = false; 523 StartConvertToPWG(); 524 } 525} 526 527void PrivetLocalPrintOperationImpl::GetCapabilities() { 528 current_response_ = base::Bind( 529 &PrivetLocalPrintOperationImpl::OnCapabilitiesResponse, 530 base::Unretained(this)); 531 532 url_fetcher_= privet_client_->CreateURLFetcher( 533 CreatePrivetURL(kPrivetCapabilitiesPath), net::URLFetcher::GET, this); 534 url_fetcher_->DoNotRetryOnTransientError(); 535 536 url_fetcher_->Start(); 537} 538 539void PrivetLocalPrintOperationImpl::DoCreatejob() { 540 current_response_ = base::Bind( 541 &PrivetLocalPrintOperationImpl::OnCreatejobResponse, 542 base::Unretained(this)); 543 544 url_fetcher_= privet_client_->CreateURLFetcher( 545 CreatePrivetURL(kPrivetCreatejobPath), net::URLFetcher::POST, this); 546 url_fetcher_->SetUploadData(kPrivetContentTypeCJT, ticket_); 547 548 url_fetcher_->Start(); 549} 550 551void PrivetLocalPrintOperationImpl::DoSubmitdoc() { 552 current_response_ = base::Bind( 553 &PrivetLocalPrintOperationImpl::OnSubmitdocResponse, 554 base::Unretained(this)); 555 556 GURL url = CreatePrivetURL(kPrivetSubmitdocPath); 557 558 url = net::AppendQueryParameter(url, 559 kPrivetURLKeyClientName, 560 kPrivetURLValueClientName); 561 562 if (!user_.empty()) { 563 url = net::AppendQueryParameter(url, 564 kPrivetURLKeyUserName, 565 user_); 566 } 567 568 if (!jobname_.empty()) { 569 url = net::AppendQueryParameter(url, 570 kPrivetURLKeyJobname, 571 jobname_); 572 } 573 574 if (!jobid_.empty()) { 575 url = net::AppendQueryParameter(url, 576 kPrivetKeyJobID, 577 jobid_); 578 } 579 580 if (offline_) { 581 url = net::AppendQueryParameter(url, 582 kPrivetURLKeyOffline, 583 kPrivetURLValueOffline); 584 } 585 586 url_fetcher_= privet_client_->CreateURLFetcher( 587 url, net::URLFetcher::POST, this); 588 589 if (!use_pdf_) { 590 url_fetcher_->SetUploadFilePath(kPrivetContentTypePWGRaster, 591 pwg_file_path_); 592 } else { 593 // TODO(noamsml): Move to file-based upload data? 594 std::string data_str((const char*)data_->front(), data_->size()); 595 url_fetcher_->SetUploadData(kPrivetContentTypePDF, data_str); 596 } 597 598 url_fetcher_->Start(); 599} 600 601void PrivetLocalPrintOperationImpl::StartPrinting() { 602 if (has_extended_workflow_ && !ticket_.empty() && jobid_.empty()) { 603 DoCreatejob(); 604 } else { 605 DoSubmitdoc(); 606 } 607} 608 609void PrivetLocalPrintOperationImpl::StartConvertToPWG() { 610 if (!pwg_raster_converter_) 611 pwg_raster_converter_ = PWGRasterConverter::CreateDefault(); 612 double scale = dpi_; 613 scale /= printing::kPointsPerInch; 614 // Make vertical rectangle to optimize streaming to printer. Fix orientation 615 // by autorotate. 616 gfx::Rect area(std::min(page_size_.width(), page_size_.height()) * scale, 617 std::max(page_size_.width(), page_size_.height()) * scale); 618 pwg_raster_converter_->Start( 619 data_, printing::PdfRenderSettings(area, dpi_, true), 620 base::Bind(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted, 621 base::Unretained(this))); 622} 623 624void PrivetLocalPrintOperationImpl::OnCapabilitiesResponse( 625 bool has_error, 626 const base::DictionaryValue* value) { 627 if (has_error) { 628 delegate_->OnPrivetPrintingError(this, 200); 629 return; 630 } 631 632 cloud_devices::CloudDeviceDescription description; 633 if (!description.InitFromDictionary(make_scoped_ptr(value->DeepCopy()))) { 634 delegate_->OnPrivetPrintingError(this, 200); 635 return; 636 } 637 638 use_pdf_ = false; 639 cloud_devices::printer::ContentTypesCapability content_types; 640 if (content_types.LoadFrom(description)) { 641 use_pdf_ = content_types.Contains(kPrivetContentTypePDF) || 642 content_types.Contains(kPrivetContentTypeAny); 643 } 644 645 if (use_pdf_) { 646 StartPrinting(); 647 } else { 648 cloud_devices::printer::DpiCapability dpis; 649 if (dpis.LoadFrom(description)) { 650 dpi_ = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical); 651 } 652 StartConvertToPWG(); 653 } 654} 655 656void PrivetLocalPrintOperationImpl::OnSubmitdocResponse( 657 bool has_error, 658 const base::DictionaryValue* value) { 659 std::string error; 660 // This error is only relevant in the case of extended workflow: 661 // If the print job ID is invalid, retry createjob and submitdoc, 662 // rather than simply retrying the current request. 663 if (has_error && value->GetString(kPrivetKeyError, &error)) { 664 if (has_extended_workflow_ && 665 error == kPrivetErrorInvalidPrintJob && 666 invalid_job_retries_ < kPrivetLocalPrintMaxRetries) { 667 invalid_job_retries_++; 668 669 int timeout = kPrivetLocalPrintDefaultTimeout; 670 value->GetInteger(kPrivetKeyTimeout, &timeout); 671 672 double random_scaling_factor = 673 1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition; 674 675 timeout = static_cast<int>(timeout * random_scaling_factor); 676 677 timeout = std::max(timeout, kPrivetMinimumTimeout); 678 679 base::MessageLoop::current()->PostDelayedTask( 680 FROM_HERE, base::Bind(&PrivetLocalPrintOperationImpl::DoCreatejob, 681 weak_factory_.GetWeakPtr()), 682 base::TimeDelta::FromSeconds(timeout)); 683 } else if (use_pdf_ && error == kPrivetErrorInvalidDocumentType) { 684 use_pdf_ = false; 685 StartConvertToPWG(); 686 } else { 687 delegate_->OnPrivetPrintingError(this, 200); 688 } 689 690 return; 691 } 692 693 // If we've gotten this far, there are no errors, so we've effectively 694 // succeeded. 695 delegate_->OnPrivetPrintingDone(this); 696} 697 698void PrivetLocalPrintOperationImpl::OnCreatejobResponse( 699 bool has_error, 700 const base::DictionaryValue* value) { 701 if (has_error) { 702 delegate_->OnPrivetPrintingError(this, 200); 703 return; 704 } 705 706 // Try to get job ID from value. If not, jobid_ will be empty and we will use 707 // simple printing. 708 value->GetString(kPrivetKeyJobID, &jobid_); 709 710 DoSubmitdoc(); 711} 712 713void PrivetLocalPrintOperationImpl::OnPWGRasterConverted( 714 bool success, 715 const base::FilePath& pwg_file_path) { 716 if (!success) { 717 delegate_->OnPrivetPrintingError(this, -1); 718 return; 719 } 720 721 DCHECK(!pwg_file_path.empty()); 722 723 pwg_file_path_ = pwg_file_path; 724 StartPrinting(); 725} 726 727PrivetHTTPClient* PrivetLocalPrintOperationImpl::GetHTTPClient() { 728 return privet_client_; 729} 730 731void PrivetLocalPrintOperationImpl::OnError( 732 PrivetURLFetcher* fetcher, 733 PrivetURLFetcher::ErrorType error) { 734 delegate_->OnPrivetPrintingError(this, -1); 735} 736 737void PrivetLocalPrintOperationImpl::OnParsedJson( 738 PrivetURLFetcher* fetcher, 739 const base::DictionaryValue* value, 740 bool has_error) { 741 DCHECK(!current_response_.is_null()); 742 current_response_.Run(has_error, value); 743} 744 745void PrivetLocalPrintOperationImpl::OnNeedPrivetToken( 746 PrivetURLFetcher* fetcher, 747 const PrivetURLFetcher::TokenCallback& callback) { 748 privet_client_->RefreshPrivetToken(callback); 749} 750 751void PrivetLocalPrintOperationImpl::SetData(base::RefCountedBytes* data) { 752 DCHECK(!started_); 753 data_ = data; 754} 755 756void PrivetLocalPrintOperationImpl::SetTicket(const std::string& ticket) { 757 DCHECK(!started_); 758 ticket_ = ticket; 759} 760 761void PrivetLocalPrintOperationImpl::SetUsername(const std::string& user) { 762 DCHECK(!started_); 763 user_= user; 764} 765 766void PrivetLocalPrintOperationImpl::SetJobname(const std::string& jobname) { 767 DCHECK(!started_); 768 jobname_ = jobname; 769} 770 771void PrivetLocalPrintOperationImpl::SetOffline(bool offline) { 772 DCHECK(!started_); 773 offline_ = offline; 774} 775 776void PrivetLocalPrintOperationImpl::SetPageSize(const gfx::Size& page_size) { 777 DCHECK(!started_); 778 page_size_ = page_size; 779} 780 781void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting( 782 scoped_ptr<PWGRasterConverter> pwg_raster_converter) { 783 pwg_raster_converter_ = pwg_raster_converter.Pass(); 784} 785 786PrivetHTTPClientImpl::PrivetHTTPClientImpl( 787 const std::string& name, 788 const net::HostPortPair& host_port, 789 net::URLRequestContextGetter* request_context) 790 : name_(name), 791 fetcher_factory_(request_context), 792 host_port_(host_port) { 793} 794 795PrivetHTTPClientImpl::~PrivetHTTPClientImpl() { 796} 797 798const base::DictionaryValue* PrivetHTTPClientImpl::GetCachedInfo() const { 799 return cached_info_.get(); 800} 801 802scoped_ptr<PrivetRegisterOperation> 803PrivetHTTPClientImpl::CreateRegisterOperation( 804 const std::string& user, 805 PrivetRegisterOperation::Delegate* delegate) { 806 return scoped_ptr<PrivetRegisterOperation>( 807 new PrivetRegisterOperationImpl(this, user, delegate)); 808} 809 810scoped_ptr<PrivetJSONOperation> PrivetHTTPClientImpl::CreateInfoOperation( 811 const PrivetJSONOperation::ResultCallback& callback) { 812 return scoped_ptr<PrivetJSONOperation>( 813 new PrivetInfoOperationImpl(this, callback)); 814} 815 816scoped_ptr<PrivetJSONOperation> 817PrivetHTTPClientImpl::CreateCapabilitiesOperation( 818 const PrivetJSONOperation::ResultCallback& callback) { 819 return scoped_ptr<PrivetJSONOperation>( 820 new PrivetJSONOperationImpl(this, kPrivetCapabilitiesPath, "", callback)); 821} 822 823scoped_ptr<PrivetLocalPrintOperation> 824PrivetHTTPClientImpl::CreateLocalPrintOperation( 825 PrivetLocalPrintOperation::Delegate* delegate) { 826 return scoped_ptr<PrivetLocalPrintOperation>( 827 new PrivetLocalPrintOperationImpl(this, delegate)); 828} 829 830scoped_ptr<PrivetJSONOperation> 831PrivetHTTPClientImpl::CreateStorageListOperation( 832 const std::string& path, 833 const PrivetJSONOperation::ResultCallback& callback) { 834 std::string url_param = base::StringPrintf(kPrivetStorageParamPathFormat, 835 path.c_str()); 836 return scoped_ptr<PrivetJSONOperation>( 837 new PrivetJSONOperationImpl(this, kPrivetStorageListPath, url_param, 838 callback)); 839} 840 841 842scoped_ptr<PrivetDataReadOperation> 843PrivetHTTPClientImpl::CreateStorageReadOperation( 844 const std::string& path, 845 const PrivetDataReadOperation::ResultCallback& callback) { 846 std::string url_param = base::StringPrintf(kPrivetStorageParamPathFormat, 847 path.c_str()); 848 return scoped_ptr<PrivetDataReadOperation>( 849 new PrivetDataReadOperationImpl(this, kPrivetStorageContentPath, 850 url_param, callback)); 851} 852 853const std::string& PrivetHTTPClientImpl::GetName() { 854 return name_; 855} 856 857scoped_ptr<PrivetURLFetcher> PrivetHTTPClientImpl::CreateURLFetcher( 858 const GURL& url, net::URLFetcher::RequestType request_type, 859 PrivetURLFetcher::Delegate* delegate) const { 860 GURL::Replacements replacements; 861 replacements.SetHostStr(host_port_.host()); 862 std::string port(base::IntToString(host_port_.port())); // Keep string alive. 863 replacements.SetPortStr(port); 864 return fetcher_factory_.CreateURLFetcher(url.ReplaceComponents(replacements), 865 request_type, delegate); 866} 867 868void PrivetHTTPClientImpl::CacheInfo(const base::DictionaryValue* cached_info) { 869 cached_info_.reset(cached_info->DeepCopy()); 870 std::string token; 871 if (cached_info_->GetString(kPrivetInfoKeyToken, &token)) { 872 fetcher_factory_.set_token(token); 873 } 874} 875 876bool PrivetHTTPClientImpl::HasToken() const { 877 return fetcher_factory_.get_token() != ""; 878}; 879 880void PrivetHTTPClientImpl::RefreshPrivetToken( 881 const PrivetURLFetcher::TokenCallback& callback) { 882 token_callbacks_.push_back(callback); 883 884 if (!info_operation_) { 885 info_operation_ = CreateInfoOperation( 886 base::Bind(&PrivetHTTPClientImpl::OnPrivetInfoDone, 887 base::Unretained(this))); 888 info_operation_->Start(); 889 } 890} 891 892void PrivetHTTPClientImpl::OnPrivetInfoDone( 893 const base::DictionaryValue* value) { 894 info_operation_.reset(); 895 std::string token; 896 897 // If this does not succeed, token will be empty, and an empty string 898 // is our sentinel value, since empty X-Privet-Tokens are not allowed. 899 if (value) { 900 value->GetString(kPrivetInfoKeyToken, &token); 901 } 902 903 TokenCallbackVector token_callbacks; 904 token_callbacks_.swap(token_callbacks); 905 906 for (TokenCallbackVector::iterator i = token_callbacks.begin(); 907 i != token_callbacks.end(); i++) { 908 i->Run(token); 909 } 910} 911 912} // namespace local_discovery 913