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