blob: fc45b942b00e1d8263e5a213f1e66a290be9e782 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commit09911bf2008-07-26 23:55:294//
5
6#include "chrome/browser/safe_browsing/safe_browsing_service.h"
7
[email protected]613a03b2008-10-24 23:02:008#include "base/command_line.h"
initial.commit09911bf2008-07-26 23:55:299#include "base/histogram.h"
10#include "base/logging.h"
11#include "base/message_loop.h"
12#include "base/path_service.h"
13#include "base/string_util.h"
14#include "chrome/browser/browser_process.h"
[email protected]0a3076572008-12-13 20:48:3615#include "chrome/browser/chrome_thread.h"
initial.commit09911bf2008-07-26 23:55:2916#include "chrome/browser/profile_manager.h"
17#include "chrome/browser/safe_browsing/protocol_manager.h"
18#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
19#include "chrome/browser/safe_browsing/safe_browsing_database.h"
20#include "chrome/common/chrome_constants.h"
21#include "chrome/common/chrome_paths.h"
[email protected]613a03b2008-10-24 23:02:0022#include "chrome/common/chrome_switches.h"
initial.commit09911bf2008-07-26 23:55:2923#include "chrome/common/pref_names.h"
24#include "chrome/common/pref_service.h"
25#include "net/base/registry_controlled_domain.h"
26
[email protected]e1acf6f2008-10-27 20:43:3327using base::Time;
28using base::TimeDelta;
29
initial.commit09911bf2008-07-26 23:55:2930SafeBrowsingService::SafeBrowsingService()
31 : io_loop_(NULL),
32 database_(NULL),
33 protocol_manager_(NULL),
34 enabled_(false),
[email protected]613a03b2008-10-24 23:02:0035 resetting_(false),
[email protected]57119c3f2008-12-04 00:33:0436 database_loaded_(false),
37 update_in_progress_(false) {
[email protected]7ab495722008-11-26 00:18:2738 new_safe_browsing_ = !CommandLine().HasSwitch(switches::kUseOldSafeBrowsing);
[email protected]0a3076572008-12-13 20:48:3639 base::SystemMonitor* monitor = base::SystemMonitor::Get();
40 DCHECK(monitor);
41 if (monitor)
42 monitor->AddObserver(this);
initial.commit09911bf2008-07-26 23:55:2943}
44
45SafeBrowsingService::~SafeBrowsingService() {
[email protected]0a3076572008-12-13 20:48:3646 base::SystemMonitor* monitor = base::SystemMonitor::Get();
47 if (monitor)
48 monitor->RemoveObserver(this);
initial.commit09911bf2008-07-26 23:55:2949}
50
51// Only called on the UI thread.
52void SafeBrowsingService::Initialize(MessageLoop* io_loop) {
53 io_loop_ = io_loop;
54
55 // Get the profile's preference for SafeBrowsing.
56 std::wstring user_data_dir;
57 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
58 ProfileManager* profile_manager = g_browser_process->profile_manager();
59 Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
60 PrefService* pref_service = profile->GetPrefs();
61 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
62 Start();
63}
64
65// Start up SafeBrowsing objects. This can be called at browser start, or when
66// the user checks the "Enable SafeBrowsing" option in the Advanced options UI.
67void SafeBrowsingService::Start() {
68 DCHECK(!db_thread_.get());
[email protected]ab820df2008-08-26 05:55:1069 db_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
initial.commit09911bf2008-07-26 23:55:2970 if (!db_thread_->Start())
71 return;
72
73 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
74 this, &SafeBrowsingService::OnDBInitialize));
75
76 // Retrieve client MAC keys.
77 PrefService* local_state = g_browser_process->local_state();
78 std::string client_key, wrapped_key;
79 if (local_state) {
80 client_key =
81 WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
82 wrapped_key =
83 WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
84 }
85
86 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
87 this, &SafeBrowsingService::OnIOInitialize, MessageLoop::current(),
88 client_key, wrapped_key));
89}
90
91void SafeBrowsingService::ShutDown() {
92 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
93 this, &SafeBrowsingService::OnIOShutdown));
94}
95
96void SafeBrowsingService::OnIOInitialize(MessageLoop* notify_loop,
97 const std::string& client_key,
98 const std::string& wrapped_key) {
99 DCHECK(MessageLoop::current() == io_loop_);
100 enabled_ = true;
101 protocol_manager_ = new SafeBrowsingProtocolManager(this,
102 notify_loop,
103 client_key,
104 wrapped_key);
[email protected]613a03b2008-10-24 23:02:00105 // We want to initialize the protocol manager only after the database has
106 // loaded, which we'll receive asynchronously (DatabaseLoadComplete). If
107 // database_loaded_ isn't true, we'll wait for that notification to do the
108 // init.
109 if (database_loaded_)
110 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29111}
112
113void SafeBrowsingService::OnDBInitialize() {
114 DCHECK(MessageLoop::current() == db_thread_->message_loop());
115 GetDatabase();
116}
117
118void SafeBrowsingService::OnIOShutdown() {
119 DCHECK(MessageLoop::current() == io_loop_);
120 if (!enabled_)
121 return;
122
123 enabled_ = false;
[email protected]fbb2b7a2008-11-18 22:54:04124 resetting_ = false;
initial.commit09911bf2008-07-26 23:55:29125
126 // This cancels all in-flight GetHash requests.
127 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04128 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29129
130 if (db_thread_.get())
131 db_thread_->message_loop()->DeleteSoon(FROM_HERE, database_);
132
133 // Flush the database thread. Any in-progress database check results will be
134 // ignored and cleaned up below.
135 db_thread_.reset(NULL);
136
137 database_ = NULL;
[email protected]fbb2b7a2008-11-18 22:54:04138 database_loaded_ = false;
initial.commit09911bf2008-07-26 23:55:29139
[email protected]613a03b2008-10-24 23:02:00140 // Delete queued and pending checks once the database thread is done, calling
141 // back any clients with 'URL_SAFE'.
142 while (!queued_checks_.empty()) {
143 QueuedCheck check = queued_checks_.front();
144 if (check.client)
145 check.client->OnUrlCheckResult(check.url, URL_SAFE);
146 queued_checks_.pop_front();
147 }
148
initial.commit09911bf2008-07-26 23:55:29149 for (CurrentChecks::iterator it = checks_.begin();
150 it != checks_.end(); ++it) {
151 if ((*it)->client)
152 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
153 delete *it;
154 }
155 checks_.clear();
156
157 gethash_requests_.clear();
158}
159
160// Runs on the UI thread.
161void SafeBrowsingService::OnEnable(bool enabled) {
162 if (enabled)
163 Start();
164 else
165 ShutDown();
166}
167
168bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
169 return url.SchemeIs("http") || url.SchemeIs("https");
170}
171
172bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
173 DCHECK(MessageLoop::current() == io_loop_);
initial.commit09911bf2008-07-26 23:55:29174 if (!enabled_ || !database_)
175 return true;
176
[email protected]613a03b2008-10-24 23:02:00177 if (new_safe_browsing_)
178 return CheckUrlNew(url, client);
179
initial.commit09911bf2008-07-26 23:55:29180 if (!resetting_) {
181 Time start_time = Time::Now();
182 bool need_check = database_->NeedToCheckUrl(url);
183 UMA_HISTOGRAM_TIMES(L"SB.BloomFilter", Time::Now() - start_time);
184 if (!need_check)
185 return true; // The url is definitely safe.
186 }
187
188 // The url may or may not be safe, need to go to the database to be sure.
189 SafeBrowsingCheck* check = new SafeBrowsingCheck();
190 check->url = url;
191 check->client = client;
192 check->result = URL_SAFE;
193 check->need_get_hash = false;
194 check->start = Time::Now();
195 checks_.insert(check);
196
[email protected]613a03b2008-10-24 23:02:00197 // Old school SafeBrowsing does an asynchronous database check.
initial.commit09911bf2008-07-26 23:55:29198 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
199 this, &SafeBrowsingService::CheckDatabase,
200 check, protocol_manager_->last_update()));
[email protected]613a03b2008-10-24 23:02:00201
202 return false;
203}
204
205bool SafeBrowsingService::CheckUrlNew(const GURL& url, Client* client) {
206 if (resetting_ || !database_loaded_) {
207 QueuedCheck check;
208 check.client = client;
209 check.url = url;
210 queued_checks_.push_back(check);
211 return false;
212 }
213
214 std::string list;
215 std::vector<SBPrefix> prefix_hits;
216 std::vector<SBFullHashResult> full_hits;
[email protected]22573822008-11-14 00:40:47217 base::Time check_start = base::Time::Now();
[email protected]c3ff89492008-11-11 02:17:51218 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
219 &full_hits,
[email protected]613a03b2008-10-24 23:02:00220 protocol_manager_->last_update());
[email protected]22573822008-11-14 00:40:47221
222 UMA_HISTOGRAM_TIMES(L"SB2.FilterCheck", base::Time::Now() - check_start);
223
[email protected]613a03b2008-10-24 23:02:00224 if (!prefix_match)
225 return true; // URL is okay.
226
227 // Needs to be asynchronous, since we could be in the constructor of a
228 // ResourceDispatcherHost event handler which can't pause there.
229 SafeBrowsingCheck* check = new SafeBrowsingCheck();
230 check->url = url;
231 check->client = client;
232 check->result = URL_SAFE;
233 check->start = Time::Now();
234 check->need_get_hash = full_hits.empty();
235 check->prefix_hits.swap(prefix_hits);
236 check->full_hits.swap(full_hits);
237 checks_.insert(check);
238
239 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
240 this, &SafeBrowsingService::OnCheckDone, check));
241
initial.commit09911bf2008-07-26 23:55:29242 return false;
243}
244
245void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
246 ResourceType::Type resource_type,
247 UrlCheckResult result,
248 Client* client,
249 MessageLoop* ui_loop,
250 int render_process_host_id,
251 int render_view_id) {
252 // Check if the user has already ignored our warning for this render_view
253 // and domain.
254 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
255 const WhiteListedEntry& entry = white_listed_entries_[i];
256 if (entry.render_process_host_id == render_process_host_id &&
257 entry.render_view_id == render_view_id &&
258 entry.result == result &&
259 entry.domain ==
[email protected]8ac1a752008-07-31 19:40:37260 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
initial.commit09911bf2008-07-26 23:55:29261 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
262 this, &SafeBrowsingService::NotifyClientBlockingComplete,
263 client, true));
264 return;
265 }
266 }
267
[email protected]cbab76d2008-10-13 22:42:47268 BlockingPageParam param;
269 param.url = url;
270 param.resource_type = resource_type;
271 param.result = result;
272 param.client = client;
273 param.render_process_host_id = render_process_host_id;
274 param.render_view_id = render_view_id;
275 // The blocking page must be created from the UI thread.
276 ui_loop->PostTask(FROM_HERE, NewRunnableMethod(this,
277 &SafeBrowsingService::DoDisplayBlockingPage,
278 param));
279}
280
281// Invoked on the UI thread.
282void SafeBrowsingService::DoDisplayBlockingPage(
283 const BlockingPageParam& param) {
284 SafeBrowsingBlockingPage* blocking_page = new SafeBrowsingBlockingPage(this,
285 param);
286 blocking_page->Show();
initial.commit09911bf2008-07-26 23:55:29287}
288
289void SafeBrowsingService::CancelCheck(Client* client) {
290 DCHECK(MessageLoop::current() == io_loop_);
291
292 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
293 if ((*i)->client == client)
294 (*i)->client = NULL;
295 }
[email protected]613a03b2008-10-24 23:02:00296
297 // Scan the queued clients store. Clients may be here if they requested a URL
298 // check before the database has finished loading or resetting.
299 if (!database_loaded_ || resetting_) {
300 std::deque<QueuedCheck>::iterator it = queued_checks_.begin();
301 for (; it != queued_checks_.end(); ++it) {
302 if (it->client == client)
303 it->client = NULL;
304 }
305 }
initial.commit09911bf2008-07-26 23:55:29306}
307
308void SafeBrowsingService::CheckDatabase(SafeBrowsingCheck* info,
309 Time last_update) {
310 DCHECK(MessageLoop::current() == db_thread_->message_loop());
311 // If client == NULL it means it was cancelled, no need for db lookup.
312 if (info->client && GetDatabase()) {
313 Time now = Time::Now();
314 std::string list;
315 if (GetDatabase()->ContainsUrl(info->url,
316 &list,
317 &info->prefix_hits,
318 &info->full_hits,
319 last_update)) {
320 if (info->prefix_hits.empty()) {
321 info->result = GetResultFromListname(list);
322 } else {
323 if (info->full_hits.empty())
324 info->need_get_hash = true;
325 }
326 }
327 info->db_time = Time::Now() - now;
328 }
329
330 if (io_loop_)
331 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
332 this, &SafeBrowsingService::OnCheckDone, info));
333}
334
[email protected]613a03b2008-10-24 23:02:00335void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
initial.commit09911bf2008-07-26 23:55:29336 DCHECK(MessageLoop::current() == io_loop_);
337
338 // If we've been shutdown during the database lookup, this check will already
339 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00340 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29341 return;
342
[email protected]613a03b2008-10-24 23:02:00343 UMA_HISTOGRAM_TIMES(L"SB.Database", Time::Now() - check->start);
344 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29345 // We have a partial match so we need to query Google for the full hash.
346 // Clean up will happen in HandleGetHashResults.
347
348 // See if we have a GetHash request already in progress for this particular
349 // prefix. If so, we just append ourselves to the list of interested parties
350 // when the results arrive. We only do this for checks involving one prefix,
351 // since that is the common case (multiple prefixes will issue the request
352 // as normal).
[email protected]613a03b2008-10-24 23:02:00353 if (check->prefix_hits.size() == 1) {
354 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29355 GetHashRequests::iterator it = gethash_requests_.find(prefix);
356 if (it != gethash_requests_.end()) {
357 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00358 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29359 return;
360 }
361
362 // No request in progress, so we're the first for this prefix.
363 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00364 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29365 gethash_requests_[prefix] = requestors;
366 }
367
368 // Reset the start time so that we can measure the network time without the
369 // database time.
[email protected]613a03b2008-10-24 23:02:00370 check->start = Time::Now();
371 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29372 } else {
373 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00374 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29375 }
376}
377
378SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
379 DCHECK(MessageLoop::current() == db_thread_->message_loop());
380 if (database_)
381 return database_;
382
383 std::wstring path;
384 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
385 DCHECK(result);
386
387 path.append(L"\\");
388 path.append(chrome::kSafeBrowsingFilename);
389
390 Time before = Time::Now();
[email protected]54d80bb02008-09-20 02:03:08391 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]613a03b2008-10-24 23:02:00392 Callback0::Type* chunk_callback =
initial.commit09911bf2008-07-26 23:55:29393 NewCallback(this, &SafeBrowsingService::ChunkInserted);
[email protected]613a03b2008-10-24 23:02:00394 bool init_success = database->Init(path, chunk_callback);
395
396 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
397 this, &SafeBrowsingService::DatabaseLoadComplete, !init_success));
398
399 if (!init_success) {
initial.commit09911bf2008-07-26 23:55:29400 NOTREACHED();
401 return NULL;
402 }
403
404 database_ = database;
405
406 TimeDelta open_time = Time::Now() - before;
407 SB_DLOG(INFO) << "SafeBrowsing database open took " <<
408 open_time.InMilliseconds() << " ms.";
409
410 return database_;
411}
412
413// Public API called only on the IO thread.
414// The SafeBrowsingProtocolManager has received the full hash results for
415// prefix hits detected in the database.
416void SafeBrowsingService::HandleGetHashResults(
417 SafeBrowsingCheck* check,
[email protected]200abc32008-09-05 01:44:33418 const std::vector<SBFullHashResult>& full_hashes,
419 bool can_cache) {
initial.commit09911bf2008-07-26 23:55:29420 if (checks_.find(check) == checks_.end())
421 return;
422
423 DCHECK(enabled_);
424
[email protected]22573822008-11-14 00:40:47425 if (new_safe_browsing_)
426 UMA_HISTOGRAM_LONG_TIMES(L"SB2.Network", Time::Now() - check->start);
427 else
428 UMA_HISTOGRAM_LONG_TIMES(L"SB.Network", Time::Now() - check->start);
429
[email protected]200abc32008-09-05 01:44:33430 std::vector<SBPrefix> prefixes = check->prefix_hits;
initial.commit09911bf2008-07-26 23:55:29431 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
432
[email protected]613a03b2008-10-24 23:02:00433 if (can_cache) {
434 if (!new_safe_browsing_) {
435 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
436 this, &SafeBrowsingService::CacheHashResults, prefixes, full_hashes));
437 } else if (database_) {
[email protected]c3ff89492008-11-11 02:17:51438 // Cache the GetHash results in memory:
[email protected]613a03b2008-10-24 23:02:00439 database_->CacheHashResults(prefixes, full_hashes);
440 }
441 }
initial.commit09911bf2008-07-26 23:55:29442}
443
444void SafeBrowsingService::OnHandleGetHashResults(
445 SafeBrowsingCheck* check,
446 const std::vector<SBFullHashResult>& full_hashes) {
447 SBPrefix prefix = check->prefix_hits[0];
448 GetHashRequests::iterator it = gethash_requests_.find(prefix);
449 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
450 HandleOneCheck(check, full_hashes);
451 return;
452 }
453
454 // Call back all interested parties.
455 GetHashRequestors& requestors = it->second;
456 for (GetHashRequestors::iterator r = requestors.begin();
457 r != requestors.end(); ++r) {
458 HandleOneCheck(*r, full_hashes);
459 }
460
461 gethash_requests_.erase(it);
462}
463
464void SafeBrowsingService::HandleOneCheck(
465 SafeBrowsingCheck* check,
466 const std::vector<SBFullHashResult>& full_hashes) {
467 if (check->client) {
468 UrlCheckResult result = URL_SAFE;
469 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
470 if (index != -1)
471 result = GetResultFromListname(full_hashes[index].list_name);
472
473 // Let the client continue handling the original request.
474 check->client->OnUrlCheckResult(check->url, result);
475 }
476
477 checks_.erase(check);
478 delete check;
479}
480
[email protected]57119c3f2008-12-04 00:33:04481void SafeBrowsingService::UpdateStarted() {
initial.commit09911bf2008-07-26 23:55:29482 DCHECK(MessageLoop::current() == io_loop_);
483 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04484 DCHECK(!update_in_progress_);
485 update_in_progress_ = true;
initial.commit09911bf2008-07-26 23:55:29486 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
487 this, &SafeBrowsingService::GetAllChunksFromDatabase));
488}
489
[email protected]613a03b2008-10-24 23:02:00490void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41491 DCHECK(MessageLoop::current() == io_loop_);
492 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04493 if (update_in_progress_) {
494 update_in_progress_ = false;
495 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
496 this, &SafeBrowsingService::DatabaseUpdateFinished, update_succeeded));
497 }
[email protected]aad08752008-10-02 22:13:41498}
499
[email protected]613a03b2008-10-24 23:02:00500void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41501 DCHECK(MessageLoop::current() == db_thread_->message_loop());
502 if (GetDatabase())
[email protected]613a03b2008-10-24 23:02:00503 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]aad08752008-10-02 22:13:41504}
505
[email protected]cbab76d2008-10-13 22:42:47506void SafeBrowsingService::OnBlockingPageDone(const BlockingPageParam& param) {
507 NotifyClientBlockingComplete(param.client, param.proceed);
initial.commit09911bf2008-07-26 23:55:29508
[email protected]cbab76d2008-10-13 22:42:47509 if (param.proceed) {
initial.commit09911bf2008-07-26 23:55:29510 // Whitelist this domain and warning type for the given tab.
511 WhiteListedEntry entry;
[email protected]cbab76d2008-10-13 22:42:47512 entry.render_process_host_id = param.render_process_host_id;
513 entry.render_view_id = param.render_view_id;
514 entry.domain =
515 net::RegistryControlledDomainService::GetDomainAndRegistry(param.url);
516 entry.result = param.result;
initial.commit09911bf2008-07-26 23:55:29517 white_listed_entries_.push_back(entry);
518 }
initial.commit09911bf2008-07-26 23:55:29519}
520
521void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
522 bool proceed) {
523 client->OnBlockingPageComplete(proceed);
524}
525
526// This method runs on the UI loop to access the prefs.
527void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
528 const std::string& wrapped_key) {
529 PrefService* prefs = g_browser_process->local_state();
530 if (prefs) {
531 prefs->SetString(prefs::kSafeBrowsingClientKey, ASCIIToWide(client_key));
532 prefs->SetString(prefs::kSafeBrowsingWrappedKey, ASCIIToWide(wrapped_key));
533 }
534}
535
536void SafeBrowsingService::ChunkInserted() {
[email protected]fbb2b7a2008-11-18 22:54:04537 DCHECK(MessageLoop::current() == db_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29538 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
539 this, &SafeBrowsingService::OnChunkInserted));
540}
541
542void SafeBrowsingService::OnChunkInserted() {
543 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04544 if (enabled_)
545 protocol_manager_->OnChunkInserted();
initial.commit09911bf2008-07-26 23:55:29546}
547
[email protected]613a03b2008-10-24 23:02:00548void SafeBrowsingService::DatabaseLoadComplete(bool database_error) {
549 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04550 if (!enabled_)
551 return;
[email protected]613a03b2008-10-24 23:02:00552
553 database_loaded_ = true;
554
555 // TODO(paulg): More robust database initialization error handling.
556 if (protocol_manager_ && !database_error)
557 protocol_manager_->Initialize();
558
559 // If we have any queued requests, we can now check them.
560 if (!resetting_)
561 RunQueuedClients();
562}
563
initial.commit09911bf2008-07-26 23:55:29564// static
565void SafeBrowsingService::RegisterUserPrefs(PrefService* prefs) {
566 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, L"");
567 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, L"");
568}
569
570void SafeBrowsingService::ResetDatabase() {
571 DCHECK(MessageLoop::current() == io_loop_);
572 resetting_ = true;
573 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
574 this, &SafeBrowsingService::OnResetDatabase));
575}
576
577void SafeBrowsingService::OnResetDatabase() {
578 DCHECK(MessageLoop::current() == db_thread_->message_loop());
579 GetDatabase()->ResetDatabase();
580 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
581 this, &SafeBrowsingService::OnResetComplete));
582}
583
584void SafeBrowsingService::OnResetComplete() {
585 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04586 if (enabled_) {
587 resetting_ = false;
588 database_loaded_ = true;
589 RunQueuedClients();
590 }
initial.commit09911bf2008-07-26 23:55:29591}
592
593void SafeBrowsingService::HandleChunk(const std::string& list,
594 std::deque<SBChunk>* chunks) {
595 DCHECK(MessageLoop::current() == io_loop_);
596 DCHECK(enabled_);
597 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
598 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
599}
600
601void SafeBrowsingService::HandleChunkForDatabase(
602 const std::string& list_name,
603 std::deque<SBChunk>* chunks) {
604 DCHECK(MessageLoop::current() == db_thread_->message_loop());
605
606 GetDatabase()->InsertChunks(list_name, chunks);
607}
608
609void SafeBrowsingService::HandleChunkDelete(
610 std::vector<SBChunkDelete>* chunk_deletes) {
611 DCHECK(MessageLoop::current() == io_loop_);
612 DCHECK(enabled_);
613 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
614 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
615}
616
617void SafeBrowsingService::DeleteChunks(
618 std::vector<SBChunkDelete>* chunk_deletes) {
619 DCHECK(MessageLoop::current() == db_thread_->message_loop());
620
621 GetDatabase()->DeleteChunks(chunk_deletes);
622}
623
624// Database worker function.
625void SafeBrowsingService::GetAllChunksFromDatabase() {
626 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]a5a37522008-11-11 21:49:56627 bool database_error = true;
initial.commit09911bf2008-07-26 23:55:29628 std::vector<SBListChunkRanges> lists;
[email protected]53ad8572008-11-13 21:50:34629 if (GetDatabase()) {
630 if (GetDatabase()->UpdateStarted()) {
631 GetDatabase()->GetListsInfo(&lists);
632 database_error = false;
633 } else {
634 GetDatabase()->UpdateFinished(false);
635 }
initial.commit09911bf2008-07-26 23:55:29636 }
637
638 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
639 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
640 database_error));
641}
642
643// Called on the io thread with the results of all chunks.
644void SafeBrowsingService::OnGetAllChunksFromDatabase(
645 const std::vector<SBListChunkRanges>& lists, bool database_error) {
646 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04647 if (enabled_)
648 protocol_manager_->OnGetChunksComplete(lists, database_error);
initial.commit09911bf2008-07-26 23:55:29649}
650
651SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
652 const std::string& list_name) {
653 if (safe_browsing_util::IsPhishingList(list_name)) {
654 return URL_PHISHING;
655 }
656
657 if (safe_browsing_util::IsMalwareList(list_name)) {
658 return URL_MALWARE;
659 }
660
661 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
662 return URL_SAFE;
663}
664
initial.commit09911bf2008-07-26 23:55:29665void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
[email protected]fc76de12008-11-14 01:51:18666 if (new_safe_browsing_)
667 UMA_HISTOGRAM_LONG_TIMES(L"SB2.Delay", time);
668 else
669 UMA_HISTOGRAM_LONG_TIMES(L"SB.Delay", time);
initial.commit09911bf2008-07-26 23:55:29670}
671
672void SafeBrowsingService::CacheHashResults(
[email protected]200abc32008-09-05 01:44:33673 const std::vector<SBPrefix>& prefixes,
initial.commit09911bf2008-07-26 23:55:29674 const std::vector<SBFullHashResult>& full_hashes) {
675 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]200abc32008-09-05 01:44:33676 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29677}
678
[email protected]0a3076572008-12-13 20:48:36679void SafeBrowsingService::OnSuspend(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29680}
681
682// Tell the SafeBrowsing database not to do expensive disk operations for a few
683// minutes after waking up. It's quite likely that the act of resuming from a
684// low power state will involve much disk activity, which we don't want to
685// exacerbate.
[email protected]0a3076572008-12-13 20:48:36686void SafeBrowsingService::OnResume(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29687 if (enabled_) {
[email protected]0a3076572008-12-13 20:48:36688 ChromeThread::GetMessageLoop(ChromeThread::DB)->PostTask(FROM_HERE,
689 NewRunnableMethod(this, &SafeBrowsingService::HandleResume));
initial.commit09911bf2008-07-26 23:55:29690 }
691}
692
693void SafeBrowsingService::HandleResume() {
694 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]53ad8572008-11-13 21:50:34695 // We don't call GetDatabase() here, since we want to avoid unnecessary calls
696 // to Open, Reset, etc, or reload the bloom filter while we're coming out of
697 // a suspended state.
698 if (database_)
699 database_->HandleResume();
license.botbf09a502008-08-24 00:55:55700}
[email protected]613a03b2008-10-24 23:02:00701
702void SafeBrowsingService::RunQueuedClients() {
703 DCHECK(MessageLoop::current() == io_loop_);
704 HISTOGRAM_COUNTS(L"SB.QueueDepth", queued_checks_.size());
705 while (!queued_checks_.empty()) {
706 QueuedCheck check = queued_checks_.front();
707 HISTOGRAM_TIMES(L"SB.QueueDelay", Time::Now() - check.start);
708 CheckUrl(check.url, check.client);
709 queued_checks_.pop_front();
710 }
711}
712