1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/local_discovery/privet_http_impl.h"
6
7 #include <algorithm>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/rand_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "chrome/browser/local_discovery/privet_constants.h"
15 #include "net/base/url_util.h"
16 #include "url/gurl.h"
17
18 namespace local_discovery {
19
20 namespace {
21 const char kUrlPlaceHolder[] = "http://host/";
22 const char kPrivetRegisterActionArgName[] = "action";
23 const char kPrivetRegisterUserArgName[] = "user";
24
25 const char kPrivetURLKeyUserName[] = "user_name";
26 const char kPrivetURLKeyClientName[] = "client_name";
27 const char kPrivetURLKeyJobname[] = "job_name";
28 const char kPrivetURLKeyOffline[] = "offline";
29 const char kPrivetURLValueOffline[] = "1";
30 const char kPrivetURLValueClientName[] = "Chrome";
31
32 const char kPrivetContentTypePDF[] = "application/pdf";
33 const char kPrivetContentTypePWGRaster[] = "image/pwg-raster";
34 const char kPrivetContentTypeAny[] = "*/*";
35 const char kPrivetContentTypeCJT[] = "application/json";
36
37 const char kPrivetCDDKeySupportedContentTypes[] =
38 "printer.supported_content_type";
39
40 const char kPrivetCDDKeyContentType[] = "content_type";
41
42 const char kPrivetKeyJobID[] = "job_id";
43
44 const int kPrivetCancelationTimeoutSeconds = 3;
45
46 const int kPrivetLocalPrintMaxRetries = 2;
47
48 const int kPrivetLocalPrintDefaultTimeout = 5;
49
CreatePrivetURL(const std::string & path)50 GURL CreatePrivetURL(const std::string& path) {
51 GURL url(kUrlPlaceHolder);
52 GURL::Replacements replacements;
53 replacements.SetPathStr(path);
54 return url.ReplaceComponents(replacements);
55 }
56
CreatePrivetRegisterURL(const std::string & action,const std::string & user)57 GURL CreatePrivetRegisterURL(const std::string& action,
58 const std::string& user) {
59 GURL url = CreatePrivetURL(kPrivetRegisterPath);
60 url = net::AppendQueryParameter(url, kPrivetRegisterActionArgName, action);
61 return net::AppendQueryParameter(url, kPrivetRegisterUserArgName, user);
62 }
63
64 } // namespace
65
PrivetInfoOperationImpl(PrivetHTTPClientImpl * privet_client,PrivetInfoOperation::Delegate * delegate)66 PrivetInfoOperationImpl::PrivetInfoOperationImpl(
67 PrivetHTTPClientImpl* privet_client,
68 PrivetInfoOperation::Delegate* delegate)
69 : privet_client_(privet_client), delegate_(delegate) {
70 }
71
~PrivetInfoOperationImpl()72 PrivetInfoOperationImpl::~PrivetInfoOperationImpl() {
73 }
74
Start()75 void PrivetInfoOperationImpl::Start() {
76 url_fetcher_ = privet_client_->CreateURLFetcher(
77 CreatePrivetURL(kPrivetInfoPath), net::URLFetcher::GET, this);
78
79 url_fetcher_->DoNotRetryOnTransientError();
80 url_fetcher_->AllowEmptyPrivetToken();
81
82 url_fetcher_->Start();
83 }
84
GetHTTPClient()85 PrivetHTTPClient* PrivetInfoOperationImpl::GetHTTPClient() {
86 return privet_client_;
87 }
88
OnError(PrivetURLFetcher * fetcher,PrivetURLFetcher::ErrorType error)89 void PrivetInfoOperationImpl::OnError(PrivetURLFetcher* fetcher,
90 PrivetURLFetcher::ErrorType error) {
91 if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
92 delegate_->OnPrivetInfoDone(this, fetcher->response_code(), NULL);
93 } else {
94 delegate_->OnPrivetInfoDone(this, kPrivetHTTPCodeInternalFailure, NULL);
95 }
96 }
97
OnParsedJson(PrivetURLFetcher * fetcher,const base::DictionaryValue * value,bool has_error)98 void PrivetInfoOperationImpl::OnParsedJson(PrivetURLFetcher* fetcher,
99 const base::DictionaryValue* value,
100 bool has_error) {
101 if (!has_error)
102 privet_client_->CacheInfo(value);
103 delegate_->OnPrivetInfoDone(this, fetcher->response_code(), value);
104 }
105
PrivetRegisterOperationImpl(PrivetHTTPClientImpl * privet_client,const std::string & user,PrivetRegisterOperation::Delegate * delegate)106 PrivetRegisterOperationImpl::PrivetRegisterOperationImpl(
107 PrivetHTTPClientImpl* privet_client,
108 const std::string& user,
109 PrivetRegisterOperation::Delegate* delegate)
110 : user_(user), delegate_(delegate), privet_client_(privet_client),
111 ongoing_(false) {
112 }
113
~PrivetRegisterOperationImpl()114 PrivetRegisterOperationImpl::~PrivetRegisterOperationImpl() {
115 }
116
Start()117 void PrivetRegisterOperationImpl::Start() {
118 ongoing_ = true;
119 next_response_handler_ =
120 base::Bind(&PrivetRegisterOperationImpl::StartResponse,
121 base::Unretained(this));
122 SendRequest(kPrivetActionStart);
123 }
124
Cancel()125 void PrivetRegisterOperationImpl::Cancel() {
126 url_fetcher_.reset();
127
128 if (ongoing_) {
129 // Owned by the message loop.
130 Cancelation* cancelation = new Cancelation(privet_client_, user_);
131
132 base::MessageLoop::current()->PostDelayedTask(
133 FROM_HERE,
134 base::Bind(&PrivetRegisterOperationImpl::Cancelation::Cleanup,
135 base::Owned(cancelation)),
136 base::TimeDelta::FromSeconds(kPrivetCancelationTimeoutSeconds));
137
138 ongoing_ = false;
139 }
140 }
141
CompleteRegistration()142 void PrivetRegisterOperationImpl::CompleteRegistration() {
143 next_response_handler_ =
144 base::Bind(&PrivetRegisterOperationImpl::CompleteResponse,
145 base::Unretained(this));
146 SendRequest(kPrivetActionComplete);
147 }
148
GetHTTPClient()149 PrivetHTTPClient* PrivetRegisterOperationImpl::GetHTTPClient() {
150 return privet_client_;
151 }
152
OnError(PrivetURLFetcher * fetcher,PrivetURLFetcher::ErrorType error)153 void PrivetRegisterOperationImpl::OnError(PrivetURLFetcher* fetcher,
154 PrivetURLFetcher::ErrorType error) {
155 ongoing_ = false;
156 int visible_http_code = -1;
157 FailureReason reason = FAILURE_NETWORK;
158
159 if (error == PrivetURLFetcher::RESPONSE_CODE_ERROR) {
160 visible_http_code = fetcher->response_code();
161 reason = FAILURE_HTTP_ERROR;
162 } else if (error == PrivetURLFetcher::JSON_PARSE_ERROR) {
163 reason = FAILURE_MALFORMED_RESPONSE;
164 } else if (error == PrivetURLFetcher::TOKEN_ERROR) {
165 reason = FAILURE_TOKEN;
166 } else if (error == PrivetURLFetcher::RETRY_ERROR) {
167 reason = FAILURE_RETRY;
168 }
169
170 delegate_->OnPrivetRegisterError(this,
171 current_action_,
172 reason,
173 visible_http_code,
174 NULL);
175 }
176
OnParsedJson(PrivetURLFetcher * fetcher,const base::DictionaryValue * value,bool has_error)177 void PrivetRegisterOperationImpl::OnParsedJson(
178 PrivetURLFetcher* fetcher,
179 const base::DictionaryValue* value,
180 bool has_error) {
181 if (has_error) {
182 std::string error;
183 value->GetString(kPrivetKeyError, &error);
184
185 ongoing_ = false;
186 delegate_->OnPrivetRegisterError(this,
187 current_action_,
188 FAILURE_JSON_ERROR,
189 fetcher->response_code(),
190 value);
191 return;
192 }
193
194 // TODO(noamsml): Match the user&action with the user&action in the object,
195 // and fail if different.
196
197 next_response_handler_.Run(*value);
198 }
199
OnNeedPrivetToken(PrivetURLFetcher * fetcher,const PrivetURLFetcher::TokenCallback & callback)200 void PrivetRegisterOperationImpl::OnNeedPrivetToken(
201 PrivetURLFetcher* fetcher,
202 const PrivetURLFetcher::TokenCallback& callback) {
203 privet_client_->RefreshPrivetToken(callback);
204 }
205
SendRequest(const std::string & action)206 void PrivetRegisterOperationImpl::SendRequest(const std::string& action) {
207 current_action_ = action;
208 url_fetcher_ = privet_client_->CreateURLFetcher(
209 CreatePrivetRegisterURL(action, user_), net::URLFetcher::POST, this);
210 url_fetcher_->Start();
211 }
212
StartResponse(const base::DictionaryValue & value)213 void PrivetRegisterOperationImpl::StartResponse(
214 const base::DictionaryValue& value) {
215 next_response_handler_ =
216 base::Bind(&PrivetRegisterOperationImpl::GetClaimTokenResponse,
217 base::Unretained(this));
218
219 SendRequest(kPrivetActionGetClaimToken);
220 }
221
GetClaimTokenResponse(const base::DictionaryValue & value)222 void PrivetRegisterOperationImpl::GetClaimTokenResponse(
223 const base::DictionaryValue& value) {
224 std::string claimUrl;
225 std::string claimToken;
226 bool got_url = value.GetString(kPrivetKeyClaimURL, &claimUrl);
227 bool got_token = value.GetString(kPrivetKeyClaimToken, &claimToken);
228 if (got_url || got_token) {
229 delegate_->OnPrivetRegisterClaimToken(this, claimToken, GURL(claimUrl));
230 } else {
231 delegate_->OnPrivetRegisterError(this,
232 current_action_,
233 FAILURE_MALFORMED_RESPONSE,
234 -1,
235 NULL);
236 }
237 }
238
CompleteResponse(const base::DictionaryValue & value)239 void PrivetRegisterOperationImpl::CompleteResponse(
240 const base::DictionaryValue& value) {
241 std::string id;
242 value.GetString(kPrivetKeyDeviceID, &id);
243 ongoing_ = false;
244 expected_id_ = id;
245 StartInfoOperation();
246 }
247
OnPrivetInfoDone(PrivetInfoOperation * operation,int http_code,const base::DictionaryValue * value)248 void PrivetRegisterOperationImpl::OnPrivetInfoDone(
249 PrivetInfoOperation* operation,
250 int http_code,
251 const base::DictionaryValue* value) {
252 // TODO(noamsml): Simplify error case.
253 if (!value) {
254 delegate_->OnPrivetRegisterError(this,
255 kPrivetActionNameInfo,
256 FAILURE_NETWORK,
257 -1,
258 NULL);
259 return;
260 }
261
262 if (!value->HasKey(kPrivetInfoKeyID)) {
263 if (value->HasKey(kPrivetKeyError)) {
264 delegate_->OnPrivetRegisterError(this,
265 kPrivetActionNameInfo,
266 FAILURE_JSON_ERROR,
267 http_code,
268 value);
269 } else {
270 delegate_->OnPrivetRegisterError(this,
271 kPrivetActionNameInfo,
272 FAILURE_MALFORMED_RESPONSE,
273 -1,
274 NULL);
275 }
276 return;
277 }
278
279 std::string id;
280
281 if (!value->GetString(kPrivetInfoKeyID, &id) ||
282 id != expected_id_) {
283 delegate_->OnPrivetRegisterError(this,
284 kPrivetActionNameInfo,
285 FAILURE_MALFORMED_RESPONSE,
286 -1,
287 NULL);
288 } else {
289 delegate_->OnPrivetRegisterDone(this, id);
290 }
291 }
292
StartInfoOperation()293 void PrivetRegisterOperationImpl::StartInfoOperation() {
294 info_operation_ = privet_client_->CreateInfoOperation(this);
295 info_operation_->Start();
296 }
297
Cancelation(PrivetHTTPClientImpl * privet_client,const std::string & user)298 PrivetRegisterOperationImpl::Cancelation::Cancelation(
299 PrivetHTTPClientImpl* privet_client,
300 const std::string& user) {
301 url_fetcher_ =
302 privet_client->CreateURLFetcher(
303 CreatePrivetRegisterURL(kPrivetActionCancel, user),
304 net::URLFetcher::POST, this);
305 url_fetcher_->DoNotRetryOnTransientError();
306 url_fetcher_->Start();
307 }
308
~Cancelation()309 PrivetRegisterOperationImpl::Cancelation::~Cancelation() {
310 }
311
OnError(PrivetURLFetcher * fetcher,PrivetURLFetcher::ErrorType error)312 void PrivetRegisterOperationImpl::Cancelation::OnError(
313 PrivetURLFetcher* fetcher,
314 PrivetURLFetcher::ErrorType error) {
315 }
316
OnParsedJson(PrivetURLFetcher * fetcher,const base::DictionaryValue * value,bool has_error)317 void PrivetRegisterOperationImpl::Cancelation::OnParsedJson(
318 PrivetURLFetcher* fetcher,
319 const base::DictionaryValue* value,
320 bool has_error) {
321 }
322
Cleanup()323 void PrivetRegisterOperationImpl::Cancelation::Cleanup() {
324 // Nothing needs to be done, as base::Owned will delete this object,
325 // this callback is just here to pass ownership of the Cancelation to
326 // the message loop.
327 }
328
PrivetCapabilitiesOperationImpl(PrivetHTTPClientImpl * privet_client,PrivetCapabilitiesOperation::Delegate * delegate)329 PrivetCapabilitiesOperationImpl::PrivetCapabilitiesOperationImpl(
330 PrivetHTTPClientImpl* privet_client,
331 PrivetCapabilitiesOperation::Delegate* delegate)
332 : privet_client_(privet_client), delegate_(delegate) {
333 }
334
~PrivetCapabilitiesOperationImpl()335 PrivetCapabilitiesOperationImpl::~PrivetCapabilitiesOperationImpl() {
336 }
337
Start()338 void PrivetCapabilitiesOperationImpl::Start() {
339 url_fetcher_ = privet_client_->CreateURLFetcher(
340 CreatePrivetURL(kPrivetCapabilitiesPath), net::URLFetcher::GET, this);
341 url_fetcher_->DoNotRetryOnTransientError();
342 url_fetcher_->Start();
343 }
344
GetHTTPClient()345 PrivetHTTPClient* PrivetCapabilitiesOperationImpl::GetHTTPClient() {
346 return privet_client_;
347 }
348
OnError(PrivetURLFetcher * fetcher,PrivetURLFetcher::ErrorType error)349 void PrivetCapabilitiesOperationImpl::OnError(
350 PrivetURLFetcher* fetcher,
351 PrivetURLFetcher::ErrorType error) {
352 delegate_->OnPrivetCapabilities(this, -1, NULL);
353 }
354
OnParsedJson(PrivetURLFetcher * fetcher,const base::DictionaryValue * value,bool has_error)355 void PrivetCapabilitiesOperationImpl::OnParsedJson(
356 PrivetURLFetcher* fetcher,
357 const base::DictionaryValue* value,
358 bool has_error) {
359 delegate_->OnPrivetCapabilities(this, fetcher->response_code(), value);
360 }
361
OnNeedPrivetToken(PrivetURLFetcher * fetcher,const PrivetURLFetcher::TokenCallback & callback)362 void PrivetCapabilitiesOperationImpl::OnNeedPrivetToken(
363 PrivetURLFetcher* fetcher,
364 const PrivetURLFetcher::TokenCallback& callback) {
365 privet_client_->RefreshPrivetToken(callback);
366 }
367
PrivetLocalPrintOperationImpl(PrivetHTTPClientImpl * privet_client,PrivetLocalPrintOperation::Delegate * delegate)368 PrivetLocalPrintOperationImpl::PrivetLocalPrintOperationImpl(
369 PrivetHTTPClientImpl* privet_client,
370 PrivetLocalPrintOperation::Delegate* delegate)
371 : privet_client_(privet_client), delegate_(delegate),
372 use_pdf_(false), has_capabilities_(false), has_extended_workflow_(false),
373 started_(false), offline_(false), invalid_job_retries_(0),
374 weak_factory_(this) {
375 }
376
~PrivetLocalPrintOperationImpl()377 PrivetLocalPrintOperationImpl::~PrivetLocalPrintOperationImpl() {
378 }
379
Start()380 void PrivetLocalPrintOperationImpl::Start() {
381 DCHECK(!started_);
382
383 // We need to get the /info response so we can know which APIs are available.
384 // TODO(noamsml): Use cached info when available.
385 info_operation_ = privet_client_->CreateInfoOperation(this);
386 info_operation_->Start();
387
388 started_ = true;
389 }
390
OnPrivetInfoDone(PrivetInfoOperation * operation,int http_code,const base::DictionaryValue * value)391 void PrivetLocalPrintOperationImpl::OnPrivetInfoDone(
392 PrivetInfoOperation* operation,
393 int http_code,
394 const base::DictionaryValue* value) {
395 if (value && !value->HasKey(kPrivetKeyError)) {
396 has_capabilities_ = false;
397 has_extended_workflow_ = false;
398 bool has_printing = false;
399
400 const base::ListValue* api_list;
401 if (value->GetList(kPrivetInfoKeyAPIList, &api_list)) {
402 for (size_t i = 0; i < api_list->GetSize(); i++) {
403 std::string api;
404 api_list->GetString(i, &api);
405 if (api == kPrivetCapabilitiesPath) {
406 has_capabilities_ = true;
407 } else if (api == kPrivetSubmitdocPath) {
408 has_printing = true;
409 } else if (api == kPrivetCreatejobPath) {
410 has_extended_workflow_ = true;
411 }
412 }
413 }
414
415 if (!has_printing) {
416 delegate_->OnPrivetPrintingError(this, -1);
417 return;
418 }
419
420 StartInitialRequest();
421 } else {
422 delegate_->OnPrivetPrintingError(this, http_code);
423 }
424 }
425
StartInitialRequest()426 void PrivetLocalPrintOperationImpl::StartInitialRequest() {
427 if (has_capabilities_) {
428 GetCapabilities();
429 } else {
430 // Since we have no capabiltties, the only reasonable format we can
431 // request is PWG Raster.
432 use_pdf_ = false;
433 StartConvertToPWG();
434 }
435 }
436
GetCapabilities()437 void PrivetLocalPrintOperationImpl::GetCapabilities() {
438 current_response_ = base::Bind(
439 &PrivetLocalPrintOperationImpl::OnCapabilitiesResponse,
440 base::Unretained(this));
441
442 url_fetcher_= privet_client_->CreateURLFetcher(
443 CreatePrivetURL(kPrivetCapabilitiesPath), net::URLFetcher::GET, this);
444 url_fetcher_->DoNotRetryOnTransientError();
445
446 url_fetcher_->Start();
447 }
448
DoCreatejob()449 void PrivetLocalPrintOperationImpl::DoCreatejob() {
450 current_response_ = base::Bind(
451 &PrivetLocalPrintOperationImpl::OnCreatejobResponse,
452 base::Unretained(this));
453
454 url_fetcher_= privet_client_->CreateURLFetcher(
455 CreatePrivetURL(kPrivetCreatejobPath), net::URLFetcher::POST, this);
456 url_fetcher_->SetUploadData(kPrivetContentTypeCJT, ticket_);
457
458 url_fetcher_->Start();
459 }
460
DoSubmitdoc()461 void PrivetLocalPrintOperationImpl::DoSubmitdoc() {
462 current_response_ = base::Bind(
463 &PrivetLocalPrintOperationImpl::OnSubmitdocResponse,
464 base::Unretained(this));
465
466 GURL url = CreatePrivetURL(kPrivetSubmitdocPath);
467
468 url = net::AppendQueryParameter(url,
469 kPrivetURLKeyClientName,
470 kPrivetURLValueClientName);
471
472 if (!user_.empty()) {
473 url = net::AppendQueryParameter(url,
474 kPrivetURLKeyUserName,
475 user_);
476 }
477
478 if (!jobname_.empty()) {
479 url = net::AppendQueryParameter(url,
480 kPrivetURLKeyJobname,
481 jobname_);
482 }
483
484 if (!jobid_.empty()) {
485 url = net::AppendQueryParameter(url,
486 kPrivetKeyJobID,
487 jobid_);
488 }
489
490 if (offline_) {
491 url = net::AppendQueryParameter(url,
492 kPrivetURLKeyOffline,
493 kPrivetURLValueOffline);
494 }
495
496 url_fetcher_= privet_client_->CreateURLFetcher(
497 url, net::URLFetcher::POST, this);
498
499 if (!use_pdf_) {
500 url_fetcher_->SetUploadFilePath(kPrivetContentTypePWGRaster,
501 pwg_file_path_);
502 } else {
503 // TODO(noamsml): Move to file-based upload data?
504 std::string data_str((const char*)data_->front(), data_->size());
505 url_fetcher_->SetUploadData(kPrivetContentTypePDF, data_str);
506 }
507
508 url_fetcher_->Start();
509 }
510
StartPrinting()511 void PrivetLocalPrintOperationImpl::StartPrinting() {
512 if (has_extended_workflow_ && !ticket_.empty() && jobid_.empty()) {
513 DoCreatejob();
514 } else {
515 DoSubmitdoc();
516 }
517 }
518
StartConvertToPWG()519 void PrivetLocalPrintOperationImpl::StartConvertToPWG() {
520 if (!pwg_raster_converter_)
521 pwg_raster_converter_ = PWGRasterConverter::CreateDefault();
522 pwg_raster_converter_->Start(
523 data_,
524 conversion_settings_,
525 base::Bind(&PrivetLocalPrintOperationImpl::OnPWGRasterConverted,
526 base::Unretained(this)));
527 }
528
OnCapabilitiesResponse(bool has_error,const base::DictionaryValue * value)529 void PrivetLocalPrintOperationImpl::OnCapabilitiesResponse(
530 bool has_error,
531 const base::DictionaryValue* value) {
532 if (has_error) {
533 delegate_->OnPrivetPrintingError(this, 200);
534 return;
535 }
536
537 const base::ListValue* supported_content_types;
538 use_pdf_ = false;
539
540 if (value->GetList(kPrivetCDDKeySupportedContentTypes,
541 &supported_content_types)) {
542 for (size_t i = 0; i < supported_content_types->GetSize(); i++) {
543 const base::DictionaryValue* content_type_value;
544 std::string content_type;
545
546 if (supported_content_types->GetDictionary(i, &content_type_value) &&
547 content_type_value->GetString(kPrivetCDDKeyContentType,
548 &content_type) &&
549 (content_type == kPrivetContentTypePDF ||
550 content_type == kPrivetContentTypeAny) ) {
551 use_pdf_ = true;
552 }
553 }
554 }
555
556 if (use_pdf_) {
557 StartPrinting();
558 } else {
559 StartConvertToPWG();
560 }
561 }
562
OnSubmitdocResponse(bool has_error,const base::DictionaryValue * value)563 void PrivetLocalPrintOperationImpl::OnSubmitdocResponse(
564 bool has_error,
565 const base::DictionaryValue* value) {
566 std::string error;
567 // This error is only relevant in the case of extended workflow:
568 // If the print job ID is invalid, retry createjob and submitdoc,
569 // rather than simply retrying the current request.
570 if (has_error && value->GetString(kPrivetKeyError, &error)) {
571 if (has_extended_workflow_ &&
572 error == kPrivetErrorInvalidPrintJob &&
573 invalid_job_retries_ < kPrivetLocalPrintMaxRetries) {
574 invalid_job_retries_++;
575
576 int timeout = kPrivetLocalPrintDefaultTimeout;
577 value->GetInteger(kPrivetKeyTimeout, &timeout);
578
579 double random_scaling_factor =
580 1 + base::RandDouble() * kPrivetMaximumTimeRandomAddition;
581
582 timeout = static_cast<int>(timeout * random_scaling_factor);
583
584 timeout = std::max(timeout, kPrivetMinimumTimeout);
585
586 base::MessageLoop::current()->PostDelayedTask(
587 FROM_HERE, base::Bind(&PrivetLocalPrintOperationImpl::DoCreatejob,
588 weak_factory_.GetWeakPtr()),
589 base::TimeDelta::FromSeconds(timeout));
590 } else if (use_pdf_ && error == kPrivetErrorInvalidDocumentType) {
591 use_pdf_ = false;
592 StartConvertToPWG();
593 } else {
594 delegate_->OnPrivetPrintingError(this, 200);
595 }
596
597 return;
598 }
599
600 // If we've gotten this far, there are no errors, so we've effectively
601 // succeeded.
602 delegate_->OnPrivetPrintingDone(this);
603 }
604
OnCreatejobResponse(bool has_error,const base::DictionaryValue * value)605 void PrivetLocalPrintOperationImpl::OnCreatejobResponse(
606 bool has_error,
607 const base::DictionaryValue* value) {
608 if (has_error) {
609 delegate_->OnPrivetPrintingError(this, 200);
610 return;
611 }
612
613 // Try to get job ID from value. If not, jobid_ will be empty and we will use
614 // simple printing.
615 value->GetString(kPrivetKeyJobID, &jobid_);
616
617 DoSubmitdoc();
618 }
619
OnPWGRasterConverted(bool success,const base::FilePath & pwg_file_path)620 void PrivetLocalPrintOperationImpl::OnPWGRasterConverted(
621 bool success,
622 const base::FilePath& pwg_file_path) {
623 if (!success) {
624 delegate_->OnPrivetPrintingError(this, -1);
625 return;
626 }
627
628 DCHECK(!pwg_file_path.empty());
629
630 pwg_file_path_ = pwg_file_path;
631 StartPrinting();
632 }
633
GetHTTPClient()634 PrivetHTTPClient* PrivetLocalPrintOperationImpl::GetHTTPClient() {
635 return privet_client_;
636 }
637
OnError(PrivetURLFetcher * fetcher,PrivetURLFetcher::ErrorType error)638 void PrivetLocalPrintOperationImpl::OnError(
639 PrivetURLFetcher* fetcher,
640 PrivetURLFetcher::ErrorType error) {
641 delegate_->OnPrivetPrintingError(this, -1);
642 }
643
OnParsedJson(PrivetURLFetcher * fetcher,const base::DictionaryValue * value,bool has_error)644 void PrivetLocalPrintOperationImpl::OnParsedJson(
645 PrivetURLFetcher* fetcher,
646 const base::DictionaryValue* value,
647 bool has_error) {
648 DCHECK(!current_response_.is_null());
649 current_response_.Run(has_error, value);
650 }
651
OnNeedPrivetToken(PrivetURLFetcher * fetcher,const PrivetURLFetcher::TokenCallback & callback)652 void PrivetLocalPrintOperationImpl::OnNeedPrivetToken(
653 PrivetURLFetcher* fetcher,
654 const PrivetURLFetcher::TokenCallback& callback) {
655 privet_client_->RefreshPrivetToken(callback);
656 }
657
SetData(base::RefCountedBytes * data)658 void PrivetLocalPrintOperationImpl::SetData(base::RefCountedBytes* data) {
659 DCHECK(!started_);
660 data_ = data;
661 }
662
SetTicket(const std::string & ticket)663 void PrivetLocalPrintOperationImpl::SetTicket(const std::string& ticket) {
664 DCHECK(!started_);
665 ticket_ = ticket;
666 }
667
SetUsername(const std::string & user)668 void PrivetLocalPrintOperationImpl::SetUsername(const std::string& user) {
669 DCHECK(!started_);
670 user_= user;
671 }
672
SetJobname(const std::string & jobname)673 void PrivetLocalPrintOperationImpl::SetJobname(const std::string& jobname) {
674 DCHECK(!started_);
675 jobname_ = jobname;
676 }
677
SetOffline(bool offline)678 void PrivetLocalPrintOperationImpl::SetOffline(bool offline) {
679 DCHECK(!started_);
680 offline_ = offline;
681 }
682
SetConversionSettings(const printing::PdfRenderSettings & conversion_settings)683 void PrivetLocalPrintOperationImpl::SetConversionSettings(
684 const printing::PdfRenderSettings& conversion_settings) {
685 DCHECK(!started_);
686 conversion_settings_ = conversion_settings;
687 }
688
SetPWGRasterConverterForTesting(scoped_ptr<PWGRasterConverter> pwg_raster_converter)689 void PrivetLocalPrintOperationImpl::SetPWGRasterConverterForTesting(
690 scoped_ptr<PWGRasterConverter> pwg_raster_converter) {
691 pwg_raster_converter_ = pwg_raster_converter.Pass();
692 }
693
PrivetHTTPClientImpl(const std::string & name,const net::HostPortPair & host_port,net::URLRequestContextGetter * request_context)694 PrivetHTTPClientImpl::PrivetHTTPClientImpl(
695 const std::string& name,
696 const net::HostPortPair& host_port,
697 net::URLRequestContextGetter* request_context)
698 : name_(name),
699 fetcher_factory_(request_context),
700 host_port_(host_port) {
701 }
702
~PrivetHTTPClientImpl()703 PrivetHTTPClientImpl::~PrivetHTTPClientImpl() {
704 }
705
GetCachedInfo() const706 const base::DictionaryValue* PrivetHTTPClientImpl::GetCachedInfo() const {
707 return cached_info_.get();
708 }
709
710 scoped_ptr<PrivetRegisterOperation>
CreateRegisterOperation(const std::string & user,PrivetRegisterOperation::Delegate * delegate)711 PrivetHTTPClientImpl::CreateRegisterOperation(
712 const std::string& user,
713 PrivetRegisterOperation::Delegate* delegate) {
714 return scoped_ptr<PrivetRegisterOperation>(
715 new PrivetRegisterOperationImpl(this, user, delegate));
716 }
717
CreateInfoOperation(PrivetInfoOperation::Delegate * delegate)718 scoped_ptr<PrivetInfoOperation> PrivetHTTPClientImpl::CreateInfoOperation(
719 PrivetInfoOperation::Delegate* delegate) {
720 return scoped_ptr<PrivetInfoOperation>(
721 new PrivetInfoOperationImpl(this, delegate));
722 }
723
724 scoped_ptr<PrivetCapabilitiesOperation>
CreateCapabilitiesOperation(PrivetCapabilitiesOperation::Delegate * delegate)725 PrivetHTTPClientImpl::CreateCapabilitiesOperation(
726 PrivetCapabilitiesOperation::Delegate* delegate) {
727 return scoped_ptr<PrivetCapabilitiesOperation>(
728 new PrivetCapabilitiesOperationImpl(this, delegate));
729 }
730
731 scoped_ptr<PrivetLocalPrintOperation>
CreateLocalPrintOperation(PrivetLocalPrintOperation::Delegate * delegate)732 PrivetHTTPClientImpl::CreateLocalPrintOperation(
733 PrivetLocalPrintOperation::Delegate* delegate) {
734 return scoped_ptr<PrivetLocalPrintOperation>(
735 new PrivetLocalPrintOperationImpl(this, delegate));
736 }
737
GetName()738 const std::string& PrivetHTTPClientImpl::GetName() {
739 return name_;
740 }
741
CreateURLFetcher(const GURL & url,net::URLFetcher::RequestType request_type,PrivetURLFetcher::Delegate * delegate) const742 scoped_ptr<PrivetURLFetcher> PrivetHTTPClientImpl::CreateURLFetcher(
743 const GURL& url, net::URLFetcher::RequestType request_type,
744 PrivetURLFetcher::Delegate* delegate) const {
745 GURL::Replacements replacements;
746 replacements.SetHostStr(host_port_.host());
747 std::string port(base::IntToString(host_port_.port())); // Keep string alive.
748 replacements.SetPortStr(port);
749 GURL url2 = url.ReplaceComponents(replacements);
750 return fetcher_factory_.CreateURLFetcher(url.ReplaceComponents(replacements),
751 request_type, delegate);
752 }
753
CacheInfo(const base::DictionaryValue * cached_info)754 void PrivetHTTPClientImpl::CacheInfo(const base::DictionaryValue* cached_info) {
755 cached_info_.reset(cached_info->DeepCopy());
756 std::string token;
757 if (cached_info_->GetString(kPrivetInfoKeyToken, &token)) {
758 fetcher_factory_.set_token(token);
759 }
760 }
761
HasToken() const762 bool PrivetHTTPClientImpl::HasToken() const {
763 return fetcher_factory_.get_token() != "";
764 };
765
RefreshPrivetToken(const PrivetURLFetcher::TokenCallback & callback)766 void PrivetHTTPClientImpl::RefreshPrivetToken(
767 const PrivetURLFetcher::TokenCallback& callback) {
768 token_callbacks_.push_back(callback);
769
770 if (!info_operation_) {
771 info_operation_ = CreateInfoOperation(this);
772 info_operation_->Start();
773 }
774 }
775
OnPrivetInfoDone(PrivetInfoOperation * operation,int http_code,const base::DictionaryValue * value)776 void PrivetHTTPClientImpl::OnPrivetInfoDone(
777 PrivetInfoOperation* operation,
778 int http_code,
779 const base::DictionaryValue* value) {
780 info_operation_.reset();
781 std::string token;
782
783 // If this does not succeed, token will be empty, and an empty string
784 // is our sentinel value, since empty X-Privet-Tokens are not allowed.
785 if (value) {
786 value->GetString(kPrivetInfoKeyToken, &token);
787 }
788
789 TokenCallbackVector token_callbacks;
790 token_callbacks_.swap(token_callbacks);
791
792 for (TokenCallbackVector::iterator i = token_callbacks.begin();
793 i != token_callbacks.end(); i++) {
794 i->Run(token);
795 }
796 }
797
798 } // namespace local_discovery
799