1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875
|
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/printing/ppd_provider.h"
#include <algorithm>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/containers/queue.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "base/time/time.h"
#include "chromeos/printing/ppd_cache.h"
#include "chromeos/printing/ppd_metadata_manager.h"
#include "chromeos/printing/printer_config_cache.h"
#include "chromeos/printing/printer_configuration.h"
#include "chromeos/printing/printing_constants.h"
#include "chromeos/printing/remote_ppd_fetcher.h"
#include "components/device_event_log/device_event_log.h"
#include "net/base/filename_util.h"
namespace chromeos {
namespace {
// Age limit for time-sensitive API calls. Typically denotes "Please
// respond with data no older than kMaxDataAge." Arbitrarily chosen.
constexpr base::TimeDelta kMaxDataAge = base::Minutes(30LL);
bool PpdReferenceIsWellFormed(const Printer::PpdReference& reference) {
int filled_fields = 0;
if (!reference.user_supplied_ppd_url.empty()) {
++filled_fields;
GURL tmp_url(reference.user_supplied_ppd_url);
const bool is_http = tmp_url.SchemeIsHTTPOrHTTPS();
const bool is_file = tmp_url.SchemeIs("file");
const bool has_supported_scheme = is_http || is_file;
if (!tmp_url.is_valid() || !has_supported_scheme) {
LOG(ERROR) << "Invalid url for a user-supplied ppd: "
<< reference.user_supplied_ppd_url;
return false;
}
}
if (!reference.effective_make_and_model.empty()) {
++filled_fields;
}
// All effective-make-and-model strings should be lowercased, since v2.
// Since make-and-model strings could include non-Latin chars, only checking
// that it excludes all upper-case chars A-Z.
if (!std::ranges::all_of(reference.effective_make_and_model,
[](char c) { return !base::IsAsciiUpper(c); })) {
return false;
}
// Should have exactly one non-empty field.
return filled_fields == 1;
}
std::string PpdPathInServingRoot(std::string_view ppd_basename) {
return base::StrCat({"ppds_for_metadata_v3/", ppd_basename});
}
// Zebra printers that support ZPL contain "Zebra" (or "Zebra Technologies") and
// "ZPL" in the IEEE 1284 device id make and model.
bool SupportsGenericZebraPPD(const PrinterSearchData& search_data) {
return search_data.printer_id.make().starts_with("Zebra") &&
base::Contains(search_data.printer_id.model(), "ZPL");
}
// This class implements the PpdProvider interface for the v3 metadata
// (https://crbug.com/888189).
class PpdProviderImpl : public PpdProvider {
public:
PpdProviderImpl(const base::Version& current_version,
scoped_refptr<PpdCache> cache,
std::unique_ptr<PpdMetadataManager> metadata_manager,
std::unique_ptr<PrinterConfigCache> config_cache,
std::unique_ptr<RemotePpdFetcher> remote_ppd_fetcher)
: version_(current_version),
ppd_cache_(cache),
metadata_manager_(std::move(metadata_manager)),
config_cache_(std::move(config_cache)),
remote_ppd_fetcher_(std::move(remote_ppd_fetcher)),
file_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::USER_VISIBLE, base::MayBlock(),
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
void ResolveManufacturers(ResolveManufacturersCallback cb) override {
metadata_manager_->GetManufacturers(kMaxDataAge, std::move(cb));
}
void ResolvePrinters(const std::string& manufacturer,
ResolvePrintersCallback cb) override {
PpdMetadataManager::GetPrintersCallback manager_callback =
base::BindOnce(&PpdProviderImpl::OnPrintersGotten,
weak_factory_.GetWeakPtr(), std::move(cb));
metadata_manager_->GetPrinters(manufacturer, kMaxDataAge,
std::move(manager_callback));
}
// This method examines the members of |search_data| in turn and seeks
// out an appropriate PPD from the serving root. The order is
// 1. |search_data|::make_and_model - we seek out
// effective-make-and-model strings from forward index metadata.
// 2. |search_data|::usb_*_id - we seek out a device with a matching
// ID from USB index metadata.
//
// * This method observes and honors PPD restrictions (furnished by
// forward index metadata) and will ignore PPDs that are not
// advertised to run with the current |version_|.
void ResolvePpdReference(const PrinterSearchData& search_data,
ResolvePpdReferenceCallback cb) override {
// In v3 metadata, effective-make-and-model strings are only
// expressed in lowercased ASCII.
PrinterSearchData lowercased_search_data(search_data);
for (std::string& emm : lowercased_search_data.make_and_model) {
emm = base::ToLowerASCII(emm);
}
// Any Zebra printer that supports ZPL uses the same PPD file, which is
// kept in the PPD index with the key "zebra zpl label printer".
if (SupportsGenericZebraPPD(lowercased_search_data)) {
lowercased_search_data.make_and_model.clear();
lowercased_search_data.make_and_model.push_back(
"zebra zpl label printer");
}
ResolvePpdReferenceContext context(lowercased_search_data, std::move(cb));
// Initiate step 1 if possible.
if (!lowercased_search_data.make_and_model.empty()) {
auto callback = base::BindOnce(
&PpdProviderImpl::TryToResolvePpdReferenceFromForwardIndices,
weak_factory_.GetWeakPtr(), std::move(context));
metadata_manager_->FindAllEmmsAvailableInIndex(
lowercased_search_data.make_and_model, kMaxDataAge,
std::move(callback));
return;
}
// Otherwise, jump straight to step 2.
TryToResolvePpdReferenceFromUsbIndices(std::move(context));
}
// This method invokes |cb| with the contents of a successfully
// retrieved PPD appropriate for |reference|.
//
// As a side effect, this method may attempt
// * to read a PPD from the user's files (if the PPD is a
// user-supplied local file) or
// * to download a PPD from an http(s) URL (if the PPD is specified by a
// user-supplied remote URL
// * to download a PPD from the serving root (if the PPD is specified by
// effective-make-and-model).
void ResolvePpd(const Printer::PpdReference& reference,
ResolvePpdCallback cb) override {
// In v3 metadata, effective-make-and-model strings are only
// expressed in lowercased ASCII.
Printer::PpdReference lowercased_reference(reference);
lowercased_reference.effective_make_and_model =
base::ToLowerASCII(lowercased_reference.effective_make_and_model);
if (!PpdReferenceIsWellFormed(lowercased_reference)) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb),
CallbackResultCode::INTERNAL_ERROR, ""));
return;
}
if (!lowercased_reference.user_supplied_ppd_url.empty()) {
ResolveUserSuppliedPpd(lowercased_reference, std::move(cb));
return;
}
std::vector<std::string> target_emm = {
lowercased_reference.effective_make_and_model};
auto callback =
base::BindOnce(&PpdProviderImpl::OnPpdBasenameSoughtFromForwardIndex,
weak_factory_.GetWeakPtr(),
std::move(lowercased_reference), std::move(cb));
metadata_manager_->FindAllEmmsAvailableInIndex(target_emm, kMaxDataAge,
std::move(callback));
}
void ReverseLookup(const std::string& effective_make_and_model,
ReverseLookupCallback cb) override {
// In v3 metadata, effective-make-and-model strings are only
// expressed in lowercased ASCII.
std::string lowercased_effective_make_and_model =
base::ToLowerASCII(effective_make_and_model);
// Delegates directly to the PpdMetadataManager.
metadata_manager_->SplitMakeAndModel(lowercased_effective_make_and_model,
kMaxDataAge, std::move(cb));
}
// This method depends on forward indices.
void ResolvePpdLicense(std::string_view effective_make_and_model,
ResolvePpdLicenseCallback cb) override {
// In v3 metadata, effective-make-and-model strings are only
// expressed in lowercased ASCII.
const std::string lowercased_effective_make_and_model =
base::ToLowerASCII(effective_make_and_model);
auto callback = base::BindOnce(
&PpdProviderImpl::FindLicenseForEmm, weak_factory_.GetWeakPtr(),
lowercased_effective_make_and_model, std::move(cb));
metadata_manager_->FindAllEmmsAvailableInIndex(
{lowercased_effective_make_and_model}, kMaxDataAge,
std::move(callback));
}
protected:
~PpdProviderImpl() override = default;
private:
// Convenience container used throughout ResolvePpdReference().
struct ResolvePpdReferenceContext {
ResolvePpdReferenceContext(const PrinterSearchData& search_data_arg,
ResolvePpdReferenceCallback cb_arg)
: search_data(search_data_arg), cb(std::move(cb_arg)) {}
~ResolvePpdReferenceContext() = default;
// This container is not copyable and is move-only.
ResolvePpdReferenceContext(ResolvePpdReferenceContext&& other) = default;
ResolvePpdReferenceContext& operator=(ResolvePpdReferenceContext&& other) =
default;
PrinterSearchData search_data;
ResolvePpdReferenceCallback cb;
};
// Used internally in ResolvePpd(). Describes the physical, bitwise
// origin of a PPD.
//
// Example: a PPD previously downloaded from the serving root is saved
// into the local PpdCache. A subsequent call to ResolvePpd() searches
// the local PpdCache and returns this PPD. Internally, the methods
// that comprise ResolvePpd() treat this as kFromPpdCache.
enum class ResolvedPpdOrigin {
kFromServingRoot,
kFromUserSuppliedUrl,
kFromPpdCache,
};
// Returns an empty string on failure.
static std::string FetchFile(const GURL& ppd_url) {
DCHECK(ppd_url.is_valid());
DCHECK(ppd_url.SchemeIs("file"));
base::ScopedBlockingCall scoped_blocking_call(
FROM_HERE, base::BlockingType::MAY_BLOCK);
base::FilePath path;
if (!net::FileURLToFilePath(ppd_url, &path)) {
LOG(ERROR) << "Not a valid file URL.";
return "";
}
std::string file_contents;
if (!base::ReadFileToString(path, &file_contents)) {
return "";
}
return file_contents;
}
// Evaluates true if our |version_| falls within the bounds set by
// |restrictions|.
bool CurrentVersionSatisfiesRestrictions(
const Restrictions& restrictions) const {
if ((restrictions.min_milestone.has_value() &&
restrictions.min_milestone.value().IsValid() &&
version_ < restrictions.min_milestone) ||
(restrictions.max_milestone.has_value() &&
restrictions.max_milestone.value().IsValid() &&
version_ > restrictions.max_milestone)) {
return false;
}
return true;
}
// Callback fed to PpdMetadataManager::GetPrinters().
void OnPrintersGotten(ResolvePrintersCallback cb,
bool succeeded,
const ParsedPrinters& printers) {
if (!succeeded) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), CallbackResultCode::SERVER_ERROR,
ResolvedPrintersList()));
return;
}
ResolvedPrintersList printers_available_to_our_version;
for (const ParsedPrinter& printer : printers) {
if (CurrentVersionSatisfiesRestrictions(printer.restrictions)) {
Printer::PpdReference ppd_reference;
ppd_reference.effective_make_and_model =
printer.effective_make_and_model;
printers_available_to_our_version.push_back(ResolvedPpdReference{
printer.user_visible_printer_name, ppd_reference});
}
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), CallbackResultCode::SUCCESS,
printers_available_to_our_version));
}
// Finds the first ParsedIndexLeaf keyed on |effective_make_and_model|
// from |forward_index_subset| (a slice of forward index metadata)
// that is allowed for use in our current |version_|.
//
// Note that |forward_index_subset| has the type returned by
// PpdMetadataManager::FindAllEmmsAvailableInIndexCallback.
const ParsedIndexLeaf* FirstAllowableParsedIndexLeaf(
std::string_view effective_make_and_model,
const base::flat_map<std::string, ParsedIndexValues>&
forward_index_subset) const {
const auto& iter = forward_index_subset.find(effective_make_and_model);
if (iter == forward_index_subset.end()) {
return nullptr;
}
for (const ParsedIndexLeaf& index_leaf : iter->second.values) {
if (CurrentVersionSatisfiesRestrictions(index_leaf.restrictions)) {
return &index_leaf;
}
}
return nullptr;
}
static void SuccessfullyResolvePpdReferenceWithEmm(
std::string_view effective_make_and_model,
ResolvePpdReferenceCallback cb) {
Printer::PpdReference reference;
reference.effective_make_and_model = std::string(effective_make_and_model);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), CallbackResultCode::SUCCESS,
std::move(reference), /*manufacturer=*/""));
}
// Fails a prior call to ResolvePpdReference().
// |usb_manufacturer| may be empty
// * if we didn't find a manufacturer name for the given vendor ID or
// * if we invoked this method directly with an empty manufacturer
// name: "this wasn't a USB printer in the first place, so there's
// no USB manufacturer to speak of."
static void FailToResolvePpdReferenceWithUsbManufacturer(
ResolvePpdReferenceCallback cb,
const std::string& usb_manufacturer) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), CallbackResultCode::NOT_FOUND,
Printer::PpdReference(), usb_manufacturer));
}
// Entry point to fail a prior call to ResolvePpdReference().
void FailToResolvePpdReference(ResolvePpdReferenceContext context) {
if (context.search_data.discovery_type ==
PrinterSearchData::PrinterDiscoveryType::kUsb) {
auto callback = base::BindOnce(
&PpdProviderImpl::FailToResolvePpdReferenceWithUsbManufacturer,
std::move(context.cb));
metadata_manager_->GetUsbManufacturerName(
context.search_data.usb_vendor_id, kMaxDataAge, std::move(callback));
return;
}
// If |search_data| does not describe a USB printer, the |cb| is
// posted in the same way, but with an empty USB manufacturer name.
FailToResolvePpdReferenceWithUsbManufacturer(std::move(context.cb),
/*manufacturer=*/"");
}
// Continues a prior call to ResolvePpdReference() (step 2).
// This callback is fed to
// PpdMetadataManager::FindAllEmmsAvailableInIndex().
void OnForwardIndicesSearchedForUsbEmm(
ResolvePpdReferenceContext context,
const std::string& effective_make_and_model_from_usb_index,
const base::flat_map<std::string, ParsedIndexValues>&
forward_index_results) {
const ParsedIndexLeaf* const index_leaf = FirstAllowableParsedIndexLeaf(
effective_make_and_model_from_usb_index, forward_index_results);
if (index_leaf) {
SuccessfullyResolvePpdReferenceWithEmm(
effective_make_and_model_from_usb_index, std::move(context.cb));
return;
}
// At this point, we couldn't build a PpdReference from the
// effective-make-and-model string sourced from the USB index.
// ResolvePpdReference() can only fail now.
FailToResolvePpdReference(std::move(context));
}
// Continues a prior call to ResolvePpdReference() (step 2).
// This callback is fed to PpdMetadataManager::FindDeviceInUsbIndex().
void OnUsbIndicesSearched(ResolvePpdReferenceContext context,
const std::string& effective_make_and_model) {
if (!effective_make_and_model.empty()) {
auto callback =
base::BindOnce(&PpdProviderImpl::OnForwardIndicesSearchedForUsbEmm,
weak_factory_.GetWeakPtr(), std::move(context),
effective_make_and_model);
metadata_manager_->FindAllEmmsAvailableInIndex(
{effective_make_and_model}, kMaxDataAge, std::move(callback));
return;
}
// At this point, we couldn't build a PpdReference from a USB index
// search. ResolvePpdReference() can only fail now.
FailToResolvePpdReference(std::move(context));
}
// Continues a prior call to ResolvePpdReference() (step 2).
void TryToResolvePpdReferenceFromUsbIndices(
ResolvePpdReferenceContext context) {
const int vendor_id = context.search_data.usb_vendor_id;
const int product_id = context.search_data.usb_product_id;
if (vendor_id && product_id) {
auto callback =
base::BindOnce(&PpdProviderImpl::OnUsbIndicesSearched,
weak_factory_.GetWeakPtr(), std::move(context));
metadata_manager_->FindDeviceInUsbIndex(vendor_id, product_id,
kMaxDataAge, std::move(callback));
return;
}
// At this point, we couldn't use |search_data| to search USB indices.
// ResolvePpdReference() can only fail now.
FailToResolvePpdReference(std::move(context));
}
// Continues a prior call to ResolvePpdReference() (step 1).
// This callback is fed to
// PpdMetadataManager::FindAllEmmsAvailableInIndexCallback().
void TryToResolvePpdReferenceFromForwardIndices(
ResolvePpdReferenceContext context,
const base::flat_map<std::string, ParsedIndexValues>&
forward_index_results) {
// Sweeps through the results of the forward index metadata search.
// If any effective-make-and-model string advertises an available
// PPD, we use that result to post |cb|.
for (std::string_view effective_make_and_model :
context.search_data.make_and_model) {
const ParsedIndexLeaf* const index_leaf = FirstAllowableParsedIndexLeaf(
effective_make_and_model, forward_index_results);
if (!index_leaf) {
continue;
}
SuccessfullyResolvePpdReferenceWithEmm(effective_make_and_model,
std::move(context.cb));
return;
}
// At this point, we couldn't build a PpdReference directly from a
// forward index search. ResolvePpdReference() continues to step 2.
TryToResolvePpdReferenceFromUsbIndices(std::move(context));
}
// Continues a prior call to ResolvePpd().
//
// Stores a PPD with |ppd_contents| in the PPD Cache.
// Caller must provide nonempty |ppd_basename| when |ppd_origin|
// identifies the PPD as coming from the the serving root.
void StorePpdWithContents(const std::string& ppd_contents,
std::optional<std::string> ppd_basename,
ResolvedPpdOrigin ppd_origin,
Printer::PpdReference reference) {
switch (ppd_origin) {
case ResolvedPpdOrigin::kFromPpdCache:
// This very PPD was retrieved from the local PpdCache; there's no
// point in storing it again.
return;
case ResolvedPpdOrigin::kFromServingRoot:
// To service the two-step "dereference" of resolving a PPD from
// the serving root, we need to Store() the basename of this PPD
// in the local PpdCache.
DCHECK(ppd_basename.has_value());
DCHECK(!ppd_basename->empty());
DCHECK(!reference.effective_make_and_model.empty());
ppd_cache_->Store(PpdBasenameToCacheKey(ppd_basename.value()),
ppd_contents);
ppd_cache_->Store(PpdReferenceToCacheKey(reference),
ppd_basename.value());
break;
case ResolvedPpdOrigin::kFromUserSuppliedUrl:
// No special considerations for a user-supplied PPD; we can
// Store() it directly by mapping the user-supplied URI to the
// PPD contents.
DCHECK(!reference.user_supplied_ppd_url.empty());
ppd_cache_->Store(PpdReferenceToCacheKey(reference), ppd_contents);
break;
}
}
// Continues a prior call to ResolvePpd().
//
// Called when we have the contents of the PPD being resolved; we are
// on the cusp of being able to invoke the |cb|.
void ResolvePpdWithContents(ResolvedPpdOrigin ppd_origin,
std::optional<std::string> ppd_basename,
std::string ppd_contents,
Printer::PpdReference reference,
ResolvePpdCallback cb) {
DCHECK(!ppd_contents.empty());
if (ppd_contents.size() > kMaxPpdSizeBytes) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), CallbackResultCode::PPD_TOO_LARGE, ""));
return;
}
StorePpdWithContents(ppd_contents, std::move(ppd_basename), ppd_origin,
std::move(reference));
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), CallbackResultCode::SUCCESS,
std::move(ppd_contents)));
}
// Continues a prior call to ResolvePpd().
//
// Called back by PrinterConfigCache::Fetch() when we've fetched
// a PPD from the serving root.
void OnPpdFetchedFromServingRoot(
Printer::PpdReference reference,
ResolvePpdCallback cb,
const PrinterConfigCache::FetchResult& result) {
if (!result.succeeded || result.contents.empty()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), CallbackResultCode::SERVER_ERROR, ""));
return;
}
ResolvePpdWithContents(ResolvedPpdOrigin::kFromServingRoot,
/*ppd_basename=*/result.key,
/*ppd_contents=*/result.contents,
std::move(reference), std::move(cb));
}
// Continues a prior call to ResolvePpd().
//
// Called when we seek a mapping from an effective-make-and-model
// string to a PPD basename by querying the local PpdCache.
void OnPpdBasenameSoughtInPpdCache(Printer::PpdReference reference,
ResolvePpdCallback cb,
const PpdCache::FindResult& result) {
if (!result.success || result.contents.empty()) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), CallbackResultCode::NOT_FOUND, ""));
return;
}
std::string cache_key = PpdBasenameToCacheKey(result.contents);
ppd_cache_->Find(
cache_key,
base::BindOnce(&PpdProviderImpl::OnPpdFromServingRootSoughtInPpdCache,
weak_factory_.GetWeakPtr(),
/*ppd_basename=*/result.contents, std::move(reference),
std::move(cb)));
}
// Continues a prior call to ResolvePpd().
//
// Called when we have a PPD basename already and seek its contents
// in the local PpdCache.
void OnPpdFromServingRootSoughtInPpdCache(
const std::string& ppd_basename,
Printer::PpdReference reference,
ResolvePpdCallback cb,
const PpdCache::FindResult& result) {
if (!result.success || result.contents.empty()) {
// We have the PPD basename, but not the contents of the PPD
// itself in our local PpdCache. We must seek out the contents
// from the serving root.
auto callback = base::BindOnce(
&PpdProviderImpl::OnPpdFetchedFromServingRoot,
weak_factory_.GetWeakPtr(), std::move(reference), std::move(cb));
config_cache_->Fetch(PpdPathInServingRoot(ppd_basename), kMaxDataAge,
std::move(callback));
return;
}
ResolvePpdWithContents(ResolvedPpdOrigin::kFromPpdCache, ppd_basename,
result.contents, std::move(reference),
std::move(cb));
}
// Continues a prior call to ResolvePpd().
//
// Called back by PpdMetadataManager::FindAllEmmsAvailableInIndex().
//
// 1. Maps |reference|::effective_make_and_model to a PPD basename.
// a. Attempts to do so with fresh forward index metadata if
// possible, searching |forward_index_subset| for the best
// available PPD.
// b. Falls back to directly querying the local PpdCache instance,
// e.g. if the network is unreachable.
// 2. Uses basename derived in previous step to retrieve the
// appropriate PPD from the local PpdCache instance.
void OnPpdBasenameSoughtFromForwardIndex(
Printer::PpdReference reference,
ResolvePpdCallback cb,
const base::flat_map<std::string, ParsedIndexValues>&
forward_index_subset) {
const ParsedIndexLeaf* const leaf = FirstAllowableParsedIndexLeaf(
reference.effective_make_and_model, forward_index_subset);
if (!leaf || leaf->ppd_basename.empty()) {
// The forward index doesn't advise what the best fit PPD is for
// |reference|::effective_make_and_model. We can look toward the
// local PpdCache to see if we saved it previously.
std::string cache_key = PpdReferenceToCacheKey(reference);
ppd_cache_->Find(
cache_key,
base::BindOnce(&PpdProviderImpl::OnPpdBasenameSoughtInPpdCache,
weak_factory_.GetWeakPtr(), std::move(reference),
std::move(cb)));
return;
}
// The forward index does advertise a best-fit PPD basename. We
// check the local PpdCache to see if we already have it.
PRINTER_LOG(DEBUG) << reference.effective_make_and_model << " mapped to "
<< leaf->ppd_basename;
ppd_cache_->Find(
PpdBasenameToCacheKey(leaf->ppd_basename),
base::BindOnce(&PpdProviderImpl::OnPpdFromServingRootSoughtInPpdCache,
weak_factory_.GetWeakPtr(), leaf->ppd_basename,
std::move(reference), std::move(cb)));
}
// Continues a prior call to ResolvePpd().
//
// Called when we finish searching the PpdCache for a user-supplied
// PPD. This contrasts with the slightly more involved two-step
// "dereference" process in searching the PpdCache for a PPD retrieved
// from the serving root.
void OnUserSuppliedPpdSoughtInPpdCache(
Printer::PpdReference reference,
CallbackResultCode result_if_unsuccessful,
ResolvePpdCallback cb,
const PpdCache::FindResult& result) {
if (!result.success) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), result_if_unsuccessful, ""));
return;
}
ResolvePpdWithContents(ResolvedPpdOrigin::kFromPpdCache,
/*ppd_basename=*/std::nullopt, result.contents,
std::move(reference), std::move(cb));
}
// Continues a prior call to ResolvePpd().
//
// Called when we finish fetching a PPD file from device-local storage
// (e.g. from the user's home directory, not from the PpdCache).
void OnUserSuppliedPpdFetchedFromLocalFile(Printer::PpdReference reference,
ResolvePpdCallback cb,
const std::string& result) {
if (result.empty()) {
// We didn't find a nonempty PPD at the location specified by the
// user. Try searching the PpdCache and fail with NOT_FOUND if not found
// in PpdCache.
std::string cache_key = PpdReferenceToCacheKey(reference);
ppd_cache_->Find(
cache_key,
base::BindOnce(&PpdProviderImpl::OnUserSuppliedPpdSoughtInPpdCache,
weak_factory_.GetWeakPtr(), std::move(reference),
CallbackResultCode::NOT_FOUND, std::move(cb)));
return;
}
ResolvePpdWithContents(ResolvedPpdOrigin::kFromUserSuppliedUrl,
/*ppd_basename=*/std::nullopt, result,
std::move(reference), std::move(cb));
}
// Continues a prior call to ResolvePpd().
//
// Called when we finish fetching the contents of a PPD file from a remote
// URL.
void OnUserSuppliedPpdFetchedFromRemoteUrl(
Printer::PpdReference reference,
ResolvePpdCallback cb,
RemotePpdFetcher::FetchResultCode code,
std::string result) {
if (code != RemotePpdFetcher::FetchResultCode::kSuccess) {
// Fetching the PPD from remote URL was unsuccessful. Try searching the
// PpdCache and fail with SERVER_ERROR if not found in PpdCache.
std::string cache_key = PpdReferenceToCacheKey(reference);
ppd_cache_->Find(
cache_key,
base::BindOnce(&PpdProviderImpl::OnUserSuppliedPpdSoughtInPpdCache,
weak_factory_.GetWeakPtr(), std::move(reference),
CallbackResultCode::SERVER_ERROR, std::move(cb)));
return;
}
ResolvePpdWithContents(ResolvedPpdOrigin::kFromUserSuppliedUrl,
/*ppd_basename=*/std::nullopt, std::move(result),
std::move(reference), std::move(cb));
}
// Continues a prior call to ResolvePpd().
//
// 1. Attempts to invoke |cb| with the file named by
// |reference|::user_suplied_ppd_url - i.e. a live fetch from
// local disk or an http:// url.
// 2. Attempts to search the local PpdCache instance for the file
// whose cache key was built from
// |reference|::user_supplied_ppd_url.
void ResolveUserSuppliedPpd(Printer::PpdReference reference,
ResolvePpdCallback cb) {
DCHECK(!reference.user_supplied_ppd_url.empty());
GURL url(reference.user_supplied_ppd_url);
if (url.SchemeIsHTTPOrHTTPS()) {
ResolveUserSuppliedPpdFromRemoteUrl(url, std::move(reference),
std::move(cb));
} else {
ResolveUserSuppliedPpdFromLocalFile(url, std::move(reference),
std::move(cb));
}
}
void ResolveUserSuppliedPpdFromLocalFile(GURL file_url,
Printer::PpdReference reference,
ResolvePpdCallback cb) {
file_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE, base::BindOnce(&FetchFile, file_url),
base::BindOnce(&PpdProviderImpl::OnUserSuppliedPpdFetchedFromLocalFile,
weak_factory_.GetWeakPtr(), std::move(reference),
std::move(cb)));
}
void ResolveUserSuppliedPpdFromRemoteUrl(GURL url,
Printer::PpdReference reference,
ResolvePpdCallback cb) {
remote_ppd_fetcher_->Fetch(
url,
base::BindOnce(&PpdProviderImpl::OnUserSuppliedPpdFetchedFromRemoteUrl,
weak_factory_.GetWeakPtr(), std::move(reference),
std::move(cb)));
}
// Continues a prior call to ResolvePpdLicense().
// This callback is fed to
// PpdMetadataManager::FindAllEmmsAvailableInIndexCallback().
void FindLicenseForEmm(const std::string& effective_make_and_model,
ResolvePpdLicenseCallback cb,
const base::flat_map<std::string, ParsedIndexValues>&
forward_index_results) {
const ParsedIndexLeaf* const index_leaf = FirstAllowableParsedIndexLeaf(
effective_make_and_model, forward_index_results);
if (!index_leaf) {
// This particular |effective_make_and_model| is invisible to the
// current |version_|; either it is restricted or it is missing
// entirely from the forward indices.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(cb), CallbackResultCode::NOT_FOUND,
/*license_name=*/""));
return;
}
// Note that the license can also be empty; this denotes that
// no license is associated with this particular
// |effective_make_and_model| in this |version_|.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(cb), CallbackResultCode::SUCCESS,
index_leaf->license));
}
// Current version used to filter restricted ppds
const base::Version version_;
// Provides PPD storage on-device.
scoped_refptr<PpdCache> ppd_cache_;
// Interacts with and controls PPD metadata.
std::unique_ptr<PpdMetadataManager> metadata_manager_;
// Fetches PPDs from the Chrome OS Printing team's serving root.
std::unique_ptr<PrinterConfigCache> config_cache_;
// Fetches PPDs from remote http:// or https:// URLs.
std::unique_ptr<RemotePpdFetcher> remote_ppd_fetcher_;
// Where to run disk operations.
const scoped_refptr<base::SequencedTaskRunner> file_task_runner_;
base::WeakPtrFactory<PpdProviderImpl> weak_factory_{this};
};
} // namespace
PrinterSearchData::PrinterSearchData() = default;
PrinterSearchData::PrinterSearchData(const PrinterSearchData& other) = default;
PrinterSearchData::~PrinterSearchData() = default;
// static
//
// Used in production but also exposed for testing.
std::string PpdProvider::PpdReferenceToCacheKey(
const Printer::PpdReference& reference) {
DCHECK(PpdReferenceIsWellFormed(reference));
// The key prefixes here are arbitrary, but ensure we can't have an (unhashed)
// collision between keys generated from different PpdReference fields.
if (!reference.effective_make_and_model.empty()) {
return base::StrCat(
{"emm_for_metadata_v3:", reference.effective_make_and_model});
} else {
// Retains the legacy salt from the v2 PpdProvider. This is done
// to minimize user breakage when we roll out the v3 PpdProvider.
return base::StrCat({"up:", reference.user_supplied_ppd_url});
}
}
// static
//
// Used in production but also exposed for testing.
std::string PpdProvider::PpdBasenameToCacheKey(std::string_view ppd_basename) {
return base::StrCat({"ppd_basename_for_metadata_v3:", ppd_basename});
}
// static
scoped_refptr<PpdProvider> PpdProvider::Create(
const base::Version& current_version,
scoped_refptr<PpdCache> cache,
std::unique_ptr<PpdMetadataManager> metadata_manager,
std::unique_ptr<PrinterConfigCache> config_cache,
std::unique_ptr<RemotePpdFetcher> remote_ppd_fetcher) {
return base::MakeRefCounted<PpdProviderImpl>(
current_version, cache, std::move(metadata_manager),
std::move(config_cache), std::move(remote_ppd_fetcher));
}
// static
std::string_view PpdProvider::CallbackResultCodeName(CallbackResultCode code) {
switch (code) {
case SUCCESS:
return "SUCCESS";
case NOT_FOUND:
return "NOT_FOUND";
case SERVER_ERROR:
return "SERVER_ERROR";
case INTERNAL_ERROR:
return "INTERNAL_ERROR";
case PPD_TOO_LARGE:
return "PPD_TOO_LARGE";
}
}
} // namespace chromeos
|