• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "components/search_provider_logos/logo_cache.h"
6 
7 #include "base/file_util.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/values.h"
12 
13 namespace {
14 
15 // The cached logo metadata is persisted as JSON using these keys.
16 const char kSourceUrlKey[] = "url";
17 const char kExpirationTimeKey[] = "expiration_time";
18 const char kCanShowAfterExpirationKey[] = "can_show_after_expiration";
19 const char kFingerprintKey[] = "fingerprint";
20 const char kOnClickURLKey[] = "on_click_url";
21 const char kAltTextKey[] = "alt_text";
22 const char kMimeTypeKey[] = "mime_type";
23 const char kNumBytesKey[] = "num_bytes";
24 
GetTimeValue(const base::DictionaryValue & dict,const std::string & key,base::Time * time)25 bool GetTimeValue(const base::DictionaryValue& dict,
26                   const std::string& key,
27                   base::Time* time) {
28   std::string str;
29   int64 internal_time_value;
30   if (dict.GetString(key, &str) &&
31       base::StringToInt64(str, &internal_time_value)) {
32     *time = base::Time::FromInternalValue(internal_time_value);
33     return true;
34   }
35   return false;
36 }
37 
SetTimeValue(base::DictionaryValue & dict,const std::string & key,const base::Time & time)38 void SetTimeValue(base::DictionaryValue& dict,
39                   const std::string& key,
40                   const base::Time& time) {
41   int64 internal_time_value = time.ToInternalValue();
42   dict.SetString(key, base::Int64ToString(internal_time_value));
43 }
44 
45 }  // namespace
46 
47 namespace search_provider_logos {
48 
LogoCache(const base::FilePath & cache_directory)49 LogoCache::LogoCache(const base::FilePath& cache_directory)
50     : cache_directory_(cache_directory),
51       metadata_is_valid_(false) {
52   // The LogoCache can be constructed on any thread, as long as it's used
53   // on a single thread after construction.
54   thread_checker_.DetachFromThread();
55 }
56 
~LogoCache()57 LogoCache::~LogoCache() {
58   DCHECK(thread_checker_.CalledOnValidThread());
59 }
60 
UpdateCachedLogoMetadata(const LogoMetadata & metadata)61 void LogoCache::UpdateCachedLogoMetadata(const LogoMetadata& metadata) {
62   DCHECK(thread_checker_.CalledOnValidThread());
63   DCHECK(metadata_);
64   DCHECK_EQ(metadata_->fingerprint, metadata.fingerprint);
65 
66   UpdateMetadata(make_scoped_ptr(new LogoMetadata(metadata)));
67   WriteMetadata();
68 }
69 
GetCachedLogoMetadata()70 const LogoMetadata* LogoCache::GetCachedLogoMetadata() {
71   DCHECK(thread_checker_.CalledOnValidThread());
72   ReadMetadataIfNeeded();
73   return metadata_.get();
74 }
75 
SetCachedLogo(const EncodedLogo * logo)76 void LogoCache::SetCachedLogo(const EncodedLogo* logo) {
77   DCHECK(thread_checker_.CalledOnValidThread());
78   scoped_ptr<LogoMetadata> metadata;
79   if (logo) {
80     metadata.reset(new LogoMetadata(logo->metadata));
81     logo_num_bytes_ = static_cast<int>(logo->encoded_image->size());
82   }
83   UpdateMetadata(metadata.Pass());
84   WriteLogo(logo ? logo->encoded_image : NULL);
85 }
86 
GetCachedLogo()87 scoped_ptr<EncodedLogo> LogoCache::GetCachedLogo() {
88   DCHECK(thread_checker_.CalledOnValidThread());
89 
90   ReadMetadataIfNeeded();
91   if (!metadata_)
92     return scoped_ptr<EncodedLogo>();
93 
94   scoped_refptr<base::RefCountedString> encoded_image =
95       new base::RefCountedString();
96   if (!base::ReadFileToString(GetLogoPath(), &encoded_image->data())) {
97     UpdateMetadata(scoped_ptr<LogoMetadata>());
98     return scoped_ptr<EncodedLogo>();
99   }
100 
101   if (encoded_image->size() != static_cast<size_t>(logo_num_bytes_)) {
102     // Delete corrupt metadata and logo.
103     DeleteLogoAndMetadata();
104     UpdateMetadata(scoped_ptr<LogoMetadata>());
105     return scoped_ptr<EncodedLogo>();
106   }
107 
108   scoped_ptr<EncodedLogo> logo(new EncodedLogo());
109   logo->encoded_image = encoded_image;
110   logo->metadata = *metadata_;
111   return logo.Pass();
112 }
113 
114 // static
LogoMetadataFromString(const std::string & str,int * logo_num_bytes)115 scoped_ptr<LogoMetadata> LogoCache::LogoMetadataFromString(
116     const std::string& str, int* logo_num_bytes) {
117   scoped_ptr<base::Value> value(base::JSONReader::Read(str));
118   base::DictionaryValue* dict;
119   if (!value || !value->GetAsDictionary(&dict))
120     return scoped_ptr<LogoMetadata>();
121 
122   scoped_ptr<LogoMetadata> metadata(new LogoMetadata());
123   if (!dict->GetString(kSourceUrlKey, &metadata->source_url) ||
124       !dict->GetString(kFingerprintKey, &metadata->fingerprint) ||
125       !dict->GetString(kOnClickURLKey, &metadata->on_click_url) ||
126       !dict->GetString(kAltTextKey, &metadata->alt_text) ||
127       !dict->GetString(kMimeTypeKey, &metadata->mime_type) ||
128       !dict->GetBoolean(kCanShowAfterExpirationKey,
129                         &metadata->can_show_after_expiration) ||
130       !dict->GetInteger(kNumBytesKey, logo_num_bytes) ||
131       !GetTimeValue(*dict, kExpirationTimeKey, &metadata->expiration_time)) {
132     return scoped_ptr<LogoMetadata>();
133   }
134 
135   return metadata.Pass();
136 }
137 
138 // static
LogoMetadataToString(const LogoMetadata & metadata,int num_bytes,std::string * str)139 void LogoCache::LogoMetadataToString(const LogoMetadata& metadata,
140                                      int num_bytes,
141                                      std::string* str) {
142   base::DictionaryValue dict;
143   dict.SetString(kSourceUrlKey, metadata.source_url);
144   dict.SetString(kFingerprintKey, metadata.fingerprint);
145   dict.SetString(kOnClickURLKey, metadata.on_click_url);
146   dict.SetString(kAltTextKey, metadata.alt_text);
147   dict.SetString(kMimeTypeKey, metadata.mime_type);
148   dict.SetBoolean(kCanShowAfterExpirationKey,
149                   metadata.can_show_after_expiration);
150   dict.SetInteger(kNumBytesKey, num_bytes);
151   SetTimeValue(dict, kExpirationTimeKey, metadata.expiration_time);
152   base::JSONWriter::Write(&dict, str);
153 }
154 
GetLogoPath()155 base::FilePath LogoCache::GetLogoPath() {
156   return cache_directory_.Append(FILE_PATH_LITERAL("logo"));
157 }
158 
GetMetadataPath()159 base::FilePath LogoCache::GetMetadataPath() {
160   return cache_directory_.Append(FILE_PATH_LITERAL("metadata"));
161 }
162 
UpdateMetadata(scoped_ptr<LogoMetadata> metadata)163 void LogoCache::UpdateMetadata(scoped_ptr<LogoMetadata> metadata) {
164   metadata_ = metadata.Pass();
165   metadata_is_valid_ = true;
166 }
167 
ReadMetadataIfNeeded()168 void LogoCache::ReadMetadataIfNeeded() {
169   if (metadata_is_valid_)
170     return;
171 
172   scoped_ptr<LogoMetadata> metadata;
173   base::FilePath metadata_path = GetMetadataPath();
174   std::string str;
175   if (base::ReadFileToString(metadata_path, &str)) {
176     metadata = LogoMetadataFromString(str, &logo_num_bytes_);
177     if (!metadata) {
178       // Delete corrupt metadata and logo.
179       DeleteLogoAndMetadata();
180     }
181   }
182 
183   UpdateMetadata(metadata.Pass());
184 }
185 
WriteMetadata()186 void LogoCache::WriteMetadata() {
187   if (!EnsureCacheDirectoryExists())
188     return;
189 
190   std::string str;
191   LogoMetadataToString(*metadata_, logo_num_bytes_, &str);
192   base::WriteFile(GetMetadataPath(), str.data(), static_cast<int>(str.size()));
193 }
194 
WriteLogo(scoped_refptr<base::RefCountedMemory> encoded_image)195 void LogoCache::WriteLogo(scoped_refptr<base::RefCountedMemory> encoded_image) {
196   if (!EnsureCacheDirectoryExists())
197     return;
198 
199   if (!metadata_ || !encoded_image) {
200     DeleteLogoAndMetadata();
201     return;
202   }
203 
204   // To minimize the chances of ending up in an undetectably broken state:
205   // First, delete the metadata file, then update the logo file, then update the
206   // metadata file.
207   base::FilePath logo_path = GetLogoPath();
208   base::FilePath metadata_path = GetMetadataPath();
209 
210   if (!base::DeleteFile(metadata_path, false))
211     return;
212 
213   if (base::WriteFile(
214           logo_path,
215           encoded_image->front_as<char>(),
216           static_cast<int>(encoded_image->size())) == -1) {
217     base::DeleteFile(logo_path, false);
218     return;
219   }
220 
221   WriteMetadata();
222 }
223 
DeleteLogoAndMetadata()224 void LogoCache::DeleteLogoAndMetadata() {
225   base::DeleteFile(GetLogoPath(), false);
226   base::DeleteFile(GetMetadataPath(), false);
227 }
228 
EnsureCacheDirectoryExists()229 bool LogoCache::EnsureCacheDirectoryExists() {
230   if (base::DirectoryExists(cache_directory_))
231     return true;
232   return base::CreateDirectory(cache_directory_);
233 }
234 
235 }  // namespace search_provider_logos
236