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 "components/content_settings/core/common/content_settings_pattern_parser.h" 6 7#include "base/strings/string_util.h" 8#include "url/url_constants.h" 9 10namespace { 11 12const char kDomainWildcard[] = "[*.]"; 13const size_t kDomainWildcardLength = 4; 14const char kHostWildcard[] = "*"; 15const char kPathWildcard[] = "*"; 16const char kPortWildcard[] = "*"; 17const char kSchemeWildcard[] = "*"; 18const char kUrlPathSeparator[] = "/"; 19const char kUrlPortSeparator[] = ":"; 20 21class Component { 22 public: 23 Component() : start(0), len(0) {} 24 Component(size_t s, size_t l) : start(s), len(l) {} 25 26 bool IsNonEmpty() { 27 return len > 0; 28 } 29 30 size_t start; 31 size_t len; 32}; 33 34} // namespace 35 36namespace content_settings { 37 38void PatternParser::Parse(const std::string& pattern_spec, 39 ContentSettingsPattern::BuilderInterface* builder) { 40 if (pattern_spec == "*") { 41 builder->WithSchemeWildcard(); 42 builder->WithDomainWildcard(); 43 builder->WithPortWildcard(); 44 return; 45 } 46 47 // Initialize components for the individual patterns parts to empty 48 // sub-strings. 49 Component scheme_component; 50 Component host_component; 51 Component port_component; 52 Component path_component; 53 54 size_t start = 0; 55 size_t current_pos = 0; 56 57 if (pattern_spec.empty()) 58 return; 59 60 // Test if a scheme pattern is in the spec. 61 const std::string standard_scheme_separator(url::kStandardSchemeSeparator); 62 current_pos = pattern_spec.find(standard_scheme_separator, start); 63 if (current_pos != std::string::npos) { 64 scheme_component = Component(start, current_pos); 65 start = current_pos + standard_scheme_separator.size(); 66 current_pos = start; 67 } else { 68 current_pos = start; 69 } 70 71 if (start >= pattern_spec.size()) 72 return; // Bad pattern spec. 73 74 // Jump to the end of domain wildcards or an IPv6 addresses. IPv6 addresses 75 // contain ':'. So first move to the end of an IPv6 address befor searching 76 // for the ':' that separates the port form the host. 77 if (pattern_spec[current_pos] == '[') 78 current_pos = pattern_spec.find("]", start); 79 80 if (current_pos == std::string::npos) 81 return; // Bad pattern spec. 82 83 current_pos = pattern_spec.find(std::string(kUrlPortSeparator), current_pos); 84 if (current_pos == std::string::npos) { 85 // No port spec found 86 current_pos = pattern_spec.find(std::string(kUrlPathSeparator), start); 87 if (current_pos == std::string::npos) { 88 current_pos = pattern_spec.size(); 89 host_component = Component(start, current_pos - start); 90 } else { 91 // Pattern has a path spec. 92 host_component = Component(start, current_pos - start); 93 } 94 start = current_pos; 95 } else { 96 // Port spec found. 97 host_component = Component(start, current_pos - start); 98 start = current_pos + 1; 99 if (start < pattern_spec.size()) { 100 current_pos = pattern_spec.find(std::string(kUrlPathSeparator), start); 101 if (current_pos == std::string::npos) { 102 current_pos = pattern_spec.size(); 103 } 104 port_component = Component(start, current_pos - start); 105 start = current_pos; 106 } 107 } 108 109 current_pos = pattern_spec.size(); 110 if (start < current_pos) { 111 // Pattern has a path spec. 112 path_component = Component(start, current_pos - start); 113 } 114 115 // Set pattern parts. 116 std::string scheme; 117 if (scheme_component.IsNonEmpty()) { 118 scheme = pattern_spec.substr(scheme_component.start, scheme_component.len); 119 if (scheme == kSchemeWildcard) { 120 builder->WithSchemeWildcard(); 121 } else { 122 builder->WithScheme(scheme); 123 } 124 } else { 125 builder->WithSchemeWildcard(); 126 } 127 128 if (host_component.IsNonEmpty()) { 129 std::string host = pattern_spec.substr(host_component.start, 130 host_component.len); 131 if (host == kHostWildcard) { 132 builder->WithDomainWildcard(); 133 } else if (StartsWithASCII(host, kDomainWildcard, true)) { 134 host = host.substr(kDomainWildcardLength); 135 builder->WithDomainWildcard(); 136 builder->WithHost(host); 137 } else { 138 // If the host contains a wildcard symbol then it is invalid. 139 if (host.find(kHostWildcard) != std::string::npos) { 140 builder->Invalid(); 141 return; 142 } 143 builder->WithHost(host); 144 } 145 } 146 147 if (port_component.IsNonEmpty()) { 148 const std::string port = pattern_spec.substr(port_component.start, 149 port_component.len); 150 if (port == kPortWildcard) { 151 builder->WithPortWildcard(); 152 } else { 153 // Check if the port string represents a valid port. 154 for (size_t i = 0; i < port.size(); ++i) { 155 if (!IsAsciiDigit(port[i])) { 156 builder->Invalid(); 157 return; 158 } 159 } 160 // TODO(markusheintz): Check port range. 161 builder->WithPort(port); 162 } 163 } else { 164 if (!ContentSettingsPattern::IsNonWildcardDomainNonPortScheme(scheme) && 165 scheme != url::kFileScheme) 166 builder->WithPortWildcard(); 167 } 168 169 if (path_component.IsNonEmpty()) { 170 const std::string path = pattern_spec.substr(path_component.start, 171 path_component.len); 172 if (path.substr(1) == kPathWildcard) 173 builder->WithPathWildcard(); 174 else 175 builder->WithPath(path); 176 } 177} 178 179// static 180std::string PatternParser::ToString( 181 const ContentSettingsPattern::PatternParts& parts) { 182 // Return the most compact form to support legacy code and legacy pattern 183 // strings. 184 if (parts.is_scheme_wildcard && 185 parts.has_domain_wildcard && 186 parts.host.empty() && 187 parts.is_port_wildcard) 188 return "*"; 189 190 std::string str; 191 if (!parts.is_scheme_wildcard) 192 str += parts.scheme + url::kStandardSchemeSeparator; 193 194 if (parts.scheme == url::kFileScheme) { 195 if (parts.is_path_wildcard) 196 return str + kUrlPathSeparator + kPathWildcard; 197 else 198 return str + parts.path; 199 } 200 201 if (parts.has_domain_wildcard) { 202 if (parts.host.empty()) 203 str += kHostWildcard; 204 else 205 str += kDomainWildcard; 206 } 207 str += parts.host; 208 209 if (ContentSettingsPattern::IsNonWildcardDomainNonPortScheme(parts.scheme)) { 210 str += parts.path.empty() ? std::string(kUrlPathSeparator) : parts.path; 211 return str; 212 } 213 214 if (!parts.is_port_wildcard) { 215 str += std::string(kUrlPortSeparator) + parts.port; 216 } 217 218 return str; 219} 220 221} // namespace content_settings 222