1 // Copyright (c) 2012 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 "extensions/common/matcher/url_matcher_factory.h" 6 7 #include <algorithm> 8 #include <cctype> 9 10 #include "base/lazy_instance.h" 11 #include "base/logging.h" 12 #include "base/strings/stringprintf.h" 13 #include "base/values.h" 14 #include "extensions/common/error_utils.h" 15 #include "extensions/common/matcher/url_matcher_constants.h" 16 #include "extensions/common/matcher/url_matcher_helpers.h" 17 #include "third_party/re2/re2/re2.h" 18 19 namespace helpers = extensions::url_matcher_helpers; 20 namespace keys = extensions::url_matcher_constants; 21 22 namespace { 23 // Error messages: 24 const char kInvalidPortRanges[] = "Invalid port ranges in UrlFilter."; 25 const char kVectorOfStringsExpected[] = 26 "UrlFilter attribute '*' expected a vector of strings as parameter."; 27 const char kUnknownURLFilterAttribute[] = 28 "Unknown attribute '*' in UrlFilter."; 29 const char kAttributeExpectedString[] = 30 "UrlFilter attribute '*' expected a string value."; 31 const char kUnparseableRegexString[] = 32 "Could not parse regular expression '*': *"; 33 const char kLowerCaseExpected[] = "* values need to be in lower case."; 34 35 // Registry for all factory methods of extensions::URLMatcherConditionFactory 36 // that allows translating string literals from the extension API into 37 // the corresponding factory method to be called. 38 class URLMatcherConditionFactoryMethods { 39 public: 40 URLMatcherConditionFactoryMethods() { 41 typedef extensions::URLMatcherConditionFactory F; 42 factory_methods_[keys::kHostContainsKey] = &F::CreateHostContainsCondition; 43 factory_methods_[keys::kHostEqualsKey] = &F::CreateHostEqualsCondition; 44 factory_methods_[keys::kHostPrefixKey] = &F::CreateHostPrefixCondition; 45 factory_methods_[keys::kHostSuffixKey] = &F::CreateHostSuffixCondition; 46 factory_methods_[keys::kOriginAndPathMatchesKey] = 47 &F::CreateOriginAndPathMatchesCondition; 48 factory_methods_[keys::kPathContainsKey] = &F::CreatePathContainsCondition; 49 factory_methods_[keys::kPathEqualsKey] = &F::CreatePathEqualsCondition; 50 factory_methods_[keys::kPathPrefixKey] = &F::CreatePathPrefixCondition; 51 factory_methods_[keys::kPathSuffixKey] = &F::CreatePathSuffixCondition; 52 factory_methods_[keys::kQueryContainsKey] = 53 &F::CreateQueryContainsCondition; 54 factory_methods_[keys::kQueryEqualsKey] = &F::CreateQueryEqualsCondition; 55 factory_methods_[keys::kQueryPrefixKey] = &F::CreateQueryPrefixCondition; 56 factory_methods_[keys::kQuerySuffixKey] = &F::CreateQuerySuffixCondition; 57 factory_methods_[keys::kURLContainsKey] = &F::CreateURLContainsCondition; 58 factory_methods_[keys::kURLEqualsKey] = &F::CreateURLEqualsCondition; 59 factory_methods_[keys::kURLPrefixKey] = &F::CreateURLPrefixCondition; 60 factory_methods_[keys::kURLSuffixKey] = &F::CreateURLSuffixCondition; 61 factory_methods_[keys::kURLMatchesKey] = &F::CreateURLMatchesCondition; 62 } 63 64 // Returns whether a factory method for the specified |pattern_type| (e.g. 65 // "host_suffix") is known. 66 bool Contains(const std::string& pattern_type) const { 67 return factory_methods_.find(pattern_type) != factory_methods_.end(); 68 } 69 70 // Creates a URLMatcherCondition instance from |url_matcher_condition_factory| 71 // of the given |pattern_type| (e.g. "host_suffix") for the given 72 // |pattern_value| (e.g. "example.com"). 73 // The |pattern_type| needs to be known to this class (see Contains()) or 74 // a CHECK is triggered. 75 extensions::URLMatcherCondition Call( 76 extensions::URLMatcherConditionFactory* url_matcher_condition_factory, 77 const std::string& pattern_type, 78 const std::string& pattern_value) const { 79 FactoryMethods::const_iterator i = factory_methods_.find(pattern_type); 80 CHECK(i != factory_methods_.end()); 81 const FactoryMethod& method = i->second; 82 return (url_matcher_condition_factory->*method)(pattern_value); 83 } 84 85 private: 86 typedef extensions::URLMatcherCondition 87 (extensions::URLMatcherConditionFactory::* FactoryMethod) 88 (const std::string& prefix); 89 typedef std::map<std::string, FactoryMethod> FactoryMethods; 90 91 FactoryMethods factory_methods_; 92 93 DISALLOW_COPY_AND_ASSIGN(URLMatcherConditionFactoryMethods); 94 }; 95 96 static base::LazyInstance<URLMatcherConditionFactoryMethods> 97 g_url_matcher_condition_factory_methods = LAZY_INSTANCE_INITIALIZER; 98 99 } // namespace 100 101 namespace extensions { 102 103 // static 104 scoped_refptr<URLMatcherConditionSet> 105 URLMatcherFactory::CreateFromURLFilterDictionary( 106 URLMatcherConditionFactory* url_matcher_condition_factory, 107 const base::DictionaryValue* url_filter_dict, 108 URLMatcherConditionSet::ID id, 109 std::string* error) { 110 scoped_ptr<URLMatcherSchemeFilter> url_matcher_schema_filter; 111 scoped_ptr<URLMatcherPortFilter> url_matcher_port_filter; 112 URLMatcherConditionSet::Conditions url_matcher_conditions; 113 114 for (base::DictionaryValue::Iterator iter(*url_filter_dict); 115 !iter.IsAtEnd(); iter.Advance()) { 116 const std::string& condition_attribute_name = iter.key(); 117 const Value& condition_attribute_value = iter.value(); 118 if (IsURLMatcherConditionAttribute(condition_attribute_name)) { 119 // Handle {host, path, ...}{Prefix, Suffix, Contains, Equals}. 120 URLMatcherCondition url_matcher_condition = 121 CreateURLMatcherCondition( 122 url_matcher_condition_factory, 123 condition_attribute_name, 124 &condition_attribute_value, 125 error); 126 if (!error->empty()) 127 return scoped_refptr<URLMatcherConditionSet>(NULL); 128 url_matcher_conditions.insert(url_matcher_condition); 129 } else if (condition_attribute_name == keys::kSchemesKey) { 130 // Handle scheme. 131 url_matcher_schema_filter = CreateURLMatcherScheme( 132 &condition_attribute_value, error); 133 if (!error->empty()) 134 return scoped_refptr<URLMatcherConditionSet>(NULL); 135 } else if (condition_attribute_name == keys::kPortsKey) { 136 // Handle ports. 137 url_matcher_port_filter = CreateURLMatcherPorts( 138 &condition_attribute_value, error); 139 if (!error->empty()) 140 return scoped_refptr<URLMatcherConditionSet>(NULL); 141 } else { 142 // Handle unknown attributes. 143 *error = ErrorUtils::FormatErrorMessage( 144 kUnknownURLFilterAttribute, 145 condition_attribute_name); 146 return scoped_refptr<URLMatcherConditionSet>(NULL); 147 } 148 } 149 150 // As the URL is the preliminary matching criterion that triggers the tests 151 // for the remaining condition attributes, we insert an empty URL match if 152 // no other url match conditions were specified. Such an empty URL is always 153 // matched. 154 if (url_matcher_conditions.empty()) { 155 url_matcher_conditions.insert( 156 url_matcher_condition_factory->CreateHostPrefixCondition( 157 std::string())); 158 } 159 160 scoped_refptr<URLMatcherConditionSet> url_matcher_condition_set( 161 new URLMatcherConditionSet(id, url_matcher_conditions, 162 url_matcher_schema_filter.Pass(), url_matcher_port_filter.Pass())); 163 return url_matcher_condition_set; 164 } 165 166 // static 167 bool URLMatcherFactory::IsURLMatcherConditionAttribute( 168 const std::string& condition_attribute_name) { 169 return g_url_matcher_condition_factory_methods.Get().Contains( 170 condition_attribute_name); 171 } 172 173 namespace { 174 175 // Returns true if some alphabetic characters in this string are upper case. 176 bool ContainsUpperCase(const std::string& str) { 177 return std::find_if(str.begin(), str.end(), ::isupper) != str.end(); 178 } 179 180 } // namespace 181 182 // static 183 URLMatcherCondition URLMatcherFactory::CreateURLMatcherCondition( 184 URLMatcherConditionFactory* url_matcher_condition_factory, 185 const std::string& condition_attribute_name, 186 const base::Value* value, 187 std::string* error) { 188 std::string str_value; 189 if (!value->GetAsString(&str_value)) { 190 *error = ErrorUtils::FormatErrorMessage(kAttributeExpectedString, 191 condition_attribute_name); 192 return URLMatcherCondition(); 193 } 194 if (condition_attribute_name == keys::kHostContainsKey || 195 condition_attribute_name == keys::kHostPrefixKey || 196 condition_attribute_name == keys::kHostSuffixKey || 197 condition_attribute_name == keys::kHostEqualsKey) { 198 if (ContainsUpperCase(str_value)) { 199 *error = ErrorUtils::FormatErrorMessage(kLowerCaseExpected, 200 "Host"); 201 return URLMatcherCondition(); 202 } 203 } 204 205 // Test regular expressions for validity. 206 if (condition_attribute_name == keys::kURLMatchesKey || 207 condition_attribute_name == keys::kOriginAndPathMatchesKey) { 208 re2::RE2 regex(str_value); 209 if (!regex.ok()) { 210 *error = ErrorUtils::FormatErrorMessage(kUnparseableRegexString, 211 str_value, regex.error()); 212 return URLMatcherCondition(); 213 } 214 } 215 return g_url_matcher_condition_factory_methods.Get().Call( 216 url_matcher_condition_factory, condition_attribute_name, str_value); 217 } 218 219 // static 220 scoped_ptr<URLMatcherSchemeFilter> URLMatcherFactory::CreateURLMatcherScheme( 221 const base::Value* value, 222 std::string* error) { 223 std::vector<std::string> schemas; 224 if (!helpers::GetAsStringVector(value, &schemas)) { 225 *error = ErrorUtils::FormatErrorMessage(kVectorOfStringsExpected, 226 keys::kSchemesKey); 227 return scoped_ptr<URLMatcherSchemeFilter>(); 228 } 229 for (std::vector<std::string>::const_iterator it = schemas.begin(); 230 it != schemas.end(); ++it) { 231 if (ContainsUpperCase(*it)) { 232 *error = ErrorUtils::FormatErrorMessage(kLowerCaseExpected, 233 "Scheme"); 234 return scoped_ptr<URLMatcherSchemeFilter>(); 235 } 236 } 237 return scoped_ptr<URLMatcherSchemeFilter>( 238 new URLMatcherSchemeFilter(schemas)); 239 } 240 241 // static 242 scoped_ptr<URLMatcherPortFilter> URLMatcherFactory::CreateURLMatcherPorts( 243 const base::Value* value, 244 std::string* error) { 245 std::vector<URLMatcherPortFilter::Range> ranges; 246 const base::ListValue* value_list = NULL; 247 if (!value->GetAsList(&value_list)) { 248 *error = kInvalidPortRanges; 249 return scoped_ptr<URLMatcherPortFilter>(); 250 } 251 252 for (ListValue::const_iterator i = value_list->begin(); 253 i != value_list->end(); ++i) { 254 Value* entry = *i; 255 int port = 0; 256 base::ListValue* range = NULL; 257 if (entry->GetAsInteger(&port)) { 258 ranges.push_back(URLMatcherPortFilter::CreateRange(port)); 259 } else if (entry->GetAsList(&range)) { 260 int from = 0, to = 0; 261 if (range->GetSize() != 2u || 262 !range->GetInteger(0, &from) || 263 !range->GetInteger(1, &to)) { 264 *error = kInvalidPortRanges; 265 return scoped_ptr<URLMatcherPortFilter>(); 266 } 267 ranges.push_back(URLMatcherPortFilter::CreateRange(from, to)); 268 } else { 269 *error = kInvalidPortRanges; 270 return scoped_ptr<URLMatcherPortFilter>(); 271 } 272 } 273 274 return scoped_ptr<URLMatcherPortFilter>(new URLMatcherPortFilter(ranges)); 275 } 276 277 } // namespace extensions 278