Home | History | Annotate | Download | only in matcher
      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