OLD | NEW |
(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 "chromeos/printing/ppd_provider.h" |
| 6 |
| 7 #include <utility> |
| 8 |
| 9 #include "base/files/file_util.h" |
| 10 #include "base/json/json_parser.h" |
| 11 #include "base/memory/ptr_util.h" |
| 12 #include "base/sequence_checker.h" |
| 13 #include "base/strings/string_number_conversions.h" |
| 14 #include "base/strings/string_util.h" |
| 15 #include "base/strings/stringprintf.h" |
| 16 #include "base/threading/sequenced_task_runner_handle.h" |
| 17 #include "base/time/time.h" |
| 18 #include "base/values.h" |
| 19 #include "chromeos/printing/ppd_cache.h" |
| 20 #include "net/base/load_flags.h" |
| 21 #include "net/http/http_status_code.h" |
| 22 #include "net/url_request/url_fetcher.h" |
| 23 #include "net/url_request/url_fetcher_delegate.h" |
| 24 #include "net/url_request/url_request_context_getter.h" |
| 25 #include "url/gurl.h" |
| 26 |
| 27 using base::FilePath; |
| 28 using net::URLFetcher; |
| 29 using std::string; |
| 30 using std::unique_ptr; |
| 31 |
| 32 namespace chromeos { |
| 33 namespace printing { |
| 34 namespace { |
| 35 |
| 36 // Expected fields from the quirks server. |
| 37 const char kJSONPPDKey[] = "compressedPpd"; |
| 38 const char kJSONLastUpdatedKey[] = "lastUpdatedTime"; |
| 39 |
| 40 class PpdProviderImpl; |
| 41 |
| 42 // URLFetcherDelegate that just forwards the complete callback back to |
| 43 // the PpdProvider that owns the delegate. |
| 44 class ForwardingURLFetcherDelegate : public net::URLFetcherDelegate { |
| 45 public: |
| 46 explicit ForwardingURLFetcherDelegate(PpdProviderImpl* owner) |
| 47 : owner_(owner) {} |
| 48 ~ForwardingURLFetcherDelegate() override {} |
| 49 |
| 50 // URLFetcherDelegate API method. Defined below since we need the |
| 51 // PpdProviderImpl definition first. |
| 52 void OnURLFetchComplete(const URLFetcher* source) override; |
| 53 |
| 54 private: |
| 55 // owner of this delegate. |
| 56 PpdProviderImpl* const owner_; |
| 57 }; |
| 58 |
| 59 class PpdProviderImpl : public PpdProvider { |
| 60 public: |
| 61 PpdProviderImpl( |
| 62 const string& api_key, |
| 63 scoped_refptr<net::URLRequestContextGetter> url_context_getter, |
| 64 unique_ptr<PpdCache> cache, |
| 65 const PpdProvider::Options& options) |
| 66 : api_key_(api_key), |
| 67 forwarding_delegate_(this), |
| 68 url_context_getter_(url_context_getter), |
| 69 cache_(std::move(cache)), |
| 70 options_(options) { |
| 71 CHECK_GT(options_.max_ppd_contents_size_, 0U); |
| 72 } |
| 73 ~PpdProviderImpl() override {} |
| 74 |
| 75 void Resolve(const Printer::PpdReference& ppd_reference, |
| 76 PpdProvider::ResolveCallback cb) override { |
| 77 CHECK(sequence_checker_.CalledOnValidSequence()); |
| 78 CHECK(base::SequencedTaskRunnerHandle::IsSet()) |
| 79 << "Resolve must be called from a SequencedTaskRunner context"; |
| 80 |
| 81 CHECK(fetcher_ == nullptr) |
| 82 << "Can't have concurrent PpdProvider Resolve calls"; |
| 83 |
| 84 base::Optional<FilePath> tmp = cache_->Find(ppd_reference); |
| 85 if (tmp) { |
| 86 // Cache hit. Schedule the callback now and return. |
| 87 base::SequencedTaskRunnerHandle::Get()->PostTask( |
| 88 FROM_HERE, base::Bind(cb, PpdProvider::SUCCESS, tmp.value())); |
| 89 return; |
| 90 } |
| 91 |
| 92 // We don't have a way to automatically resolve user-supplied PPDs yet. So |
| 93 // if we have one specified, and it's not cached, we fail out rather than |
| 94 // fall back to quirks-server based resolution. The reasoning here is that |
| 95 // if the user has specified a PPD when a quirks-server one exists, it |
| 96 // probably means the quirks server one doesn't work for some reason, so we |
| 97 // shouldn't silently use it. |
| 98 if (!ppd_reference.user_supplied_ppd_url.empty()) { |
| 99 base::SequencedTaskRunnerHandle::Get()->PostTask( |
| 100 FROM_HERE, base::Bind(cb, PpdProvider::NOT_FOUND, base::FilePath())); |
| 101 return; |
| 102 } |
| 103 |
| 104 active_reference_ = ppd_reference; |
| 105 done_callback_ = cb; |
| 106 |
| 107 fetcher_ = URLFetcher::Create(GetQuirksServerLookupURL(ppd_reference), |
| 108 URLFetcher::GET, &forwarding_delegate_); |
| 109 fetcher_->SetRequestContext(url_context_getter_.get()); |
| 110 fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | |
| 111 net::LOAD_DO_NOT_SAVE_COOKIES | |
| 112 net::LOAD_DO_NOT_SEND_COOKIES | |
| 113 net::LOAD_DO_NOT_SEND_AUTH_DATA); |
| 114 fetcher_->Start(); |
| 115 }; |
| 116 |
| 117 void AbortResolve() override { |
| 118 // UrlFetcher guarantees that when the object has been destroyed, no further |
| 119 // callbacks will occur. |
| 120 fetcher_.reset(); |
| 121 } |
| 122 |
| 123 bool CachePpd(const Printer::PpdReference& ppd_reference, |
| 124 const base::FilePath& ppd_path) override { |
| 125 string buf; |
| 126 if (!base::ReadFileToStringWithMaxSize(ppd_path, &buf, |
| 127 options_.max_ppd_contents_size_)) { |
| 128 return false; |
| 129 } |
| 130 return static_cast<bool>(cache_->Store(ppd_reference, buf)); |
| 131 } |
| 132 |
| 133 // Called on the same thread as Resolve() when the fetcher completes its |
| 134 // fetch. |
| 135 void OnURLFetchComplete() { |
| 136 CHECK(sequence_checker_.CalledOnValidSequence()); |
| 137 // Scope the allocated |fetcher_| into this function so we clean it up when |
| 138 // we're done here instead of leaving it around until the next Resolve call. |
| 139 auto fetcher = std::move(fetcher_); |
| 140 string contents; |
| 141 if ((fetcher->GetStatus().status() != net::URLRequestStatus::SUCCESS) || |
| 142 (fetcher->GetResponseCode() != net::HTTP_OK) || |
| 143 !fetcher->GetResponseAsString(&contents)) { |
| 144 // Something went wrong with the fetch. |
| 145 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath()); |
| 146 return; |
| 147 } |
| 148 |
| 149 auto dict = base::DictionaryValue::From(base::JSONReader::Read(contents)); |
| 150 if (dict == nullptr) { |
| 151 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath()); |
| 152 return; |
| 153 } |
| 154 string ppd_contents; |
| 155 string last_updated_time_string; |
| 156 uint64_t last_updated_time; |
| 157 if (!(dict->GetString(kJSONPPDKey, &ppd_contents) && |
| 158 dict->GetString(kJSONLastUpdatedKey, &last_updated_time_string) && |
| 159 base::StringToUint64(last_updated_time_string, &last_updated_time))) { |
| 160 // Malformed response. TODO(justincarlson) - LOG something here? |
| 161 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath()); |
| 162 return; |
| 163 } |
| 164 |
| 165 if (ppd_contents.size() > options_.max_ppd_contents_size_) { |
| 166 // PPD is too big. |
| 167 // |
| 168 // Note -- if we ever add shared-ppd-sourcing, e.g. we may serve a ppd to |
| 169 // a user that's not from an explicitly trusted source, we should also |
| 170 // check *uncompressed* size here to head off zip-bombs (e.g. let's |
| 171 // compress 1GBs of zeros into a 900kb file and see what cups does when it |
| 172 // tries to expand that...) |
| 173 done_callback_.Run(PpdProvider::SERVER_ERROR, FilePath()); |
| 174 return; |
| 175 } |
| 176 |
| 177 auto ppd_file = cache_->Store(active_reference_, ppd_contents); |
| 178 if (!ppd_file) { |
| 179 // Failed to store. |
| 180 done_callback_.Run(PpdProvider::INTERNAL_ERROR, FilePath()); |
| 181 return; |
| 182 } |
| 183 done_callback_.Run(PpdProvider::SUCCESS, ppd_file.value()); |
| 184 } |
| 185 |
| 186 private: |
| 187 // Generate a url to look up a manufacturer/model from the quirks server |
| 188 GURL GetQuirksServerLookupURL( |
| 189 const Printer::PpdReference& ppd_reference) const { |
| 190 return GURL(base::StringPrintf( |
| 191 "https://%s/v2/printer/manufacturers/%s/models/%s?key=%s", |
| 192 options_.quirks_server.c_str(), |
| 193 ppd_reference.effective_manufacturer.c_str(), |
| 194 ppd_reference.effective_model.c_str(), api_key_.c_str())); |
| 195 } |
| 196 |
| 197 // API key for accessing quirks server. |
| 198 const string api_key_; |
| 199 |
| 200 // Reference we're currently trying to resolve. |
| 201 Printer::PpdReference active_reference_; |
| 202 |
| 203 ForwardingURLFetcherDelegate forwarding_delegate_; |
| 204 scoped_refptr<net::URLRequestContextGetter> url_context_getter_; |
| 205 unique_ptr<PpdCache> cache_; |
| 206 |
| 207 PpdProvider::ResolveCallback done_callback_; |
| 208 |
| 209 // Check that Resolve() and its callback are sequenced appropriately. |
| 210 base::SequenceChecker sequence_checker_; |
| 211 |
| 212 // Fetcher for the current resolve call, if any. |
| 213 unique_ptr<URLFetcher> fetcher_; |
| 214 |
| 215 // Construction-time options, immutable. |
| 216 const PpdProvider::Options options_; |
| 217 }; |
| 218 |
| 219 void ForwardingURLFetcherDelegate::OnURLFetchComplete( |
| 220 const URLFetcher* source) { |
| 221 owner_->OnURLFetchComplete(); |
| 222 } |
| 223 |
| 224 } // namespace |
| 225 |
| 226 // static |
| 227 unique_ptr<PpdProvider> PpdProvider::Create( |
| 228 const string& api_key, |
| 229 scoped_refptr<net::URLRequestContextGetter> url_context_getter, |
| 230 unique_ptr<PpdCache> cache, |
| 231 const PpdProvider::Options& options) { |
| 232 return base::MakeUnique<PpdProviderImpl>(api_key, url_context_getter, |
| 233 std::move(cache), options); |
| 234 } |
| 235 |
| 236 } // namespace printing |
| 237 } // namespace chromeos |
OLD | NEW |