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