blob: 5a7a617eec0b0cf603d3fde043325b3726664dcc [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]a3a1d142008-12-19 00:42:3020#include "chrome/browser/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]7ab495722008-11-26 00:18:2739 new_safe_browsing_ = !CommandLine().HasSwitch(switches::kUseOldSafeBrowsing);
[email protected]0a3076572008-12-13 20:48:3640 base::SystemMonitor* monitor = base::SystemMonitor::Get();
41 DCHECK(monitor);
42 if (monitor)
43 monitor->AddObserver(this);
initial.commit09911bf2008-07-26 23:55:2944}
45
46SafeBrowsingService::~SafeBrowsingService() {
[email protected]0a3076572008-12-13 20:48:3647 base::SystemMonitor* monitor = base::SystemMonitor::Get();
48 if (monitor)
49 monitor->RemoveObserver(this);
initial.commit09911bf2008-07-26 23:55:2950}
51
52// Only called on the UI thread.
53void SafeBrowsingService::Initialize(MessageLoop* io_loop) {
54 io_loop_ = io_loop;
55
56 // Get the profile's preference for SafeBrowsing.
57 std::wstring user_data_dir;
58 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
59 ProfileManager* profile_manager = g_browser_process->profile_manager();
60 Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
61 PrefService* pref_service = profile->GetPrefs();
62 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
63 Start();
64}
65
66// Start up SafeBrowsing objects. This can be called at browser start, or when
67// the user checks the "Enable SafeBrowsing" option in the Advanced options UI.
68void SafeBrowsingService::Start() {
69 DCHECK(!db_thread_.get());
[email protected]ab820df2008-08-26 05:55:1070 db_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
initial.commit09911bf2008-07-26 23:55:2971 if (!db_thread_->Start())
72 return;
73
initial.commit09911bf2008-07-26 23:55:2974 // Retrieve client MAC keys.
75 PrefService* local_state = g_browser_process->local_state();
76 std::string client_key, wrapped_key;
77 if (local_state) {
78 client_key =
79 WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
80 wrapped_key =
81 WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
82 }
83
84 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
85 this, &SafeBrowsingService::OnIOInitialize, MessageLoop::current(),
86 client_key, wrapped_key));
[email protected]2c2fb222008-12-17 02:35:4687
88 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
89 this, &SafeBrowsingService::OnDBInitialize));
initial.commit09911bf2008-07-26 23:55:2990}
91
92void SafeBrowsingService::ShutDown() {
93 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
94 this, &SafeBrowsingService::OnIOShutdown));
95}
96
97void SafeBrowsingService::OnIOInitialize(MessageLoop* notify_loop,
98 const std::string& client_key,
99 const std::string& wrapped_key) {
100 DCHECK(MessageLoop::current() == io_loop_);
101 enabled_ = true;
102 protocol_manager_ = new SafeBrowsingProtocolManager(this,
103 notify_loop,
104 client_key,
105 wrapped_key);
[email protected]613a03b2008-10-24 23:02:00106 // We want to initialize the protocol manager only after the database has
107 // loaded, which we'll receive asynchronously (DatabaseLoadComplete). If
108 // database_loaded_ isn't true, we'll wait for that notification to do the
109 // init.
110 if (database_loaded_)
111 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29112}
113
114void SafeBrowsingService::OnDBInitialize() {
115 DCHECK(MessageLoop::current() == db_thread_->message_loop());
116 GetDatabase();
117}
118
119void SafeBrowsingService::OnIOShutdown() {
120 DCHECK(MessageLoop::current() == io_loop_);
121 if (!enabled_)
122 return;
123
124 enabled_ = false;
[email protected]fbb2b7a2008-11-18 22:54:04125 resetting_ = false;
initial.commit09911bf2008-07-26 23:55:29126
127 // This cancels all in-flight GetHash requests.
128 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04129 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29130
131 if (db_thread_.get())
132 db_thread_->message_loop()->DeleteSoon(FROM_HERE, database_);
133
134 // Flush the database thread. Any in-progress database check results will be
135 // ignored and cleaned up below.
136 db_thread_.reset(NULL);
137
138 database_ = NULL;
[email protected]fbb2b7a2008-11-18 22:54:04139 database_loaded_ = false;
initial.commit09911bf2008-07-26 23:55:29140
[email protected]613a03b2008-10-24 23:02:00141 // Delete queued and pending checks once the database thread is done, calling
142 // back any clients with 'URL_SAFE'.
143 while (!queued_checks_.empty()) {
144 QueuedCheck check = queued_checks_.front();
145 if (check.client)
146 check.client->OnUrlCheckResult(check.url, URL_SAFE);
147 queued_checks_.pop_front();
148 }
149
initial.commit09911bf2008-07-26 23:55:29150 for (CurrentChecks::iterator it = checks_.begin();
151 it != checks_.end(); ++it) {
152 if ((*it)->client)
153 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
154 delete *it;
155 }
156 checks_.clear();
157
158 gethash_requests_.clear();
159}
160
161// Runs on the UI thread.
162void SafeBrowsingService::OnEnable(bool enabled) {
163 if (enabled)
164 Start();
165 else
166 ShutDown();
167}
168
169bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
170 return url.SchemeIs("http") || url.SchemeIs("https");
171}
172
173bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
174 DCHECK(MessageLoop::current() == io_loop_);
initial.commit09911bf2008-07-26 23:55:29175 if (!enabled_ || !database_)
176 return true;
177
[email protected]613a03b2008-10-24 23:02:00178 if (new_safe_browsing_)
179 return CheckUrlNew(url, client);
180
initial.commit09911bf2008-07-26 23:55:29181 if (!resetting_) {
182 Time start_time = Time::Now();
183 bool need_check = database_->NeedToCheckUrl(url);
184 UMA_HISTOGRAM_TIMES(L"SB.BloomFilter", Time::Now() - start_time);
185 if (!need_check)
186 return true; // The url is definitely safe.
187 }
188
189 // The url may or may not be safe, need to go to the database to be sure.
190 SafeBrowsingCheck* check = new SafeBrowsingCheck();
191 check->url = url;
192 check->client = client;
193 check->result = URL_SAFE;
194 check->need_get_hash = false;
195 check->start = Time::Now();
196 checks_.insert(check);
197
[email protected]613a03b2008-10-24 23:02:00198 // Old school SafeBrowsing does an asynchronous database check.
initial.commit09911bf2008-07-26 23:55:29199 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
200 this, &SafeBrowsingService::CheckDatabase,
201 check, protocol_manager_->last_update()));
[email protected]613a03b2008-10-24 23:02:00202
203 return false;
204}
205
206bool SafeBrowsingService::CheckUrlNew(const GURL& url, Client* client) {
207 if (resetting_ || !database_loaded_) {
208 QueuedCheck check;
209 check.client = client;
210 check.url = url;
211 queued_checks_.push_back(check);
212 return false;
213 }
214
215 std::string list;
216 std::vector<SBPrefix> prefix_hits;
217 std::vector<SBFullHashResult> full_hits;
[email protected]22573822008-11-14 00:40:47218 base::Time check_start = base::Time::Now();
[email protected]c3ff89492008-11-11 02:17:51219 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
220 &full_hits,
[email protected]613a03b2008-10-24 23:02:00221 protocol_manager_->last_update());
[email protected]22573822008-11-14 00:40:47222
223 UMA_HISTOGRAM_TIMES(L"SB2.FilterCheck", base::Time::Now() - check_start);
224
[email protected]613a03b2008-10-24 23:02:00225 if (!prefix_match)
226 return true; // URL is okay.
227
228 // Needs to be asynchronous, since we could be in the constructor of a
229 // ResourceDispatcherHost event handler which can't pause there.
230 SafeBrowsingCheck* check = new SafeBrowsingCheck();
231 check->url = url;
232 check->client = client;
233 check->result = URL_SAFE;
234 check->start = Time::Now();
235 check->need_get_hash = full_hits.empty();
236 check->prefix_hits.swap(prefix_hits);
237 check->full_hits.swap(full_hits);
238 checks_.insert(check);
239
240 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
241 this, &SafeBrowsingService::OnCheckDone, check));
242
initial.commit09911bf2008-07-26 23:55:29243 return false;
244}
245
246void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
247 ResourceType::Type resource_type,
248 UrlCheckResult result,
249 Client* client,
250 MessageLoop* ui_loop,
251 int render_process_host_id,
252 int render_view_id) {
253 // Check if the user has already ignored our warning for this render_view
254 // and domain.
255 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
256 const WhiteListedEntry& entry = white_listed_entries_[i];
257 if (entry.render_process_host_id == render_process_host_id &&
258 entry.render_view_id == render_view_id &&
259 entry.result == result &&
260 entry.domain ==
[email protected]8ac1a752008-07-31 19:40:37261 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
initial.commit09911bf2008-07-26 23:55:29262 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
263 this, &SafeBrowsingService::NotifyClientBlockingComplete,
264 client, true));
265 return;
266 }
267 }
268
[email protected]cbab76d2008-10-13 22:42:47269 BlockingPageParam param;
270 param.url = url;
271 param.resource_type = resource_type;
272 param.result = result;
273 param.client = client;
274 param.render_process_host_id = render_process_host_id;
275 param.render_view_id = render_view_id;
276 // The blocking page must be created from the UI thread.
277 ui_loop->PostTask(FROM_HERE, NewRunnableMethod(this,
278 &SafeBrowsingService::DoDisplayBlockingPage,
279 param));
280}
281
282// Invoked on the UI thread.
283void SafeBrowsingService::DoDisplayBlockingPage(
284 const BlockingPageParam& param) {
[email protected]a3a1d142008-12-19 00:42:30285 // The tab might have been closed.
286 if (!tab_util::GetWebContentsByID(param.render_process_host_id,
287 param.render_view_id)) {
288 // The tab is gone and we did not have a chance at showing the interstitial.
289 // Just act as "Don't Proceed" was chosen.
290 base::Thread* io_thread = g_browser_process->io_thread();
291 if (!io_thread)
292 return;
293 BlockingPageParam response_param = param;
294 response_param.proceed = false;
295 io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
296 this, &SafeBrowsingService::OnBlockingPageDone, response_param));
297 return;
298 }
[email protected]cbab76d2008-10-13 22:42:47299 SafeBrowsingBlockingPage* blocking_page = new SafeBrowsingBlockingPage(this,
300 param);
301 blocking_page->Show();
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
398 std::wstring path;
399 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
400 DCHECK(result);
401
402 path.append(L"\\");
403 path.append(chrome::kSafeBrowsingFilename);
404
405 Time before = Time::Now();
[email protected]54d80bb02008-09-20 02:03:08406 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]613a03b2008-10-24 23:02:00407 Callback0::Type* chunk_callback =
initial.commit09911bf2008-07-26 23:55:29408 NewCallback(this, &SafeBrowsingService::ChunkInserted);
[email protected]613a03b2008-10-24 23:02:00409 bool init_success = database->Init(path, chunk_callback);
410
411 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
412 this, &SafeBrowsingService::DatabaseLoadComplete, !init_success));
413
414 if (!init_success) {
initial.commit09911bf2008-07-26 23:55:29415 NOTREACHED();
416 return NULL;
417 }
418
419 database_ = database;
420
421 TimeDelta open_time = Time::Now() - before;
422 SB_DLOG(INFO) << "SafeBrowsing database open took " <<
423 open_time.InMilliseconds() << " ms.";
424
425 return database_;
426}
427
428// Public API called only on the IO thread.
429// The SafeBrowsingProtocolManager has received the full hash results for
430// prefix hits detected in the database.
431void SafeBrowsingService::HandleGetHashResults(
432 SafeBrowsingCheck* check,
[email protected]200abc32008-09-05 01:44:33433 const std::vector<SBFullHashResult>& full_hashes,
434 bool can_cache) {
initial.commit09911bf2008-07-26 23:55:29435 if (checks_.find(check) == checks_.end())
436 return;
437
438 DCHECK(enabled_);
439
[email protected]22573822008-11-14 00:40:47440 if (new_safe_browsing_)
441 UMA_HISTOGRAM_LONG_TIMES(L"SB2.Network", Time::Now() - check->start);
442 else
443 UMA_HISTOGRAM_LONG_TIMES(L"SB.Network", Time::Now() - check->start);
444
[email protected]200abc32008-09-05 01:44:33445 std::vector<SBPrefix> prefixes = check->prefix_hits;
initial.commit09911bf2008-07-26 23:55:29446 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
447
[email protected]613a03b2008-10-24 23:02:00448 if (can_cache) {
449 if (!new_safe_browsing_) {
450 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
451 this, &SafeBrowsingService::CacheHashResults, prefixes, full_hashes));
452 } else if (database_) {
[email protected]c3ff89492008-11-11 02:17:51453 // Cache the GetHash results in memory:
[email protected]613a03b2008-10-24 23:02:00454 database_->CacheHashResults(prefixes, full_hashes);
455 }
456 }
initial.commit09911bf2008-07-26 23:55:29457}
458
459void SafeBrowsingService::OnHandleGetHashResults(
460 SafeBrowsingCheck* check,
461 const std::vector<SBFullHashResult>& full_hashes) {
462 SBPrefix prefix = check->prefix_hits[0];
463 GetHashRequests::iterator it = gethash_requests_.find(prefix);
464 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
465 HandleOneCheck(check, full_hashes);
466 return;
467 }
468
469 // Call back all interested parties.
470 GetHashRequestors& requestors = it->second;
471 for (GetHashRequestors::iterator r = requestors.begin();
472 r != requestors.end(); ++r) {
473 HandleOneCheck(*r, full_hashes);
474 }
475
476 gethash_requests_.erase(it);
477}
478
479void SafeBrowsingService::HandleOneCheck(
480 SafeBrowsingCheck* check,
481 const std::vector<SBFullHashResult>& full_hashes) {
482 if (check->client) {
483 UrlCheckResult result = URL_SAFE;
484 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
485 if (index != -1)
486 result = GetResultFromListname(full_hashes[index].list_name);
487
488 // Let the client continue handling the original request.
489 check->client->OnUrlCheckResult(check->url, result);
490 }
491
492 checks_.erase(check);
493 delete check;
494}
495
[email protected]57119c3f2008-12-04 00:33:04496void SafeBrowsingService::UpdateStarted() {
initial.commit09911bf2008-07-26 23:55:29497 DCHECK(MessageLoop::current() == io_loop_);
498 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04499 DCHECK(!update_in_progress_);
500 update_in_progress_ = true;
initial.commit09911bf2008-07-26 23:55:29501 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
502 this, &SafeBrowsingService::GetAllChunksFromDatabase));
503}
504
[email protected]613a03b2008-10-24 23:02:00505void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41506 DCHECK(MessageLoop::current() == io_loop_);
507 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04508 if (update_in_progress_) {
509 update_in_progress_ = false;
510 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
511 this, &SafeBrowsingService::DatabaseUpdateFinished, update_succeeded));
512 }
[email protected]aad08752008-10-02 22:13:41513}
514
[email protected]613a03b2008-10-24 23:02:00515void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41516 DCHECK(MessageLoop::current() == db_thread_->message_loop());
517 if (GetDatabase())
[email protected]613a03b2008-10-24 23:02:00518 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]aad08752008-10-02 22:13:41519}
520
[email protected]cbab76d2008-10-13 22:42:47521void SafeBrowsingService::OnBlockingPageDone(const BlockingPageParam& param) {
522 NotifyClientBlockingComplete(param.client, param.proceed);
initial.commit09911bf2008-07-26 23:55:29523
[email protected]cbab76d2008-10-13 22:42:47524 if (param.proceed) {
initial.commit09911bf2008-07-26 23:55:29525 // Whitelist this domain and warning type for the given tab.
526 WhiteListedEntry entry;
[email protected]cbab76d2008-10-13 22:42:47527 entry.render_process_host_id = param.render_process_host_id;
528 entry.render_view_id = param.render_view_id;
529 entry.domain =
530 net::RegistryControlledDomainService::GetDomainAndRegistry(param.url);
531 entry.result = param.result;
initial.commit09911bf2008-07-26 23:55:29532 white_listed_entries_.push_back(entry);
533 }
initial.commit09911bf2008-07-26 23:55:29534}
535
536void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
537 bool proceed) {
538 client->OnBlockingPageComplete(proceed);
539}
540
541// This method runs on the UI loop to access the prefs.
542void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
543 const std::string& wrapped_key) {
544 PrefService* prefs = g_browser_process->local_state();
545 if (prefs) {
546 prefs->SetString(prefs::kSafeBrowsingClientKey, ASCIIToWide(client_key));
547 prefs->SetString(prefs::kSafeBrowsingWrappedKey, ASCIIToWide(wrapped_key));
548 }
549}
550
551void SafeBrowsingService::ChunkInserted() {
[email protected]fbb2b7a2008-11-18 22:54:04552 DCHECK(MessageLoop::current() == db_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29553 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
554 this, &SafeBrowsingService::OnChunkInserted));
555}
556
557void SafeBrowsingService::OnChunkInserted() {
558 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04559 if (enabled_)
560 protocol_manager_->OnChunkInserted();
initial.commit09911bf2008-07-26 23:55:29561}
562
[email protected]613a03b2008-10-24 23:02:00563void SafeBrowsingService::DatabaseLoadComplete(bool database_error) {
564 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04565 if (!enabled_)
566 return;
[email protected]613a03b2008-10-24 23:02:00567
568 database_loaded_ = true;
569
570 // TODO(paulg): More robust database initialization error handling.
571 if (protocol_manager_ && !database_error)
572 protocol_manager_->Initialize();
573
574 // If we have any queued requests, we can now check them.
575 if (!resetting_)
576 RunQueuedClients();
577}
578
initial.commit09911bf2008-07-26 23:55:29579// static
580void SafeBrowsingService::RegisterUserPrefs(PrefService* prefs) {
581 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, L"");
582 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, L"");
583}
584
585void SafeBrowsingService::ResetDatabase() {
586 DCHECK(MessageLoop::current() == io_loop_);
587 resetting_ = true;
588 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
589 this, &SafeBrowsingService::OnResetDatabase));
590}
591
592void SafeBrowsingService::OnResetDatabase() {
593 DCHECK(MessageLoop::current() == db_thread_->message_loop());
594 GetDatabase()->ResetDatabase();
595 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
596 this, &SafeBrowsingService::OnResetComplete));
597}
598
599void SafeBrowsingService::OnResetComplete() {
600 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04601 if (enabled_) {
602 resetting_ = false;
603 database_loaded_ = true;
604 RunQueuedClients();
605 }
initial.commit09911bf2008-07-26 23:55:29606}
607
608void SafeBrowsingService::HandleChunk(const std::string& list,
609 std::deque<SBChunk>* chunks) {
610 DCHECK(MessageLoop::current() == io_loop_);
611 DCHECK(enabled_);
612 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
613 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
614}
615
616void SafeBrowsingService::HandleChunkForDatabase(
617 const std::string& list_name,
618 std::deque<SBChunk>* chunks) {
619 DCHECK(MessageLoop::current() == db_thread_->message_loop());
620
621 GetDatabase()->InsertChunks(list_name, chunks);
622}
623
624void SafeBrowsingService::HandleChunkDelete(
625 std::vector<SBChunkDelete>* chunk_deletes) {
626 DCHECK(MessageLoop::current() == io_loop_);
627 DCHECK(enabled_);
628 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
629 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
630}
631
632void SafeBrowsingService::DeleteChunks(
633 std::vector<SBChunkDelete>* chunk_deletes) {
634 DCHECK(MessageLoop::current() == db_thread_->message_loop());
635
636 GetDatabase()->DeleteChunks(chunk_deletes);
637}
638
639// Database worker function.
640void SafeBrowsingService::GetAllChunksFromDatabase() {
641 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]a5a37522008-11-11 21:49:56642 bool database_error = true;
initial.commit09911bf2008-07-26 23:55:29643 std::vector<SBListChunkRanges> lists;
[email protected]53ad8572008-11-13 21:50:34644 if (GetDatabase()) {
645 if (GetDatabase()->UpdateStarted()) {
646 GetDatabase()->GetListsInfo(&lists);
647 database_error = false;
648 } else {
649 GetDatabase()->UpdateFinished(false);
650 }
initial.commit09911bf2008-07-26 23:55:29651 }
652
653 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
654 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
655 database_error));
656}
657
658// Called on the io thread with the results of all chunks.
659void SafeBrowsingService::OnGetAllChunksFromDatabase(
660 const std::vector<SBListChunkRanges>& lists, bool database_error) {
661 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04662 if (enabled_)
663 protocol_manager_->OnGetChunksComplete(lists, database_error);
initial.commit09911bf2008-07-26 23:55:29664}
665
666SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
667 const std::string& list_name) {
668 if (safe_browsing_util::IsPhishingList(list_name)) {
669 return URL_PHISHING;
670 }
671
672 if (safe_browsing_util::IsMalwareList(list_name)) {
673 return URL_MALWARE;
674 }
675
676 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
677 return URL_SAFE;
678}
679
initial.commit09911bf2008-07-26 23:55:29680void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
[email protected]fc76de12008-11-14 01:51:18681 if (new_safe_browsing_)
682 UMA_HISTOGRAM_LONG_TIMES(L"SB2.Delay", time);
683 else
684 UMA_HISTOGRAM_LONG_TIMES(L"SB.Delay", time);
initial.commit09911bf2008-07-26 23:55:29685}
686
687void SafeBrowsingService::CacheHashResults(
[email protected]200abc32008-09-05 01:44:33688 const std::vector<SBPrefix>& prefixes,
initial.commit09911bf2008-07-26 23:55:29689 const std::vector<SBFullHashResult>& full_hashes) {
690 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]200abc32008-09-05 01:44:33691 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29692}
693
[email protected]0a3076572008-12-13 20:48:36694void SafeBrowsingService::OnSuspend(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29695}
696
697// Tell the SafeBrowsing database not to do expensive disk operations for a few
698// minutes after waking up. It's quite likely that the act of resuming from a
699// low power state will involve much disk activity, which we don't want to
700// exacerbate.
[email protected]0a3076572008-12-13 20:48:36701void SafeBrowsingService::OnResume(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29702 if (enabled_) {
[email protected]0a3076572008-12-13 20:48:36703 ChromeThread::GetMessageLoop(ChromeThread::DB)->PostTask(FROM_HERE,
704 NewRunnableMethod(this, &SafeBrowsingService::HandleResume));
initial.commit09911bf2008-07-26 23:55:29705 }
706}
707
708void SafeBrowsingService::HandleResume() {
709 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]53ad8572008-11-13 21:50:34710 // We don't call GetDatabase() here, since we want to avoid unnecessary calls
711 // to Open, Reset, etc, or reload the bloom filter while we're coming out of
712 // a suspended state.
713 if (database_)
714 database_->HandleResume();
license.botbf09a502008-08-24 00:55:55715}
[email protected]613a03b2008-10-24 23:02:00716
717void SafeBrowsingService::RunQueuedClients() {
718 DCHECK(MessageLoop::current() == io_loop_);
719 HISTOGRAM_COUNTS(L"SB.QueueDepth", queued_checks_.size());
720 while (!queued_checks_.empty()) {
721 QueuedCheck check = queued_checks_.front();
722 HISTOGRAM_TIMES(L"SB.QueueDelay", Time::Now() - check.start);
723 CheckUrl(check.url, check.client);
724 queued_checks_.pop_front();
725 }
726}
727