| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2011 Google Inc. All rights reserved. | 2 * Copyright (C) 2011 Google Inc. All rights reserved. |
| 3 * Copyright (C) 2012 Intel Corporation. All rights reserved. | 3 * Copyright (C) 2012 Intel Corporation. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are | 6 * modification, are permitted provided that the following conditions are |
| 7 * met: | 7 * met: |
| 8 * | 8 * |
| 9 * * Redistributions of source code must retain the above copyright | 9 * * Redistributions of source code must retain the above copyright |
| 10 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 */ | 30 */ |
| 31 | 31 |
| 32 #include "platform/network/ParsedContentType.h" | 32 #include "platform/network/ParsedContentType.h" |
| 33 | 33 |
| 34 #include "wtf/text/CString.h" | |
| 35 #include "wtf/text/StringBuilder.h" | 34 #include "wtf/text/StringBuilder.h" |
| 35 #include "wtf/text/StringView.h" |
| 36 | 36 |
| 37 namespace blink { | 37 namespace blink { |
| 38 | 38 |
| 39 using SubstringRange = ParsedContentType::SubstringRange; | 39 using Mode = ParsedContentType::Mode; |
| 40 | 40 |
| 41 namespace { | 41 namespace { |
| 42 | 42 |
| 43 void skipSpaces(const String& input, unsigned& startIndex) { | 43 bool isTokenCharacter(Mode mode, UChar c) { |
| 44 while (startIndex < input.length() && input[startIndex] == ' ') | 44 if (c >= 128) |
| 45 ++startIndex; | 45 return false; |
| 46 if (c < 0x20) |
| 47 return false; |
| 48 |
| 49 switch (c) { |
| 50 case ' ': |
| 51 case ';': |
| 52 case '"': |
| 53 return false; |
| 54 case '(': |
| 55 case ')': |
| 56 case '<': |
| 57 case '>': |
| 58 case '@': |
| 59 case ',': |
| 60 case ':': |
| 61 case '\\': |
| 62 case '/': |
| 63 case '[': |
| 64 case ']': |
| 65 case '?': |
| 66 case '=': |
| 67 return mode == Mode::Relaxed; |
| 68 default: |
| 69 return true; |
| 70 } |
| 46 } | 71 } |
| 47 | 72 |
| 48 SubstringRange parseParameterPart(const String& input, unsigned& startIndex) { | 73 bool consume(char c, const String& input, unsigned& index) { |
| 49 unsigned inputLength = input.length(); | 74 DCHECK_NE(c, ' '); |
| 50 unsigned tokenStart = startIndex; | 75 while (index < input.length() && input[index] == ' ') |
| 51 unsigned& tokenEnd = startIndex; | 76 ++index; |
| 52 | 77 |
| 53 if (tokenEnd >= inputLength) | 78 if (index < input.length() && input[index] == c) { |
| 54 return SubstringRange(); | 79 ++index; |
| 55 | 80 return true; |
| 56 bool quoted = input[tokenStart] == '\"'; | |
| 57 bool escape = false; | |
| 58 | |
| 59 while (tokenEnd < inputLength) { | |
| 60 UChar c = input[tokenEnd]; | |
| 61 if (quoted && tokenStart != tokenEnd && c == '\"' && !escape) | |
| 62 return SubstringRange(tokenStart + 1, tokenEnd++ - tokenStart - 1); | |
| 63 if (!quoted && (c == ';' || c == '=')) | |
| 64 return SubstringRange(tokenStart, tokenEnd - tokenStart); | |
| 65 escape = !escape && c == '\\'; | |
| 66 ++tokenEnd; | |
| 67 } | 81 } |
| 68 | 82 return false; |
| 69 if (quoted) | |
| 70 return SubstringRange(); | |
| 71 return SubstringRange(tokenStart, tokenEnd - tokenStart); | |
| 72 } | 83 } |
| 73 | 84 |
| 74 String substringForRange(const String& string, const SubstringRange& range) { | 85 bool consumeToken(Mode mode, |
| 75 return string.substring(range.first, range.second); | 86 const String& input, |
| 87 unsigned& index, |
| 88 StringView& output) { |
| 89 DCHECK(output.isNull()); |
| 90 |
| 91 while (index < input.length() && input[index] == ' ') |
| 92 ++index; |
| 93 |
| 94 auto start = index; |
| 95 while (index < input.length() && isTokenCharacter(mode, input[index])) |
| 96 ++index; |
| 97 |
| 98 if (start == index) |
| 99 return false; |
| 100 |
| 101 output = StringView(input, start, index - start); |
| 102 return true; |
| 103 } |
| 104 |
| 105 bool consumeQuotedString(const String& input, unsigned& index, String& output) { |
| 106 StringBuilder builder; |
| 107 DCHECK_EQ('"', input[index]); |
| 108 ++index; |
| 109 while (index < input.length()) { |
| 110 if (input[index] == '\\') { |
| 111 ++index; |
| 112 if (index == input.length()) |
| 113 return false; |
| 114 builder.append(input[index]); |
| 115 ++index; |
| 116 continue; |
| 117 } |
| 118 if (input[index] == '"') { |
| 119 output = builder.toString(); |
| 120 ++index; |
| 121 return true; |
| 122 } |
| 123 builder.append(input[index]); |
| 124 ++index; |
| 125 } |
| 126 return false; |
| 127 } |
| 128 |
| 129 bool consumeTokenOrQuotedString(Mode mode, |
| 130 const String& input, |
| 131 unsigned& index, |
| 132 String& output) { |
| 133 while (index < input.length() && input[index] == ' ') |
| 134 ++index; |
| 135 if (input.length() == index) |
| 136 return false; |
| 137 if (input[index] == '"') { |
| 138 return consumeQuotedString(input, index, output); |
| 139 } |
| 140 StringView view; |
| 141 auto result = consumeToken(mode, input, index, view); |
| 142 output = view.toString(); |
| 143 return result; |
| 144 } |
| 145 |
| 146 bool isEnd(const String& input, unsigned index) { |
| 147 while (index < input.length()) { |
| 148 if (input[index] != ' ') |
| 149 return false; |
| 150 ++index; |
| 151 } |
| 152 return true; |
| 76 } | 153 } |
| 77 | 154 |
| 78 } // namespace | 155 } // namespace |
| 79 | 156 |
| 80 ParsedContentType::ParsedContentType(const String& contentType) { | 157 ParsedContentType::ParsedContentType(const String& contentType, Mode mode) |
| 81 if (contentType.contains('\r') || contentType.contains('\n')) | 158 : m_mode(mode) { |
| 82 m_isValid = false; | 159 m_isValid = parse(contentType); |
| 83 else | |
| 84 m_isValid = parse(contentType.stripWhiteSpace()); | |
| 85 } | 160 } |
| 86 | 161 |
| 87 String ParsedContentType::charset() const { | 162 String ParsedContentType::charset() const { |
| 88 return parameterValueForName("charset"); | 163 return parameterValueForName("charset"); |
| 89 } | 164 } |
| 90 | 165 |
| 91 String ParsedContentType::parameterValueForName(const String& name) const { | 166 String ParsedContentType::parameterValueForName(const String& name) const { |
| 92 return m_parameters.get(name); | 167 return m_parameters.get(name); |
| 93 } | 168 } |
| 94 | 169 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 // or tspecials> | 212 // or tspecials> |
| 138 // | 213 // |
| 139 // tspecials := "(" / ")" / "<" / ">" / "@" / | 214 // tspecials := "(" / ")" / "<" / ">" / "@" / |
| 140 // "," / ";" / ":" / "\" / <"> | 215 // "," / ";" / ":" / "\" / <"> |
| 141 // "/" / "[" / "]" / "?" / "=" | 216 // "/" / "[" / "]" / "?" / "=" |
| 142 // ; Must be in quoted-string, | 217 // ; Must be in quoted-string, |
| 143 // ; to use within parameter values | 218 // ; to use within parameter values |
| 144 | 219 |
| 145 bool ParsedContentType::parse(const String& contentType) { | 220 bool ParsedContentType::parse(const String& contentType) { |
| 146 unsigned index = 0; | 221 unsigned index = 0; |
| 147 unsigned contentTypeLength = contentType.length(); | 222 |
| 148 skipSpaces(contentType, index); | 223 StringView type, subtype; |
| 149 if (index >= contentTypeLength) { | 224 if (!consumeToken(Mode::Normal, contentType, index, type)) { |
| 150 DVLOG(1) << "Invalid Content-Type string '" << contentType << "'"; | 225 DVLOG(1) << "Failed to find `type' in '" << contentType << "'"; |
| 226 return false; |
| 227 } |
| 228 if (!consume('/', contentType, index)) { |
| 229 DVLOG(1) << "Failed to find '/' in '" << contentType << "'"; |
| 230 return false; |
| 231 } |
| 232 if (!consumeToken(Mode::Normal, contentType, index, subtype)) { |
| 233 DVLOG(1) << "Failed to find `type' in '" << contentType << "'"; |
| 151 return false; | 234 return false; |
| 152 } | 235 } |
| 153 | 236 |
| 154 // There should not be any quoted strings until we reach the parameters. | 237 StringBuilder builder; |
| 155 size_t semiColonIndex = contentType.find(';', index); | 238 builder.append(type); |
| 156 if (semiColonIndex == kNotFound) { | 239 builder.append('/'); |
| 157 m_mimeType = | 240 builder.append(subtype); |
| 158 substringForRange(contentType, | 241 m_mimeType = builder.toString(); |
| 159 SubstringRange(index, contentTypeLength - index)) | |
| 160 .stripWhiteSpace(); | |
| 161 return true; | |
| 162 } | |
| 163 | 242 |
| 164 m_mimeType = substringForRange(contentType, | 243 KeyValuePairs map; |
| 165 SubstringRange(index, semiColonIndex - index)) | 244 while (!isEnd(contentType, index)) { |
| 166 .stripWhiteSpace(); | 245 if (!consume(';', contentType, index)) { |
| 167 index = semiColonIndex + 1; | 246 DVLOG(1) << "Failed to find ';'"; |
| 168 do { | 247 return false; |
| 169 skipSpaces(contentType, index); | 248 } |
| 170 SubstringRange keyRange = parseParameterPart(contentType, index); | 249 |
| 171 if (!keyRange.second || index >= contentTypeLength) { | 250 StringView key; |
| 251 String value; |
| 252 if (!consumeToken(Mode::Normal, contentType, index, key)) { |
| 172 DVLOG(1) << "Invalid Content-Type parameter name. (at " << index << ")"; | 253 DVLOG(1) << "Invalid Content-Type parameter name. (at " << index << ")"; |
| 173 return false; | 254 return false; |
| 174 } | 255 } |
| 175 | 256 if (!consume('=', contentType, index)) { |
| 176 // Should we tolerate spaces here? | 257 DVLOG(1) << "Failed to find '='"; |
| 177 if (contentType[index++] != '=' || index >= contentTypeLength) { | |
| 178 DVLOG(1) << "Invalid Content-Type malformed parameter (at " << index | |
| 179 << ")."; | |
| 180 return false; | 258 return false; |
| 181 } | 259 } |
| 182 | 260 if (!consumeTokenOrQuotedString(m_mode, contentType, index, value)) { |
| 183 // Should we tolerate spaces here? | |
| 184 SubstringRange valueRange = parseParameterPart(contentType, index); | |
| 185 | |
| 186 if (!valueRange.second) { | |
| 187 DVLOG(1) << "Invalid Content-Type, invalid parameter value (at " << index | 261 DVLOG(1) << "Invalid Content-Type, invalid parameter value (at " << index |
| 188 << ", for '" | 262 << ", for '" << key.toString() << "')."; |
| 189 << substringForRange(contentType, keyRange).stripWhiteSpace() | |
| 190 << "')."; | |
| 191 return false; | 263 return false; |
| 192 } | 264 } |
| 193 | 265 map.set(key.toString(), value); |
| 194 // Should we tolerate spaces here? | 266 } |
| 195 if (index < contentTypeLength && contentType[index++] != ';') { | 267 m_parameters = std::move(map); |
| 196 DVLOG(1) << "Invalid Content-Type, invalid character at the end of " | |
| 197 "key/value parameter (at " | |
| 198 << index << ")."; | |
| 199 return false; | |
| 200 } | |
| 201 | |
| 202 m_parameters.set(substringForRange(contentType, keyRange), | |
| 203 substringForRange(contentType, valueRange)); | |
| 204 } while (index < contentTypeLength); | |
| 205 | |
| 206 return true; | 268 return true; |
| 207 } | 269 } |
| 208 | 270 |
| 209 } // namespace blink | 271 } // namespace blink |
| OLD | NEW |