| // 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(url::kStandardSchemeSeparator), start); |
| if (current_pos != std::string::npos) { |
| scheme_component = Component(start, current_pos); |
| start = current_pos + strlen(url::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(url::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 + url::kStandardSchemeSeparator; |
| |
| if (parts.scheme == url::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 |