privet_http_impl.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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
24using namespace cloud_devices::printer;
25
26namespace cloud_print {
27extern const char kContentTypeJSON[];
28}
29
30namespace local_discovery {
31
32namespace {
33const char kUrlPlaceHolder[] = "http://host/";
34const char kPrivetRegisterActionArgName[] = "action";
35const char kPrivetRegisterUserArgName[] = "user";
36
37const char kPrivetURLKeyUserName[] = "user_name";
38const char kPrivetURLKeyClientName[] = "client_name";
39const char kPrivetURLKeyJobname[] = "job_name";
40const char kPrivetURLKeyOffline[] = "offline";
41const char kPrivetURLValueOffline[] = "1";
42const char kPrivetURLValueClientName[] = "Chrome";
43
44const char kPrivetContentTypePDF[] = "application/pdf";
45const char kPrivetContentTypePWGRaster[] = "image/pwg-raster";
46const char kPrivetContentTypeAny[] = "*/*";
47
48const char kPrivetStorageListPath[] = "/privet/storage/list";
49const char kPrivetStorageContentPath[] = "/privet/storage/content";
50const char kPrivetStorageParamPathFormat[] = "path=%s";
51
52const char kPrivetKeyJobID[] = "job_id";
53
54const int kPrivetCancelationTimeoutSeconds = 3;
55
56const int kPrivetLocalPrintMaxRetries = 2;
57
58const int kPrivetLocalPrintDefaultTimeout = 5;
59
60const size_t kPrivetLocalPrintMaxJobNameLength = 64;
61
62GURL CreatePrivetURL(const std::string& path) {
63  GURL url(kUrlPlaceHolder);
64  GURL::Replacements replacements;
65  replacements.SetPathStr(path);
66  return url.ReplaceComponents(replacements);
67}
68
69GURL 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
76GURL 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
89PrivetInfoOperationImpl::PrivetInfoOperationImpl(
90    PrivetHTTPClient* privet_client,
91    const PrivetJSONOperation::ResultCallback& callback)
92    : privet_client_(privet_client), callback_(callback) {
93}
94
95PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
96}
97
98void 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
108PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() {
109  return privet_client_;
110}
111
112void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher,
113                                      PrivetURLFetcher::ErrorType error) {
114  callback_.Run(NULL);
115}
116
117void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
118                                           const base::DictionaryValue* value,
119                                           bool has_error) {
120  callback_.Run(value);
121}
122
123PrivetRegisterOperationImpl::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
133PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
134}
135
136void PrivetRegisterOperationImpl::Start() {
137  ongoing_ = true;
138  next_response_handler_ =
139      base::Bind(&PrivetRegisterOperationImpl::StartResponse,
140                 base::Unretained(this));
141  SendRequest(kPrivetActionStart);
142}
143
144void 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
161void PrivetRegisterOperationImpl::CompleteRegistration() {
162  next_response_handler_ =
163      base::Bind(&PrivetRegisterOperationImpl::CompleteResponse,
164                 base::Unretained(this));
165  SendRequest(kPrivetActionComplete);
166}
167
168PrivetHTTPClient* PrivetRegisterOperationImpl::GetHTTPClient() {
169  return privet_client_;
170}
171
172void 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
196void 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
219void PrivetRegisterOperationImpl::OnNeedPrivetToken(
220    PrivetURLFetcher* fetcher,
221    const PrivetURLFetcher::TokenCallback& callback) {
222  privet_client_->RefreshPrivetToken(callback);
223}
224
225void 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
232void 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
241void 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
258void 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
267void 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
311void PrivetRegisterOperationImpl::StartInfoOperation() {
312  info_operation_ = privet_client_->CreateInfoOperation(
313      base::Bind(&PrivetRegisterOperationImpl::OnPrivetInfoDone,
314                 base::Unretained(this)));
315  info_operation_->Start();
316}
317
318PrivetRegisterOperationImpl::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
329PrivetRegisterOperationImpl::Cancelation::~Cancelation() {
330}
331
332void PrivetRegisterOperationImpl::Cancelation::OnError(
333    PrivetURLFetcher* fetcher,
334    PrivetURLFetcher::ErrorType error) {
335}
336
337void PrivetRegisterOperationImpl::Cancelation::OnParsedJson(
338    PrivetURLFetcher* fetcher,
339    const base::DictionaryValue* value,
340    bool has_error) {
341}
342
343void 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
349PrivetJSONOperationImpl::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
360PrivetJSONOperationImpl::~PrivetJSONOperationImpl() {
361}
362
363void 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
370PrivetHTTPClient* PrivetJSONOperationImpl::GetHTTPClient() {
371  return privet_client_;
372}
373
374void PrivetJSONOperationImpl::OnError(
375    PrivetURLFetcher* fetcher,
376    PrivetURLFetcher::ErrorType error) {
377  callback_.Run(NULL);
378}
379
380void PrivetJSONOperationImpl::OnParsedJson(
381    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
468PrivetLocalPrintOperationImpl::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
482PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() {
483}
484
485void 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
498void 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
528void 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
547void 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
560void 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
615void PrivetLocalPrintOperationImpl::StartPrinting() {
616  if (has_extended_workflow_ && jobid_.empty()) {
617    DoCreatejob();
618  } else {
619    DoSubmitdoc();
620  }
621}
622
623void 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
669void 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
691void 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
733void 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
748void 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
762PrivetHTTPClient* PrivetLocalPrintOperationImpl::GetHTTPClient() {
763  return privet_client_;
764}
765
766void PrivetLocalPrintOperationImpl::OnError(
767    PrivetURLFetcher* fetcher,
768    PrivetURLFetcher::ErrorType error) {
769  delegate_->OnPrivetPrintingError(this, -1);
770}
771
772void 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
780void PrivetLocalPrintOperationImpl::OnNeedPrivetToken(
781    PrivetURLFetcher* fetcher,
782    const PrivetURLFetcher::TokenCallback& callback) {
783  privet_client_->RefreshPrivetToken(callback);
784}
785
786void PrivetLocalPrintOperationImpl::SetData(base::RefCountedBytes* data) {
787  DCHECK(!started_);
788  data_ = data;
789}
790
791void PrivetLocalPrintOperationImpl::SetTicket(const std::string& ticket) {
792  DCHECK(!started_);
793  ticket_.InitFromString(ticket);
794}
795
796void PrivetLocalPrintOperationImpl::SetCapabilities(
797    const std::string& capabilities) {
798  DCHECK(!started_);
799  capabilities_.InitFromString(capabilities);
800}
801
802void PrivetLocalPrintOperationImpl::SetUsername(const std::string& user) {
803  DCHECK(!started_);
804  user_= user;
805}
806
807void PrivetLocalPrintOperationImpl::SetJobname(const std::string& jobname) {
808  DCHECK(!started_);
809  jobname_ = jobname;
810}
811
812void PrivetLocalPrintOperationImpl::SetOffline(bool offline) {
813  DCHECK(!started_);
814  offline_ = offline;
815}
816
817void PrivetLocalPrintOperationImpl::SetPageSize(const gfx::Size& page_size) {
818  DCHECK(!started_);
819  page_size_ = page_size;
820}
821
822void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting(
823    scoped_ptr<PWGRasterConverter> pwg_raster_converter) {
824  pwg_raster_converter_ = pwg_raster_converter.Pass();
825}
826
827PrivetHTTPClientImpl::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
833PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
834}
835
836const std::string& PrivetHTTPClientImpl::GetName() {
837  return name_;
838}
839
840scoped_ptr<PrivetJSONOperation> PrivetHTTPClientImpl::CreateInfoOperation(
841    const PrivetJSONOperation::ResultCallback& callback) {
842  return scoped_ptr<PrivetJSONOperation>(
843      new PrivetInfoOperationImpl(this, callback));
844}
845
846scoped_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
861void 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
873void 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
893PrivetV1HTTPClientImpl::PrivetV1HTTPClientImpl(
894    scoped_ptr<PrivetHTTPClient> info_client)
895    : info_client_(info_client.Pass()) {
896}
897
898PrivetV1HTTPClientImpl::~PrivetV1HTTPClientImpl() {
899}
900
901const std::string& PrivetV1HTTPClientImpl::GetName() {
902  return info_client()->GetName();
903}
904
905scoped_ptr<PrivetJSONOperation> PrivetV1HTTPClientImpl::CreateInfoOperation(
906    const PrivetJSONOperation::ResultCallback& callback) {
907  return info_client()->CreateInfoOperation(callback);
908}
909
910scoped_ptr<PrivetRegisterOperation>
911PrivetV1HTTPClientImpl::CreateRegisterOperation(
912    const std::string& user,
913    PrivetRegisterOperation::Delegate* delegate) {
914  return scoped_ptr<PrivetRegisterOperation>(
915      new PrivetRegisterOperationImpl(info_client(), user, delegate));
916}
917
918scoped_ptr<PrivetJSONOperation>
919PrivetV1HTTPClientImpl::CreateCapabilitiesOperation(
920    const PrivetJSONOperation::ResultCallback& callback) {
921  return scoped_ptr<PrivetJSONOperation>(new PrivetJSONOperationImpl(
922      info_client(), kPrivetCapabilitiesPath, "", callback));
923}
924
925scoped_ptr<PrivetLocalPrintOperation>
926PrivetV1HTTPClientImpl::CreateLocalPrintOperation(
927    PrivetLocalPrintOperation::Delegate* delegate) {
928  return scoped_ptr<PrivetLocalPrintOperation>(
929      new PrivetLocalPrintOperationImpl(info_client(), delegate));
930}
931
932scoped_ptr<PrivetJSONOperation>
933PrivetV1HTTPClientImpl::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
942scoped_ptr<PrivetDataReadOperation>
943PrivetV1HTTPClientImpl::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
952PrivetV3HTTPClientImpl::PrivetV3HTTPClientImpl(
953    scoped_ptr<PrivetHTTPClient> info_client)
954    : info_client_(info_client.Pass()) {
955}
956
957PrivetV3HTTPClientImpl::~PrivetV3HTTPClientImpl() {
958}
959
960const std::string& PrivetV3HTTPClientImpl::GetName() {
961  return info_client()->GetName();
962}
963
964scoped_ptr<PrivetJSONOperation> PrivetV3HTTPClientImpl::CreateInfoOperation(
965    const PrivetJSONOperation::ResultCallback& callback) {
966  return info_client()->CreateInfoOperation(callback);
967}
968
969}  // namespace local_discovery
970