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

Side by Side Diff: chrome/browser/safe_browsing/safe_browsing_database.cc

Issue 611603002: Add the goog-unwanted-shavar list to a new SafeBrowsing PrefixSet. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: review:mattm Created 6 years, 1 month 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/safe_browsing/safe_browsing_database.h" 5 #include "chrome/browser/safe_browsing/safe_browsing_database.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <iterator> 8 #include <iterator>
9 9
10 #include "base/bind.h" 10 #include "base/bind.h"
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
50 FILE_PATH_LITERAL(" Download Whitelist"); 50 FILE_PATH_LITERAL(" Download Whitelist");
51 // Filename suffix for the extension blacklist store. 51 // Filename suffix for the extension blacklist store.
52 const base::FilePath::CharType kExtensionBlacklistDBFile[] = 52 const base::FilePath::CharType kExtensionBlacklistDBFile[] =
53 FILE_PATH_LITERAL(" Extension Blacklist"); 53 FILE_PATH_LITERAL(" Extension Blacklist");
54 // Filename suffix for the side-effect free whitelist store. 54 // Filename suffix for the side-effect free whitelist store.
55 const base::FilePath::CharType kSideEffectFreeWhitelistDBFile[] = 55 const base::FilePath::CharType kSideEffectFreeWhitelistDBFile[] =
56 FILE_PATH_LITERAL(" Side-Effect Free Whitelist"); 56 FILE_PATH_LITERAL(" Side-Effect Free Whitelist");
57 // Filename suffix for the csd malware IP blacklist store. 57 // Filename suffix for the csd malware IP blacklist store.
58 const base::FilePath::CharType kIPBlacklistDBFile[] = 58 const base::FilePath::CharType kIPBlacklistDBFile[] =
59 FILE_PATH_LITERAL(" IP Blacklist"); 59 FILE_PATH_LITERAL(" IP Blacklist");
60 // Filename suffix for the unwanted software blacklist store.
61 const base::FilePath::CharType kUnwantedSoftwareDBFile[] =
62 FILE_PATH_LITERAL(" UwS List");
60 63
61 // Filename suffix for browse store. 64 // Filename suffix for browse store.
62 // TODO(shess): "Safe Browsing Bloom Prefix Set" is full of win. 65 // TODO(shess): "Safe Browsing Bloom Prefix Set" is full of win.
63 // Unfortunately, to change the name implies lots of transition code 66 // Unfortunately, to change the name implies lots of transition code
64 // for little benefit. If/when file formats change (say to put all 67 // for little benefit. If/when file formats change (say to put all
65 // the data in one file), that would be a convenient point to rectify 68 // the data in one file), that would be a convenient point to rectify
66 // this. 69 // this.
67 // TODO(shess): This shouldn't be OS-driven <http://crbug.com/394379> 70 // TODO(shess): This shouldn't be OS-driven <http://crbug.com/394379>
68 #if defined(OS_ANDROID) 71 #if defined(OS_ANDROID)
69 // NOTE(shess): This difference is also reflected in the list name in 72 // NOTE(shess): This difference is also reflected in the list name in
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 // Generate the set of full hashes to check for |url|. If 118 // Generate the set of full hashes to check for |url|. If
116 // |include_whitelist_hashes| is true we will generate additional path-prefixes 119 // |include_whitelist_hashes| is true we will generate additional path-prefixes
117 // to match against the csd whitelist. E.g., if the path-prefix /foo is on the 120 // to match against the csd whitelist. E.g., if the path-prefix /foo is on the
118 // whitelist it should also match /foo/bar which is not the case for all the 121 // whitelist it should also match /foo/bar which is not the case for all the
119 // other lists. We'll also always add a pattern for the empty path. 122 // other lists. We'll also always add a pattern for the empty path.
120 // TODO(shess): This function is almost the same as 123 // TODO(shess): This function is almost the same as
121 // |CompareFullHashes()| in safe_browsing_util.cc, except that code 124 // |CompareFullHashes()| in safe_browsing_util.cc, except that code
122 // does an early exit on match. Since match should be the infrequent 125 // does an early exit on match. Since match should be the infrequent
123 // case (phishing or malware found), consider combining this function 126 // case (phishing or malware found), consider combining this function
124 // with that one. 127 // with that one.
125 void BrowseFullHashesToCheck(const GURL& url, 128 void UrlToFullHashes(const GURL& url,
126 bool include_whitelist_hashes, 129 bool include_whitelist_hashes,
127 std::vector<SBFullHash>* full_hashes) { 130 std::vector<SBFullHash>* full_hashes) {
128 std::vector<std::string> hosts; 131 std::vector<std::string> hosts;
129 if (url.HostIsIPAddress()) { 132 if (url.HostIsIPAddress()) {
130 hosts.push_back(url.host()); 133 hosts.push_back(url.host());
131 } else { 134 } else {
132 safe_browsing_util::GenerateHostsToCheck(url, &hosts); 135 safe_browsing_util::GenerateHostsToCheck(url, &hosts);
133 } 136 }
134 137
135 std::vector<std::string> paths; 138 std::vector<std::string> paths;
136 safe_browsing_util::GeneratePathsToCheck(url, &paths); 139 safe_browsing_util::GeneratePathsToCheck(url, &paths);
137 140
(...skipping 13 matching lines...) Expand all
151 } 154 }
152 } 155 }
153 } 156 }
154 } 157 }
155 158
156 // Get the prefixes matching the download |urls|. 159 // Get the prefixes matching the download |urls|.
157 void GetDownloadUrlPrefixes(const std::vector<GURL>& urls, 160 void GetDownloadUrlPrefixes(const std::vector<GURL>& urls,
158 std::vector<SBPrefix>* prefixes) { 161 std::vector<SBPrefix>* prefixes) {
159 std::vector<SBFullHash> full_hashes; 162 std::vector<SBFullHash> full_hashes;
160 for (size_t i = 0; i < urls.size(); ++i) 163 for (size_t i = 0; i < urls.size(); ++i)
161 BrowseFullHashesToCheck(urls[i], false, &full_hashes); 164 UrlToFullHashes(urls[i], false, &full_hashes);
162 165
163 for (size_t i = 0; i < full_hashes.size(); ++i) 166 for (size_t i = 0; i < full_hashes.size(); ++i)
164 prefixes->push_back(full_hashes[i].prefix); 167 prefixes->push_back(full_hashes[i].prefix);
165 } 168 }
166 169
167 // Helper function to compare addprefixes in |store| with |prefixes|. 170 // Helper function to compare addprefixes in |store| with |prefixes|.
168 // The |list_bit| indicates which list (url or hash) to compare. 171 // The |list_bit| indicates which list (url or hash) to compare.
169 // 172 //
170 // Returns true if there is a match, |*prefix_hits| (if non-NULL) will contain 173 // Returns true if there is a match, |*prefix_hits| (if non-NULL) will contain
171 // the actual matching prefixes. 174 // the actual matching prefixes.
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
245 DCHECK_EQ(safe_browsing_util::GetListId(listname) % 2, 248 DCHECK_EQ(safe_browsing_util::GetListId(listname) % 2,
246 static_cast<int>(i % 2)); 249 static_cast<int>(i % 2));
247 DCHECK_NE(safe_browsing_util::GetListId(listname), 250 DCHECK_NE(safe_browsing_util::GetListId(listname),
248 safe_browsing_util::INVALID); 251 safe_browsing_util::INVALID);
249 lists->push_back(SBListChunkRanges(listname)); 252 lists->push_back(SBListChunkRanges(listname));
250 lists->back().adds.swap(adds[i]); 253 lists->back().adds.swap(adds[i]);
251 lists->back().subs.swap(subs[i]); 254 lists->back().subs.swap(subs[i]);
252 } 255 }
253 } 256 }
254 257
255 void UpdateChunkRangesForLists(SafeBrowsingStore* store, 258 void UpdateChunkRangesForBrowseLists(SafeBrowsingStore* store,
mattm 2014/11/12 22:26:57 I think the old name was fine (it's just the brows
gab 2014/11/13 02:45:24 Oops my bad this is left over from the first patch
256 const std::string& listname0, 259 const std::string& listname0,
257 const std::string& listname1, 260 const std::string& listname1,
258 std::vector<SBListChunkRanges>* lists) { 261 std::vector<SBListChunkRanges>* lists) {
259 std::vector<std::string> listnames; 262 std::vector<std::string> listnames;
260 listnames.push_back(listname0); 263 listnames.push_back(listname0);
261 listnames.push_back(listname1); 264 listnames.push_back(listname1);
262 UpdateChunkRanges(store, listnames, lists); 265 UpdateChunkRanges(store, listnames, lists);
263 } 266 }
264 267
265 void UpdateChunkRangesForList(SafeBrowsingStore* store, 268 void UpdateChunkRangesForList(SafeBrowsingStore* store,
266 const std::string& listname, 269 const std::string& listname,
267 std::vector<SBListChunkRanges>* lists) { 270 std::vector<SBListChunkRanges>* lists) {
268 UpdateChunkRanges(store, std::vector<std::string>(1, listname), lists); 271 UpdateChunkRanges(store, std::vector<std::string>(1, listname), lists);
269 } 272 }
270 273
271 // This code always checks for non-zero file size. This helper makes 274 // This code always checks for non-zero file size. This helper makes
272 // that less verbose. 275 // that less verbose.
273 int64 GetFileSizeOrZero(const base::FilePath& file_path) { 276 int64 GetFileSizeOrZero(const base::FilePath& file_path) {
274 int64 size_64; 277 int64 size_64;
275 if (!base::GetFileSize(file_path, &size_64)) 278 if (!base::GetFileSize(file_path, &size_64))
276 return 0; 279 return 0;
277 return size_64; 280 return size_64;
278 } 281 }
279 282
280 // Helper for ContainsBrowseUrlHashes(). Returns true if an un-expired match 283 // Helper for PrefixSetContainsUrlHashes(). Returns true if an un-expired match
281 // for |full_hash| is found in |cache|, with any matches appended to |results| 284 // for |full_hash| is found in |cache|, with any matches appended to |results|
282 // (true can be returned with zero matches). |expire_base| is used to check the 285 // (true can be returned with zero matches). |expire_base| is used to check the
283 // cache lifetime of matches, expired matches will be discarded from |cache|. 286 // cache lifetime of matches, expired matches will be discarded from |cache|.
284 bool GetCachedFullHash(std::map<SBPrefix, SBCachedFullHashResult>* cache, 287 bool GetCachedFullHash(std::map<SBPrefix, SBCachedFullHashResult>* cache,
285 const SBFullHash& full_hash, 288 const SBFullHash& full_hash,
286 const base::Time& expire_base, 289 const base::Time& expire_base,
287 std::vector<SBFullHashResult>* results) { 290 std::vector<SBFullHashResult>* results) {
288 // First check if there is a valid cached result for this prefix. 291 // First check if there is a valid cached result for this prefix.
289 std::map<SBPrefix, SBCachedFullHashResult>::iterator 292 std::map<SBPrefix, SBCachedFullHashResult>::iterator
290 citer = cache->find(full_hash.prefix); 293 citer = cache->find(full_hash.prefix);
(...skipping 21 matching lines...) Expand all
312 315
313 // The default SafeBrowsingDatabaseFactory. 316 // The default SafeBrowsingDatabaseFactory.
314 class SafeBrowsingDatabaseFactoryImpl : public SafeBrowsingDatabaseFactory { 317 class SafeBrowsingDatabaseFactoryImpl : public SafeBrowsingDatabaseFactory {
315 public: 318 public:
316 SafeBrowsingDatabase* CreateSafeBrowsingDatabase( 319 SafeBrowsingDatabase* CreateSafeBrowsingDatabase(
317 bool enable_download_protection, 320 bool enable_download_protection,
318 bool enable_client_side_whitelist, 321 bool enable_client_side_whitelist,
319 bool enable_download_whitelist, 322 bool enable_download_whitelist,
320 bool enable_extension_blacklist, 323 bool enable_extension_blacklist,
321 bool enable_side_effect_free_whitelist, 324 bool enable_side_effect_free_whitelist,
322 bool enable_ip_blacklist) override { 325 bool enable_ip_blacklist,
326 bool enable_unwanted_software_list) override {
323 return new SafeBrowsingDatabaseNew( 327 return new SafeBrowsingDatabaseNew(
324 new SafeBrowsingStoreFile, 328 new SafeBrowsingStoreFile,
325 enable_download_protection ? new SafeBrowsingStoreFile : NULL, 329 enable_download_protection ? new SafeBrowsingStoreFile : NULL,
326 enable_client_side_whitelist ? new SafeBrowsingStoreFile : NULL, 330 enable_client_side_whitelist ? new SafeBrowsingStoreFile : NULL,
327 enable_download_whitelist ? new SafeBrowsingStoreFile : NULL, 331 enable_download_whitelist ? new SafeBrowsingStoreFile : NULL,
328 enable_extension_blacklist ? new SafeBrowsingStoreFile : NULL, 332 enable_extension_blacklist ? new SafeBrowsingStoreFile : NULL,
329 enable_side_effect_free_whitelist ? new SafeBrowsingStoreFile : NULL, 333 enable_side_effect_free_whitelist ? new SafeBrowsingStoreFile : NULL,
330 enable_ip_blacklist ? new SafeBrowsingStoreFile : NULL); 334 enable_ip_blacklist ? new SafeBrowsingStoreFile : NULL,
335 enable_unwanted_software_list ? new SafeBrowsingStoreFile : NULL);
331 } 336 }
332 337
333 SafeBrowsingDatabaseFactoryImpl() { } 338 SafeBrowsingDatabaseFactoryImpl() { }
334 339
335 private: 340 private:
336 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingDatabaseFactoryImpl); 341 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingDatabaseFactoryImpl);
337 }; 342 };
338 343
339 // static 344 // static
340 SafeBrowsingDatabaseFactory* SafeBrowsingDatabase::factory_ = NULL; 345 SafeBrowsingDatabaseFactory* SafeBrowsingDatabase::factory_ = NULL;
341 346
342 // Factory method, non-thread safe. Caller has to make sure this s called 347 // Factory method, non-thread safe. Caller has to make sure this s called
343 // on SafeBrowsing Thread. 348 // on SafeBrowsing Thread.
344 // TODO(shess): There's no need for a factory any longer. Convert 349 // TODO(shess): There's no need for a factory any longer. Convert
345 // SafeBrowsingDatabaseNew to SafeBrowsingDatabase, and have Create() 350 // SafeBrowsingDatabaseNew to SafeBrowsingDatabase, and have Create()
346 // callers just construct things directly. 351 // callers just construct things directly.
347 SafeBrowsingDatabase* SafeBrowsingDatabase::Create( 352 SafeBrowsingDatabase* SafeBrowsingDatabase::Create(
348 bool enable_download_protection, 353 bool enable_download_protection,
349 bool enable_client_side_whitelist, 354 bool enable_client_side_whitelist,
350 bool enable_download_whitelist, 355 bool enable_download_whitelist,
351 bool enable_extension_blacklist, 356 bool enable_extension_blacklist,
352 bool enable_side_effect_free_whitelist, 357 bool enable_side_effect_free_whitelist,
353 bool enable_ip_blacklist) { 358 bool enable_ip_blacklist,
359 bool enable_unwanted_software_list) {
354 if (!factory_) 360 if (!factory_)
355 factory_ = new SafeBrowsingDatabaseFactoryImpl(); 361 factory_ = new SafeBrowsingDatabaseFactoryImpl();
356 return factory_->CreateSafeBrowsingDatabase( 362 return factory_->CreateSafeBrowsingDatabase(
357 enable_download_protection, 363 enable_download_protection,
358 enable_client_side_whitelist, 364 enable_client_side_whitelist,
359 enable_download_whitelist, 365 enable_download_whitelist,
360 enable_extension_blacklist, 366 enable_extension_blacklist,
361 enable_side_effect_free_whitelist, 367 enable_side_effect_free_whitelist,
362 enable_ip_blacklist); 368 enable_ip_blacklist,
369 enable_unwanted_software_list);
363 } 370 }
364 371
365 SafeBrowsingDatabase::~SafeBrowsingDatabase() { 372 SafeBrowsingDatabase::~SafeBrowsingDatabase() {
366 } 373 }
367 374
368 // static 375 // static
369 base::FilePath SafeBrowsingDatabase::BrowseDBFilename( 376 base::FilePath SafeBrowsingDatabase::BrowseDBFilename(
370 const base::FilePath& db_base_filename) { 377 const base::FilePath& db_base_filename) {
371 return base::FilePath(db_base_filename.value() + kBrowseDBFile); 378 return base::FilePath(db_base_filename.value() + kBrowseDBFile);
372 } 379 }
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
412 const base::FilePath& db_filename) { 419 const base::FilePath& db_filename) {
413 return base::FilePath(db_filename.value() + kSideEffectFreeWhitelistDBFile); 420 return base::FilePath(db_filename.value() + kSideEffectFreeWhitelistDBFile);
414 } 421 }
415 422
416 // static 423 // static
417 base::FilePath SafeBrowsingDatabase::IpBlacklistDBFilename( 424 base::FilePath SafeBrowsingDatabase::IpBlacklistDBFilename(
418 const base::FilePath& db_filename) { 425 const base::FilePath& db_filename) {
419 return base::FilePath(db_filename.value() + kIPBlacklistDBFile); 426 return base::FilePath(db_filename.value() + kIPBlacklistDBFile);
420 } 427 }
421 428
429 // static
430 base::FilePath SafeBrowsingDatabase::UnwantedSoftwareDBFilename(
431 const base::FilePath& db_filename) {
432 return base::FilePath(db_filename.value() + kUnwantedSoftwareDBFile);
433 }
434
422 SafeBrowsingStore* SafeBrowsingDatabaseNew::GetStore(const int list_id) { 435 SafeBrowsingStore* SafeBrowsingDatabaseNew::GetStore(const int list_id) {
423 if (list_id == safe_browsing_util::PHISH || 436 if (list_id == safe_browsing_util::PHISH ||
424 list_id == safe_browsing_util::MALWARE) { 437 list_id == safe_browsing_util::MALWARE) {
425 return browse_store_.get(); 438 return browse_store_.get();
426 } else if (list_id == safe_browsing_util::BINURL) { 439 } else if (list_id == safe_browsing_util::BINURL) {
427 return download_store_.get(); 440 return download_store_.get();
428 } else if (list_id == safe_browsing_util::CSDWHITELIST) { 441 } else if (list_id == safe_browsing_util::CSDWHITELIST) {
429 return csd_whitelist_store_.get(); 442 return csd_whitelist_store_.get();
430 } else if (list_id == safe_browsing_util::DOWNLOADWHITELIST) { 443 } else if (list_id == safe_browsing_util::DOWNLOADWHITELIST) {
431 return download_whitelist_store_.get(); 444 return download_whitelist_store_.get();
432 } else if (list_id == safe_browsing_util::EXTENSIONBLACKLIST) { 445 } else if (list_id == safe_browsing_util::EXTENSIONBLACKLIST) {
433 return extension_blacklist_store_.get(); 446 return extension_blacklist_store_.get();
434 } else if (list_id == safe_browsing_util::SIDEEFFECTFREEWHITELIST) { 447 } else if (list_id == safe_browsing_util::SIDEEFFECTFREEWHITELIST) {
435 return side_effect_free_whitelist_store_.get(); 448 return side_effect_free_whitelist_store_.get();
436 } else if (list_id == safe_browsing_util::IPBLACKLIST) { 449 } else if (list_id == safe_browsing_util::IPBLACKLIST) {
437 return ip_blacklist_store_.get(); 450 return ip_blacklist_store_.get();
451 } else if (list_id == safe_browsing_util::UNWANTEDURL) {
452 return unwanted_software_store_.get();
438 } 453 }
439 return NULL; 454 return NULL;
440 } 455 }
441 456
442 // static 457 // static
443 void SafeBrowsingDatabase::RecordFailure(FailureType failure_type) { 458 void SafeBrowsingDatabase::RecordFailure(FailureType failure_type) {
444 UMA_HISTOGRAM_ENUMERATION("SB2.DatabaseFailure", failure_type, 459 UMA_HISTOGRAM_ENUMERATION("SB2.DatabaseFailure", failure_type,
445 FAILURE_DATABASE_MAX); 460 FAILURE_DATABASE_MAX);
446 } 461 }
447 462
448 SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew() 463 SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew()
449 : creation_loop_(base::MessageLoop::current()), 464 : creation_loop_(base::MessageLoop::current()),
450 browse_store_(new SafeBrowsingStoreFile), 465 browse_store_(new SafeBrowsingStoreFile),
451 corruption_detected_(false), 466 corruption_detected_(false),
452 change_detected_(false), 467 change_detected_(false),
453 reset_factory_(this) { 468 reset_factory_(this) {
454 DCHECK(browse_store_.get()); 469 DCHECK(browse_store_.get());
455 DCHECK(!download_store_.get()); 470 DCHECK(!download_store_.get());
456 DCHECK(!csd_whitelist_store_.get()); 471 DCHECK(!csd_whitelist_store_.get());
457 DCHECK(!download_whitelist_store_.get()); 472 DCHECK(!download_whitelist_store_.get());
458 DCHECK(!extension_blacklist_store_.get()); 473 DCHECK(!extension_blacklist_store_.get());
459 DCHECK(!side_effect_free_whitelist_store_.get()); 474 DCHECK(!side_effect_free_whitelist_store_.get());
460 DCHECK(!ip_blacklist_store_.get()); 475 DCHECK(!ip_blacklist_store_.get());
476 DCHECK(!unwanted_software_store_.get());
461 } 477 }
462 478
463 SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew( 479 SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew(
464 SafeBrowsingStore* browse_store, 480 SafeBrowsingStore* browse_store,
465 SafeBrowsingStore* download_store, 481 SafeBrowsingStore* download_store,
466 SafeBrowsingStore* csd_whitelist_store, 482 SafeBrowsingStore* csd_whitelist_store,
467 SafeBrowsingStore* download_whitelist_store, 483 SafeBrowsingStore* download_whitelist_store,
468 SafeBrowsingStore* extension_blacklist_store, 484 SafeBrowsingStore* extension_blacklist_store,
469 SafeBrowsingStore* side_effect_free_whitelist_store, 485 SafeBrowsingStore* side_effect_free_whitelist_store,
470 SafeBrowsingStore* ip_blacklist_store) 486 SafeBrowsingStore* ip_blacklist_store,
487 SafeBrowsingStore* unwanted_software_store)
471 : creation_loop_(base::MessageLoop::current()), 488 : creation_loop_(base::MessageLoop::current()),
472 browse_store_(browse_store), 489 browse_store_(browse_store),
473 download_store_(download_store), 490 download_store_(download_store),
474 csd_whitelist_store_(csd_whitelist_store), 491 csd_whitelist_store_(csd_whitelist_store),
475 download_whitelist_store_(download_whitelist_store), 492 download_whitelist_store_(download_whitelist_store),
476 extension_blacklist_store_(extension_blacklist_store), 493 extension_blacklist_store_(extension_blacklist_store),
477 side_effect_free_whitelist_store_(side_effect_free_whitelist_store), 494 side_effect_free_whitelist_store_(side_effect_free_whitelist_store),
478 ip_blacklist_store_(ip_blacklist_store), 495 ip_blacklist_store_(ip_blacklist_store),
496 unwanted_software_store_(unwanted_software_store),
479 corruption_detected_(false), 497 corruption_detected_(false),
480 reset_factory_(this) { 498 reset_factory_(this) {
481 DCHECK(browse_store_.get()); 499 DCHECK(browse_store_.get());
482 } 500 }
483 501
484 SafeBrowsingDatabaseNew::~SafeBrowsingDatabaseNew() { 502 SafeBrowsingDatabaseNew::~SafeBrowsingDatabaseNew() {
485 // The DCHECK is disabled due to crbug.com/338486 . 503 // The DCHECK is disabled due to crbug.com/338486 .
486 // DCHECK_EQ(creation_loop_, base::MessageLoop::current()); 504 // DCHECK_EQ(creation_loop_, base::MessageLoop::current());
487 } 505 }
488 506
489 void SafeBrowsingDatabaseNew::Init(const base::FilePath& filename_base) { 507 void SafeBrowsingDatabaseNew::Init(const base::FilePath& filename_base) {
490 DCHECK_EQ(creation_loop_, base::MessageLoop::current()); 508 DCHECK_EQ(creation_loop_, base::MessageLoop::current());
491 509
492 // This should not be run multiple times. 510 // This should not be run multiple times.
493 DCHECK(filename_base_.empty()); 511 DCHECK(filename_base_.empty());
494 512
495 filename_base_ = filename_base; 513 filename_base_ = filename_base;
496 514
497 // TODO(shess): The various stores are really only necessary while doing 515 // TODO(shess): The various stores are really only necessary while doing
498 // updates, or when querying a store directly (see |ContainsDownloadUrl()|). 516 // updates (see |UpdateFinished()|) or when querying a store directly (see
517 // |ContainsDownloadUrl()|).
499 // The store variables are also tested to see if a list is enabled. Perhaps 518 // The store variables are also tested to see if a list is enabled. Perhaps
500 // the stores could be refactored into an update object so that they are only 519 // the stores could be refactored into an update object so that they are only
501 // live in memory while being actively used. The sense of enabled probably 520 // live in memory while being actively used. The sense of enabled probably
502 // belongs in protocol_manager or database_manager. 521 // belongs in protocol_manager or database_manager.
503 522
504 browse_store_->Init( 523 browse_store_->Init(
505 BrowseDBFilename(filename_base_), 524 BrowseDBFilename(filename_base_),
506 base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase, 525 base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase,
507 base::Unretained(this))); 526 base::Unretained(this)));
508 527
528 if (unwanted_software_store_.get()) {
529 unwanted_software_store_->Init(
530 UnwantedSoftwareDBFilename(filename_base_),
531 base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase,
532 base::Unretained(this)));
533 }
534
509 { 535 {
510 // NOTE: There is no need to grab the lock in this function, since 536 // NOTE: There is no need to grab the lock in this function, since
511 // until it returns, there are no pointers to this class on other 537 // until it returns, there are no pointers to this class on other
512 // threads. Then again, that means there is no possibility of 538 // threads. Then again, that means there is no possibility of
513 // contention on the lock... 539 // contention on the lock...
514 base::AutoLock locked(lookup_lock_); 540 base::AutoLock locked(lookup_lock_);
515 browse_gethash_cache_.clear(); 541 prefix_gethash_cache_.clear();
516 LoadPrefixSet(); 542 LoadPrefixSet(BrowseDBFilename(filename_base_), &browse_prefix_set_,
543 FAILURE_BROWSE_PREFIX_SET_READ);
544 if (unwanted_software_store_.get()) {
545 LoadPrefixSet(UnwantedSoftwareDBFilename(filename_base_),
546 &unwanted_software_prefix_set_,
547 FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_READ);
548 }
517 } 549 }
518 550
519 if (download_store_.get()) { 551 if (download_store_.get()) {
520 download_store_->Init( 552 download_store_->Init(
521 DownloadDBFilename(filename_base_), 553 DownloadDBFilename(filename_base_),
522 base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase, 554 base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase,
523 base::Unretained(this))); 555 base::Unretained(this)));
524 } 556 }
525 557
526 if (csd_whitelist_store_.get()) { 558 if (csd_whitelist_store_.get()) {
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
613 645
614 // Delete files on disk. 646 // Delete files on disk.
615 // TODO(shess): Hard to see where one might want to delete without a 647 // TODO(shess): Hard to see where one might want to delete without a
616 // reset. Perhaps inline |Delete()|? 648 // reset. Perhaps inline |Delete()|?
617 if (!Delete()) 649 if (!Delete())
618 return false; 650 return false;
619 651
620 // Reset objects in memory. 652 // Reset objects in memory.
621 { 653 {
622 base::AutoLock locked(lookup_lock_); 654 base::AutoLock locked(lookup_lock_);
623 browse_gethash_cache_.clear(); 655 prefix_gethash_cache_.clear();
624 browse_prefix_set_.reset(); 656 browse_prefix_set_.reset();
625 side_effect_free_whitelist_prefix_set_.reset(); 657 side_effect_free_whitelist_prefix_set_.reset();
626 ip_blacklist_.clear(); 658 ip_blacklist_.clear();
659 unwanted_software_prefix_set_.reset();
627 } 660 }
628 // Wants to acquire the lock itself. 661 // Wants to acquire the lock itself.
629 WhitelistEverything(&csd_whitelist_); 662 WhitelistEverything(&csd_whitelist_);
630 WhitelistEverything(&download_whitelist_); 663 WhitelistEverything(&download_whitelist_);
631 return true; 664 return true;
632 } 665 }
633 666
634 bool SafeBrowsingDatabaseNew::ContainsBrowseUrl( 667 bool SafeBrowsingDatabaseNew::ContainsBrowseUrl(
635 const GURL& url, 668 const GURL& url,
636 std::vector<SBPrefix>* prefix_hits, 669 std::vector<SBPrefix>* prefix_hits,
637 std::vector<SBFullHashResult>* cache_hits) { 670 std::vector<SBFullHashResult>* cache_hits) {
671 return PrefixSetContainsUrl(url, &browse_prefix_set_, prefix_hits,
672 cache_hits);
673 }
674
675 bool SafeBrowsingDatabaseNew::ContainsUnwantedSoftwareUrl(
676 const GURL& url,
677 std::vector<SBPrefix>* prefix_hits,
678 std::vector<SBFullHashResult>* cache_hits) {
679 return PrefixSetContainsUrl(url, &unwanted_software_prefix_set_,
680 prefix_hits, cache_hits);
681 }
682
683 bool SafeBrowsingDatabaseNew::PrefixSetContainsUrl(
684 const GURL& url,
685 scoped_ptr<safe_browsing::PrefixSet>* prefix_set_getter,
686 std::vector<SBPrefix>* prefix_hits,
687 std::vector<SBFullHashResult>* cache_hits) {
638 // Clear the results first. 688 // Clear the results first.
639 prefix_hits->clear(); 689 prefix_hits->clear();
640 cache_hits->clear(); 690 cache_hits->clear();
641 691
642 std::vector<SBFullHash> full_hashes; 692 std::vector<SBFullHash> full_hashes;
643 BrowseFullHashesToCheck(url, false, &full_hashes); 693 UrlToFullHashes(url, false, &full_hashes);
644 if (full_hashes.empty()) 694 if (full_hashes.empty())
645 return false; 695 return false;
646 696
647 return ContainsBrowseUrlHashes(full_hashes, prefix_hits, cache_hits); 697 return PrefixSetContainsUrlHashes(full_hashes, prefix_set_getter, prefix_hits,
698 cache_hits);
648 } 699 }
649 700
650 bool SafeBrowsingDatabaseNew::ContainsBrowseUrlHashes( 701 bool SafeBrowsingDatabaseNew::ContainsBrowseUrlHashesForTesting(
651 const std::vector<SBFullHash>& full_hashes, 702 const std::vector<SBFullHash>& full_hashes,
652 std::vector<SBPrefix>* prefix_hits, 703 std::vector<SBPrefix>* prefix_hits,
653 std::vector<SBFullHashResult>* cache_hits) { 704 std::vector<SBFullHashResult>* cache_hits) {
705 return PrefixSetContainsUrlHashes(full_hashes, &browse_prefix_set_,
706 prefix_hits, cache_hits);
707 }
708
709 bool SafeBrowsingDatabaseNew::PrefixSetContainsUrlHashes(
710 const std::vector<SBFullHash>& full_hashes,
711 scoped_ptr<safe_browsing::PrefixSet>* prefix_set_getter,
712 std::vector<SBPrefix>* prefix_hits,
713 std::vector<SBFullHashResult>* cache_hits) {
654 // Used to determine cache expiration. 714 // Used to determine cache expiration.
655 const base::Time now = base::Time::Now(); 715 const base::Time now = base::Time::Now();
656 716
657 // This function is called on the I/O thread, prevent changes to 717 // This function is called on the I/O thread, prevent changes to
658 // filter and caches. 718 // filter and caches.
659 base::AutoLock locked(lookup_lock_); 719 base::AutoLock locked(lookup_lock_);
660 720
661 // |browse_prefix_set_| is empty until it is either read from disk, or the 721 // |prefix_set| is empty until it is either read from disk, or the first
662 // first update populates it. Bail out without a hit if not yet 722 // update populates it. Bail out without a hit if not yet available.
663 // available. 723 // |prefix_set_getter| can only be accessed while holding |lookup_lock_| hence
664 if (!browse_prefix_set_.get()) 724 // why it is passed as a parameter rather than passing the |prefix_set|
725 // directly.
726 safe_browsing::PrefixSet* prefix_set = prefix_set_getter->get();
727 if (!prefix_set)
665 return false; 728 return false;
666 729
667 for (size_t i = 0; i < full_hashes.size(); ++i) { 730 for (size_t i = 0; i < full_hashes.size(); ++i) {
668 if (!GetCachedFullHash(&browse_gethash_cache_, 731 if (!GetCachedFullHash(&prefix_gethash_cache_,
669 full_hashes[i], 732 full_hashes[i],
670 now, 733 now,
671 cache_hits)) { 734 cache_hits)) {
672 // No valid cached result, check the database. 735 // No valid cached result, check the database.
673 if (browse_prefix_set_->Exists(full_hashes[i])) 736 if (prefix_set->Exists(full_hashes[i]))
674 prefix_hits->push_back(full_hashes[i].prefix); 737 prefix_hits->push_back(full_hashes[i].prefix);
675 } 738 }
676 } 739 }
677 740
678 // Multiple full hashes could share prefix, remove duplicates. 741 // Multiple full hashes could share prefix, remove duplicates.
679 std::sort(prefix_hits->begin(), prefix_hits->end()); 742 std::sort(prefix_hits->begin(), prefix_hits->end());
680 prefix_hits->erase(std::unique(prefix_hits->begin(), prefix_hits->end()), 743 prefix_hits->erase(std::unique(prefix_hits->begin(), prefix_hits->end()),
681 prefix_hits->end()); 744 prefix_hits->end());
682 745
683 return !prefix_hits->empty() || !cache_hits->empty(); 746 return !prefix_hits->empty() || !cache_hits->empty();
(...skipping 14 matching lines...) Expand all
698 safe_browsing_util::BINURL % 2, 761 safe_browsing_util::BINURL % 2,
699 prefixes, 762 prefixes,
700 prefix_hits); 763 prefix_hits);
701 } 764 }
702 765
703 bool SafeBrowsingDatabaseNew::ContainsCsdWhitelistedUrl(const GURL& url) { 766 bool SafeBrowsingDatabaseNew::ContainsCsdWhitelistedUrl(const GURL& url) {
704 // This method is theoretically thread-safe but we expect all calls to 767 // This method is theoretically thread-safe but we expect all calls to
705 // originate from the IO thread. 768 // originate from the IO thread.
706 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 769 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
707 std::vector<SBFullHash> full_hashes; 770 std::vector<SBFullHash> full_hashes;
708 BrowseFullHashesToCheck(url, true, &full_hashes); 771 UrlToFullHashes(url, true, &full_hashes);
709 return ContainsWhitelistedHashes(csd_whitelist_, full_hashes); 772 return ContainsWhitelistedHashes(csd_whitelist_, full_hashes);
710 } 773 }
711 774
712 bool SafeBrowsingDatabaseNew::ContainsDownloadWhitelistedUrl(const GURL& url) { 775 bool SafeBrowsingDatabaseNew::ContainsDownloadWhitelistedUrl(const GURL& url) {
713 std::vector<SBFullHash> full_hashes; 776 std::vector<SBFullHash> full_hashes;
714 BrowseFullHashesToCheck(url, true, &full_hashes); 777 UrlToFullHashes(url, true, &full_hashes);
715 return ContainsWhitelistedHashes(download_whitelist_, full_hashes); 778 return ContainsWhitelistedHashes(download_whitelist_, full_hashes);
716 } 779 }
717 780
718 bool SafeBrowsingDatabaseNew::ContainsExtensionPrefixes( 781 bool SafeBrowsingDatabaseNew::ContainsExtensionPrefixes(
719 const std::vector<SBPrefix>& prefixes, 782 const std::vector<SBPrefix>& prefixes,
720 std::vector<SBPrefix>* prefix_hits) { 783 std::vector<SBPrefix>* prefix_hits) {
721 DCHECK_EQ(creation_loop_, base::MessageLoop::current()); 784 DCHECK_EQ(creation_loop_, base::MessageLoop::current());
722 if (!extension_blacklist_store_) 785 if (!extension_blacklist_store_)
723 return false; 786 return false;
724 787
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after
944 const std::vector<SBPrefix>& prefixes, 1007 const std::vector<SBPrefix>& prefixes,
945 const std::vector<SBFullHashResult>& full_hits, 1008 const std::vector<SBFullHashResult>& full_hits,
946 const base::TimeDelta& cache_lifetime) { 1009 const base::TimeDelta& cache_lifetime) {
947 const base::Time expire_after = base::Time::Now() + cache_lifetime; 1010 const base::Time expire_after = base::Time::Now() + cache_lifetime;
948 1011
949 // This is called on the I/O thread, lock against updates. 1012 // This is called on the I/O thread, lock against updates.
950 base::AutoLock locked(lookup_lock_); 1013 base::AutoLock locked(lookup_lock_);
951 1014
952 // Create or reset all cached results for these prefixes. 1015 // Create or reset all cached results for these prefixes.
953 for (size_t i = 0; i < prefixes.size(); ++i) { 1016 for (size_t i = 0; i < prefixes.size(); ++i) {
954 browse_gethash_cache_[prefixes[i]] = SBCachedFullHashResult(expire_after); 1017 prefix_gethash_cache_[prefixes[i]] = SBCachedFullHashResult(expire_after);
955 } 1018 }
956 1019
957 // Insert any fullhash hits. Note that there may be one, multiple, or no 1020 // Insert any fullhash hits. Note that there may be one, multiple, or no
958 // fullhashes for any given entry in |prefixes|. 1021 // fullhashes for any given entry in |prefixes|.
959 for (size_t i = 0; i < full_hits.size(); ++i) { 1022 for (size_t i = 0; i < full_hits.size(); ++i) {
960 const SBPrefix prefix = full_hits[i].hash.prefix; 1023 const SBPrefix prefix = full_hits[i].hash.prefix;
961 browse_gethash_cache_[prefix].full_hashes.push_back(full_hits[i]); 1024 prefix_gethash_cache_[prefix].full_hashes.push_back(full_hits[i]);
962 } 1025 }
963 } 1026 }
964 1027
965 bool SafeBrowsingDatabaseNew::UpdateStarted( 1028 bool SafeBrowsingDatabaseNew::UpdateStarted(
966 std::vector<SBListChunkRanges>* lists) { 1029 std::vector<SBListChunkRanges>* lists) {
967 DCHECK_EQ(creation_loop_, base::MessageLoop::current()); 1030 DCHECK_EQ(creation_loop_, base::MessageLoop::current());
968 DCHECK(lists); 1031 DCHECK(lists);
969 1032
970 // If |BeginUpdate()| fails, reset the database. 1033 // If |BeginUpdate()| fails, reset the database.
971 if (!browse_store_->BeginUpdate()) { 1034 if (!browse_store_->BeginUpdate()) {
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
1006 HandleCorruptDatabase(); 1069 HandleCorruptDatabase();
1007 return false; 1070 return false;
1008 } 1071 }
1009 1072
1010 if (ip_blacklist_store_ && !ip_blacklist_store_->BeginUpdate()) { 1073 if (ip_blacklist_store_ && !ip_blacklist_store_->BeginUpdate()) {
1011 RecordFailure(FAILURE_IP_BLACKLIST_UPDATE_BEGIN); 1074 RecordFailure(FAILURE_IP_BLACKLIST_UPDATE_BEGIN);
1012 HandleCorruptDatabase(); 1075 HandleCorruptDatabase();
1013 return false; 1076 return false;
1014 } 1077 }
1015 1078
1079 if (unwanted_software_store_ && !unwanted_software_store_->BeginUpdate()) {
1080 RecordFailure(FAILURE_UNWANTED_SOFTWARE_DATABASE_UPDATE_BEGIN);
1081 HandleCorruptDatabase();
1082 return false;
1083 }
1084
1016 { 1085 {
1017 base::AutoLock locked(lookup_lock_); 1086 base::AutoLock locked(lookup_lock_);
1018 // Cached fullhash results must be cleared on every database update (whether 1087 // Cached fullhash results must be cleared on every database update (whether
1019 // successful or not.) 1088 // successful or not.)
1020 browse_gethash_cache_.clear(); 1089 prefix_gethash_cache_.clear();
1021 } 1090 }
1022 1091
1023 UpdateChunkRangesForLists(browse_store_.get(), 1092 UpdateChunkRangesForBrowseLists(browse_store_.get(),
1024 safe_browsing_util::kMalwareList, 1093 safe_browsing_util::kMalwareList,
1025 safe_browsing_util::kPhishingList, 1094 safe_browsing_util::kPhishingList,
1026 lists); 1095 lists);
1027 1096
1028 // NOTE(shess): |download_store_| used to contain kBinHashList, which has been 1097 // NOTE(shess): |download_store_| used to contain kBinHashList, which has been
1029 // deprecated. Code to delete the list from the store shows ~15k hits/day as 1098 // deprecated. Code to delete the list from the store shows ~15k hits/day as
1030 // of Feb 2014, so it has been removed. Everything _should_ be resilient to 1099 // of Feb 2014, so it has been removed. Everything _should_ be resilient to
1031 // extra data of that sort. 1100 // extra data of that sort.
1032 UpdateChunkRangesForList(download_store_.get(), 1101 UpdateChunkRangesForList(download_store_.get(),
1033 safe_browsing_util::kBinUrlList, lists); 1102 safe_browsing_util::kBinUrlList, lists);
1034 1103
1035 UpdateChunkRangesForList(csd_whitelist_store_.get(), 1104 UpdateChunkRangesForList(csd_whitelist_store_.get(),
1036 safe_browsing_util::kCsdWhiteList, lists); 1105 safe_browsing_util::kCsdWhiteList, lists);
1037 1106
1038 UpdateChunkRangesForList(download_whitelist_store_.get(), 1107 UpdateChunkRangesForList(download_whitelist_store_.get(),
1039 safe_browsing_util::kDownloadWhiteList, lists); 1108 safe_browsing_util::kDownloadWhiteList, lists);
1040 1109
1041 UpdateChunkRangesForList(extension_blacklist_store_.get(), 1110 UpdateChunkRangesForList(extension_blacklist_store_.get(),
1042 safe_browsing_util::kExtensionBlacklist, lists); 1111 safe_browsing_util::kExtensionBlacklist, lists);
1043 1112
1044 UpdateChunkRangesForList(side_effect_free_whitelist_store_.get(), 1113 UpdateChunkRangesForList(side_effect_free_whitelist_store_.get(),
1045 safe_browsing_util::kSideEffectFreeWhitelist, lists); 1114 safe_browsing_util::kSideEffectFreeWhitelist, lists);
1046 1115
1047 UpdateChunkRangesForList(ip_blacklist_store_.get(), 1116 UpdateChunkRangesForList(ip_blacklist_store_.get(),
1048 safe_browsing_util::kIPBlacklist, lists); 1117 safe_browsing_util::kIPBlacklist, lists);
1049 1118
1119 UpdateChunkRangesForList(unwanted_software_store_.get(),
1120 safe_browsing_util::kUnwantedUrlList, lists);
1121
1050 corruption_detected_ = false; 1122 corruption_detected_ = false;
1051 change_detected_ = false; 1123 change_detected_ = false;
1052 return true; 1124 return true;
1053 } 1125 }
1054 1126
1055 void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) { 1127 void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) {
1056 DCHECK_EQ(creation_loop_, base::MessageLoop::current()); 1128 DCHECK_EQ(creation_loop_, base::MessageLoop::current());
1057 1129
1058 // The update may have failed due to corrupt storage (for instance, 1130 // The update may have failed due to corrupt storage (for instance,
1059 // an excessive number of invalid add_chunks and sub_chunks). 1131 // an excessive number of invalid add_chunks and sub_chunks).
(...skipping 23 matching lines...) Expand all
1083 1155
1084 if (side_effect_free_whitelist_store_ && 1156 if (side_effect_free_whitelist_store_ &&
1085 !side_effect_free_whitelist_store_->CheckValidity()) { 1157 !side_effect_free_whitelist_store_->CheckValidity()) {
1086 DLOG(ERROR) << "Safe-browsing side-effect free whitelist database " 1158 DLOG(ERROR) << "Safe-browsing side-effect free whitelist database "
1087 << "corrupt."; 1159 << "corrupt.";
1088 } 1160 }
1089 1161
1090 if (ip_blacklist_store_ && !ip_blacklist_store_->CheckValidity()) { 1162 if (ip_blacklist_store_ && !ip_blacklist_store_->CheckValidity()) {
1091 DLOG(ERROR) << "Safe-browsing IP blacklist database corrupt."; 1163 DLOG(ERROR) << "Safe-browsing IP blacklist database corrupt.";
1092 } 1164 }
1165
1166 if (unwanted_software_store_ &&
1167 !unwanted_software_store_->CheckValidity()) {
1168 DLOG(ERROR) << "Unwanted software url list database corrupt.";
1169 }
1093 } 1170 }
1094 1171
1095 if (corruption_detected_) 1172 if (corruption_detected_)
1096 return; 1173 return;
1097 1174
1098 // Unroll the transaction if there was a protocol error or if the 1175 // Unroll the transaction if there was a protocol error or if the
1099 // transaction was empty. This will leave the prefix set, the 1176 // transaction was empty. This will leave the prefix set, the
1100 // pending hashes, and the prefix miss cache in place. 1177 // pending hashes, and the prefix miss cache in place.
1101 if (!update_succeeded || !change_detected_) { 1178 if (!update_succeeded || !change_detected_) {
1102 // Track empty updates to answer questions at http://crbug.com/72216 . 1179 // Track empty updates to answer questions at http://crbug.com/72216 .
1103 if (update_succeeded && !change_detected_) 1180 if (update_succeeded && !change_detected_)
1104 UMA_HISTOGRAM_COUNTS("SB2.DatabaseUpdateKilobytes", 0); 1181 UMA_HISTOGRAM_COUNTS("SB2.DatabaseUpdateKilobytes", 0);
1105 browse_store_->CancelUpdate(); 1182 browse_store_->CancelUpdate();
1106 if (download_store_.get()) 1183 if (download_store_.get())
1107 download_store_->CancelUpdate(); 1184 download_store_->CancelUpdate();
1108 if (csd_whitelist_store_.get()) 1185 if (csd_whitelist_store_.get())
1109 csd_whitelist_store_->CancelUpdate(); 1186 csd_whitelist_store_->CancelUpdate();
1110 if (download_whitelist_store_.get()) 1187 if (download_whitelist_store_.get())
1111 download_whitelist_store_->CancelUpdate(); 1188 download_whitelist_store_->CancelUpdate();
1112 if (extension_blacklist_store_) 1189 if (extension_blacklist_store_)
1113 extension_blacklist_store_->CancelUpdate(); 1190 extension_blacklist_store_->CancelUpdate();
1114 if (side_effect_free_whitelist_store_) 1191 if (side_effect_free_whitelist_store_)
1115 side_effect_free_whitelist_store_->CancelUpdate(); 1192 side_effect_free_whitelist_store_->CancelUpdate();
1116 if (ip_blacklist_store_) 1193 if (ip_blacklist_store_)
1117 ip_blacklist_store_->CancelUpdate(); 1194 ip_blacklist_store_->CancelUpdate();
1195 if (unwanted_software_store_)
1196 unwanted_software_store_->CancelUpdate();
1118 return; 1197 return;
1119 } 1198 }
1120 1199
1121 if (download_store_) { 1200 if (download_store_) {
1122 int64 size_bytes = UpdateHashPrefixStore( 1201 int64 size_bytes = UpdateHashPrefixStore(
1123 DownloadDBFilename(filename_base_), 1202 DownloadDBFilename(filename_base_),
1124 download_store_.get(), 1203 download_store_.get(),
1125 FAILURE_DOWNLOAD_DATABASE_UPDATE_FINISH); 1204 FAILURE_DOWNLOAD_DATABASE_UPDATE_FINISH);
1126 UMA_HISTOGRAM_COUNTS("SB2.DownloadDatabaseKilobytes", 1205 UMA_HISTOGRAM_COUNTS("SB2.DownloadDatabaseKilobytes",
1127 static_cast<int>(size_bytes / 1024)); 1206 static_cast<int>(size_bytes / 1024));
1128 } 1207 }
1129 1208
1130 UpdateBrowseStore(); 1209 UpdatePrefixSetUrlStore(BrowseDBFilename(filename_base_),
1210 browse_store_.get(),
1211 &browse_prefix_set_,
1212 FAILURE_BROWSE_DATABASE_UPDATE_FINISH,
1213 FAILURE_BROWSE_PREFIX_SET_WRITE);
1214
1131 UpdateWhitelistStore(CsdWhitelistDBFilename(filename_base_), 1215 UpdateWhitelistStore(CsdWhitelistDBFilename(filename_base_),
1132 csd_whitelist_store_.get(), 1216 csd_whitelist_store_.get(),
1133 &csd_whitelist_); 1217 &csd_whitelist_);
1134 UpdateWhitelistStore(DownloadWhitelistDBFilename(filename_base_), 1218 UpdateWhitelistStore(DownloadWhitelistDBFilename(filename_base_),
1135 download_whitelist_store_.get(), 1219 download_whitelist_store_.get(),
1136 &download_whitelist_); 1220 &download_whitelist_);
1137 1221
1138 if (extension_blacklist_store_) { 1222 if (extension_blacklist_store_) {
1139 int64 size_bytes = UpdateHashPrefixStore( 1223 int64 size_bytes = UpdateHashPrefixStore(
1140 ExtensionBlacklistDBFilename(filename_base_), 1224 ExtensionBlacklistDBFilename(filename_base_),
1141 extension_blacklist_store_.get(), 1225 extension_blacklist_store_.get(),
1142 FAILURE_EXTENSION_BLACKLIST_UPDATE_FINISH); 1226 FAILURE_EXTENSION_BLACKLIST_UPDATE_FINISH);
1143 UMA_HISTOGRAM_COUNTS("SB2.ExtensionBlacklistKilobytes", 1227 UMA_HISTOGRAM_COUNTS("SB2.ExtensionBlacklistKilobytes",
1144 static_cast<int>(size_bytes / 1024)); 1228 static_cast<int>(size_bytes / 1024));
1145 } 1229 }
1146 1230
1147 if (side_effect_free_whitelist_store_) 1231 if (side_effect_free_whitelist_store_)
1148 UpdateSideEffectFreeWhitelistStore(); 1232 UpdateSideEffectFreeWhitelistStore();
1149 1233
1150 if (ip_blacklist_store_) 1234 if (ip_blacklist_store_)
1151 UpdateIpBlacklistStore(); 1235 UpdateIpBlacklistStore();
1236
1237 if (unwanted_software_store_) {
1238 UpdatePrefixSetUrlStore(UnwantedSoftwareDBFilename(filename_base_),
1239 unwanted_software_store_.get(),
1240 &unwanted_software_prefix_set_,
1241 FAILURE_UNWANTED_SOFTWARE_DATABASE_UPDATE_FINISH,
1242 FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_WRITE);
1243 }
1152 } 1244 }
1153 1245
1154 void SafeBrowsingDatabaseNew::UpdateWhitelistStore( 1246 void SafeBrowsingDatabaseNew::UpdateWhitelistStore(
1155 const base::FilePath& store_filename, 1247 const base::FilePath& store_filename,
1156 SafeBrowsingStore* store, 1248 SafeBrowsingStore* store,
1157 SBWhitelist* whitelist) { 1249 SBWhitelist* whitelist) {
1158 if (!store) 1250 if (!store)
1159 return; 1251 return;
1160 1252
1161 // Note: |builder| will not be empty. The current data store implementation 1253 // Note: |builder| will not be empty. The current data store implementation
(...skipping 25 matching lines...) Expand all
1187 if (!store->FinishUpdate(&builder, &add_full_hashes_result)) 1279 if (!store->FinishUpdate(&builder, &add_full_hashes_result))
1188 RecordFailure(failure_type); 1280 RecordFailure(failure_type);
1189 1281
1190 #if defined(OS_MACOSX) 1282 #if defined(OS_MACOSX)
1191 base::mac::SetFileBackupExclusion(store_filename); 1283 base::mac::SetFileBackupExclusion(store_filename);
1192 #endif 1284 #endif
1193 1285
1194 return GetFileSizeOrZero(store_filename); 1286 return GetFileSizeOrZero(store_filename);
1195 } 1287 }
1196 1288
1197 void SafeBrowsingDatabaseNew::UpdateBrowseStore() { 1289 void SafeBrowsingDatabaseNew::UpdatePrefixSetUrlStore(
1290 const base::FilePath& db_filename,
1291 SafeBrowsingStore* url_store,
1292 scoped_ptr<safe_browsing::PrefixSet>* prefix_set,
1293 FailureType finish_failure_type,
1294 FailureType write_failure_type) {
1198 // Measure the amount of IO during the filter build. 1295 // Measure the amount of IO during the filter build.
1199 base::IoCounters io_before, io_after; 1296 base::IoCounters io_before, io_after;
1200 base::ProcessHandle handle = base::GetCurrentProcessHandle(); 1297 base::ProcessHandle handle = base::GetCurrentProcessHandle();
1201 scoped_ptr<base::ProcessMetrics> metric( 1298 scoped_ptr<base::ProcessMetrics> metric(
1202 #if !defined(OS_MACOSX) 1299 #if !defined(OS_MACOSX)
1203 base::ProcessMetrics::CreateProcessMetrics(handle) 1300 base::ProcessMetrics::CreateProcessMetrics(handle)
1204 #else 1301 #else
1205 // Getting stats only for the current process is enough, so NULL is fine. 1302 // Getting stats only for the current process is enough, so NULL is fine.
1206 base::ProcessMetrics::CreateProcessMetrics(handle, NULL) 1303 base::ProcessMetrics::CreateProcessMetrics(handle, NULL)
1207 #endif 1304 #endif
1208 ); 1305 );
1209 1306
1210 // IoCounters are currently not supported on Mac, and may not be 1307 // IoCounters are currently not supported on Mac, and may not be
1211 // available for Linux, so we check the result and only show IO 1308 // available for Linux, so we check the result and only show IO
1212 // stats if they are available. 1309 // stats if they are available.
1213 const bool got_counters = metric->GetIOCounters(&io_before); 1310 const bool got_counters = metric->GetIOCounters(&io_before);
1214 1311
1215 const base::TimeTicks before = base::TimeTicks::Now(); 1312 const base::TimeTicks before = base::TimeTicks::Now();
1216 1313
1217 // TODO(shess): Perhaps refactor to let builder accumulate full hashes on the 1314 // TODO(shess): Perhaps refactor to let builder accumulate full hashes on the
1218 // fly? Other clients use the SBAddFullHash vector, but AFAICT they only use 1315 // fly? Other clients use the SBAddFullHash vector, but AFAICT they only use
1219 // the SBFullHash portion. It would need an accessor on PrefixSet. 1316 // the SBFullHash portion. It would need an accessor on PrefixSet.
1220 safe_browsing::PrefixSetBuilder builder; 1317 safe_browsing::PrefixSetBuilder builder;
1221 std::vector<SBAddFullHash> add_full_hashes; 1318 std::vector<SBAddFullHash> add_full_hashes;
1222 if (!browse_store_->FinishUpdate(&builder, &add_full_hashes)) { 1319 if (!url_store->FinishUpdate(&builder, &add_full_hashes)) {
1223 RecordFailure(FAILURE_BROWSE_DATABASE_UPDATE_FINISH); 1320 RecordFailure(finish_failure_type);
1224 return; 1321 return;
1225 } 1322 }
1226 1323
1227 std::vector<SBFullHash> full_hash_results; 1324 std::vector<SBFullHash> full_hash_results;
1228 for (size_t i = 0; i < add_full_hashes.size(); ++i) { 1325 for (size_t i = 0; i < add_full_hashes.size(); ++i) {
1229 full_hash_results.push_back(add_full_hashes[i].full_hash); 1326 full_hash_results.push_back(add_full_hashes[i].full_hash);
1230 } 1327 }
1231 1328
1232 scoped_ptr<safe_browsing::PrefixSet> 1329 scoped_ptr<safe_browsing::PrefixSet> new_prefix_set(
1233 prefix_set(builder.GetPrefixSet(full_hash_results)); 1330 builder.GetPrefixSet(full_hash_results));
1234 1331
1235 // Swap in the newly built filter. 1332 // Swap in the newly built filter.
1236 { 1333 {
1237 base::AutoLock locked(lookup_lock_); 1334 base::AutoLock locked(lookup_lock_);
1238 browse_prefix_set_.swap(prefix_set); 1335 prefix_set->swap(new_prefix_set);
1239 } 1336 }
1240 1337
1241 UMA_HISTOGRAM_LONG_TIMES("SB2.BuildFilter", base::TimeTicks::Now() - before); 1338 UMA_HISTOGRAM_LONG_TIMES("SB2.BuildFilter", base::TimeTicks::Now() - before);
1242 1339
1243 // Persist the prefix set to disk. Since only this thread changes 1340 // Persist the prefix set to disk. Note: there is no need to lock since the
1244 // |browse_prefix_set_|, there is no need to lock. 1341 // only write to |*prefix_set| is on this thread (in the swap() above).
1245 WritePrefixSet(); 1342 // TODO(gab): Strengthen this requirement by design (const pointers) rather
1343 // than assumptions.
1344 WritePrefixSet(db_filename, prefix_set->get(), write_failure_type);
1246 1345
1247 // Gather statistics. 1346 // Gather statistics.
1248 if (got_counters && metric->GetIOCounters(&io_after)) { 1347 if (got_counters && metric->GetIOCounters(&io_after)) {
1249 UMA_HISTOGRAM_COUNTS("SB2.BuildReadKilobytes", 1348 UMA_HISTOGRAM_COUNTS("SB2.BuildReadKilobytes",
1250 static_cast<int>(io_after.ReadTransferCount - 1349 static_cast<int>(io_after.ReadTransferCount -
1251 io_before.ReadTransferCount) / 1024); 1350 io_before.ReadTransferCount) / 1024);
1252 UMA_HISTOGRAM_COUNTS("SB2.BuildWriteKilobytes", 1351 UMA_HISTOGRAM_COUNTS("SB2.BuildWriteKilobytes",
1253 static_cast<int>(io_after.WriteTransferCount - 1352 static_cast<int>(io_after.WriteTransferCount -
1254 io_before.WriteTransferCount) / 1024); 1353 io_before.WriteTransferCount) / 1024);
1255 UMA_HISTOGRAM_COUNTS("SB2.BuildReadOperations", 1354 UMA_HISTOGRAM_COUNTS("SB2.BuildReadOperations",
1256 static_cast<int>(io_after.ReadOperationCount - 1355 static_cast<int>(io_after.ReadOperationCount -
1257 io_before.ReadOperationCount)); 1356 io_before.ReadOperationCount));
1258 UMA_HISTOGRAM_COUNTS("SB2.BuildWriteOperations", 1357 UMA_HISTOGRAM_COUNTS("SB2.BuildWriteOperations",
1259 static_cast<int>(io_after.WriteOperationCount - 1358 static_cast<int>(io_after.WriteOperationCount -
1260 io_before.WriteOperationCount)); 1359 io_before.WriteOperationCount));
1261 } 1360 }
1262 1361
1263 const base::FilePath browse_filename = BrowseDBFilename(filename_base_); 1362 const int64 file_size = GetFileSizeOrZero(db_filename);
1264 const int64 file_size = GetFileSizeOrZero(browse_filename);
1265 UMA_HISTOGRAM_COUNTS("SB2.BrowseDatabaseKilobytes", 1363 UMA_HISTOGRAM_COUNTS("SB2.BrowseDatabaseKilobytes",
1266 static_cast<int>(file_size / 1024)); 1364 static_cast<int>(file_size / 1024));
1267 1365
1268 #if defined(OS_MACOSX) 1366 #if defined(OS_MACOSX)
1269 base::mac::SetFileBackupExclusion(browse_filename); 1367 base::mac::SetFileBackupExclusion(db_filename);
1270 #endif 1368 #endif
1271 } 1369 }
1272 1370
1273 void SafeBrowsingDatabaseNew::UpdateSideEffectFreeWhitelistStore() { 1371 void SafeBrowsingDatabaseNew::UpdateSideEffectFreeWhitelistStore() {
1274 safe_browsing::PrefixSetBuilder builder; 1372 safe_browsing::PrefixSetBuilder builder;
1275 std::vector<SBAddFullHash> add_full_hashes_result; 1373 std::vector<SBAddFullHash> add_full_hashes_result;
1276 1374
1277 if (!side_effect_free_whitelist_store_->FinishUpdate( 1375 if (!side_effect_free_whitelist_store_->FinishUpdate(
1278 &builder, &add_full_hashes_result)) { 1376 &builder, &add_full_hashes_result)) {
1279 RecordFailure(FAILURE_SIDE_EFFECT_FREE_WHITELIST_UPDATE_FINISH); 1377 RecordFailure(FAILURE_SIDE_EFFECT_FREE_WHITELIST_UPDATE_FINISH);
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
1353 1451
1354 // NOTE(shess): ResetDatabase() should remove the corruption, so this should 1452 // NOTE(shess): ResetDatabase() should remove the corruption, so this should
1355 // only happen once. If you are here because you are hitting this after a 1453 // only happen once. If you are here because you are hitting this after a
1356 // restart, then I would be very interested in working with you to figure out 1454 // restart, then I would be very interested in working with you to figure out
1357 // what is happening, since it may affect real users. 1455 // what is happening, since it may affect real users.
1358 DLOG(FATAL) << "SafeBrowsing database was corrupt and reset"; 1456 DLOG(FATAL) << "SafeBrowsing database was corrupt and reset";
1359 } 1457 }
1360 1458
1361 // TODO(shess): I'm not clear why this code doesn't have any 1459 // TODO(shess): I'm not clear why this code doesn't have any
1362 // real error-handling. 1460 // real error-handling.
1363 void SafeBrowsingDatabaseNew::LoadPrefixSet() { 1461 void SafeBrowsingDatabaseNew::LoadPrefixSet(
1462 const base::FilePath& db_filename,
1463 scoped_ptr<safe_browsing::PrefixSet>* prefix_set,
1464 FailureType read_failure_type) {
1465 if (!prefix_set)
1466 return;
1467
1364 DCHECK_EQ(creation_loop_, base::MessageLoop::current()); 1468 DCHECK_EQ(creation_loop_, base::MessageLoop::current());
1365 DCHECK(!filename_base_.empty()); 1469 DCHECK(!filename_base_.empty());
1366 1470
1367 const base::FilePath browse_filename = BrowseDBFilename(filename_base_); 1471 const base::FilePath prefix_set_filename = PrefixSetForFilename(db_filename);
1368 const base::FilePath browse_prefix_set_filename =
1369 PrefixSetForFilename(browse_filename);
1370 1472
1371 // Only use the prefix set if database is present and non-empty. 1473 // Only use the prefix set if database is present and non-empty.
1372 if (!GetFileSizeOrZero(browse_filename)) 1474 if (!GetFileSizeOrZero(db_filename))
1373 return; 1475 return;
1374 1476
1375 // Cleanup any stale bloom filter (no longer used). 1477 // Cleanup any stale bloom filter (no longer used).
1376 // TODO(shess): Track existence to drive removal of this code? 1478 // TODO(shess): Track existence to drive removal of this code?
1377 const base::FilePath bloom_filter_filename = 1479 const base::FilePath bloom_filter_filename =
1378 BloomFilterForFilename(browse_filename); 1480 BloomFilterForFilename(db_filename);
1379 base::DeleteFile(bloom_filter_filename, false); 1481 base::DeleteFile(bloom_filter_filename, false);
1380 1482
1381 const base::TimeTicks before = base::TimeTicks::Now(); 1483 const base::TimeTicks before = base::TimeTicks::Now();
1382 browse_prefix_set_ = safe_browsing::PrefixSet::LoadFile( 1484 *prefix_set = safe_browsing::PrefixSet::LoadFile(prefix_set_filename);
1383 browse_prefix_set_filename);
1384 UMA_HISTOGRAM_TIMES("SB2.PrefixSetLoad", base::TimeTicks::Now() - before); 1485 UMA_HISTOGRAM_TIMES("SB2.PrefixSetLoad", base::TimeTicks::Now() - before);
1385 1486
1386 if (!browse_prefix_set_.get()) 1487 if (!prefix_set->get())
1387 RecordFailure(FAILURE_BROWSE_PREFIX_SET_READ); 1488 RecordFailure(read_failure_type);
1388 } 1489 }
1389 1490
1390 bool SafeBrowsingDatabaseNew::Delete() { 1491 bool SafeBrowsingDatabaseNew::Delete() {
1391 DCHECK_EQ(creation_loop_, base::MessageLoop::current()); 1492 DCHECK_EQ(creation_loop_, base::MessageLoop::current());
1392 DCHECK(!filename_base_.empty()); 1493 DCHECK(!filename_base_.empty());
1393 1494
1394 // TODO(shess): This is a mess. SafeBrowsingFileStore::Delete() closes the 1495 // TODO(shess): This is a mess. SafeBrowsingFileStore::Delete() closes the
1395 // store before calling DeleteStore(). DeleteStore() deletes transient files 1496 // store before calling DeleteStore(). DeleteStore() deletes transient files
1396 // in addition to the main file. Probably all of these should be converted to 1497 // in addition to the main file. Probably all of these should be converted to
1397 // a helper which calls Delete() if the store exists, else DeleteStore() on 1498 // a helper which calls Delete() if the store exists, else DeleteStore() on
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
1452 side_effect_free_whitelist_prefix_set_filename, 1553 side_effect_free_whitelist_prefix_set_filename,
1453 false); 1554 false);
1454 if (!r9) 1555 if (!r9)
1455 RecordFailure(FAILURE_SIDE_EFFECT_FREE_WHITELIST_PREFIX_SET_DELETE); 1556 RecordFailure(FAILURE_SIDE_EFFECT_FREE_WHITELIST_PREFIX_SET_DELETE);
1456 1557
1457 const bool r10 = base::DeleteFile(IpBlacklistDBFilename(filename_base_), 1558 const bool r10 = base::DeleteFile(IpBlacklistDBFilename(filename_base_),
1458 false); 1559 false);
1459 if (!r10) 1560 if (!r10)
1460 RecordFailure(FAILURE_IP_BLACKLIST_DELETE); 1561 RecordFailure(FAILURE_IP_BLACKLIST_DELETE);
1461 1562
1462 return r1 && r2 && r3 && r4 && r5 && r6 && r7 && r8 && r9 && r10; 1563 const bool r11 =
1564 base::DeleteFile(UnwantedSoftwareDBFilename(filename_base_), false);
1565 if (!r11)
1566 RecordFailure(FAILURE_UNWANTED_SOFTWARE_PREFIX_SET_DELETE);
1567
1568 return r1 && r2 && r3 && r4 && r5 && r6 && r7 && r8 && r9 && r10 && r11;
1463 } 1569 }
1464 1570
1465 void SafeBrowsingDatabaseNew::WritePrefixSet() { 1571 void SafeBrowsingDatabaseNew::WritePrefixSet(
1572 const base::FilePath& db_filename,
1573 safe_browsing::PrefixSet* prefix_set,
1574 FailureType write_failure_type) {
1466 DCHECK_EQ(creation_loop_, base::MessageLoop::current()); 1575 DCHECK_EQ(creation_loop_, base::MessageLoop::current());
1467 1576
1468 if (!browse_prefix_set_.get()) 1577 if (!prefix_set)
1469 return; 1578 return;
1470 1579
1471 const base::FilePath browse_filename = BrowseDBFilename(filename_base_); 1580 const base::FilePath prefix_set_filename = PrefixSetForFilename(db_filename);
1472 const base::FilePath browse_prefix_set_filename =
1473 PrefixSetForFilename(browse_filename);
1474 1581
1475 const base::TimeTicks before = base::TimeTicks::Now(); 1582 const base::TimeTicks before = base::TimeTicks::Now();
1476 const bool write_ok = browse_prefix_set_->WriteFile( 1583 const bool write_ok = prefix_set->WriteFile(prefix_set_filename);
1477 browse_prefix_set_filename);
1478 UMA_HISTOGRAM_TIMES("SB2.PrefixSetWrite", base::TimeTicks::Now() - before); 1584 UMA_HISTOGRAM_TIMES("SB2.PrefixSetWrite", base::TimeTicks::Now() - before);
1479 1585
1480 const int64 file_size = GetFileSizeOrZero(browse_prefix_set_filename); 1586 const int64 file_size = GetFileSizeOrZero(prefix_set_filename);
1481 UMA_HISTOGRAM_COUNTS("SB2.PrefixSetKilobytes", 1587 UMA_HISTOGRAM_COUNTS("SB2.PrefixSetKilobytes",
1482 static_cast<int>(file_size / 1024)); 1588 static_cast<int>(file_size / 1024));
1483 1589
1484 if (!write_ok) 1590 if (!write_ok)
1485 RecordFailure(FAILURE_BROWSE_PREFIX_SET_WRITE); 1591 RecordFailure(write_failure_type);
1486 1592
1487 #if defined(OS_MACOSX) 1593 #if defined(OS_MACOSX)
1488 base::mac::SetFileBackupExclusion(browse_prefix_set_filename); 1594 base::mac::SetFileBackupExclusion(prefix_set_filename);
1489 #endif 1595 #endif
1490 } 1596 }
1491 1597
1492 void SafeBrowsingDatabaseNew::WhitelistEverything(SBWhitelist* whitelist) { 1598 void SafeBrowsingDatabaseNew::WhitelistEverything(SBWhitelist* whitelist) {
1493 base::AutoLock locked(lookup_lock_); 1599 base::AutoLock locked(lookup_lock_);
1494 whitelist->second = true; 1600 whitelist->second = true;
1495 whitelist->first.clear(); 1601 whitelist->first.clear();
1496 } 1602 }
1497 1603
1498 void SafeBrowsingDatabaseNew::LoadWhitelist( 1604 void SafeBrowsingDatabaseNew::LoadWhitelist(
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
1567 bool SafeBrowsingDatabaseNew::IsMalwareIPMatchKillSwitchOn() { 1673 bool SafeBrowsingDatabaseNew::IsMalwareIPMatchKillSwitchOn() {
1568 SBFullHash malware_kill_switch = SBFullHashForString(kMalwareIPKillSwitchUrl); 1674 SBFullHash malware_kill_switch = SBFullHashForString(kMalwareIPKillSwitchUrl);
1569 std::vector<SBFullHash> full_hashes; 1675 std::vector<SBFullHash> full_hashes;
1570 full_hashes.push_back(malware_kill_switch); 1676 full_hashes.push_back(malware_kill_switch);
1571 return ContainsWhitelistedHashes(csd_whitelist_, full_hashes); 1677 return ContainsWhitelistedHashes(csd_whitelist_, full_hashes);
1572 } 1678 }
1573 1679
1574 bool SafeBrowsingDatabaseNew::IsCsdWhitelistKillSwitchOn() { 1680 bool SafeBrowsingDatabaseNew::IsCsdWhitelistKillSwitchOn() {
1575 return csd_whitelist_.second; 1681 return csd_whitelist_.second;
1576 } 1682 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698