// Copyright (c) 2012 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/common/content_settings_pattern_parser.h" #include "base/strings/string_util.h" #include "chrome/common/url_constants.h" #include "extensions/common/constants.h" #include "net/base/net_util.h" #include "url/gurl.h" #include "url/url_canon.h" namespace { const char* kUrlPathSeparator = "/"; const char* kUrlPortSeparator = ":"; class Component { public: Component() : start(0), len(0) {} Component(size_t s, size_t l) : start(s), len(l) {} bool IsNonEmpty() { return len > 0; } size_t start; size_t len; }; } // namespace namespace content_settings { const char* PatternParser::kDomainWildcard = "[*.]"; const size_t PatternParser::kDomainWildcardLength = 4; const char* PatternParser::kSchemeWildcard = "*"; const char* PatternParser::kHostWildcard = "*"; const char* PatternParser::kPortWildcard = "*"; const char* PatternParser::kPathWildcard = "*"; // static void PatternParser::Parse(const std::string& pattern_spec, ContentSettingsPattern::BuilderInterface* builder) { if (pattern_spec == "*") { builder->WithSchemeWildcard(); builder->WithDomainWildcard(); builder->WithPortWildcard(); return; } // Initialize components for the individual patterns parts to empty // sub-strings. Component scheme_component; Component host_component; Component port_component; Component path_component; size_t start = 0; size_t current_pos = 0; if (pattern_spec.empty()) return; // Test if a scheme pattern is in the spec. current_pos = pattern_spec.find( std::string(content::kStandardSchemeSeparator), start); if (current_pos != std::string::npos) { scheme_component = Component(start, current_pos); start = current_pos + strlen(content::kStandardSchemeSeparator); current_pos = start; } else { current_pos = start; } if (start >= pattern_spec.size()) return; // Bad pattern spec. // Jump to the end of domain wildcards or an IPv6 addresses. IPv6 addresses // contain ':'. So first move to the end of an IPv6 address befor searching // for the ':' that separates the port form the host. if (pattern_spec[current_pos] == '[') current_pos = pattern_spec.find("]", start); if (current_pos == std::string::npos) return; // Bad pattern spec. current_pos = pattern_spec.find(std::string(kUrlPortSeparator), current_pos); if (current_pos == std::string::npos) { // No port spec found current_pos = pattern_spec.find(std::string(kUrlPathSeparator), start); if (current_pos == std::string::npos) { current_pos = pattern_spec.size(); host_component = Component(start, current_pos - start); } else { // Pattern has a path spec. host_component = Component(start, current_pos - start); } start = current_pos; } else { // Port spec found. host_component = Component(start, current_pos - start); start = current_pos + 1; if (start < pattern_spec.size()) { current_pos = pattern_spec.find(std::string(kUrlPathSeparator), start); if (current_pos == std::string::npos) { current_pos = pattern_spec.size(); } port_component = Component(start, current_pos - start); start = current_pos; } } current_pos = pattern_spec.size(); if (start < current_pos) { // Pattern has a path spec. path_component = Component(start, current_pos - start); } // Set pattern parts. std::string scheme; if (scheme_component.IsNonEmpty()) { scheme = pattern_spec.substr(scheme_component.start, scheme_component.len); if (scheme == kSchemeWildcard) { builder->WithSchemeWildcard(); } else { builder->WithScheme(scheme); } } else { builder->WithSchemeWildcard(); } if (host_component.IsNonEmpty()) { std::string host = pattern_spec.substr(host_component.start, host_component.len); if (host == kHostWildcard) { builder->WithDomainWildcard(); } else if (StartsWithASCII(host, kDomainWildcard, true)) { host = host.substr(kDomainWildcardLength); builder->WithDomainWildcard(); builder->WithHost(host); } else { // If the host contains a wildcard symbol then it is invalid. if (host.find(kHostWildcard) != std::string::npos) { builder->Invalid(); return; } builder->WithHost(host); } } if (port_component.IsNonEmpty()) { const std::string port = pattern_spec.substr(port_component.start, port_component.len); if (port == kPortWildcard) { builder->WithPortWildcard(); } else { // Check if the port string represents a valid port. for (size_t i = 0; i < port.size(); ++i) { if (!IsAsciiDigit(port[i])) { builder->Invalid(); return; } } // TODO(markusheintz): Check port range. builder->WithPort(port); } } else { if (scheme != std::string(extensions::kExtensionScheme) && scheme != std::string(chrome::kFileScheme)) builder->WithPortWildcard(); } if (path_component.IsNonEmpty()) { const std::string path = pattern_spec.substr(path_component.start, path_component.len); if (path.substr(1) == kPathWildcard) builder->WithPathWildcard(); else builder->WithPath(path); } } // static std::string PatternParser::ToString( const ContentSettingsPattern::PatternParts& parts) { // Return the most compact form to support legacy code and legacy pattern // strings. if (parts.is_scheme_wildcard && parts.has_domain_wildcard && parts.host.empty() && parts.is_port_wildcard) return "*"; std::string str; if (!parts.is_scheme_wildcard) str += parts.scheme + content::kStandardSchemeSeparator; if (parts.scheme == chrome::kFileScheme) { if (parts.is_path_wildcard) return str + kUrlPathSeparator + kPathWildcard; else return str + parts.path; } if (parts.has_domain_wildcard) { if (parts.host.empty()) str += kHostWildcard; else str += kDomainWildcard; } str += parts.host; if (parts.scheme == std::string(extensions::kExtensionScheme)) { str += parts.path.empty() ? std::string(kUrlPathSeparator) : parts.path; return str; } if (!parts.is_port_wildcard) { str += std::string(kUrlPortSeparator) + parts.port; } return str; } } // namespace content_settings