Home | History | Annotate | Download | only in local_discovery
      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