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
28using namespace cloud_devices::printer;
29
30namespace cloud_print {
31extern const char kContentTypeJSON[];
32}
33
34namespace local_discovery {
35
36namespace {
37const char kUrlPlaceHolder[] = "http://host/";
38const char kPrivetRegisterActionArgName[] = "action";
39const char kPrivetRegisterUserArgName[] = "user";
40
41const int kPrivetCancelationTimeoutSeconds = 3;
42
43#if defined(ENABLE_FULL_PRINTING)
44const char kPrivetURLKeyUserName[] = "user_name";
45const char kPrivetURLKeyClientName[] = "client_name";
46const char kPrivetURLKeyJobname[] = "job_name";
47const char kPrivetURLKeyOffline[] = "offline";
48const char kPrivetURLValueOffline[] = "1";
49const char kPrivetURLValueClientName[] = "Chrome";
50
51const char kPrivetContentTypePDF[] = "application/pdf";
52const char kPrivetContentTypePWGRaster[] = "image/pwg-raster";
53const char kPrivetContentTypeAny[] = "*/*";
54
55const char kPrivetKeyJobID[] = "job_id";
56
57const int kPrivetLocalPrintMaxRetries = 2;
58const int kPrivetLocalPrintDefaultTimeout = 5;
59
60const size_t kPrivetLocalPrintMaxJobNameLength = 64;
61#endif  // ENABLE_FULL_PRINTING
62
63GURL CreatePrivetURL(const std::string& path) {
64  GURL url(kUrlPlaceHolder);
65  GURL::Replacements replacements;
66  replacements.SetPathStr(path);
67  return url.ReplaceComponents(replacements);
68}
69
70GURL 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
77GURL 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
90PrivetInfoOperationImpl::PrivetInfoOperationImpl(
91    PrivetHTTPClient* privet_client,
92    const PrivetJSONOperation::ResultCallback& callback)
93    : privet_client_(privet_client), callback_(callback) {
94}
95
96PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
97}
98
99void 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
109PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() {
110  return privet_client_;
111}
112
113void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher,
114                                      PrivetURLFetcher::ErrorType error) {
115  callback_.Run(NULL);
116}
117
118void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
119                                           const base::DictionaryValue& value,
120                                           bool has_error) {
121  callback_.Run(&value);
122}
123
124PrivetRegisterOperationImpl::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
134PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
135}
136
137void PrivetRegisterOperationImpl::Start() {
138  ongoing_ = true;
139  next_response_handler_ =
140      base::Bind(&PrivetRegisterOperationImpl::StartResponse,
141                 base::Unretained(this));
142  SendRequest(kPrivetActionStart);
143}
144
145void 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
162void PrivetRegisterOperationImpl::CompleteRegistration() {
163  next_response_handler_ =
164      base::Bind(&PrivetRegisterOperationImpl::CompleteResponse,
165                 base::Unretained(this));
166  SendRequest(kPrivetActionComplete);
167}
168
169PrivetHTTPClient* PrivetRegisterOperationImpl::GetHTTPClient() {
170  return privet_client_;
171}
172
173void 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
197void 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
220void PrivetRegisterOperationImpl::OnNeedPrivetToken(
221    PrivetURLFetcher* fetcher,
222    const PrivetURLFetcher::TokenCallback& callback) {
223  privet_client_->RefreshPrivetToken(callback);
224}
225
226void 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
233void 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
242void 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
259void 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
268void 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
312void PrivetRegisterOperationImpl::StartInfoOperation() {
313  info_operation_ = privet_client_->CreateInfoOperation(
314      base::Bind(&PrivetRegisterOperationImpl::OnPrivetInfoDone,
315                 base::Unretained(this)));
316  info_operation_->Start();
317}
318
319PrivetRegisterOperationImpl::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
330PrivetRegisterOperationImpl::Cancelation::~Cancelation() {
331}
332
333void PrivetRegisterOperationImpl::Cancelation::OnError(
334    PrivetURLFetcher* fetcher,
335    PrivetURLFetcher::ErrorType error) {
336}
337
338void PrivetRegisterOperationImpl::Cancelation::OnParsedJson(
339    PrivetURLFetcher* fetcher,
340    const base::DictionaryValue& value,
341    bool has_error) {
342}
343
344void 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
350PrivetJSONOperationImpl::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
361PrivetJSONOperationImpl::~PrivetJSONOperationImpl() {
362}
363
364void 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
371PrivetHTTPClient* PrivetJSONOperationImpl::GetHTTPClient() {
372  return privet_client_;
373}
374
375void PrivetJSONOperationImpl::OnError(
376    PrivetURLFetcher* fetcher,
377    PrivetURLFetcher::ErrorType error) {
378  callback_.Run(NULL);
379}
380
381void PrivetJSONOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
382                                           const base::DictionaryValue& value,
383                                           bool has_error) {
384  callback_.Run(&value);
385}
386
387void PrivetJSONOperationImpl::OnNeedPrivetToken(
388    PrivetURLFetcher* fetcher,
389    const PrivetURLFetcher::TokenCallback& callback) {
390  privet_client_->RefreshPrivetToken(callback);
391}
392
393PrivetDataReadOperationImpl::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
406PrivetDataReadOperationImpl::~PrivetDataReadOperationImpl() {
407}
408
409
410void 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
426void PrivetDataReadOperationImpl::SetDataRange(int range_start, int range_end) {
427  has_range_ = true;
428  range_start_ = range_start;
429  range_end_ = range_end;
430}
431
432void PrivetDataReadOperationImpl::SaveDataToFile() {
433  save_to_file_ = false;
434}
435
436PrivetHTTPClient* PrivetDataReadOperationImpl::GetHTTPClient() {
437  return privet_client_;
438}
439
440void PrivetDataReadOperationImpl::OnError(
441    PrivetURLFetcher* fetcher,
442    PrivetURLFetcher::ErrorType error) {
443  callback_.Run(RESPONSE_TYPE_ERROR, std::string(), base::FilePath());
444}
445
446void PrivetDataReadOperationImpl::OnParsedJson(
447    PrivetURLFetcher* fetcher,
448    const base::DictionaryValue& value,
449    bool has_error) {
450  NOTREACHED();
451}
452
453void PrivetDataReadOperationImpl::OnNeedPrivetToken(
454    PrivetURLFetcher* fetcher,
455    const PrivetURLFetcher::TokenCallback& callback) {
456  privet_client_->RefreshPrivetToken(callback);
457}
458
459bool 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)
469PrivetLocalPrintOperationImpl::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
483PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() {
484}
485
486void 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
499void 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
529void 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
548void 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
561void 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
616void PrivetLocalPrintOperationImpl::StartPrinting() {
617  if (has_extended_workflow_ && jobid_.empty()) {
618    DoCreatejob();
619  } else {
620    DoSubmitdoc();
621  }
622}
623
624void 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
670void 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
692void 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
734void 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
749void 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
763PrivetHTTPClient* PrivetLocalPrintOperationImpl::GetHTTPClient() {
764  return privet_client_;
765}
766
767void PrivetLocalPrintOperationImpl::OnError(
768    PrivetURLFetcher* fetcher,
769    PrivetURLFetcher::ErrorType error) {
770  delegate_->OnPrivetPrintingError(this, -1);
771}
772
773void 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
781void PrivetLocalPrintOperationImpl::OnNeedPrivetToken(
782    PrivetURLFetcher* fetcher,
783    const PrivetURLFetcher::TokenCallback& callback) {
784  privet_client_->RefreshPrivetToken(callback);
785}
786
787void PrivetLocalPrintOperationImpl::SetData(
788    const scoped_refptr<base::RefCountedBytes>& data) {
789  DCHECK(!started_);
790  data_ = data;
791}
792
793void PrivetLocalPrintOperationImpl::SetTicket(const std::string& ticket) {
794  DCHECK(!started_);
795  ticket_.InitFromString(ticket);
796}
797
798void PrivetLocalPrintOperationImpl::SetCapabilities(
799    const std::string& capabilities) {
800  DCHECK(!started_);
801  capabilities_.InitFromString(capabilities);
802}
803
804void PrivetLocalPrintOperationImpl::SetUsername(const std::string& user) {
805  DCHECK(!started_);
806  user_= user;
807}
808
809void PrivetLocalPrintOperationImpl::SetJobname(const std::string& jobname) {
810  DCHECK(!started_);
811  jobname_ = jobname;
812}
813
814void PrivetLocalPrintOperationImpl::SetOffline(bool offline) {
815  DCHECK(!started_);
816  offline_ = offline;
817}
818
819void PrivetLocalPrintOperationImpl::SetPageSize(const gfx::Size& page_size) {
820  DCHECK(!started_);
821  page_size_ = page_size;
822}
823
824void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting(
825    scoped_ptr<PWGRasterConverter> pwg_raster_converter) {
826  pwg_raster_converter_ = pwg_raster_converter.Pass();
827}
828#endif  // ENABLE_FULL_PRINTING
829
830PrivetHTTPClientImpl::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
836PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
837}
838
839const std::string& PrivetHTTPClientImpl::GetName() {
840  return name_;
841}
842
843scoped_ptr<PrivetJSONOperation> PrivetHTTPClientImpl::CreateInfoOperation(
844    const PrivetJSONOperation::ResultCallback& callback) {
845  return scoped_ptr<PrivetJSONOperation>(
846      new PrivetInfoOperationImpl(this, callback));
847}
848
849scoped_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
864void 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
876void 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
896PrivetV1HTTPClientImpl::PrivetV1HTTPClientImpl(
897    scoped_ptr<PrivetHTTPClient> info_client)
898    : info_client_(info_client.Pass()) {
899}
900
901PrivetV1HTTPClientImpl::~PrivetV1HTTPClientImpl() {
902}
903
904const std::string& PrivetV1HTTPClientImpl::GetName() {
905  return info_client()->GetName();
906}
907
908scoped_ptr<PrivetJSONOperation> PrivetV1HTTPClientImpl::CreateInfoOperation(
909    const PrivetJSONOperation::ResultCallback& callback) {
910  return info_client()->CreateInfoOperation(callback);
911}
912
913scoped_ptr<PrivetRegisterOperation>
914PrivetV1HTTPClientImpl::CreateRegisterOperation(
915    const std::string& user,
916    PrivetRegisterOperation::Delegate* delegate) {
917  return scoped_ptr<PrivetRegisterOperation>(
918      new PrivetRegisterOperationImpl(info_client(), user, delegate));
919}
920
921scoped_ptr<PrivetJSONOperation>
922PrivetV1HTTPClientImpl::CreateCapabilitiesOperation(
923    const PrivetJSONOperation::ResultCallback& callback) {
924  return scoped_ptr<PrivetJSONOperation>(new PrivetJSONOperationImpl(
925      info_client(), kPrivetCapabilitiesPath, "", callback));
926}
927
928scoped_ptr<PrivetLocalPrintOperation>
929PrivetV1HTTPClientImpl::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