root/chrome/browser/local_discovery/privet_http_impl.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CreatePrivetURL
  2. CreatePrivetRegisterURL
  3. CreatePrivetParamURL
  4. callback_
  5. Start
  6. GetHTTPClient
  7. OnError
  8. OnParsedJson
  9. ongoing_
  10. Start
  11. Cancel
  12. CompleteRegistration
  13. GetHTTPClient
  14. OnError
  15. OnParsedJson
  16. OnNeedPrivetToken
  17. SendRequest
  18. StartResponse
  19. GetClaimTokenResponse
  20. CompleteResponse
  21. OnPrivetInfoDone
  22. StartInfoOperation
  23. OnError
  24. OnParsedJson
  25. Cleanup
  26. callback_
  27. Start
  28. GetHTTPClient
  29. OnError
  30. OnParsedJson
  31. OnNeedPrivetToken
  32. save_to_file_
  33. Start
  34. SetDataRange
  35. SaveDataToFile
  36. GetHTTPClient
  37. OnError
  38. OnParsedJson
  39. OnNeedPrivetToken
  40. OnRawData
  41. weak_factory_
  42. Start
  43. OnPrivetInfoDone
  44. StartInitialRequest
  45. DoCreatejob
  46. DoSubmitdoc
  47. StartPrinting
  48. FillPwgRasterSettings
  49. StartConvertToPWG
  50. OnSubmitdocResponse
  51. OnCreatejobResponse
  52. OnPWGRasterConverted
  53. GetHTTPClient
  54. OnError
  55. OnParsedJson
  56. OnNeedPrivetToken
  57. SetData
  58. SetTicket
  59. SetCapabilities
  60. SetUsername
  61. SetJobname
  62. SetOffline
  63. SetPageSize
  64. SetPWGRasterConverterForTesting
  65. host_port_
  66. CreateRegisterOperation
  67. CreateInfoOperation
  68. CreateCapabilitiesOperation
  69. CreateLocalPrintOperation
  70. CreateStorageListOperation
  71. CreateStorageReadOperation
  72. GetName
  73. CreateURLFetcher
  74. RefreshPrivetToken
  75. OnPrivetInfoDone

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/local_discovery/privet_http_impl.h"

#include <algorithm>
#include <vector>

#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/local_discovery/privet_constants.h"
#include "components/cloud_devices/printer_description.h"
#include "net/base/url_util.h"
#include "printing/pwg_raster_settings.h"
#include "printing/units.h"
#include "ui/gfx/text_elider.h"
#include "url/gurl.h"

using namespace cloud_devices::printer;

namespace local_discovery {

namespace {
const char kUrlPlaceHolder[] = "http://host/";
const char kPrivetRegisterActionArgName[] = "action";
const char kPrivetRegisterUserArgName[] = "user";

const char kPrivetURLKeyUserName[] = "user_name";
const char kPrivetURLKeyClientName[] = "client_name";
const char kPrivetURLKeyJobname[] = "job_name";
const char kPrivetURLKeyOffline[] = "offline";
const char kPrivetURLValueOffline[] = "1";
const char kPrivetURLValueClientName[] = "Chrome";

const char kPrivetContentTypePDF[] = "application/pdf";
const char kPrivetContentTypePWGRaster[] = "image/pwg-raster";
const char kPrivetContentTypeAny[] = "*/*";
const char kPrivetContentTypeCJT[] = "application/json";

const char kPrivetStorageListPath[] = "/privet/storage/list";
const char kPrivetStorageContentPath[] = "/privet/storage/content";
const char kPrivetStorageParamPathFormat[] = "path=%s";

const char kPrivetKeyJobID[] = "job_id";

const int kPrivetCancelationTimeoutSeconds = 3;

const int kPrivetLocalPrintMaxRetries = 2;

const int kPrivetLocalPrintDefaultTimeout = 5;

const size_t kPrivetLocalPrintMaxJobNameLength = 64;

GURL CreatePrivetURL(const std::string& path) {
  GURL url(kUrlPlaceHolder);
  GURL::Replacements replacements;
  replacements.SetPathStr(path);
  return url.ReplaceComponents(replacements);
}

GURL CreatePrivetRegisterURL(const std::string& action,
                             const std::string& user) {
  GURL url = CreatePrivetURL(kPrivetRegisterPath);
  url = net::AppendQueryParameter(url, kPrivetRegisterActionArgName, action);
  return net::AppendQueryParameter(url, kPrivetRegisterUserArgName, user);
}

GURL CreatePrivetParamURL(const std::string& path,
                          const std::string& query_params) {
  GURL url(kUrlPlaceHolder);
  GURL::Replacements replacements;
  replacements.SetPathStr(path);
  if (!query_params.empty()) {
    replacements.SetQueryStr(query_params);
  }
  return url.ReplaceComponents(replacements);
}

}  // namespace

PrivetInfoOperationImpl::PrivetInfoOperationImpl(
    PrivetHTTPClientImpl* privet_client,
    const PrivetJSONOperation::ResultCallback& callback)
    : privet_client_(privet_client), callback_(callback) {
}

PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
}

void PrivetInfoOperationImpl::Start() {
  url_fetcher_ = privet_client_->CreateURLFetcher(
      CreatePrivetURL(kPrivetInfoPath), net::URLFetcher::GET, this);

  url_fetcher_->DoNotRetryOnTransientError();
  url_fetcher_->SendEmptyPrivetToken();

  url_fetcher_->Start();
}

PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() {
  return privet_client_;
}

void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher,
                                      PrivetURLFetcher::ErrorType error) {
  callback_.Run(NULL);
}

void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
                                           const base::DictionaryValue* value,
                                           bool has_error) {
  callback_.Run(value);
}

PrivetRegisterOperationImpl::PrivetRegisterOperationImpl(
    PrivetHTTPClientImpl* privet_client,
    const std::string& user,
    PrivetRegisterOperation::Delegate* delegate)
    : user_(user), delegate_(delegate), privet_client_(privet_client),
      ongoing_(false) {
}

PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
}

void PrivetRegisterOperationImpl::Start() {
  ongoing_ = true;
  next_response_handler_ =
      base::Bind(&PrivetRegisterOperationImpl::StartResponse,
                 base::Unretained(this));
  SendRequest(kPrivetActionStart);
}

void PrivetRegisterOperationImpl::Cancel() {
  url_fetcher_.reset();

  if (ongoing_) {
    // Owned by the message loop.
    Cancelation* cancelation = new Cancelation(privet_client_, user_);

    base::MessageLoop::current()->PostDelayedTask(
        FROM_HERE,
        base::Bind(&PrivetRegisterOperationImpl::Cancelation::Cleanup,
                   base::Owned(cancelation)),
        base::TimeDelta::FromSeconds(kPrivetCancelationTimeoutSeconds));

    ongoing_ = false;
  }
}

void PrivetRegisterOperationImpl::CompleteRegistration() {
  next_response_handler_ =
      base::Bind(&PrivetRegisterOperationImpl::CompleteResponse,
                 base::Unretained(this));
  SendRequest(kPrivetActionComplete);
}

PrivetHTTPClient* PrivetRegisterOperationImpl::GetHTTPClient() {
  return privet_client_;
}

void PrivetRegisterOperationImpl::OnError(PrivetURLFetcher* fetcher,
                                          PrivetURLFetcher::ErrorType error) {
  ongoing_ = false;
  int visible_http_code = -1;
  FailureReason reason = FAILURE_NETWORK;

  if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
    visible_http_code = fetcher->response_code();
    reason = FAILURE_HTTP_ERROR;
  } else if (error == PrivetURLFetcher::JSON_PARSE_ERROR) {
    reason = FAILURE_MALFORMED_RESPONSE;
  } else if (error == PrivetURLFetcher::TOKEN_ERROR) {
    reason = FAILURE_TOKEN;
  } else if (error == PrivetURLFetcher::RETRY_ERROR) {
    reason = FAILURE_RETRY;
  }

  delegate_->OnPrivetRegisterError(this,
                                   current_action_,
                                   reason,
                                   visible_http_code,
                                   NULL);
}

void PrivetRegisterOperationImpl::OnParsedJson(
    PrivetURLFetcher* fetcher,
    const base::DictionaryValue* value,
    bool has_error) {
  if (has_error) {
    std::string error;
    value->GetString(kPrivetKeyError, &error);

    ongoing_ = false;
    delegate_->OnPrivetRegisterError(this,
                                     current_action_,
                                     FAILURE_JSON_ERROR,
                                     fetcher->response_code(),
                                     value);
    return;
  }

  // TODO(noamsml): Match the user&action with the user&action in the object,
  // and fail if different.

  next_response_handler_.Run(*value);
}

void PrivetRegisterOperationImpl::OnNeedPrivetToken(
    PrivetURLFetcher* fetcher,
    const PrivetURLFetcher::TokenCallback& callback) {
  privet_client_->RefreshPrivetToken(callback);
}

void PrivetRegisterOperationImpl::SendRequest(const std::string& action) {
  current_action_ = action;
  url_fetcher_ = privet_client_->CreateURLFetcher(
      CreatePrivetRegisterURL(action, user_), net::URLFetcher::POST, this);
  url_fetcher_->Start();
}

void PrivetRegisterOperationImpl::StartResponse(
    const base::DictionaryValue& value) {
  next_response_handler_ =
      base::Bind(&PrivetRegisterOperationImpl::GetClaimTokenResponse,
                 base::Unretained(this));

  SendRequest(kPrivetActionGetClaimToken);
}

void PrivetRegisterOperationImpl::GetClaimTokenResponse(
    const base::DictionaryValue& value) {
  std::string claimUrl;
  std::string claimToken;
  bool got_url = value.GetString(kPrivetKeyClaimURL, &claimUrl);
  bool got_token = value.GetString(kPrivetKeyClaimToken, &claimToken);
  if (got_url || got_token) {
    delegate_->OnPrivetRegisterClaimToken(this, claimToken, GURL(claimUrl));
  } else {
    delegate_->OnPrivetRegisterError(this,
                                     current_action_,
                                     FAILURE_MALFORMED_RESPONSE,
                                     -1,
                                     NULL);
  }
}

void PrivetRegisterOperationImpl::CompleteResponse(
    const base::DictionaryValue& value) {
  std::string id;
  value.GetString(kPrivetKeyDeviceID, &id);
  ongoing_ = false;
  expected_id_ = id;
  StartInfoOperation();
}

void PrivetRegisterOperationImpl::OnPrivetInfoDone(
    const base::DictionaryValue* value) {
  // TODO(noamsml): Simplify error case and depracate HTTP error value in
  // OnPrivetRegisterError.
  if (!value) {
    delegate_->OnPrivetRegisterError(this,
                                     kPrivetActionNameInfo,
                                     FAILURE_NETWORK,
                                     -1,
                                     NULL);
    return;
  }

  if (!value->HasKey(kPrivetInfoKeyID)) {
    if (value->HasKey(kPrivetKeyError)) {
      delegate_->OnPrivetRegisterError(this,
                                       kPrivetActionNameInfo,
                                        FAILURE_JSON_ERROR,
                                       -1,
                                       value);
    } else {
      delegate_->OnPrivetRegisterError(this,
                                       kPrivetActionNameInfo,
                                       FAILURE_MALFORMED_RESPONSE,
                                       -1,
                                       NULL);
    }
    return;
  }

  std::string id;

  if (!value->GetString(kPrivetInfoKeyID, &id) ||
      id != expected_id_) {
    delegate_->OnPrivetRegisterError(this,
                                     kPrivetActionNameInfo,
                                     FAILURE_MALFORMED_RESPONSE,
                                     -1,
                                     NULL);
  } else {
    delegate_->OnPrivetRegisterDone(this, id);
  }
}

void PrivetRegisterOperationImpl::StartInfoOperation() {
  info_operation_ = privet_client_->CreateInfoOperation(
      base::Bind(&PrivetRegisterOperationImpl::OnPrivetInfoDone,
                 base::Unretained(this)));
  info_operation_->Start();
}

PrivetRegisterOperationImpl::Cancelation::Cancelation(
    PrivetHTTPClientImpl* privet_client,
    const std::string& user) {
  url_fetcher_ =
      privet_client->CreateURLFetcher(
          CreatePrivetRegisterURL(kPrivetActionCancel, user),
          net::URLFetcher::POST, this);
  url_fetcher_->DoNotRetryOnTransientError();
  url_fetcher_->Start();
}

PrivetRegisterOperationImpl::Cancelation::~Cancelation() {
}

void PrivetRegisterOperationImpl::Cancelation::OnError(
    PrivetURLFetcher* fetcher,
    PrivetURLFetcher::ErrorType error) {
}

void PrivetRegisterOperationImpl::Cancelation::OnParsedJson(
    PrivetURLFetcher* fetcher,
    const base::DictionaryValue* value,
    bool has_error) {
}

void PrivetRegisterOperationImpl::Cancelation::Cleanup() {
  // Nothing needs to be done, as base::Owned will delete this object,
  // this callback is just here to pass ownership of the Cancelation to
  // the message loop.
}

PrivetJSONOperationImpl::PrivetJSONOperationImpl(
    PrivetHTTPClientImpl* privet_client,
    const std::string& path,
    const std::string& query_params,
    const PrivetJSONOperation::ResultCallback& callback)
    : privet_client_(privet_client), path_(path), query_params_(query_params),
      callback_(callback) {
}

PrivetJSONOperationImpl::~PrivetJSONOperationImpl() {
}

void PrivetJSONOperationImpl::Start() {
  url_fetcher_ = privet_client_->CreateURLFetcher(
      CreatePrivetParamURL(path_, query_params_), net::URLFetcher::GET, this);
  url_fetcher_->DoNotRetryOnTransientError();
  url_fetcher_->Start();
}

PrivetHTTPClient* PrivetJSONOperationImpl::GetHTTPClient() {
  return privet_client_;
}

void PrivetJSONOperationImpl::OnError(
    PrivetURLFetcher* fetcher,
    PrivetURLFetcher::ErrorType error) {
  callback_.Run(NULL);
}

void PrivetJSONOperationImpl::OnParsedJson(
    PrivetURLFetcher* fetcher,
    const base::DictionaryValue* value,
    bool has_error) {
  callback_.Run(value);
}

void PrivetJSONOperationImpl::OnNeedPrivetToken(
    PrivetURLFetcher* fetcher,
    const PrivetURLFetcher::TokenCallback& callback) {
  privet_client_->RefreshPrivetToken(callback);
}

PrivetDataReadOperationImpl::PrivetDataReadOperationImpl(
    PrivetHTTPClientImpl* privet_client,
    const std::string& path,
    const std::string& query_params,
    const PrivetDataReadOperation::ResultCallback& callback)
    : privet_client_(privet_client), path_(path), query_params_(query_params),
      callback_(callback), has_range_(false), save_to_file_(false) {
}

PrivetDataReadOperationImpl::~PrivetDataReadOperationImpl() {
}


void PrivetDataReadOperationImpl::Start() {
  url_fetcher_ = privet_client_->CreateURLFetcher(
      CreatePrivetParamURL(path_, query_params_), net::URLFetcher::GET, this);
  url_fetcher_->DoNotRetryOnTransientError();

  if (has_range_) {
    url_fetcher_->SetByteRange(range_start_, range_end_);
  }

  if (save_to_file_) {
    url_fetcher_->SaveResponseToFile();
  }

  url_fetcher_->Start();
}

void PrivetDataReadOperationImpl::SetDataRange(int range_start, int range_end) {
  has_range_ = true;
  range_start_ = range_start;
  range_end_ = range_end;
}

void PrivetDataReadOperationImpl::SaveDataToFile() {
  save_to_file_ = false;
}

PrivetHTTPClient* PrivetDataReadOperationImpl::GetHTTPClient() {
  return privet_client_;
}

void PrivetDataReadOperationImpl::OnError(
    PrivetURLFetcher* fetcher,
    PrivetURLFetcher::ErrorType error) {
  callback_.Run(RESPONSE_TYPE_ERROR, std::string(), base::FilePath());
}

void PrivetDataReadOperationImpl::OnParsedJson(
    PrivetURLFetcher* fetcher,
    const base::DictionaryValue* value,
    bool has_error) {
  NOTREACHED();
}

void PrivetDataReadOperationImpl::OnNeedPrivetToken(
    PrivetURLFetcher* fetcher,
    const PrivetURLFetcher::TokenCallback& callback) {
  privet_client_->RefreshPrivetToken(callback);
}

bool PrivetDataReadOperationImpl::OnRawData(PrivetURLFetcher* fetcher,
                                            bool is_file,
                                            const std::string& data_str,
                                            const base::FilePath& file_path) {
  ResponseType type = (is_file) ? RESPONSE_TYPE_FILE : RESPONSE_TYPE_STRING;
  callback_.Run(type, data_str, file_path);
  return true;
}

PrivetLocalPrintOperationImpl::PrivetLocalPrintOperationImpl(
    PrivetHTTPClientImpl* privet_client,
    PrivetLocalPrintOperation::Delegate* delegate)
    : privet_client_(privet_client),
      delegate_(delegate),
      use_pdf_(false),
      has_extended_workflow_(false),
      started_(false),
      offline_(false),
      dpi_(printing::kDefaultPdfDpi),
      invalid_job_retries_(0),
      weak_factory_(this) {
}

PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() {
}

void PrivetLocalPrintOperationImpl::Start() {
  DCHECK(!started_);

  // We need to get the /info response so we can know which APIs are available.
  // TODO(noamsml): Use cached info when available.
  info_operation_ = privet_client_->CreateInfoOperation(
      base::Bind(&PrivetLocalPrintOperationImpl::OnPrivetInfoDone,
                 base::Unretained(this)));
  info_operation_->Start();

  started_ = true;
}

void PrivetLocalPrintOperationImpl::OnPrivetInfoDone(
    const base::DictionaryValue* value) {
  if (value && !value->HasKey(kPrivetKeyError)) {
    has_extended_workflow_ = false;
    bool has_printing = false;

    const base::ListValue* api_list;
    if (value->GetList(kPrivetInfoKeyAPIList, &api_list)) {
      for (size_t i = 0; i < api_list->GetSize(); i++) {
        std::string api;
        api_list->GetString(i, &api);
        if (api == kPrivetSubmitdocPath) {
          has_printing = true;
        } else if (api == kPrivetCreatejobPath) {
          has_extended_workflow_ = true;
        }
      }
    }

    if (!has_printing) {
      delegate_->OnPrivetPrintingError(this, -1);
      return;
    }

    StartInitialRequest();
  } else {
    delegate_->OnPrivetPrintingError(this, -1);
  }
}

void PrivetLocalPrintOperationImpl::StartInitialRequest() {
  use_pdf_ = false;
  ContentTypesCapability content_types;
  if (content_types.LoadFrom(capabilities_)) {
    use_pdf_ = content_types.Contains(kPrivetContentTypePDF) ||
               content_types.Contains(kPrivetContentTypeAny);
  }

  if (use_pdf_) {
    StartPrinting();
  } else {
    DpiCapability dpis;
    if (dpis.LoadFrom(capabilities_)) {
      dpi_ = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical);
    }
    StartConvertToPWG();
  }
}

void PrivetLocalPrintOperationImpl::DoCreatejob() {
  current_response_ = base::Bind(
      &PrivetLocalPrintOperationImpl::OnCreatejobResponse,
      base::Unretained(this));

  // Add PWG raster settings to ticket if they are supplied by the printer.
  PwgRasterConfigCapability raster_capability;
  PwgRasterConfigTicketItem raster_ticket_item;
  if (raster_capability.LoadFrom(capabilities_)) {
    raster_ticket_item.set_value(raster_capability.value());
    raster_ticket_item.SaveTo(&ticket_);
  }

  url_fetcher_= privet_client_->CreateURLFetcher(
      CreatePrivetURL(kPrivetCreatejobPath), net::URLFetcher::POST, this);
  url_fetcher_->SetUploadData(kPrivetContentTypeCJT, ticket_.ToString());

  url_fetcher_->Start();
}

void PrivetLocalPrintOperationImpl::DoSubmitdoc() {
  current_response_ = base::Bind(
      &PrivetLocalPrintOperationImpl::OnSubmitdocResponse,
      base::Unretained(this));

  GURL url = CreatePrivetURL(kPrivetSubmitdocPath);

  url = net::AppendQueryParameter(url,
                                  kPrivetURLKeyClientName,
                                  kPrivetURLValueClientName);

  if (!user_.empty()) {
    url = net::AppendQueryParameter(url,
                                    kPrivetURLKeyUserName,
                                    user_);
  }

  base::string16 shortened_jobname;

  gfx::ElideString(base::UTF8ToUTF16(jobname_),
                   kPrivetLocalPrintMaxJobNameLength,
                   &shortened_jobname);

  if (!jobname_.empty()) {
    url = net::AppendQueryParameter(
        url, kPrivetURLKeyJobname, base::UTF16ToUTF8(shortened_jobname));
  }

  if (!jobid_.empty()) {
    url = net::AppendQueryParameter(url,
                                    kPrivetKeyJobID,
                                    jobid_);
  }

  if (offline_) {
    url = net::AppendQueryParameter(url,
                                    kPrivetURLKeyOffline,
                                    kPrivetURLValueOffline);
  }

  url_fetcher_= privet_client_->CreateURLFetcher(
      url, net::URLFetcher::POST, this);

  if (!use_pdf_) {
    url_fetcher_->SetUploadFilePath(kPrivetContentTypePWGRaster,
                                    pwg_file_path_);
  } else {
    // TODO(noamsml): Move to file-based upload data?
    std::string data_str((const char*)data_->front(), data_->size());
    url_fetcher_->SetUploadData(kPrivetContentTypePDF, data_str);
  }

  url_fetcher_->Start();
}

void PrivetLocalPrintOperationImpl::StartPrinting() {
  if (has_extended_workflow_ && jobid_.empty()) {
    DoCreatejob();
  } else {
    DoSubmitdoc();
  }
}

void PrivetLocalPrintOperationImpl::FillPwgRasterSettings(
    printing::PwgRasterSettings* transform_settings) {
  PwgRasterConfigCapability raster_capability;
  // If the raster capability fails to load, raster_capability will contain
  // the default value.
  raster_capability.LoadFrom(capabilities_);

  DuplexTicketItem duplex_item;
  DuplexType duplex_value = NO_DUPLEX;

  DocumentSheetBack document_sheet_back =
      raster_capability.value().document_sheet_back;

  if (duplex_item.LoadFrom(ticket_)) {
    duplex_value = duplex_item.value();
  }

  transform_settings->odd_page_transform = printing::TRANSFORM_NORMAL;
  switch (duplex_value) {
    case NO_DUPLEX:
      transform_settings->odd_page_transform = printing::TRANSFORM_NORMAL;
      break;
    case LONG_EDGE:
      if (document_sheet_back == ROTATED) {
        transform_settings->odd_page_transform = printing::TRANSFORM_ROTATE_180;
      } else if (document_sheet_back == FLIPPED) {
        transform_settings->odd_page_transform =
            printing::TRANSFORM_FLIP_VERTICAL;
      }
      break;
    case SHORT_EDGE:
      if (document_sheet_back == MANUAL_TUMBLE) {
        transform_settings->odd_page_transform = printing::TRANSFORM_ROTATE_180;
      } else if (document_sheet_back == FLIPPED) {
        transform_settings->odd_page_transform =
            printing::TRANSFORM_FLIP_HORIZONTAL;
      }
  }

  transform_settings->rotate_all_pages =
      raster_capability.value().rotate_all_pages;

  transform_settings->reverse_page_order =
      raster_capability.value().reverse_order_streaming;
}

void PrivetLocalPrintOperationImpl::StartConvertToPWG() {
  printing::PwgRasterSettings transform_settings;

  FillPwgRasterSettings(&transform_settings);

  if (!pwg_raster_converter_)
    pwg_raster_converter_ = PWGRasterConverter::CreateDefault();

  double scale = dpi_;
  scale /= printing::kPointsPerInch;
  // Make vertical rectangle to optimize streaming to printer. Fix orientation
  // by autorotate.
  gfx::Rect area(std::min(page_size_.width(), page_size_.height()) * scale,
                 std::max(page_size_.width(), page_size_.height()) * scale);
  pwg_raster_converter_->Start(
      data_,
      printing::PdfRenderSettings(area, dpi_, true),
      transform_settings,
      base::Bind(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted,
                 base::Unretained(this)));
}

void PrivetLocalPrintOperationImpl::OnSubmitdocResponse(
    bool has_error,
    const base::DictionaryValue* value) {
  std::string error;
  // This error is only relevant in the case of extended workflow:
  // If the print job ID is invalid, retry createjob and submitdoc,
  // rather than simply retrying the current request.
  if (has_error && value->GetString(kPrivetKeyError, &error)) {
    if (has_extended_workflow_ &&
        error == kPrivetErrorInvalidPrintJob &&
        invalid_job_retries_ < kPrivetLocalPrintMaxRetries) {
      invalid_job_retries_++;

      int timeout = kPrivetLocalPrintDefaultTimeout;
      value->GetInteger(kPrivetKeyTimeout, &timeout);

      double random_scaling_factor =
          1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition;

      timeout = static_cast<int>(timeout * random_scaling_factor);

      timeout = std::max(timeout, kPrivetMinimumTimeout);

      base::MessageLoop::current()->PostDelayedTask(
          FROM_HERE, base::Bind(&PrivetLocalPrintOperationImpl::DoCreatejob,
                                weak_factory_.GetWeakPtr()),
          base::TimeDelta::FromSeconds(timeout));
    } else if (use_pdf_ && error == kPrivetErrorInvalidDocumentType) {
      use_pdf_ = false;
      StartConvertToPWG();
    } else {
      delegate_->OnPrivetPrintingError(this, 200);
    }

    return;
  }

  // If we've gotten this far, there are no errors, so we've effectively
  // succeeded.
  delegate_->OnPrivetPrintingDone(this);
}

void PrivetLocalPrintOperationImpl::OnCreatejobResponse(
    bool has_error,
    const base::DictionaryValue* value) {
  if (has_error) {
    delegate_->OnPrivetPrintingError(this, 200);
    return;
  }

  // Try to get job ID from value. If not, jobid_ will be empty and we will use
  // simple printing.
  value->GetString(kPrivetKeyJobID, &jobid_);

  DoSubmitdoc();
}

void PrivetLocalPrintOperationImpl::OnPWGRasterConverted(
    bool success,
    const base::FilePath& pwg_file_path) {
  if (!success) {
    delegate_->OnPrivetPrintingError(this, -1);
    return;
  }

  DCHECK(!pwg_file_path.empty());

  pwg_file_path_ = pwg_file_path;
  StartPrinting();
}

PrivetHTTPClient* PrivetLocalPrintOperationImpl::GetHTTPClient() {
  return privet_client_;
}

void PrivetLocalPrintOperationImpl::OnError(
    PrivetURLFetcher* fetcher,
    PrivetURLFetcher::ErrorType error) {
  delegate_->OnPrivetPrintingError(this, -1);
}

void PrivetLocalPrintOperationImpl::OnParsedJson(
    PrivetURLFetcher* fetcher,
    const base::DictionaryValue* value,
    bool has_error) {
  DCHECK(!current_response_.is_null());
  current_response_.Run(has_error, value);
}

void PrivetLocalPrintOperationImpl::OnNeedPrivetToken(
    PrivetURLFetcher* fetcher,
    const PrivetURLFetcher::TokenCallback& callback) {
  privet_client_->RefreshPrivetToken(callback);
}

void PrivetLocalPrintOperationImpl::SetData(base::RefCountedBytes* data) {
  DCHECK(!started_);
  data_ = data;
}

void PrivetLocalPrintOperationImpl::SetTicket(const std::string& ticket) {
  DCHECK(!started_);
  ticket_.InitFromString(ticket);
}

void PrivetLocalPrintOperationImpl::SetCapabilities(
    const std::string& capabilities) {
  DCHECK(!started_);
  capabilities_.InitFromString(capabilities);
}

void PrivetLocalPrintOperationImpl::SetUsername(const std::string& user) {
  DCHECK(!started_);
  user_= user;
}

void PrivetLocalPrintOperationImpl::SetJobname(const std::string& jobname) {
  DCHECK(!started_);
  jobname_ = jobname;
}

void PrivetLocalPrintOperationImpl::SetOffline(bool offline) {
  DCHECK(!started_);
  offline_ = offline;
}

void PrivetLocalPrintOperationImpl::SetPageSize(const gfx::Size& page_size) {
  DCHECK(!started_);
  page_size_ = page_size;
}

void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting(
    scoped_ptr<PWGRasterConverter> pwg_raster_converter) {
  pwg_raster_converter_ = pwg_raster_converter.Pass();
}

PrivetHTTPClientImpl::PrivetHTTPClientImpl(
    const std::string& name,
    const net::HostPortPair& host_port,
    net::URLRequestContextGetter* request_context)
    : name_(name), request_context_(request_context), host_port_(host_port) {}

PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
}

scoped_ptr<PrivetRegisterOperation>
PrivetHTTPClientImpl::CreateRegisterOperation(
    const std::string& user,
    PrivetRegisterOperation::Delegate* delegate) {
  return scoped_ptr<PrivetRegisterOperation>(
      new PrivetRegisterOperationImpl(this, user, delegate));
}

scoped_ptr<PrivetJSONOperation> PrivetHTTPClientImpl::CreateInfoOperation(
    const PrivetJSONOperation::ResultCallback& callback) {
  return scoped_ptr<PrivetJSONOperation>(
      new PrivetInfoOperationImpl(this, callback));
}

scoped_ptr<PrivetJSONOperation>
PrivetHTTPClientImpl::CreateCapabilitiesOperation(
    const PrivetJSONOperation::ResultCallback& callback) {
  return scoped_ptr<PrivetJSONOperation>(
      new PrivetJSONOperationImpl(this, kPrivetCapabilitiesPath, "", callback));
}

scoped_ptr<PrivetLocalPrintOperation>
PrivetHTTPClientImpl::CreateLocalPrintOperation(
    PrivetLocalPrintOperation::Delegate* delegate) {
  return scoped_ptr<PrivetLocalPrintOperation>(
      new PrivetLocalPrintOperationImpl(this, delegate));
}

scoped_ptr<PrivetJSONOperation>
PrivetHTTPClientImpl::CreateStorageListOperation(
      const std::string& path,
      const PrivetJSONOperation::ResultCallback& callback) {
  std::string url_param = base::StringPrintf(kPrivetStorageParamPathFormat,
                                            path.c_str());
  return scoped_ptr<PrivetJSONOperation>(
      new PrivetJSONOperationImpl(this, kPrivetStorageListPath, url_param,
                                  callback));
}


scoped_ptr<PrivetDataReadOperation>
PrivetHTTPClientImpl::CreateStorageReadOperation(
    const std::string& path,
    const PrivetDataReadOperation::ResultCallback& callback) {
  std::string url_param = base::StringPrintf(kPrivetStorageParamPathFormat,
                                             path.c_str());
  return scoped_ptr<PrivetDataReadOperation>(
      new PrivetDataReadOperationImpl(this, kPrivetStorageContentPath,
                                      url_param, callback));
}

const std::string& PrivetHTTPClientImpl::GetName() {
  return name_;
}

scoped_ptr<PrivetURLFetcher> PrivetHTTPClientImpl::CreateURLFetcher(
    const GURL& url, net::URLFetcher::RequestType request_type,
    PrivetURLFetcher::Delegate* delegate) const {
  GURL::Replacements replacements;
  replacements.SetHostStr(host_port_.host());
  std::string port(base::IntToString(host_port_.port()));  // Keep string alive.
  replacements.SetPortStr(port);
  return scoped_ptr<PrivetURLFetcher>(
      new PrivetURLFetcher(url.ReplaceComponents(replacements),
                           request_type,
                           request_context_.get(),
                           delegate));
}

void PrivetHTTPClientImpl::RefreshPrivetToken(
    const PrivetURLFetcher::TokenCallback& callback) {
  token_callbacks_.push_back(callback);

  if (!info_operation_) {
    info_operation_ = CreateInfoOperation(
        base::Bind(&PrivetHTTPClientImpl::OnPrivetInfoDone,
                   base::Unretained(this)));
    info_operation_->Start();
  }
}

void PrivetHTTPClientImpl::OnPrivetInfoDone(
    const base::DictionaryValue* value) {
  info_operation_.reset();
  std::string token;

  // If this does not succeed, token will be empty, and an empty string
  // is our sentinel value, since empty X-Privet-Tokens are not allowed.
  if (value) {
    value->GetString(kPrivetInfoKeyToken, &token);
  }

  TokenCallbackVector token_callbacks;
  token_callbacks_.swap(token_callbacks);

  for (TokenCallbackVector::iterator i = token_callbacks.begin();
       i != token_callbacks.end(); i++) {
    i->Run(token);
  }
}

}  // namespace local_discovery

/* [<][>][^][v][top][bottom][index][help] */