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