Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(800)

Side by Side Diff: chromeos/printing/ppd_cache.cc

Issue 2343983004: Add PPDProvider barebones implementation and associated cache skeleton. (Closed)
Patch Set: Initial PPDProvider/PPDCache implementation. Also, add associated unittests. This doesn't plumb th… Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include <vector>
6
7 #include "base/files/file_util.h"
8 #include "base/memory/ptr_util.h"
9 #include "base/path_service.h"
Lei Zhang 2016/10/19 01:25:09 Not used?
Carlson 2016/10/19 16:38:26 Thanks. Is there any iwyu tooling that's easy to
Lei Zhang 2016/10/19 23:15:45 They exist but they don't work well for Chromium i
Carlson 2016/10/19 23:38:56 Acknowledged.
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "chromeos/printing/ppd_cache.h"
Lei Zhang 2016/10/19 01:25:09 This should go above everything else.
Carlson 2016/10/19 16:38:26 Done.
13 #include "crypto/sha2.h"
Lei Zhang 2016/10/19 01:25:09 Not used?
Carlson 2016/10/19 16:38:26 Oh boy, this was actually a serious error on my pa
14 #include "net/base/io_buffer.h"
15 #include "net/filter/filter.h"
16 #include "net/filter/gzip_header.h"
17
18 using base::File;
Lei Zhang 2016/10/19 01:25:09 Can we be consistent with the changes to the unit
Carlson 2016/10/19 16:38:26 As a compromise, I removed the ones that were seld
19 using base::FilePath;
20 using base::Optional;
21 using net::Filter;
22 using std::string;
23 using std::unique_ptr;
24 using std::vector;
25
26 namespace chromeos {
27 namespace printing {
28 namespace {
29
30 // Return true if it looks like contents is already gzipped, false otherwise.
31 bool IsGZipped(const string& contents) {
32 const char* ignored;
33 net::GZipHeader header;
34 return header.ReadMore(contents.data(), contents.size(), &ignored) ==
35 net::GZipHeader::COMPLETE_HEADER;
36 }
37
38 class PpdCacheImpl : public PpdCache {
39 public:
40 explicit PpdCacheImpl(const FilePath& cache_base_dir)
41 : cache_base_dir_(cache_base_dir) {}
42 ~PpdCacheImpl() override {}
43
44 // Public API functions.
45 Optional<FilePath> Find(
46 const Printer::PpdReference& reference) const override {
47 Optional<FilePath> ret;
48
49 // We can't know here if we have a gzipped or un-gzipped version, so just
50 // look for both.
51 FilePath contents_path_base = GetCachePathBase(reference);
52 for (const string& extension : {".ppd", ".ppd.gz"}) {
53 FilePath contents_path = contents_path_base.AddExtension(extension);
54 if (base::PathExists(contents_path)) {
55 ret = contents_path;
56 break;
57 }
58 }
59 return ret;
60 }
61
62 Optional<FilePath> Store(const Printer::PpdReference& reference,
63 const string& ppd_contents) override {
64 Optional<FilePath> ret;
65 FilePath contents_path;
66 contents_path = GetCachePathBase(reference).AddExtension(".ppd");
Lei Zhang 2016/10/19 01:25:09 combine with previous line?
Carlson 2016/10/19 16:38:26 Done.
67 if (IsGZipped(ppd_contents)) {
68 contents_path = contents_path.AddExtension(".gz");
69 }
70 if (base::WriteFile(contents_path, ppd_contents.data(),
71 ppd_contents.size()) ==
72 static_cast<int>(ppd_contents.size())) {
73 ret = contents_path;
74 } else {
75 LOG(ERROR) << "Failed to write " << contents_path.LossyDisplayName();
76 // Try to clean up the file, as it may have partial contents. Note
77 // that DeleteFile(nonexistant file) should return true, so failure here
78 // means something is exceptionally hosed.
79 if (!base::DeleteFile(contents_path, false)) {
80 LOG(ERROR) << "Failed to cleanup partially-written file "
81 << contents_path.LossyDisplayName();
82 return ret;
83 }
84 }
85 return ret;
86 }
87
88 private:
89 // Get the file path at which we expect to find a PPD if it's cached.
90 //
91 // This is, ultimately, just a hash function. It's extremely infrequently
92 // used (called once when trying to look up information on a printer or store
93 // a PPD), and should be stable, as changing the function will make previously
94 // cached entries unfindable, causing resolve logic to be reinvoked
95 // unnecessarily.
96 //
97 // There's also a faint possibility that a bad actor might try to do something
98 // nefarious by intentionally causing a cache collision that makes the wrong
99 // PPD be used for a printer. There's no obvious attack vector, but
100 // there's also no real cost to being paranoid here, so we use SHA-256 as the
Lei Zhang 2016/10/19 01:25:09 Is this out of date or not implemented?
Carlson 2016/10/19 16:38:26 Noted above, forgot to implement. Thanks for noti
101 // underlying hash function, and inject fixed field prefixes to prevent
102 // field-substitution spoofing. This also buys us hash function stability at
103 // the same time.
104 //
105 // Also, care should be taken to preserve the existing hash values if new
106 // fields are added to PpdReference -- that is, if a new field F is added
107 // to PpdReference, a PpdReference with a default F value should hash to
108 // the same thing as a PpdReference that predates the addition of F to the
109 // structure.
110 //
111 // Note this function expects that the caller will append ".ppd", or ".ppd.gz"
112 // to the output as needed.
113 FilePath GetCachePathBase(const Printer::PpdReference& ref) const {
114 vector<string> pieces;
115 if (!ref.user_supplied_ppd_url.empty()) {
116 pieces.push_back("user_supplied_ppd_url:");
117 pieces.push_back(ref.user_supplied_ppd_url);
118 } else if (!ref.effective_manufacturer.empty() &&
119 !ref.effective_model.empty()) {
120 pieces.push_back("manufacturer:");
121 pieces.push_back(ref.effective_manufacturer);
122 pieces.push_back("model:");
123 pieces.push_back(ref.effective_model);
124 } else {
125 LOG(DFATAL) << "PpdCache hashing empty PpdReference, this probably means "
Lei Zhang 2016/10/19 01:25:09 NOTREACHED()
Carlson 2016/10/19 16:38:26 Sure. Out of curiosity, what's the difference? I
Lei Zhang 2016/10/19 23:15:45 NOTREACHED() better describes the intent.
Carlson 2016/10/19 23:38:56 Acknowledged.
126 "something is wrong";
127 }
128 // The separator here is not needed, but makes debug output more readable.
129 string full_key = base::JoinString(pieces, "|");
130 string ascii_hash = base::HexEncode(full_key.data(), full_key.size());
131 VLOG(3) << "PPD Cache key is " << full_key << " which hashes to "
132 << ascii_hash;
133
134 return cache_base_dir_.Append(ascii_hash);
135 }
136
137 const FilePath cache_base_dir_;
138
139 DISALLOW_COPY_AND_ASSIGN(PpdCacheImpl);
140 };
141
142 } // namespace
143
144 // static
145 unique_ptr<PpdCache> PpdCache::Create(const FilePath& cache_base_dir) {
146 return ::base::MakeUnique<PpdCacheImpl>(cache_base_dir);
Lei Zhang 2016/10/19 01:25:09 More ::base
Carlson 2016/10/19 16:38:26 Done.
147 }
148
149 } // namespace printing
150 } // namespace chromeos
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698