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