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

Side by Side Diff: chrome/browser/policy/url_blacklist_manager.cc

Issue 7716003: WIP: URL blacklisting by policy. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Reviewed Created 9 years, 3 months 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/policy/url_blacklist_manager.h"
6
7 #include "base/string_number_conversions.h"
8 #include "base/string_util.h"
9 #include "base/values.h"
10 #include "chrome/browser/net/url_fixer_upper.h"
11 #include "chrome/browser/prefs/pref_service.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/common/chrome_notification_types.h"
14 #include "chrome/common/pref_names.h"
15 #include "content/browser/browser_thread.h"
16 #include "content/common/notification_details.h"
17 #include "content/common/notification_source.h"
18 #include "googleurl/src/gurl.h"
19
20 namespace policy {
21
22 namespace {
23
24 // Time to wait before starting an update of the blacklist. Scheduling another
25 // update during this period will reset the timer.
26 const int64 kUpdateDelayMs = 1000;
27
28 // Maximum filters per policy. Filters over this index are ignored.
29 const size_t kMaxFiltersPerPolicy = 100;
30
31 typedef std::vector<std::string> StringVector;
32
33 StringVector* ListValueToStringVector(const base::ListValue* list) {
34 StringVector* vector = new StringVector;
35
36 if (!list)
37 return vector;
38
39 vector->reserve(list->GetSize());
40 std::string s;
41 for (base::ListValue::const_iterator it = list->begin();
42 it != list->end() && vector->size() < kMaxFiltersPerPolicy; ++it) {
43 if ((*it)->GetAsString(&s))
44 vector->push_back(s);
45 }
46
47 return vector;
48 }
49
50 // A task that owns the Blacklist, and passes it to the URLBlacklistManager
51 // on the IO thread, if the URLBlacklistManager still exists.
52 class SetBlacklistTask : public Task {
53 public:
54 SetBlacklistTask(
55 base::WeakPtr<URLBlacklistManager> url_blacklist_manager,
56 Blacklist* blacklist)
57 : url_blacklist_manager_(url_blacklist_manager),
58 blacklist_(blacklist) {
59 }
60
61 virtual void Run() OVERRIDE {
62 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
63 if (url_blacklist_manager_)
64 url_blacklist_manager_->SetBlacklist(blacklist_.release());
65 }
66
67 private:
68 base::WeakPtr<URLBlacklistManager> url_blacklist_manager_;
69 scoped_ptr<Blacklist> blacklist_;
70 DISALLOW_COPY_AND_ASSIGN(SetBlacklistTask);
71 };
72
73 // A task that builds the blacklist on the FILE thread, and then posts another
74 // task to pass it to the URLBlacklistManager on the IO thread.
75 class BuildBlacklistTask : public Task {
76 public:
77 BuildBlacklistTask(
78 base::WeakPtr<URLBlacklistManager> url_blacklist_manager,
79 StringVector* blacklist,
80 StringVector* whitelist)
81 : url_blacklist_manager_(url_blacklist_manager),
82 blacklist_(blacklist),
83 whitelist_(whitelist) {
84 }
85
86 virtual void Run() OVERRIDE {
87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
88
89 Blacklist* blacklist = NULL;
90
91 if (!blacklist_->empty()) {
92 blacklist = new Blacklist;
93 for (StringVector::iterator it = blacklist_->begin();
94 it != blacklist_->end(); ++it) {
95 blacklist->Block(*it);
96 }
97 for (StringVector::iterator it = whitelist_->begin();
98 it != whitelist_->end(); ++it) {
99 blacklist->Allow(*it);
100 }
101 }
102
103 BrowserThread::PostTask(
104 BrowserThread::IO, FROM_HERE,
105 new SetBlacklistTask(url_blacklist_manager_, blacklist));
106 }
107
108 private:
109 base::WeakPtr<URLBlacklistManager> url_blacklist_manager_;
110 scoped_ptr<StringVector> blacklist_;
111 scoped_ptr<StringVector> whitelist_;
112 DISALLOW_COPY_AND_ASSIGN(BuildBlacklistTask);
113 };
114
115 } // namespace
116
117 Blacklist::Blacklist() {
118 }
119
120 Blacklist::~Blacklist() {
121 }
122
123 void Blacklist::AddFilter(const std::string& filter, bool block) {
124 std::string scheme;
125 std::string host;
126 uint16 port;
127 std::string path;
128 SchemeFlag flag;
129 bool match_subdomains = true;
130
131 if (!FilterToComponents(filter, &scheme, &host, &port, &path)) {
132 LOG(WARNING) << "Invalid filter, ignoring: " << filter;
133 return;
134 }
135
136 if (!SchemeToFlag(scheme, &flag)) {
137 LOG(WARNING) << "Unsupported scheme in filter, ignoring filter: " << filter;
138 return;
139 }
140
141 // Special syntax to disable subdomain matching.
142 if (!host.empty() && host[0] == '.') {
143 host.erase(0, 1);
144 match_subdomains = false;
145 }
146
147 // Try to find an existing PathFilter with the same path prefix, port and
148 // match_subdomains value.
149 PathFilterList& list = host_filters_[host];
150 PathFilterList::iterator it;
151 for (it = list.begin(); it != list.end(); ++it) {
152 if (it->port == port && it->match_subdomains == match_subdomains &&
153 it->path_prefix == path)
154 break;
155 }
156 PathFilter* path_filter;
157 if (it == list.end()) {
158 list.push_back(PathFilter(path, port, match_subdomains));
159 path_filter = &list.back();
160 } else {
161 path_filter = &(*it);
162 }
163
164 if (block)
165 path_filter->blocked_schemes |= flag;
166 else
167 path_filter->allowed_schemes |= flag;
168 }
169
170 void Blacklist::Block(const std::string& filter) {
171 AddFilter(filter, true);
172 }
173
174 void Blacklist::Allow(const std::string& filter) {
175 AddFilter(filter, false);
176 }
177
178 bool Blacklist::IsURLBlocked(const GURL& url) const {
179 SchemeFlag flag;
180 if (!SchemeToFlag(url.scheme(), &flag)) {
181 // Not a scheme that can be filtered.
182 return false;
183 }
184
185 std::string host(url.host());
186 int int_port = url.EffectiveIntPort();
187 const uint16 port = int_port > 0 ? int_port : 0;
188 const std::string& path = url.path();
189
190 // The first iteration through the loop will be an exact host match.
191 // Subsequent iterations are subdomain matches, and some filters don't apply
192 // to those.
193 bool is_matching_subdomains = false;
194 const bool host_is_ip = url.HostIsIPAddress();
195 for (;;) {
196 HostFilterTable::const_iterator host_filter = host_filters_.find(host);
197 if (host_filter != host_filters_.end()) {
198 const PathFilterList& v = host_filter->second;
199 size_t longest_length = 0;
200 bool is_blocked = false;
201 bool has_match = false;
202 bool has_exact_host_match = false;
203 for (PathFilterList::const_iterator it = v.begin(); it != v.end(); ++it) {
204 // Filters that apply to an exact hostname only take precedence over
205 // filters that can apply to subdomains too.
206 // E.g. ".google.com" filters take priority over "google.com".
207 if (has_exact_host_match && it->match_subdomains)
208 continue;
209
210 // Skip if filter doesn't apply to subdomains, and this is a subdomain.
211 if (is_matching_subdomains && !it->match_subdomains)
212 continue;
213
214 if (it->port != 0 && it->port != port)
215 continue;
216
217 // If this match can't be longer than the current match, skip it.
218 // For same size matches, the first rule to match takes precedence.
219 // If this is an exact host match, it can be actually shorter than
220 // a previous, non-exact match.
221 if ((has_match && it->path_prefix.length() <= longest_length) &&
222 (has_exact_host_match || it->match_subdomains)) {
223 continue;
224 }
225
226 // Skip if the filter's |path_prefix| is not a prefix of |path|.
227 if (path.compare(0, it->path_prefix.length(), it->path_prefix) != 0)
228 continue;
229
230 // Check if there is a blocked or allowed bit set for the scheme.
231 if ((it->allowed_schemes & flag) || (it->blocked_schemes & flag)) {
232 // This is the best match so far.
233 has_match = true;
234 has_exact_host_match = !it->match_subdomains;
235 longest_length = it->path_prefix.length();
236 // If both blocked and allowed bits are set, allowed takes precedence.
237 is_blocked = !(it->allowed_schemes & flag);
238 }
239 }
240 // If a match was found, return its decision.
241 if (has_match)
242 return is_blocked;
243 }
244
245 // Quit after trying the empty string (corresponding to host '*').
246 // Also skip subdomain matching for IP addresses.
247 if (host.empty() || host_is_ip)
248 break;
249
250 // No match found for this host. Try a subdomain match, by removing the
251 // leftmost subdomain from the hostname.
252 is_matching_subdomains = true;
253 size_t i = host.find('.');
254 if (i != std::string::npos)
255 ++i;
256 host.erase(0, i);
257 }
258
259 // Default is to allow.
260 return false;
261 }
262
263 // static
264 bool Blacklist::SchemeToFlag(const std::string& scheme, SchemeFlag* flag) {
265 if (scheme.empty())
266 *flag = SCHEME_ALL;
267 else if (scheme == "http")
268 *flag = SCHEME_HTTP;
269 else if (scheme == "https")
270 *flag = SCHEME_HTTPS;
271 else if (scheme == "ftp")
272 *flag = SCHEME_FTP;
273 else
274 return false;
275 return true;
276 }
277
278 // static
279 bool Blacklist::FilterToComponents(const std::string& filter,
280 std::string* scheme,
281 std::string* host,
282 uint16* port,
283 std::string* path) {
284 url_parse::Parsed parsed;
285 URLFixerUpper::SegmentURL(filter, &parsed);
286
287 if (!parsed.host.is_nonempty())
288 return false;
289
290 if (parsed.scheme.is_nonempty())
291 scheme->assign(filter, parsed.scheme.begin, parsed.scheme.len);
292 else
293 scheme->clear();
294
295 host->assign(filter, parsed.host.begin, parsed.host.len);
296 // Special '*' host, matches all hosts.
297 if (*host == "*")
298 host->clear();
299
300 if (parsed.port.is_nonempty()) {
301 int int_port;
302 if (!base::StringToInt(filter.substr(parsed.port.begin, parsed.port.len),
303 &int_port)) {
304 return false;
305 }
306 if (int_port <= 0 || int_port > kuint16max)
307 return false;
308 *port = int_port;
309 } else {
310 // Match any port.
311 *port = 0;
312 }
313
314 if (parsed.path.is_nonempty())
315 path->assign(filter, parsed.path.begin, parsed.path.len);
316 else
317 path->clear();
318
319 return true;
320 }
321
322 URLBlacklistManager::URLBlacklistManager(Profile* profile)
323 : ALLOW_THIS_IN_INITIALIZER_LIST(ui_method_factory_(this)),
324 ALLOW_THIS_IN_INITIALIZER_LIST(io_weak_ptr_factory_(this)),
325 pref_service_(profile->GetPrefs()) {
326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
327 // This has to be created on the UI thread, but is only posted later, after
328 // the initialization on the IO thread is complete.
329 // Posting a task to invoke InitializeOnIOThread from here isn't safe, since
330 // we can't get weak ptrs for the IO thread yet.
331 initialize_on_ui_task_ = ui_method_factory_.NewRunnableMethod(
332 &URLBlacklistManager::InitializeOnUIThread);
333 }
334
335 void URLBlacklistManager::InitializeOnIOThread() {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
337 DCHECK(initialize_on_ui_task_ != NULL);
338 io_weak_ptr_factory_.DetachFromThread();
339 weak_ptr_ = io_weak_ptr_factory_.GetWeakPtr();
340
341 // It is now safe to resume initialization on the UI thread, since it is now
342 // possible to get weak refs to self to use on the IO thread.
343 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, initialize_on_ui_task_);
344 }
345
346 void URLBlacklistManager::InitializeOnUIThread() {
347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
348 DCHECK(initialize_on_ui_task_ != NULL);
349 initialize_on_ui_task_ = NULL;
350
351 pref_change_registrar_.Init(pref_service_);
352 pref_change_registrar_.Add(prefs::kUrlBlacklist, this);
353 pref_change_registrar_.Add(prefs::kUrlWhitelist, this);
354
355 // Start enforcing the policies without a delay when they are present at
356 // startup.
357 if (pref_service_->HasPrefPath(prefs::kUrlBlacklist))
358 Update();
359 }
360
361 void URLBlacklistManager::ShutdownOnUIThread() {
362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
363 // Cancel any pending updates, and stop listening for pref change updates.
364 // This also cancels a pending InitializeOnUIThread, if it hasn't executed
365 // yet.
366 ui_method_factory_.RevokeAll();
367 pref_change_registrar_.RemoveAll();
368 }
369
370 URLBlacklistManager::~URLBlacklistManager() {
371 // Cancel any weak ptrs on the IO thread. Pending tasks won't execute their
372 // updates anymore.
373 io_weak_ptr_factory_.InvalidateWeakPtrs();
374 }
375
376 void URLBlacklistManager::Observe(int type,
377 const NotificationSource& source,
378 const NotificationDetails& details) {
379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
380 DCHECK(type == chrome::NOTIFICATION_PREF_CHANGED);
381 PrefService* prefs = Source<PrefService>(source).ptr();
382 DCHECK(prefs == pref_service_);
383 std::string* pref_name = Details<std::string>(details).ptr();
384 DCHECK(*pref_name == prefs::kUrlBlacklist ||
385 *pref_name == prefs::kUrlWhitelist);
386 ScheduleUpdate();
387 }
388
389 void URLBlacklistManager::ScheduleUpdate() {
390 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
391 // Cancel pending updates, if any.
392 ui_method_factory_.RevokeAll();
393 PostUpdateTask(
394 ui_method_factory_.NewRunnableMethod(&URLBlacklistManager::Update));
395 }
396
397 void URLBlacklistManager::PostUpdateTask(Task* task) {
398 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
399 // This is overriden in tests to post the task without the delay.
400 MessageLoop::current()->PostDelayedTask(FROM_HERE, task, kUpdateDelayMs);
401 }
402
403 void URLBlacklistManager::Update() {
404 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
405
406 // The preferences can only be read on the UI thread.
407 StringVector* blacklist = ListValueToStringVector(
408 pref_service_->GetList(prefs::kUrlBlacklist));
409 StringVector* whitelist = ListValueToStringVector(
410 pref_service_->GetList(prefs::kUrlWhitelist));
411
412 BrowserThread::PostTask(
413 BrowserThread::FILE, FROM_HERE,
414 new BuildBlacklistTask(weak_ptr_, blacklist, whitelist));
415 }
416
417 void URLBlacklistManager::SetBlacklist(Blacklist* blacklist) {
418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
419 blacklist_.reset(blacklist);
420 }
421
422 bool URLBlacklistManager::IsURLBlocked(const GURL& url) const {
423 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
424
425 if (!blacklist_.get())
426 return false;
427
428 return blacklist_->IsURLBlocked(url);
429 }
430
431 // static
432 void URLBlacklistManager::RegisterPrefs(PrefService* pref_service) {
433 pref_service->RegisterListPref(prefs::kUrlBlacklist,
434 PrefService::UNSYNCABLE_PREF);
435 pref_service->RegisterListPref(prefs::kUrlWhitelist,
436 PrefService::UNSYNCABLE_PREF);
437 }
438
439 } // namespace policy
OLDNEW
« no previous file with comments | « chrome/browser/policy/url_blacklist_manager.h ('k') | chrome/browser/policy/url_blacklist_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698