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