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