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