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