// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/privacy_blacklist/blacklist.h" #include #include #include #include "base/file_path.h" #include "base/file_util.h" #include "base/string_util.h" #include "chrome/browser/pref_service.h" #include "chrome/browser/profile.h" #include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "net/http/http_util.h" namespace { const char* const cookie_headers[2] = { "cookie", "set-cookie" }; } // namespace const unsigned int Blacklist::kBlockAll = 1; const unsigned int Blacklist::kBlockCookies = 1 << 1; const unsigned int Blacklist::kDontSendReferrer = 1 << 2; const unsigned int Blacklist::kDontSendUserAgent = 1 << 3; const unsigned int Blacklist::kBlockUnsecure = 1 << 4; const unsigned int Blacklist::kBlockRequest = kBlockAll | kBlockUnsecure; const unsigned int Blacklist::kModifySentHeaders = kBlockCookies | kDontSendUserAgent | kDontSendReferrer; const unsigned int Blacklist::kModifyReceivedHeaders = kBlockCookies; // static bool Blacklist::Matches(const std::string& pattern, const std::string& url) { if (pattern.size() > url.size()) return false; std::string::size_type p = 0; std::string::size_type u = 0; while (pattern[p] != '\0' && url[u] != '\0') { if (pattern[p] == '@') { while (pattern[++p] == '@'); // Consecutive @ are redundant. if (pattern[p] == '\0') return true; // Nothing to match after the @. // Look for another wildcard to determine pattern-chunk. std::string::size_type tp = pattern.find_first_of("@", p); // If it must match until the end, compare the last characters. if (tp == std::string::npos) { std::string::size_type ur = url.size() - u; std::string::size_type pr = pattern.size() - p; return (pr <= ur) && (url.compare(url.size() - pr, pr, pattern.c_str() + p) == 0); } // Else find the pattern chunk which is pattern[p:tp] std::string::size_type tu = url.find(pattern.c_str() + p, u, tp - p); if (tu == std::string::npos) return false; // Pattern chunk not found. // Since tp is strictly greater than p, both u and p always increase. u = tu + tp - p; p = tp; continue; } // Match non-wildcard character. if (pattern[p++] != url[u++]) return false; } return pattern[p] == '\0'; } bool Blacklist::Entry::IsBlocked(const GURL& url) const { return (attributes_ & kBlockAll) || ((attributes_ & kBlockUnsecure) && !url.SchemeIsSecure()); } Blacklist::Entry::Entry(const std::string& pattern, const Provider* provider, bool is_exception) : attributes_(0), is_exception_(is_exception), pattern_(pattern), provider_(provider) {} void Blacklist::Entry::AddAttributes(unsigned int attributes) { attributes_ |= attributes; } bool Blacklist::Match::IsBlocked(const GURL& url) const { return (attributes() & kBlockAll) || ((attributes() & kBlockUnsecure) && !url.SchemeIsSecure()); } Blacklist::Match::Match() : matching_attributes_(0), exception_attributes_(0) {} void Blacklist::Match::AddEntry(const Entry* entry) { if (entry->is_exception()) { exception_attributes_ |= entry->attributes(); exception_entries_.push_back(entry); } else { matching_attributes_ |= entry->attributes(); matching_entries_.push_back(entry); } } Blacklist::Blacklist(PrefService* prefs) : prefs_(prefs) { LoadPreferences(); } Blacklist::~Blacklist() { } void Blacklist::AddEntry(Entry* entry) { DCHECK(entry); blacklist_.push_back(linked_ptr(entry)); } void Blacklist::AddProvider(Provider* provider) { DCHECK(provider); providers_.push_back(linked_ptr(provider)); } // Returns a pointer to the Blacklist-owned entry which matches the given // URL. If no matching Entry is found, returns null. Blacklist::Match* Blacklist::FindMatch(const GURL& url) const { // Never match something which is not http, https or ftp. // TODO(idanan): Investigate if this would be an inclusion test instead of an // exclusion test and if there are other schemes to test for. if (!url.SchemeIs(chrome::kHttpScheme) && !url.SchemeIs(chrome::kHttpsScheme) && !url.SchemeIs(chrome::kFtpScheme)) return 0; std::string url_spec = GetURLAsLookupString(url); Match* match = NULL; for (EntryList::const_iterator i = blacklist_.begin(); i != blacklist_.end(); ++i) { if (Matches((*i)->pattern(), url_spec)) { if (!match) match = new Match; match->AddEntry(i->get()); } } return match; } bool Blacklist::LoadEntryPreference(const ListValue& pref, const Provider* provider) { EntryList entries; for (ListValue::const_iterator i = pref.begin(); i != pref.end(); ++i) { if (!(*i)->IsType(Value::TYPE_DICTIONARY)) return false; const DictionaryValue* entry_pref = static_cast(*i); std::string pattern; int attributes; bool is_exception; if (!(entry_pref->GetString(L"pattern", &pattern) && entry_pref->GetInteger(L"attributes", &attributes) && entry_pref->GetBoolean(L"exception", &is_exception))) return false; Entry* entry = new Entry(pattern, provider, is_exception); entry->AddAttributes(static_cast(attributes)); entries.push_back(linked_ptr(entry)); } blacklist_.insert(blacklist_.end(), entries.begin(), entries.end()); return true; } bool Blacklist::LoadProviderPreference(const DictionaryValue& pref, const std::wstring& path) { std::string name, url; ListValue* entries; if (!(pref.GetString(L"name", &name) && pref.GetString(L"url", &url) && pref.GetList(L"entries", &entries))) return false; linked_ptr provider(new Provider(name, url, path)); if (LoadEntryPreference(*entries, provider.get())) providers_.push_back(provider); return true; return false; } bool Blacklist::LoadPreferences() { DCHECK(prefs_); const DictionaryValue* blacklist_rules = prefs_->GetDictionary( prefs::kPrivacyFilterRules); if (!blacklist_rules) return false; bool result = true; for (DictionaryValue::key_iterator key = blacklist_rules->begin_keys(); key != blacklist_rules->end_keys(); ++key) { DictionaryValue* provider; if (!(blacklist_rules->GetDictionaryWithoutPathExpansion(*key, &provider) && LoadProviderPreference(*provider, *key))) result = false; } return result; } // static std::string Blacklist::GetURLAsLookupString(const GURL& url) { std::string url_spec = url.host() + url.path(); if (!url.query().empty()) url_spec = url_spec + "?" + url.query(); return url_spec; } // static void Blacklist::RegisterUserPrefs(PrefService* user_prefs) { user_prefs->RegisterDictionaryPref(prefs::kPrivacyFilterRules); } // static std::string Blacklist::StripCookies(const std::string& header) { return net::HttpUtil::StripHeaders(header, cookie_headers, 2); }