• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 namespace local_discovery {
19 
20 namespace {
21 const char kUrlPlaceHolder[] = "http://host/";
22 const char kPrivetRegisterActionArgName[] = "action";
23 const char kPrivetRegisterUserArgName[] = "user";
24 
25 const char kPrivetURLKeyUserName[] = "user_name";
26 const char kPrivetURLKeyClientName[] = "client_name";
27 const char kPrivetURLKeyJobname[] = "job_name";
28 const char kPrivetURLKeyOffline[] = "offline";
29 const char kPrivetURLValueOffline[] = "1";
30 const char kPrivetURLValueClientName[] = "Chrome";
31 
32 const char kPrivetContentTypePDF[] = "application/pdf";
33 const char kPrivetContentTypePWGRaster[] = "image/pwg-raster";
34 const char kPrivetContentTypeAny[] = "*/*";
35 const char kPrivetContentTypeCJT[] = "application/json";
36 
37 const char kPrivetCDDKeySupportedContentTypes[] =
38     "printer.supported_content_type";
39 
40 const char kPrivetCDDKeyContentType[] = "content_type";
41 
42 const char kPrivetKeyJobID[] = "job_id";
43 
44 const int kPrivetCancelationTimeoutSeconds = 3;
45 
46 const int kPrivetLocalPrintMaxRetries = 2;
47 
48 const int kPrivetLocalPrintDefaultTimeout = 5;
49 
CreatePrivetURL(const std::string & path)50 GURL CreatePrivetURL(const std::string& path) {
51   GURL url(kUrlPlaceHolder);
52   GURL::Replacements replacements;
53   replacements.SetPathStr(path);
54   return url.ReplaceComponents(replacements);
55 }
56 
CreatePrivetRegisterURL(const std::string & action,const std::string & user)57 GURL 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 
PrivetInfoOperationImpl(PrivetHTTPClientImpl * privet_client,PrivetInfoOperation::Delegate * delegate)66 PrivetInfoOperationImpl::PrivetInfoOperationImpl(
67     PrivetHTTPClientImpl* privet_client,
68     PrivetInfoOperation::Delegate* delegate)
69     : privet_client_(privet_client), delegate_(delegate) {
70 }
71 
~PrivetInfoOperationImpl()72 PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
73 }
74 
Start()75 void 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 
GetHTTPClient()85 PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() {
86   return privet_client_;
87 }
88 
OnError(PrivetURLFetcher * fetcher,PrivetURLFetcher::ErrorType error)89 void 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 
OnParsedJson(PrivetURLFetcher * fetcher,const base::DictionaryValue * value,bool has_error)98 void 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 
PrivetRegisterOperationImpl(PrivetHTTPClientImpl * privet_client,const std::string & user,PrivetRegisterOperation::Delegate * delegate)106 PrivetRegisterOperationImpl::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 
~PrivetRegisterOperationImpl()114 PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
115 }
116 
Start()117 void PrivetRegisterOperationImpl::Start() {
118   ongoing_ = true;
119   next_response_handler_ =
120       base::Bind(&PrivetRegisterOperationImpl::StartResponse,
121                  base::Unretained(this));
122   SendRequest(kPrivetActionStart);
123 }
124 
Cancel()125 void 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 
CompleteRegistration()142 void PrivetRegisterOperationImpl::CompleteRegistration() {
143   next_response_handler_ =
144       base::Bind(&PrivetRegisterOperationImpl::CompleteResponse,
145                  base::Unretained(this));
146   SendRequest(kPrivetActionComplete);
147 }
148 
GetHTTPClient()149 PrivetHTTPClient* PrivetRegisterOperationImpl::GetHTTPClient() {
150   return privet_client_;
151 }
152 
OnError(PrivetURLFetcher * fetcher,PrivetURLFetcher::ErrorType error)153 void 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 
OnParsedJson(PrivetURLFetcher * fetcher,const base::DictionaryValue * value,bool has_error)177 void 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 
OnNeedPrivetToken(PrivetURLFetcher * fetcher,const PrivetURLFetcher::TokenCallback & callback)200 void PrivetRegisterOperationImpl::OnNeedPrivetToken(
201     PrivetURLFetcher* fetcher,
202     const PrivetURLFetcher::TokenCallback& callback) {
203   privet_client_->RefreshPrivetToken(callback);
204 }
205 
SendRequest(const std::string & action)206 void 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 
StartResponse(const base::DictionaryValue & value)213 void 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 
GetClaimTokenResponse(const base::DictionaryValue & value)222 void 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 
CompleteResponse(const base::DictionaryValue & value)239 void 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 
OnPrivetInfoDone(PrivetInfoOperation * operation,int http_code,const base::DictionaryValue * value)248 void 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 
StartInfoOperation()293 void PrivetRegisterOperationImpl::StartInfoOperation() {
294   info_operation_ = privet_client_->CreateInfoOperation(this);
295   info_operation_->Start();
296 }
297 
Cancelation(PrivetHTTPClientImpl * privet_client,const std::string & user)298 PrivetRegisterOperationImpl::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 
~Cancelation()309 PrivetRegisterOperationImpl::Cancelation::~Cancelation() {
310 }
311 
OnError(PrivetURLFetcher * fetcher,PrivetURLFetcher::ErrorType error)312 void PrivetRegisterOperationImpl::Cancelation::OnError(
313     PrivetURLFetcher* fetcher,
314     PrivetURLFetcher::ErrorType error) {
315 }
316 
OnParsedJson(PrivetURLFetcher * fetcher,const base::DictionaryValue * value,bool has_error)317 void PrivetRegisterOperationImpl::Cancelation::OnParsedJson(
318     PrivetURLFetcher* fetcher,
319     const base::DictionaryValue* value,
320     bool has_error) {
321 }
322 
Cleanup()323 void 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 
PrivetCapabilitiesOperationImpl(PrivetHTTPClientImpl * privet_client,PrivetCapabilitiesOperation::Delegate * delegate)329 PrivetCapabilitiesOperationImpl::PrivetCapabilitiesOperationImpl(
330     PrivetHTTPClientImpl* privet_client,
331     PrivetCapabilitiesOperation::Delegate* delegate)
332     : privet_client_(privet_client), delegate_(delegate) {
333 }
334 
~PrivetCapabilitiesOperationImpl()335 PrivetCapabilitiesOperationImpl::~PrivetCapabilitiesOperationImpl() {
336 }
337 
Start()338 void 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 
GetHTTPClient()345 PrivetHTTPClient* PrivetCapabilitiesOperationImpl::GetHTTPClient() {
346   return privet_client_;
347 }
348 
OnError(PrivetURLFetcher * fetcher,PrivetURLFetcher::ErrorType error)349 void PrivetCapabilitiesOperationImpl::OnError(
350     PrivetURLFetcher* fetcher,
351     PrivetURLFetcher::ErrorType error) {
352   delegate_->OnPrivetCapabilities(this, -1, NULL);
353 }
354 
OnParsedJson(PrivetURLFetcher * fetcher,const base::DictionaryValue * value,bool has_error)355 void PrivetCapabilitiesOperationImpl::OnParsedJson(
356     PrivetURLFetcher* fetcher,
357     const base::DictionaryValue* value,
358     bool has_error) {
359   delegate_->OnPrivetCapabilities(this, fetcher->response_code(), value);
360 }
361 
OnNeedPrivetToken(PrivetURLFetcher * fetcher,const PrivetURLFetcher::TokenCallback & callback)362 void PrivetCapabilitiesOperationImpl::OnNeedPrivetToken(
363     PrivetURLFetcher* fetcher,
364     const PrivetURLFetcher::TokenCallback& callback) {
365   privet_client_->RefreshPrivetToken(callback);
366 }
367 
PrivetLocalPrintOperationImpl(PrivetHTTPClientImpl * privet_client,PrivetLocalPrintOperation::Delegate * delegate)368 PrivetLocalPrintOperationImpl::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 
~PrivetLocalPrintOperationImpl()377 PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() {
378 }
379 
Start()380 void 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 
OnPrivetInfoDone(PrivetInfoOperation * operation,int http_code,const base::DictionaryValue * value)391 void 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 
StartInitialRequest()426 void 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 
GetCapabilities()437 void 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 
DoCreatejob()449 void 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 
DoSubmitdoc()461 void 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 
StartPrinting()511 void PrivetLocalPrintOperationImpl::StartPrinting() {
512   if (has_extended_workflow_ && !ticket_.empty() && jobid_.empty()) {
513     DoCreatejob();
514   } else {
515     DoSubmitdoc();
516   }
517 }
518 
StartConvertToPWG()519 void 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 
OnCapabilitiesResponse(bool has_error,const base::DictionaryValue * value)529 void 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 
OnSubmitdocResponse(bool has_error,const base::DictionaryValue * value)563 void 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 
OnCreatejobResponse(bool has_error,const base::DictionaryValue * value)605 void 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 
OnPWGRasterConverted(bool success,const base::FilePath & pwg_file_path)620 void 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 
GetHTTPClient()634 PrivetHTTPClient* PrivetLocalPrintOperationImpl::GetHTTPClient() {
635   return privet_client_;
636 }
637 
OnError(PrivetURLFetcher * fetcher,PrivetURLFetcher::ErrorType error)638 void PrivetLocalPrintOperationImpl::OnError(
639     PrivetURLFetcher* fetcher,
640     PrivetURLFetcher::ErrorType error) {
641   delegate_->OnPrivetPrintingError(this, -1);
642 }
643 
OnParsedJson(PrivetURLFetcher * fetcher,const base::DictionaryValue * value,bool has_error)644 void 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 
OnNeedPrivetToken(PrivetURLFetcher * fetcher,const PrivetURLFetcher::TokenCallback & callback)652 void PrivetLocalPrintOperationImpl::OnNeedPrivetToken(
653     PrivetURLFetcher* fetcher,
654     const PrivetURLFetcher::TokenCallback& callback) {
655   privet_client_->RefreshPrivetToken(callback);
656 }
657 
SetData(base::RefCountedBytes * data)658 void PrivetLocalPrintOperationImpl::SetData(base::RefCountedBytes* data) {
659   DCHECK(!started_);
660   data_ = data;
661 }
662 
SetTicket(const std::string & ticket)663 void PrivetLocalPrintOperationImpl::SetTicket(const std::string& ticket) {
664   DCHECK(!started_);
665   ticket_ = ticket;
666 }
667 
SetUsername(const std::string & user)668 void PrivetLocalPrintOperationImpl::SetUsername(const std::string& user) {
669   DCHECK(!started_);
670   user_= user;
671 }
672 
SetJobname(const std::string & jobname)673 void PrivetLocalPrintOperationImpl::SetJobname(const std::string& jobname) {
674   DCHECK(!started_);
675   jobname_ = jobname;
676 }
677 
SetOffline(bool offline)678 void PrivetLocalPrintOperationImpl::SetOffline(bool offline) {
679   DCHECK(!started_);
680   offline_ = offline;
681 }
682 
SetConversionSettings(const printing::PdfRenderSettings & conversion_settings)683 void PrivetLocalPrintOperationImpl::SetConversionSettings(
684     const printing::PdfRenderSettings& conversion_settings) {
685   DCHECK(!started_);
686   conversion_settings_ = conversion_settings;
687 }
688 
SetPWGRasterConverterForTesting(scoped_ptr<PWGRasterConverter> pwg_raster_converter)689 void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting(
690     scoped_ptr<PWGRasterConverter> pwg_raster_converter) {
691   pwg_raster_converter_ = pwg_raster_converter.Pass();
692 }
693 
PrivetHTTPClientImpl(const std::string & name,const net::HostPortPair & host_port,net::URLRequestContextGetter * request_context)694 PrivetHTTPClientImpl::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 
~PrivetHTTPClientImpl()703 PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
704 }
705 
GetCachedInfo() const706 const base::DictionaryValue* PrivetHTTPClientImpl::GetCachedInfo() const {
707   return cached_info_.get();
708 }
709 
710 scoped_ptr<PrivetRegisterOperation>
CreateRegisterOperation(const std::string & user,PrivetRegisterOperation::Delegate * delegate)711 PrivetHTTPClientImpl::CreateRegisterOperation(
712     const std::string& user,
713     PrivetRegisterOperation::Delegate* delegate) {
714   return scoped_ptr<PrivetRegisterOperation>(
715       new PrivetRegisterOperationImpl(this, user, delegate));
716 }
717 
CreateInfoOperation(PrivetInfoOperation::Delegate * delegate)718 scoped_ptr<PrivetInfoOperation> PrivetHTTPClientImpl::CreateInfoOperation(
719     PrivetInfoOperation::Delegate* delegate) {
720   return scoped_ptr<PrivetInfoOperation>(
721       new PrivetInfoOperationImpl(this, delegate));
722 }
723 
724 scoped_ptr<PrivetCapabilitiesOperation>
CreateCapabilitiesOperation(PrivetCapabilitiesOperation::Delegate * delegate)725 PrivetHTTPClientImpl::CreateCapabilitiesOperation(
726     PrivetCapabilitiesOperation::Delegate* delegate) {
727   return scoped_ptr<PrivetCapabilitiesOperation>(
728       new PrivetCapabilitiesOperationImpl(this, delegate));
729 }
730 
731 scoped_ptr<PrivetLocalPrintOperation>
CreateLocalPrintOperation(PrivetLocalPrintOperation::Delegate * delegate)732 PrivetHTTPClientImpl::CreateLocalPrintOperation(
733     PrivetLocalPrintOperation::Delegate* delegate) {
734   return scoped_ptr<PrivetLocalPrintOperation>(
735       new PrivetLocalPrintOperationImpl(this, delegate));
736 }
737 
GetName()738 const std::string& PrivetHTTPClientImpl::GetName() {
739   return name_;
740 }
741 
CreateURLFetcher(const GURL & url,net::URLFetcher::RequestType request_type,PrivetURLFetcher::Delegate * delegate) const742 scoped_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 
CacheInfo(const base::DictionaryValue * cached_info)754 void 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 
HasToken() const762 bool PrivetHTTPClientImpl::HasToken() const {
763   return fetcher_factory_.get_token() != "";
764 };
765 
RefreshPrivetToken(const PrivetURLFetcher::TokenCallback & callback)766 void 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 
OnPrivetInfoDone(PrivetInfoOperation * operation,int http_code,const base::DictionaryValue * value)776 void 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