blob: 57d4697b7007e58f44911666e53c1c9beb621541 [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"
[email protected]f3ec7742009-01-15 00:59:1620#include "chrome/browser/tab_contents/tab_util.h"
initial.commit09911bf2008-07-26 23:55:2921#include "chrome/common/chrome_constants.h"
22#include "chrome/common/chrome_paths.h"
[email protected]613a03b2008-10-24 23:02:0023#include "chrome/common/chrome_switches.h"
initial.commit09911bf2008-07-26 23:55:2924#include "chrome/common/pref_names.h"
25#include "chrome/common/pref_service.h"
26#include "net/base/registry_controlled_domain.h"
27
[email protected]e1acf6f2008-10-27 20:43:3328using base::Time;
29using base::TimeDelta;
30
initial.commit09911bf2008-07-26 23:55:2931SafeBrowsingService::SafeBrowsingService()
32 : io_loop_(NULL),
33 database_(NULL),
34 protocol_manager_(NULL),
35 enabled_(false),
[email protected]613a03b2008-10-24 23:02:0036 resetting_(false),
[email protected]57119c3f2008-12-04 00:33:0437 database_loaded_(false),
38 update_in_progress_(false) {
[email protected]bb975362009-01-21 01:00:2239 new_safe_browsing_ = !CommandLine::ForCurrentProcess()->HasSwitch(
40 switches::kUseOldSafeBrowsing);
[email protected]0a3076572008-12-13 20:48:3641 base::SystemMonitor* monitor = base::SystemMonitor::Get();
42 DCHECK(monitor);
43 if (monitor)
44 monitor->AddObserver(this);
initial.commit09911bf2008-07-26 23:55:2945}
46
47SafeBrowsingService::~SafeBrowsingService() {
[email protected]0a3076572008-12-13 20:48:3648 base::SystemMonitor* monitor = base::SystemMonitor::Get();
49 if (monitor)
50 monitor->RemoveObserver(this);
initial.commit09911bf2008-07-26 23:55:2951}
52
53// Only called on the UI thread.
54void SafeBrowsingService::Initialize(MessageLoop* io_loop) {
55 io_loop_ = io_loop;
56
57 // Get the profile's preference for SafeBrowsing.
[email protected]c870c762009-01-28 05:47:1558 FilePath user_data_dir;
initial.commit09911bf2008-07-26 23:55:2959 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
60 ProfileManager* profile_manager = g_browser_process->profile_manager();
[email protected]c870c762009-01-28 05:47:1561 Profile* profile = profile_manager->GetDefaultProfile(
62 user_data_dir.ToWStringHack());
initial.commit09911bf2008-07-26 23:55:2963 PrefService* pref_service = profile->GetPrefs();
64 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
65 Start();
66}
67
68// Start up SafeBrowsing objects. This can be called at browser start, or when
69// the user checks the "Enable SafeBrowsing" option in the Advanced options UI.
70void SafeBrowsingService::Start() {
71 DCHECK(!db_thread_.get());
[email protected]ab820df2008-08-26 05:55:1072 db_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
initial.commit09911bf2008-07-26 23:55:2973 if (!db_thread_->Start())
74 return;
75
initial.commit09911bf2008-07-26 23:55:2976 // 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));
[email protected]2c2fb222008-12-17 02:35:4689
90 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
91 this, &SafeBrowsingService::OnDBInitialize));
initial.commit09911bf2008-07-26 23:55:2992}
93
94void SafeBrowsingService::ShutDown() {
95 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
96 this, &SafeBrowsingService::OnIOShutdown));
97}
98
99void SafeBrowsingService::OnIOInitialize(MessageLoop* notify_loop,
100 const std::string& client_key,
101 const std::string& wrapped_key) {
102 DCHECK(MessageLoop::current() == io_loop_);
103 enabled_ = true;
104 protocol_manager_ = new SafeBrowsingProtocolManager(this,
105 notify_loop,
106 client_key,
107 wrapped_key);
[email protected]613a03b2008-10-24 23:02:00108 // We want to initialize the protocol manager only after the database has
109 // loaded, which we'll receive asynchronously (DatabaseLoadComplete). If
110 // database_loaded_ isn't true, we'll wait for that notification to do the
111 // init.
112 if (database_loaded_)
113 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29114}
115
116void SafeBrowsingService::OnDBInitialize() {
117 DCHECK(MessageLoop::current() == db_thread_->message_loop());
118 GetDatabase();
119}
120
121void SafeBrowsingService::OnIOShutdown() {
122 DCHECK(MessageLoop::current() == io_loop_);
123 if (!enabled_)
124 return;
125
126 enabled_ = false;
[email protected]fbb2b7a2008-11-18 22:54:04127 resetting_ = false;
initial.commit09911bf2008-07-26 23:55:29128
129 // This cancels all in-flight GetHash requests.
130 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04131 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29132
133 if (db_thread_.get())
134 db_thread_->message_loop()->DeleteSoon(FROM_HERE, database_);
135
136 // Flush the database thread. Any in-progress database check results will be
137 // ignored and cleaned up below.
138 db_thread_.reset(NULL);
139
140 database_ = NULL;
[email protected]fbb2b7a2008-11-18 22:54:04141 database_loaded_ = false;
initial.commit09911bf2008-07-26 23:55:29142
[email protected]613a03b2008-10-24 23:02:00143 // Delete queued and pending checks once the database thread is done, calling
144 // back any clients with 'URL_SAFE'.
145 while (!queued_checks_.empty()) {
146 QueuedCheck check = queued_checks_.front();
147 if (check.client)
148 check.client->OnUrlCheckResult(check.url, URL_SAFE);
149 queued_checks_.pop_front();
150 }
151
initial.commit09911bf2008-07-26 23:55:29152 for (CurrentChecks::iterator it = checks_.begin();
153 it != checks_.end(); ++it) {
154 if ((*it)->client)
155 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
156 delete *it;
157 }
158 checks_.clear();
159
160 gethash_requests_.clear();
161}
162
163// Runs on the UI thread.
164void SafeBrowsingService::OnEnable(bool enabled) {
165 if (enabled)
166 Start();
167 else
168 ShutDown();
169}
170
171bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
172 return url.SchemeIs("http") || url.SchemeIs("https");
173}
174
175bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
176 DCHECK(MessageLoop::current() == io_loop_);
initial.commit09911bf2008-07-26 23:55:29177 if (!enabled_ || !database_)
178 return true;
179
[email protected]613a03b2008-10-24 23:02:00180 if (new_safe_browsing_)
181 return CheckUrlNew(url, client);
182
initial.commit09911bf2008-07-26 23:55:29183 if (!resetting_) {
184 Time start_time = Time::Now();
185 bool need_check = database_->NeedToCheckUrl(url);
186 UMA_HISTOGRAM_TIMES(L"SB.BloomFilter", Time::Now() - start_time);
187 if (!need_check)
188 return true; // The url is definitely safe.
189 }
190
191 // The url may or may not be safe, need to go to the database to be sure.
192 SafeBrowsingCheck* check = new SafeBrowsingCheck();
193 check->url = url;
194 check->client = client;
195 check->result = URL_SAFE;
196 check->need_get_hash = false;
197 check->start = Time::Now();
198 checks_.insert(check);
199
[email protected]613a03b2008-10-24 23:02:00200 // Old school SafeBrowsing does an asynchronous database check.
initial.commit09911bf2008-07-26 23:55:29201 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
202 this, &SafeBrowsingService::CheckDatabase,
203 check, protocol_manager_->last_update()));
[email protected]613a03b2008-10-24 23:02:00204
205 return false;
206}
207
208bool SafeBrowsingService::CheckUrlNew(const GURL& url, Client* client) {
209 if (resetting_ || !database_loaded_) {
210 QueuedCheck check;
211 check.client = client;
212 check.url = url;
213 queued_checks_.push_back(check);
214 return false;
215 }
216
217 std::string list;
218 std::vector<SBPrefix> prefix_hits;
219 std::vector<SBFullHashResult> full_hits;
[email protected]22573822008-11-14 00:40:47220 base::Time check_start = base::Time::Now();
[email protected]c3ff89492008-11-11 02:17:51221 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
222 &full_hits,
[email protected]613a03b2008-10-24 23:02:00223 protocol_manager_->last_update());
[email protected]22573822008-11-14 00:40:47224
225 UMA_HISTOGRAM_TIMES(L"SB2.FilterCheck", base::Time::Now() - check_start);
226
[email protected]613a03b2008-10-24 23:02:00227 if (!prefix_match)
228 return true; // URL is okay.
229
230 // Needs to be asynchronous, since we could be in the constructor of a
231 // ResourceDispatcherHost event handler which can't pause there.
232 SafeBrowsingCheck* check = new SafeBrowsingCheck();
233 check->url = url;
234 check->client = client;
235 check->result = URL_SAFE;
236 check->start = Time::Now();
237 check->need_get_hash = full_hits.empty();
238 check->prefix_hits.swap(prefix_hits);
239 check->full_hits.swap(full_hits);
240 checks_.insert(check);
241
242 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
243 this, &SafeBrowsingService::OnCheckDone, check));
244
initial.commit09911bf2008-07-26 23:55:29245 return false;
246}
247
248void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
249 ResourceType::Type resource_type,
250 UrlCheckResult result,
251 Client* client,
252 MessageLoop* ui_loop,
253 int render_process_host_id,
254 int render_view_id) {
255 // Check if the user has already ignored our warning for this render_view
256 // and domain.
257 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
258 const WhiteListedEntry& entry = white_listed_entries_[i];
259 if (entry.render_process_host_id == render_process_host_id &&
260 entry.render_view_id == render_view_id &&
261 entry.result == result &&
262 entry.domain ==
[email protected]8ac1a752008-07-31 19:40:37263 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
initial.commit09911bf2008-07-26 23:55:29264 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
265 this, &SafeBrowsingService::NotifyClientBlockingComplete,
266 client, true));
267 return;
268 }
269 }
270
[email protected]1b277e72009-01-23 21:05:34271 UnsafeResource resource;
272 resource.url = url;
273 resource.resource_type = resource_type;
274 resource.threat_type= result;
275 resource.client = client;
276 resource.render_process_host_id = render_process_host_id;
277 resource.render_view_id = render_view_id;
[email protected]cbab76d2008-10-13 22:42:47278 // The blocking page must be created from the UI thread.
279 ui_loop->PostTask(FROM_HERE, NewRunnableMethod(this,
280 &SafeBrowsingService::DoDisplayBlockingPage,
[email protected]1b277e72009-01-23 21:05:34281 resource));
[email protected]cbab76d2008-10-13 22:42:47282}
283
284// Invoked on the UI thread.
285void SafeBrowsingService::DoDisplayBlockingPage(
[email protected]1b277e72009-01-23 21:05:34286 const UnsafeResource& resource) {
[email protected]a3a1d142008-12-19 00:42:30287 // The tab might have been closed.
[email protected]1b277e72009-01-23 21:05:34288 if (!tab_util::GetWebContentsByID(resource.render_process_host_id,
289 resource.render_view_id)) {
[email protected]a3a1d142008-12-19 00:42:30290 // The tab is gone and we did not have a chance at showing the interstitial.
291 // Just act as "Don't Proceed" was chosen.
292 base::Thread* io_thread = g_browser_process->io_thread();
293 if (!io_thread)
294 return;
[email protected]1b277e72009-01-23 21:05:34295 std::vector<UnsafeResource> resources;
296 resources.push_back(resource);
[email protected]a3a1d142008-12-19 00:42:30297 io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]1b277e72009-01-23 21:05:34298 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
[email protected]a3a1d142008-12-19 00:42:30299 return;
300 }
[email protected]1b277e72009-01-23 21:05:34301 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
initial.commit09911bf2008-07-26 23:55:29302}
303
304void SafeBrowsingService::CancelCheck(Client* client) {
305 DCHECK(MessageLoop::current() == io_loop_);
306
307 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
308 if ((*i)->client == client)
309 (*i)->client = NULL;
310 }
[email protected]613a03b2008-10-24 23:02:00311
312 // Scan the queued clients store. Clients may be here if they requested a URL
313 // check before the database has finished loading or resetting.
314 if (!database_loaded_ || resetting_) {
315 std::deque<QueuedCheck>::iterator it = queued_checks_.begin();
316 for (; it != queued_checks_.end(); ++it) {
317 if (it->client == client)
318 it->client = NULL;
319 }
320 }
initial.commit09911bf2008-07-26 23:55:29321}
322
323void SafeBrowsingService::CheckDatabase(SafeBrowsingCheck* info,
324 Time last_update) {
325 DCHECK(MessageLoop::current() == db_thread_->message_loop());
326 // If client == NULL it means it was cancelled, no need for db lookup.
327 if (info->client && GetDatabase()) {
328 Time now = Time::Now();
329 std::string list;
330 if (GetDatabase()->ContainsUrl(info->url,
331 &list,
332 &info->prefix_hits,
333 &info->full_hits,
334 last_update)) {
335 if (info->prefix_hits.empty()) {
336 info->result = GetResultFromListname(list);
337 } else {
338 if (info->full_hits.empty())
339 info->need_get_hash = true;
340 }
341 }
342 info->db_time = Time::Now() - now;
343 }
344
345 if (io_loop_)
346 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
347 this, &SafeBrowsingService::OnCheckDone, info));
348}
349
[email protected]613a03b2008-10-24 23:02:00350void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
initial.commit09911bf2008-07-26 23:55:29351 DCHECK(MessageLoop::current() == io_loop_);
352
353 // If we've been shutdown during the database lookup, this check will already
354 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00355 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29356 return;
357
[email protected]613a03b2008-10-24 23:02:00358 UMA_HISTOGRAM_TIMES(L"SB.Database", Time::Now() - check->start);
359 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29360 // We have a partial match so we need to query Google for the full hash.
361 // Clean up will happen in HandleGetHashResults.
362
363 // See if we have a GetHash request already in progress for this particular
364 // prefix. If so, we just append ourselves to the list of interested parties
365 // when the results arrive. We only do this for checks involving one prefix,
366 // since that is the common case (multiple prefixes will issue the request
367 // as normal).
[email protected]613a03b2008-10-24 23:02:00368 if (check->prefix_hits.size() == 1) {
369 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29370 GetHashRequests::iterator it = gethash_requests_.find(prefix);
371 if (it != gethash_requests_.end()) {
372 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00373 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29374 return;
375 }
376
377 // No request in progress, so we're the first for this prefix.
378 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00379 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29380 gethash_requests_[prefix] = requestors;
381 }
382
383 // Reset the start time so that we can measure the network time without the
384 // database time.
[email protected]613a03b2008-10-24 23:02:00385 check->start = Time::Now();
386 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29387 } else {
388 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00389 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29390 }
391}
392
393SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
394 DCHECK(MessageLoop::current() == db_thread_->message_loop());
395 if (database_)
396 return database_;
397
[email protected]c870c762009-01-28 05:47:15398 FilePath path;
initial.commit09911bf2008-07-26 23:55:29399 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
400 DCHECK(result);
[email protected]c870c762009-01-28 05:47:15401 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29402
403 Time before = Time::Now();
[email protected]54d80bb02008-09-20 02:03:08404 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]613a03b2008-10-24 23:02:00405 Callback0::Type* chunk_callback =
initial.commit09911bf2008-07-26 23:55:29406 NewCallback(this, &SafeBrowsingService::ChunkInserted);
[email protected]613a03b2008-10-24 23:02:00407 bool init_success = database->Init(path, chunk_callback);
408
409 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
410 this, &SafeBrowsingService::DatabaseLoadComplete, !init_success));
411
412 if (!init_success) {
initial.commit09911bf2008-07-26 23:55:29413 NOTREACHED();
414 return NULL;
415 }
416
417 database_ = database;
418
419 TimeDelta open_time = Time::Now() - before;
420 SB_DLOG(INFO) << "SafeBrowsing database open took " <<
421 open_time.InMilliseconds() << " ms.";
422
423 return database_;
424}
425
426// Public API called only on the IO thread.
427// The SafeBrowsingProtocolManager has received the full hash results for
428// prefix hits detected in the database.
429void SafeBrowsingService::HandleGetHashResults(
430 SafeBrowsingCheck* check,
[email protected]200abc32008-09-05 01:44:33431 const std::vector<SBFullHashResult>& full_hashes,
432 bool can_cache) {
initial.commit09911bf2008-07-26 23:55:29433 if (checks_.find(check) == checks_.end())
434 return;
435
436 DCHECK(enabled_);
437
[email protected]22573822008-11-14 00:40:47438 if (new_safe_browsing_)
439 UMA_HISTOGRAM_LONG_TIMES(L"SB2.Network", Time::Now() - check->start);
440 else
441 UMA_HISTOGRAM_LONG_TIMES(L"SB.Network", Time::Now() - check->start);
442
[email protected]200abc32008-09-05 01:44:33443 std::vector<SBPrefix> prefixes = check->prefix_hits;
initial.commit09911bf2008-07-26 23:55:29444 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
445
[email protected]613a03b2008-10-24 23:02:00446 if (can_cache) {
447 if (!new_safe_browsing_) {
448 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
449 this, &SafeBrowsingService::CacheHashResults, prefixes, full_hashes));
450 } else if (database_) {
[email protected]c3ff89492008-11-11 02:17:51451 // Cache the GetHash results in memory:
[email protected]613a03b2008-10-24 23:02:00452 database_->CacheHashResults(prefixes, full_hashes);
453 }
454 }
initial.commit09911bf2008-07-26 23:55:29455}
456
457void SafeBrowsingService::OnHandleGetHashResults(
458 SafeBrowsingCheck* check,
459 const std::vector<SBFullHashResult>& full_hashes) {
460 SBPrefix prefix = check->prefix_hits[0];
461 GetHashRequests::iterator it = gethash_requests_.find(prefix);
462 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
463 HandleOneCheck(check, full_hashes);
464 return;
465 }
466
467 // Call back all interested parties.
468 GetHashRequestors& requestors = it->second;
469 for (GetHashRequestors::iterator r = requestors.begin();
470 r != requestors.end(); ++r) {
471 HandleOneCheck(*r, full_hashes);
472 }
473
474 gethash_requests_.erase(it);
475}
476
477void SafeBrowsingService::HandleOneCheck(
478 SafeBrowsingCheck* check,
479 const std::vector<SBFullHashResult>& full_hashes) {
480 if (check->client) {
481 UrlCheckResult result = URL_SAFE;
482 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
483 if (index != -1)
484 result = GetResultFromListname(full_hashes[index].list_name);
485
486 // Let the client continue handling the original request.
487 check->client->OnUrlCheckResult(check->url, result);
488 }
489
490 checks_.erase(check);
491 delete check;
492}
493
[email protected]57119c3f2008-12-04 00:33:04494void SafeBrowsingService::UpdateStarted() {
initial.commit09911bf2008-07-26 23:55:29495 DCHECK(MessageLoop::current() == io_loop_);
496 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04497 DCHECK(!update_in_progress_);
498 update_in_progress_ = true;
initial.commit09911bf2008-07-26 23:55:29499 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
500 this, &SafeBrowsingService::GetAllChunksFromDatabase));
501}
502
[email protected]613a03b2008-10-24 23:02:00503void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41504 DCHECK(MessageLoop::current() == io_loop_);
505 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04506 if (update_in_progress_) {
507 update_in_progress_ = false;
508 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
509 this, &SafeBrowsingService::DatabaseUpdateFinished, update_succeeded));
510 }
[email protected]aad08752008-10-02 22:13:41511}
512
[email protected]613a03b2008-10-24 23:02:00513void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41514 DCHECK(MessageLoop::current() == db_thread_->message_loop());
515 if (GetDatabase())
[email protected]613a03b2008-10-24 23:02:00516 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]aad08752008-10-02 22:13:41517}
518
[email protected]1b277e72009-01-23 21:05:34519void SafeBrowsingService::OnBlockingPageDone(
520 const std::vector<UnsafeResource>& resources,
521 bool proceed) {
522 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
523 iter != resources.end(); ++iter) {
524 const UnsafeResource& resource = *iter;
525 NotifyClientBlockingComplete(resource.client, proceed);
initial.commit09911bf2008-07-26 23:55:29526
[email protected]1b277e72009-01-23 21:05:34527 if (proceed) {
528 // Whitelist this domain and warning type for the given tab.
529 WhiteListedEntry entry;
530 entry.render_process_host_id = resource.render_process_host_id;
531 entry.render_view_id = resource.render_view_id;
532 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
533 resource.url);
534 entry.result = resource.threat_type;
535 white_listed_entries_.push_back(entry);
536 }
initial.commit09911bf2008-07-26 23:55:29537 }
initial.commit09911bf2008-07-26 23:55:29538}
539
540void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
541 bool proceed) {
542 client->OnBlockingPageComplete(proceed);
543}
544
545// This method runs on the UI loop to access the prefs.
546void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
547 const std::string& wrapped_key) {
548 PrefService* prefs = g_browser_process->local_state();
549 if (prefs) {
550 prefs->SetString(prefs::kSafeBrowsingClientKey, ASCIIToWide(client_key));
551 prefs->SetString(prefs::kSafeBrowsingWrappedKey, ASCIIToWide(wrapped_key));
552 }
553}
554
555void SafeBrowsingService::ChunkInserted() {
[email protected]fbb2b7a2008-11-18 22:54:04556 DCHECK(MessageLoop::current() == db_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29557 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
558 this, &SafeBrowsingService::OnChunkInserted));
559}
560
561void SafeBrowsingService::OnChunkInserted() {
562 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04563 if (enabled_)
564 protocol_manager_->OnChunkInserted();
initial.commit09911bf2008-07-26 23:55:29565}
566
[email protected]613a03b2008-10-24 23:02:00567void SafeBrowsingService::DatabaseLoadComplete(bool database_error) {
568 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04569 if (!enabled_)
570 return;
[email protected]613a03b2008-10-24 23:02:00571
572 database_loaded_ = true;
573
574 // TODO(paulg): More robust database initialization error handling.
575 if (protocol_manager_ && !database_error)
576 protocol_manager_->Initialize();
577
578 // If we have any queued requests, we can now check them.
579 if (!resetting_)
580 RunQueuedClients();
581}
582
initial.commit09911bf2008-07-26 23:55:29583// static
[email protected]919d77f02009-01-06 19:48:35584void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
initial.commit09911bf2008-07-26 23:55:29585 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, L"");
586 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, L"");
587}
588
589void SafeBrowsingService::ResetDatabase() {
590 DCHECK(MessageLoop::current() == io_loop_);
591 resetting_ = true;
592 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
593 this, &SafeBrowsingService::OnResetDatabase));
594}
595
596void SafeBrowsingService::OnResetDatabase() {
597 DCHECK(MessageLoop::current() == db_thread_->message_loop());
598 GetDatabase()->ResetDatabase();
599 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
600 this, &SafeBrowsingService::OnResetComplete));
601}
602
603void SafeBrowsingService::OnResetComplete() {
604 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04605 if (enabled_) {
606 resetting_ = false;
607 database_loaded_ = true;
608 RunQueuedClients();
609 }
initial.commit09911bf2008-07-26 23:55:29610}
611
612void SafeBrowsingService::HandleChunk(const std::string& list,
613 std::deque<SBChunk>* chunks) {
614 DCHECK(MessageLoop::current() == io_loop_);
615 DCHECK(enabled_);
616 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
617 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
618}
619
620void SafeBrowsingService::HandleChunkForDatabase(
621 const std::string& list_name,
622 std::deque<SBChunk>* chunks) {
623 DCHECK(MessageLoop::current() == db_thread_->message_loop());
624
625 GetDatabase()->InsertChunks(list_name, chunks);
626}
627
628void SafeBrowsingService::HandleChunkDelete(
629 std::vector<SBChunkDelete>* chunk_deletes) {
630 DCHECK(MessageLoop::current() == io_loop_);
631 DCHECK(enabled_);
632 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
633 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
634}
635
636void SafeBrowsingService::DeleteChunks(
637 std::vector<SBChunkDelete>* chunk_deletes) {
638 DCHECK(MessageLoop::current() == db_thread_->message_loop());
639
640 GetDatabase()->DeleteChunks(chunk_deletes);
641}
642
643// Database worker function.
644void SafeBrowsingService::GetAllChunksFromDatabase() {
645 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]a5a37522008-11-11 21:49:56646 bool database_error = true;
initial.commit09911bf2008-07-26 23:55:29647 std::vector<SBListChunkRanges> lists;
[email protected]53ad8572008-11-13 21:50:34648 if (GetDatabase()) {
649 if (GetDatabase()->UpdateStarted()) {
650 GetDatabase()->GetListsInfo(&lists);
651 database_error = false;
652 } else {
653 GetDatabase()->UpdateFinished(false);
654 }
initial.commit09911bf2008-07-26 23:55:29655 }
656
657 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
658 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
659 database_error));
660}
661
662// Called on the io thread with the results of all chunks.
663void SafeBrowsingService::OnGetAllChunksFromDatabase(
664 const std::vector<SBListChunkRanges>& lists, bool database_error) {
665 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04666 if (enabled_)
667 protocol_manager_->OnGetChunksComplete(lists, database_error);
initial.commit09911bf2008-07-26 23:55:29668}
669
670SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
671 const std::string& list_name) {
672 if (safe_browsing_util::IsPhishingList(list_name)) {
673 return URL_PHISHING;
674 }
675
676 if (safe_browsing_util::IsMalwareList(list_name)) {
677 return URL_MALWARE;
678 }
679
680 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
681 return URL_SAFE;
682}
683
initial.commit09911bf2008-07-26 23:55:29684void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
[email protected]fc76de12008-11-14 01:51:18685 if (new_safe_browsing_)
686 UMA_HISTOGRAM_LONG_TIMES(L"SB2.Delay", time);
687 else
688 UMA_HISTOGRAM_LONG_TIMES(L"SB.Delay", time);
initial.commit09911bf2008-07-26 23:55:29689}
690
691void SafeBrowsingService::CacheHashResults(
[email protected]200abc32008-09-05 01:44:33692 const std::vector<SBPrefix>& prefixes,
initial.commit09911bf2008-07-26 23:55:29693 const std::vector<SBFullHashResult>& full_hashes) {
694 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]200abc32008-09-05 01:44:33695 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29696}
697
[email protected]0a3076572008-12-13 20:48:36698void SafeBrowsingService::OnSuspend(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29699}
700
701// Tell the SafeBrowsing database not to do expensive disk operations for a few
702// minutes after waking up. It's quite likely that the act of resuming from a
703// low power state will involve much disk activity, which we don't want to
704// exacerbate.
[email protected]0a3076572008-12-13 20:48:36705void SafeBrowsingService::OnResume(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29706 if (enabled_) {
[email protected]0a3076572008-12-13 20:48:36707 ChromeThread::GetMessageLoop(ChromeThread::DB)->PostTask(FROM_HERE,
708 NewRunnableMethod(this, &SafeBrowsingService::HandleResume));
initial.commit09911bf2008-07-26 23:55:29709 }
710}
711
712void SafeBrowsingService::HandleResume() {
713 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]53ad8572008-11-13 21:50:34714 // We don't call GetDatabase() here, since we want to avoid unnecessary calls
715 // to Open, Reset, etc, or reload the bloom filter while we're coming out of
716 // a suspended state.
717 if (database_)
718 database_->HandleResume();
license.botbf09a502008-08-24 00:55:55719}
[email protected]613a03b2008-10-24 23:02:00720
721void SafeBrowsingService::RunQueuedClients() {
722 DCHECK(MessageLoop::current() == io_loop_);
723 HISTOGRAM_COUNTS(L"SB.QueueDepth", queued_checks_.size());
724 while (!queued_checks_.empty()) {
725 QueuedCheck check = queued_checks_.front();
726 HISTOGRAM_TIMES(L"SB.QueueDelay", Time::Now() - check.start);
727 CheckUrl(check.url, check.client);
728 queued_checks_.pop_front();
729 }
730}
731