Chromium Code Reviews| Index: chromeos/printing/ppd_cache.cc |
| diff --git a/chromeos/printing/ppd_cache.cc b/chromeos/printing/ppd_cache.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..8584ea20b5a8f190bb96c2357913f7fa82d8b0a0 |
| --- /dev/null |
| +++ b/chromeos/printing/ppd_cache.cc |
| @@ -0,0 +1,170 @@ |
| +// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chromeos/printing/ppd_cache.h" |
| + |
| +#include "base/files/file_util.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "base/path_service.h" |
| +#include "base/strings/stringprintf.h" |
| +#include "chromeos/chromeos_paths.h" |
| + |
| +using base::FilePath; |
| +using base::Optional; |
| +using std::string; |
| +using std::unique_ptr; |
| + |
| +namespace chromeos { |
| +namespace printing { |
| +namespace { |
| + |
| +// Limits on key strings we'll accept. This limit is pretty arbitrary, we |
| +// just don't want to accept strings that are so long they might cause |
| +// memory pressure or somesuch. |
| +constexpr int kKeySizeLimit = 512; |
| + |
| +// "Manufacturer" used when storing and retrieving locally-keyed |
| +// files. |
| +const char* kLocalModel = "local"; |
|
skau
2016/10/07 16:29:06
const char[] for string constants
Carlson
2016/10/14 19:28:56
OK, but any particular reason why?
skau
2016/10/14 22:10:16
Consistency. It looks to be the common interpreta
|
| + |
| +// Placeholder cache. |
| +class PPDCacheImpl : public PPDCache { |
| + public: |
| + PPDCacheImpl(const PPDCache::Options& options) |
| + : cache_base_dir_(GetCacheBaseDirOrDie()) {} |
| + ~PPDCacheImpl() override {} |
| + |
| + // Public API functions. |
| + Optional<FilePath> Find(const string& manufacturer, |
| + const string& model) const override { |
| + Optional<FilePath> ret; |
| + if (model == kLocalModel) { |
| + LOG(ERROR) << "Denying attempt to store auto-ppd under local namespace"; |
|
skau
2016/10/07 16:29:06
Denying attempt to retrieve local ppd from auto na
Carlson
2016/10/14 19:28:56
Obsolete
|
| + return ret; |
| + } |
| + if (!(KeyIsValid("Printer manufacturer", manufacturer) && |
|
skau
2016/10/07 16:29:06
Is "Printer manufacturer" a constant?
Carlson
2016/10/14 19:28:56
Obsolete
|
| + KeyIsValid("Printer model", model))) { |
| + return ret; |
| + } |
| + |
| + FilePath contents_path = GetCachePath(manufacturer, model); |
| + if (base::PathExists(contents_path)) { |
| + ret = contents_path; |
| + } |
| + return ret; |
| + } |
| + |
| + Optional<FilePath> FindLocal(const string& key) const override { |
| + Optional<FilePath> ret; |
| + if (!KeyIsValid("Printer identifier", key)) { |
| + return ret; |
| + } |
| + FilePath contents_path = GetCachePath(kLocalModel, key); |
| + if (base::PathExists(contents_path)) { |
| + ret = contents_path; |
| + } |
| + return ret; |
| + } |
| + |
| + // Store a ppd that was manually added by a user, using the given key. |
| + Optional<FilePath> StoreLocal( |
| + const string& key, |
| + const string& compressed_ppd_contents) override { |
| + if (!KeyIsValid("Printer identifier", key)) { |
| + return Optional<FilePath>(); |
| + } |
| + return StoreCommon(kLocalModel, key, compressed_ppd_contents); |
| + } |
| + |
| + Optional<FilePath> Store(const string& manufacturer, |
| + const string& model, |
| + const string& compressed_ppd_contents) override { |
| + if (model == kLocalModel) { |
| + LOG(ERROR) << "Denying attempt to store auto-ppd under local namespace"; |
| + return Optional<FilePath>(); |
| + } |
| + if (!(KeyIsValid("Printer manufacturer", manufacturer) && |
| + KeyIsValid("Printer model", model))) { |
| + return Optional<FilePath>(); |
| + } |
| + return StoreCommon(manufacturer, model, compressed_ppd_contents); |
| + } |
| + |
| + private: |
| + // Shared Store* functionality. This can assume that all arguments have been |
| + // pre-vetted and are valid. |
| + Optional<FilePath> StoreCommon(const string& manufacturer, |
| + const string& model, |
| + const string& compressed_ppd_contents) { |
| + Optional<FilePath> ret; |
| + FilePath contents_path = GetCachePath(manufacturer, model); |
| + if (base::WriteFile(contents_path, compressed_ppd_contents.data(), |
| + compressed_ppd_contents.size()) == |
| + static_cast<int>(compressed_ppd_contents.size())) { |
| + ret = contents_path; |
| + } else { |
| + LOG(ERROR) << "Failed to write " << compressed_ppd_contents.size() |
| + << " bytes to " << contents_path.LossyDisplayName(); |
| + // Try to clean up the file, as it may have partial contents. Note that |
| + // DeleteFile(nonexistant file) should return true, so failure here means |
| + // something is exceptionally hosed. |
| + if (!base::DeleteFile(contents_path, false)) { |
| + LOG(FATAL) << "Failed to cleanup partially-written file " |
|
skau
2016/10/07 16:29:06
LOG(FATAL) will crash Chrome and thus the entire U
Carlson
2016/10/14 19:28:56
Done.
|
| + << contents_path.LossyDisplayName(); |
| + } |
| + } |
| + return ret; |
| + } |
| + |
| + // File paths are generated via hashes of the manufacturer/model strings. |
| + // This isn't because we're overly worried about collisions; rather, using the |
| + // hash instead of the strings directly means we don't have to do any fiddly |
| + // path escaping, stress about unicode concerns, or worry overmuch about path |
| + // length limits. |
| + FilePath GetCachePath(const string& manufacturer, const string& model) const { |
| + size_t h = base::HashInts64(std::hash<std::string>()(manufacturer), |
| + std::hash<std::string>()(model)); |
| + return cache_base_dir_.Append(base::StringPrintf("%zx", h)); |
|
skau
2016/10/07 16:29:06
Can we attach an appropriate file extension like .
Carlson
2016/10/14 19:28:56
Done.
|
| + } |
| + |
| + // Return whether or not the given cache key is valid. Log an error |
| + // describing the problem if it's not valid. |
| + bool KeyIsValid(const string& key_name, const string& key) const { |
|
skau
2016/10/07 16:29:06
Is there a way to clarify that this validation is
Carlson
2016/10/14 19:28:56
Obsolete
|
| + if (key.empty()) { |
| + LOG(ERROR) << key_name << " cannot be empty."; |
| + return false; |
| + } |
| + if (key.size() > kKeySizeLimit) { |
| + LOG(ERROR) << key_name << " is too long. Limit is " << kKeySizeLimit; |
| + return false; |
| + } |
| + return true; |
| + } |
| + |
| + // Retrieve the printer cache filepath from PathService, or die trying. |
| + FilePath GetCacheBaseDirOrDie() const { |
| + FilePath ret; |
| + if (!PathService::Get(::chromeos::DIR_PRINTER_DRIVERS_CACHE, &ret)) { |
| + LOG(FATAL) << "Failed to get printer cache directory."; |
| + } |
| + return ret; |
| + } |
| + |
| + const FilePath cache_base_dir_; |
| +}; |
| + |
| +} // namespace |
| + |
| +// static |
| +unique_ptr<PPDCache> PPDCache::Create(const PPDCache::Options& options) { |
| + return ::base::MakeUnique<PPDCacheImpl>(options); |
| +} |
| + |
| +// static |
| +unique_ptr<PPDCache> PPDCache::Create() { |
| + return ::base::MakeUnique<PPDCacheImpl>(PPDCache::Options()); |
|
skau
2016/10/07 16:29:06
nit: If we're always passing an Options struct, we
Carlson
2016/10/14 19:28:56
Done.
|
| +} |
| + |
| +} // namespace printing |
| +} // namespace chromeos |