Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chromeos/printing/ppd_provider.h" | 5 #include "chromeos/printing/ppd_provider.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <deque> | 8 #include <deque> |
| 9 #include <set> | |
| 9 #include <unordered_map> | 10 #include <unordered_map> |
| 10 #include <utility> | 11 #include <utility> |
| 11 #include <vector> | 12 #include <vector> |
| 12 | 13 |
| 13 #include "base/base64.h" | 14 #include "base/base64.h" |
| 14 #include "base/bind_helpers.h" | 15 #include "base/bind_helpers.h" |
| 15 #include "base/files/file.h" | 16 #include "base/files/file.h" |
| 16 #include "base/files/file_util.h" | 17 #include "base/files/file_util.h" |
| 17 #include "base/json/json_parser.h" | 18 #include "base/json/json_parser.h" |
| 18 #include "base/memory/ptr_util.h" | 19 #include "base/memory/ptr_util.h" |
| 19 #include "base/strings/string_number_conversions.h" | 20 #include "base/strings/string_number_conversions.h" |
| 20 #include "base/strings/string_split.h" | 21 #include "base/strings/string_split.h" |
| 22 #include "base/strings/string_tokenizer.h" | |
| 21 #include "base/strings/string_util.h" | 23 #include "base/strings/string_util.h" |
| 22 #include "base/strings/stringprintf.h" | 24 #include "base/strings/stringprintf.h" |
| 23 #include "base/synchronization/lock.h" | 25 #include "base/synchronization/lock.h" |
| 24 #include "base/task_runner_util.h" | 26 #include "base/task_runner_util.h" |
| 25 #include "base/threading/sequenced_task_runner_handle.h" | 27 #include "base/threading/sequenced_task_runner_handle.h" |
| 26 #include "base/threading/thread_restrictions.h" | 28 #include "base/threading/thread_restrictions.h" |
| 27 #include "base/time/time.h" | 29 #include "base/time/time.h" |
| 28 #include "base/values.h" | 30 #include "base/values.h" |
| 29 #include "chromeos/printing/ppd_cache.h" | 31 #include "chromeos/printing/ppd_cache.h" |
| 30 #include "chromeos/printing/printing_constants.h" | 32 #include "chromeos/printing/printing_constants.h" |
| 31 #include "net/base/load_flags.h" | 33 #include "net/base/load_flags.h" |
| 32 #include "net/http/http_status_code.h" | 34 #include "net/http/http_status_code.h" |
| 33 #include "net/url_request/url_fetcher.h" | 35 #include "net/url_request/url_fetcher.h" |
| 34 #include "net/url_request/url_fetcher_delegate.h" | 36 #include "net/url_request/url_fetcher_delegate.h" |
| 35 #include "net/url_request/url_request_context_getter.h" | 37 #include "net/url_request/url_request_context_getter.h" |
| 36 #include "url/gurl.h" | 38 #include "url/gurl.h" |
| 37 | 39 |
| 38 namespace chromeos { | 40 namespace chromeos { |
| 39 namespace printing { | 41 namespace printing { |
| 40 namespace { | 42 namespace { |
| 41 | 43 |
| 44 // Extract cupsFilter/cupsFilter2 filter names from the contents | |
| 45 // of a ppd, pre-split into lines. | |
| 46 | |
| 47 // cupsFilter2 lines look like this: | |
| 48 // | |
| 49 // *cupsFilter2: "application/vnd.cups-raster application/vnd.foo 100 | |
| 50 // rastertofoo" | |
| 51 // | |
| 52 // cupsFilter lines look like this: | |
| 53 // | |
| 54 // *cupsFilter: "application/vnd.cups-raster 100 rastertofoo" | |
| 55 // | |
| 56 // |field_name| is the starting token we look for (*cupsFilter: or | |
| 57 // *cupsFilter2:). | |
| 58 // | |
| 59 // |num_mime_types| is the number of mime types we expect to see in the quoted | |
| 60 // string. | |
| 61 // | |
| 62 // This function looks at each line in ppd_lines for lines of this format, and, | |
| 63 // for each one found, adds the name of the filter (rastertofoo in the examples | |
| 64 // above) to the returned set. | |
| 65 // | |
| 66 // This would be simpler with re2, but re2 is not an allowed dependency in | |
| 67 // this part of the tree. | |
| 68 std::set<std::string> ExtractCupsFilters( | |
| 69 const std::vector<std::string>& ppd_lines, | |
| 70 const std::string& field_name, | |
| 71 int num_mime_types) { | |
| 72 std::set<std::string> ret; | |
| 73 std::string delims(" \n\t\r\""); | |
| 74 | |
| 75 for (const std::string& line : ppd_lines) { | |
| 76 base::StringTokenizer line_tok(line, delims); | |
| 77 | |
| 78 if (!line_tok.GetNext()) { | |
| 79 continue; | |
| 80 } | |
| 81 if (line_tok.token_piece() != field_name) { | |
| 82 continue; | |
| 83 } | |
| 84 | |
| 85 // Skip the mime type(s) and cost, landing on the filter | |
| 86 // name. | |
| 87 for (int i = 0; i < num_mime_types + 2; ++i) { | |
|
skau
2017/04/11 16:59:37
Can you make this slightly less opaque? Shouldn't
Carlson
2017/04/11 19:06:49
Could do, but the tradeoffs are:
* We accept more
skau
2017/04/11 22:14:57
Thanks for the explanation. I wasn't thinking of
| |
| 88 if (!line_tok.GetNext()) { | |
| 89 // Continue the outer loop. | |
| 90 goto next_line; | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 if (line_tok.token_piece() != "") { | |
| 95 ret.insert(line_tok.token_piece().as_string()); | |
| 96 } | |
| 97 next_line : {} // Lint requires {} instead of ; for an empty statement. | |
| 98 } | |
| 99 return ret; | |
| 100 } | |
| 101 | |
| 102 // The ppd spec explicitly disallows quotes inside quoted strings, and provides | |
| 103 // no way for including escaped quotes in a quoted string. It also requires | |
| 104 // that the string be a single line, and that everything in these fields be | |
| 105 // 7-bit ASCII. The CUPS spec on these particular fields is not particularly | |
| 106 // rigorous, but specifies no way of including escaped spaces in the tokens | |
| 107 // themselves, and the cups *code* just parses out these lines with a sscanf | |
| 108 // call that uses spaces as delimiters. | |
| 109 // | |
| 110 // Furthermore, cups (post 1.5) discards all cupsFilter lines if *any* | |
| 111 // cupsFilter2 lines exist. | |
| 112 // | |
| 113 // All of this is a long way of saying the regular-expression based parsing | |
| 114 // done here is, to the best of my knowledge, actually conformant to the specs | |
| 115 // that exist, and not just a hack. | |
| 116 std::vector<std::string> ExtractFiltersFromPpd( | |
| 117 const std::string& ppd_contents) { | |
| 118 std::vector<std::string> lines = base::SplitString( | |
| 119 ppd_contents, "\n\r", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); | |
| 120 std::set<std::string> filters = ExtractCupsFilters(lines, "*cupsFilter2:", 2); | |
| 121 if (filters.empty()) { | |
| 122 // No cupsFilter2 lines found, fall back to looking for cupsFilter lines. | |
| 123 filters = ExtractCupsFilters(lines, "*cupsFilter:", 1); | |
| 124 } | |
| 125 return std::vector<std::string>(filters.begin(), filters.end()); | |
| 126 } | |
| 127 | |
| 42 // Returns false if there are obvious errors in the reference that will prevent | 128 // Returns false if there are obvious errors in the reference that will prevent |
| 43 // resolution. | 129 // resolution. |
| 44 bool PpdReferenceIsWellFormed(const Printer::PpdReference& reference) { | 130 bool PpdReferenceIsWellFormed(const Printer::PpdReference& reference) { |
| 45 int filled_fields = 0; | 131 int filled_fields = 0; |
| 46 if (!reference.user_supplied_ppd_url.empty()) { | 132 if (!reference.user_supplied_ppd_url.empty()) { |
| 47 ++filled_fields; | 133 ++filled_fields; |
| 48 GURL tmp_url(reference.user_supplied_ppd_url); | 134 GURL tmp_url(reference.user_supplied_ppd_url); |
| 49 if (!tmp_url.is_valid() || !tmp_url.SchemeIs("file")) { | 135 if (!tmp_url.is_valid() || !tmp_url.SchemeIs("file")) { |
| 50 LOG(ERROR) << "Invalid url for a user-supplied ppd: " | 136 LOG(ERROR) << "Invalid url for a user-supplied ppd: " |
| 51 << reference.user_supplied_ppd_url | 137 << reference.user_supplied_ppd_url |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 196 auto it = cached_ppd_index_->find(next.first.effective_make_and_model); | 282 auto it = cached_ppd_index_->find(next.first.effective_make_and_model); |
| 197 if (it != cached_ppd_index_->end()) { | 283 if (it != cached_ppd_index_->end()) { |
| 198 StartFetch(GetPpdURL(it->second), FT_PPD); | 284 StartFetch(GetPpdURL(it->second), FT_PPD); |
| 199 return; | 285 return; |
| 200 } | 286 } |
| 201 // This ppd reference isn't in the index. That's not good. Fail | 287 // This ppd reference isn't in the index. That's not good. Fail |
| 202 // out the current resolution and go try to start the next | 288 // out the current resolution and go try to start the next |
| 203 // thing if there is one. | 289 // thing if there is one. |
| 204 LOG(ERROR) << "PPD " << next.first.effective_make_and_model | 290 LOG(ERROR) << "PPD " << next.first.effective_make_and_model |
| 205 << " not found in server index"; | 291 << " not found in server index"; |
| 206 base::SequencedTaskRunnerHandle::Get()->PostTask( | 292 FinishPpdResolution(next.second, PpdProvider::INTERNAL_ERROR, |
| 207 FROM_HERE, | 293 std::string()); |
| 208 base::Bind(next.second, PpdProvider::INTERNAL_ERROR, std::string())); | |
| 209 ppd_resolution_queue_.pop_front(); | 294 ppd_resolution_queue_.pop_front(); |
| 210 } | 295 } |
| 211 } | 296 } |
| 212 | 297 |
| 213 void ResolvePrinters(const std::string& manufacturer, | 298 void ResolvePrinters(const std::string& manufacturer, |
| 214 const ResolvePrintersCallback& cb) override { | 299 const ResolvePrintersCallback& cb) override { |
| 215 std::unordered_map<std::string, ManufacturerMetadata>::iterator it; | 300 std::unordered_map<std::string, ManufacturerMetadata>::iterator it; |
| 216 if (cached_metadata_.get() == nullptr || | 301 if (cached_metadata_.get() == nullptr || |
| 217 (it = cached_metadata_->find(manufacturer)) == | 302 (it = cached_metadata_->find(manufacturer)) == |
| 218 cached_metadata_->end()) { | 303 cached_metadata_->end()) { |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 266 *reference = Printer::PpdReference(); | 351 *reference = Printer::PpdReference(); |
| 267 reference->effective_make_and_model = it2->second; | 352 reference->effective_make_and_model = it2->second; |
| 268 return true; | 353 return true; |
| 269 } | 354 } |
| 270 | 355 |
| 271 void ResolvePpd(const Printer::PpdReference& reference, | 356 void ResolvePpd(const Printer::PpdReference& reference, |
| 272 const ResolvePpdCallback& cb) override { | 357 const ResolvePpdCallback& cb) override { |
| 273 // Do a sanity check here, so we can assumed |reference| is well-formed in | 358 // Do a sanity check here, so we can assumed |reference| is well-formed in |
| 274 // the rest of this class. | 359 // the rest of this class. |
| 275 if (!PpdReferenceIsWellFormed(reference)) { | 360 if (!PpdReferenceIsWellFormed(reference)) { |
| 276 base::SequencedTaskRunnerHandle::Get()->PostTask( | 361 FinishPpdResolution(cb, PpdProvider::INTERNAL_ERROR, std::string()); |
| 277 FROM_HERE, base::Bind(cb, PpdProvider::INTERNAL_ERROR, "")); | |
| 278 return; | 362 return; |
| 279 } | 363 } |
| 280 // First step, check the cache. If the cache lookup fails, we'll (try to) | 364 // First step, check the cache. If the cache lookup fails, we'll (try to) |
| 281 // consult the server. | 365 // consult the server. |
| 282 ppd_cache_->Find(PpdReferenceToCacheKey(reference), | 366 ppd_cache_->Find(PpdReferenceToCacheKey(reference), |
| 283 base::Bind(&PpdProviderImpl::ResolvePpdCacheLookupDone, | 367 base::Bind(&PpdProviderImpl::ResolvePpdCacheLookupDone, |
| 284 weak_factory_.GetWeakPtr(), reference, cb)); | 368 weak_factory_.GetWeakPtr(), reference, cb)); |
| 285 } | 369 } |
| 286 | 370 |
| 287 // Our only sources of long running ops are cache fetches and network fetches. | 371 // Our only sources of long running ops are cache fetches and network fetches. |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 382 // Fetch the file pointed at by url and store it in |file_fetch_contents_|. | 466 // Fetch the file pointed at by url and store it in |file_fetch_contents_|. |
| 383 // Use |file_fetch_success_| to indicate success or failure. | 467 // Use |file_fetch_success_| to indicate success or failure. |
| 384 void FetchFile(const GURL& url) { | 468 void FetchFile(const GURL& url) { |
| 385 CHECK(url.is_valid()); | 469 CHECK(url.is_valid()); |
| 386 CHECK(url.SchemeIs("file")); | 470 CHECK(url.SchemeIs("file")); |
| 387 base::ThreadRestrictions::AssertIOAllowed(); | 471 base::ThreadRestrictions::AssertIOAllowed(); |
| 388 file_fetch_success_ = base::ReadFileToString(base::FilePath(url.path()), | 472 file_fetch_success_ = base::ReadFileToString(base::FilePath(url.path()), |
| 389 &file_fetch_contents_); | 473 &file_fetch_contents_); |
| 390 } | 474 } |
| 391 | 475 |
| 476 void FinishPpdResolution(const ResolvePpdCallback& cb, | |
| 477 PpdProvider::CallbackResultCode result_code, | |
| 478 const std::string& ppd_contents) { | |
| 479 if (result_code == PpdProvider::SUCCESS) { | |
| 480 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 481 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, ppd_contents, | |
| 482 ExtractFiltersFromPpd(ppd_contents))); | |
| 483 } else { | |
| 484 // Just post the failure. | |
| 485 base::SequencedTaskRunnerHandle::Get()->PostTask( | |
| 486 FROM_HERE, base::Bind(cb, result_code, std::string(), | |
| 487 std::vector<std::string>())); | |
| 488 } | |
| 489 } | |
| 490 | |
| 392 // Callback when the cache lookup for a ppd request finishes. If we hit in | 491 // Callback when the cache lookup for a ppd request finishes. If we hit in |
| 393 // the cache, satisfy the resolution, otherwise kick it over to the fetcher | 492 // the cache, satisfy the resolution, otherwise kick it over to the fetcher |
| 394 // queue to be grabbed from a server. | 493 // queue to be grabbed from a server. |
| 395 void ResolvePpdCacheLookupDone(const Printer::PpdReference& reference, | 494 void ResolvePpdCacheLookupDone(const Printer::PpdReference& reference, |
| 396 const ResolvePpdCallback& cb, | 495 const ResolvePpdCallback& cb, |
| 397 const PpdCache::FindResult& result) { | 496 const PpdCache::FindResult& result) { |
| 398 if (result.success) { | 497 if (result.success) { |
| 399 // Cache hit. | 498 // Cache hit. |
| 400 base::SequencedTaskRunnerHandle::Get()->PostTask( | 499 FinishPpdResolution(cb, PpdProvider::SUCCESS, result.contents); |
| 401 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, result.contents)); | |
| 402 } else { | 500 } else { |
| 403 // Cache miss. Queue it to be satisfied by the fetcher queue. | 501 // Cache miss. Queue it to be satisfied by the fetcher queue. |
| 404 ppd_resolution_queue_.push_back({reference, cb}); | 502 ppd_resolution_queue_.push_back({reference, cb}); |
| 405 MaybeStartFetch(); | 503 MaybeStartFetch(); |
| 406 } | 504 } |
| 407 } | 505 } |
| 408 | 506 |
| 409 // Handler for the completion of the locales fetch. This response should be a | 507 // Handler for the completion of the locales fetch. This response should be a |
| 410 // list of strings, each of which is a locale in which we can answer queries | 508 // list of strings, each of which is a locale in which we can answer queries |
| 411 // on the server. The server (should) guarantee that we get 'en' as an | 509 // on the server. The server (should) guarantee that we get 'en' as an |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 552 } | 650 } |
| 553 } | 651 } |
| 554 } | 652 } |
| 555 | 653 |
| 556 // This is called when |fetcher_| should have just downloaded a ppd. If we | 654 // This is called when |fetcher_| should have just downloaded a ppd. If we |
| 557 // downloaded something successfully, use it to satisfy the front of the ppd | 655 // downloaded something successfully, use it to satisfy the front of the ppd |
| 558 // resolution queue, otherwise fail out that resolution. | 656 // resolution queue, otherwise fail out that resolution. |
| 559 void OnPpdFetchComplete() { | 657 void OnPpdFetchComplete() { |
| 560 DCHECK(!ppd_resolution_queue_.empty()); | 658 DCHECK(!ppd_resolution_queue_.empty()); |
| 561 std::string contents; | 659 std::string contents; |
| 660 | |
| 562 if ((ValidateAndGetResponseAsString(&contents) != PpdProvider::SUCCESS) || | 661 if ((ValidateAndGetResponseAsString(&contents) != PpdProvider::SUCCESS) || |
| 563 contents.size() > kMaxPpdSizeBytes) { | 662 contents.size() > kMaxPpdSizeBytes) { |
| 564 base::SequencedTaskRunnerHandle::Get()->PostTask( | 663 FinishPpdResolution(ppd_resolution_queue_.front().second, |
| 565 FROM_HERE, base::Bind(ppd_resolution_queue_.front().second, | 664 PpdProvider::SERVER_ERROR, std::string()); |
| 566 PpdProvider::SERVER_ERROR, std::string())); | |
| 567 } else { | 665 } else { |
| 568 ppd_cache_->Store( | 666 ppd_cache_->Store( |
| 569 PpdReferenceToCacheKey(ppd_resolution_queue_.front().first), contents, | 667 PpdReferenceToCacheKey(ppd_resolution_queue_.front().first), contents, |
| 570 | |
| 571 base::Callback<void()>()); | 668 base::Callback<void()>()); |
| 572 base::SequencedTaskRunnerHandle::Get()->PostTask( | 669 FinishPpdResolution(ppd_resolution_queue_.front().second, |
| 573 FROM_HERE, base::Bind(ppd_resolution_queue_.front().second, | 670 PpdProvider::SUCCESS, contents); |
| 574 PpdProvider::SUCCESS, contents)); | |
| 575 } | 671 } |
| 576 ppd_resolution_queue_.pop_front(); | 672 ppd_resolution_queue_.pop_front(); |
| 577 } | 673 } |
| 578 | 674 |
| 579 // Called when |fetcher_| should have just downloaded a usb device map | 675 // Called when |fetcher_| should have just downloaded a usb device map |
| 580 // for the vendor at the head of the |usb_resolution_queue_|. | 676 // for the vendor at the head of the |usb_resolution_queue_|. |
| 581 void OnUsbFetchComplete() { | 677 void OnUsbFetchComplete() { |
| 582 DCHECK(!usb_resolution_queue_.empty()); | 678 DCHECK(!usb_resolution_queue_.empty()); |
| 583 std::string contents; | 679 std::string contents; |
| 584 std::string buffer; | 680 std::string buffer; |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 646 // the necessary index data from the server. Note we leave any user-based ppd | 742 // the necessary index data from the server. Note we leave any user-based ppd |
| 647 // resolutions intact, as they don't depend on the data we're missing. | 743 // resolutions intact, as they don't depend on the data we're missing. |
| 648 void FailQueuedServerPpdResolutions(PpdProvider::CallbackResultCode code) { | 744 void FailQueuedServerPpdResolutions(PpdProvider::CallbackResultCode code) { |
| 649 std::deque<std::pair<Printer::PpdReference, ResolvePpdCallback>> | 745 std::deque<std::pair<Printer::PpdReference, ResolvePpdCallback>> |
| 650 filtered_queue; | 746 filtered_queue; |
| 651 | 747 |
| 652 for (const auto& entry : ppd_resolution_queue_) { | 748 for (const auto& entry : ppd_resolution_queue_) { |
| 653 if (!entry.first.user_supplied_ppd_url.empty()) { | 749 if (!entry.first.user_supplied_ppd_url.empty()) { |
| 654 filtered_queue.push_back(entry); | 750 filtered_queue.push_back(entry); |
| 655 } else { | 751 } else { |
| 656 base::SequencedTaskRunnerHandle::Get()->PostTask( | 752 FinishPpdResolution(entry.second, code, std::string()); |
| 657 FROM_HERE, base::Bind(entry.second, code, std::string())); | |
| 658 } | 753 } |
| 659 } | 754 } |
| 660 ppd_resolution_queue_ = std::move(filtered_queue); | 755 ppd_resolution_queue_ = std::move(filtered_queue); |
| 661 } | 756 } |
| 662 | 757 |
| 663 // Given a list of possible locale strings (e.g. 'en-GB'), determine which of | 758 // Given a list of possible locale strings (e.g. 'en-GB'), determine which of |
| 664 // them we should use to best serve results for the browser locale (which was | 759 // them we should use to best serve results for the browser locale (which was |
| 665 // given to us at construction time). | 760 // given to us at construction time). |
| 666 std::string GetBestLocale(const std::vector<std::string>& available_locales) { | 761 std::string GetBestLocale(const std::vector<std::string>& available_locales) { |
| 667 // First look for an exact match. If we find one, just use that. | 762 // First look for an exact match. If we find one, just use that. |
| (...skipping 240 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 908 scoped_refptr<net::URLRequestContextGetter> url_context_getter, | 1003 scoped_refptr<net::URLRequestContextGetter> url_context_getter, |
| 909 scoped_refptr<PpdCache> ppd_cache, | 1004 scoped_refptr<PpdCache> ppd_cache, |
| 910 scoped_refptr<base::SequencedTaskRunner> disk_task_runner, | 1005 scoped_refptr<base::SequencedTaskRunner> disk_task_runner, |
| 911 const PpdProvider::Options& options) { | 1006 const PpdProvider::Options& options) { |
| 912 return scoped_refptr<PpdProvider>( | 1007 return scoped_refptr<PpdProvider>( |
| 913 new PpdProviderImpl(browser_locale, url_context_getter, ppd_cache, | 1008 new PpdProviderImpl(browser_locale, url_context_getter, ppd_cache, |
| 914 disk_task_runner, options)); | 1009 disk_task_runner, options)); |
| 915 } | 1010 } |
| 916 } // namespace printing | 1011 } // namespace printing |
| 917 } // namespace chromeos | 1012 } // namespace chromeos |
| OLD | NEW |