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