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