• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2012 Intel Corporation. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
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.
30  */
31 
32 #include "config.h"
33 #include "platform/network/ParsedContentType.h"
34 
35 #include "wtf/text/CString.h"
36 #include "wtf/text/StringBuilder.h"
37 
38 namespace WebCore {
39 
40 class DummyParsedContentType {
41 public:
setContentType(const SubstringRange &) const42     void setContentType(const SubstringRange&) const { }
setContentTypeParameter(const SubstringRange &,const SubstringRange &) const43     void setContentTypeParameter(const SubstringRange&, const SubstringRange&) const { }
44 };
45 
skipSpaces(const String & input,unsigned & startIndex)46 static void skipSpaces(const String& input, unsigned& startIndex)
47 {
48     while (startIndex < input.length() && input[startIndex] == ' ')
49         ++startIndex;
50 }
51 
parseParameterPart(const String & input,unsigned & startIndex)52 static SubstringRange parseParameterPart(const String& input, unsigned& startIndex)
53 {
54     unsigned inputLength = input.length();
55     unsigned tokenStart = startIndex;
56     unsigned& tokenEnd = startIndex;
57 
58     if (tokenEnd >= inputLength)
59         return SubstringRange();
60 
61     bool quoted = input[tokenStart] == '\"';
62     bool escape = false;
63 
64     while (tokenEnd < inputLength) {
65         UChar c = input[tokenEnd];
66         if (quoted && tokenStart != tokenEnd && c == '\"' && !escape)
67             return SubstringRange(tokenStart + 1, tokenEnd++ - tokenStart - 1);
68         if (!quoted && (c == ';' || c == '='))
69             return SubstringRange(tokenStart, tokenEnd - tokenStart);
70         escape = !escape && c == '\\';
71         ++tokenEnd;
72     }
73 
74     if (quoted)
75         return SubstringRange();
76     return SubstringRange(tokenStart, tokenEnd - tokenStart);
77 }
78 
substringForRange(const String & string,const SubstringRange & range)79 static String substringForRange(const String& string, const SubstringRange& range)
80 {
81     return string.substring(range.first, range.second);
82 }
83 
84 // From http://tools.ietf.org/html/rfc2045#section-5.1:
85 //
86 // content := "Content-Type" ":" type "/" subtype
87 //            *(";" parameter)
88 //            ; Matching of media type and subtype
89 //            ; is ALWAYS case-insensitive.
90 //
91 // type := discrete-type / composite-type
92 //
93 // discrete-type := "text" / "image" / "audio" / "video" /
94 //                  "application" / extension-token
95 //
96 // composite-type := "message" / "multipart" / extension-token
97 //
98 // extension-token := ietf-token / x-token
99 //
100 // ietf-token := <An extension token defined by a
101 //                standards-track RFC and registered
102 //                with IANA.>
103 //
104 // x-token := <The two characters "X-" or "x-" followed, with
105 //             no intervening white space, by any token>
106 //
107 // subtype := extension-token / iana-token
108 //
109 // iana-token := <A publicly-defined extension token. Tokens
110 //                of this form must be registered with IANA
111 //                as specified in RFC 2048.>
112 //
113 // parameter := attribute "=" value
114 //
115 // attribute := token
116 //              ; Matching of attributes
117 //              ; is ALWAYS case-insensitive.
118 //
119 // value := token / quoted-string
120 //
121 // token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
122 //             or tspecials>
123 //
124 // tspecials :=  "(" / ")" / "<" / ">" / "@" /
125 //               "," / ";" / ":" / "\" / <">
126 //               "/" / "[" / "]" / "?" / "="
127 //               ; Must be in quoted-string,
128 //               ; to use within parameter values
129 
130 template <class ReceiverType>
parseContentType(const String & contentType,ReceiverType & receiver)131 bool parseContentType(const String& contentType, ReceiverType& receiver)
132 {
133     unsigned index = 0;
134     unsigned contentTypeLength = contentType.length();
135     skipSpaces(contentType, index);
136     if (index >= contentTypeLength)  {
137         WTF_LOG_ERROR("Invalid Content-Type string '%s'", contentType.ascii().data());
138         return false;
139     }
140 
141     // There should not be any quoted strings until we reach the parameters.
142     size_t semiColonIndex = contentType.find(';', index);
143     if (semiColonIndex == kNotFound) {
144         receiver.setContentType(SubstringRange(index, contentTypeLength - index));
145         return true;
146     }
147 
148     receiver.setContentType(SubstringRange(index, semiColonIndex - index));
149     index = semiColonIndex + 1;
150     while (true) {
151         skipSpaces(contentType, index);
152         SubstringRange keyRange = parseParameterPart(contentType, index);
153         if (!keyRange.second || index >= contentTypeLength) {
154             WTF_LOG_ERROR("Invalid Content-Type parameter name. (at %i)", index);
155             return false;
156         }
157 
158         // Should we tolerate spaces here?
159         if (contentType[index++] != '=' || index >= contentTypeLength) {
160             WTF_LOG_ERROR("Invalid Content-Type malformed parameter (at %i).", index);
161             return false;
162         }
163 
164         // Should we tolerate spaces here?
165         SubstringRange valueRange = parseParameterPart(contentType, index);
166 
167         if (!valueRange.second) {
168             WTF_LOG_ERROR("Invalid Content-Type, invalid parameter value (at %i, for '%s').", index, substringForRange(contentType, keyRange).stripWhiteSpace().ascii().data());
169             return false;
170         }
171 
172         // Should we tolerate spaces here?
173         if (index < contentTypeLength && contentType[index++] != ';') {
174             WTF_LOG_ERROR("Invalid Content-Type, invalid character at the end of key/value parameter (at %i).", index);
175             return false;
176         }
177 
178         receiver.setContentTypeParameter(keyRange, valueRange);
179 
180         if (index >= contentTypeLength)
181             return true;
182     }
183 
184     return true;
185 }
186 
isValidContentType(const String & contentType)187 bool isValidContentType(const String& contentType)
188 {
189     if (contentType.contains('\r') || contentType.contains('\n'))
190         return false;
191 
192     DummyParsedContentType parsedContentType = DummyParsedContentType();
193     return parseContentType<DummyParsedContentType>(contentType, parsedContentType);
194 }
195 
ParsedContentType(const String & contentType)196 ParsedContentType::ParsedContentType(const String& contentType)
197     : m_contentType(contentType.stripWhiteSpace())
198 {
199     parseContentType<ParsedContentType>(m_contentType, *this);
200 }
201 
charset() const202 String ParsedContentType::charset() const
203 {
204     return parameterValueForName("charset");
205 }
206 
parameterValueForName(const String & name) const207 String ParsedContentType::parameterValueForName(const String& name) const
208 {
209     return m_parameters.get(name);
210 }
211 
parameterCount() const212 size_t ParsedContentType::parameterCount() const
213 {
214     return m_parameters.size();
215 }
216 
setContentType(const SubstringRange & contentRange)217 void ParsedContentType::setContentType(const SubstringRange& contentRange)
218 {
219     m_mimeType = substringForRange(m_contentType, contentRange).stripWhiteSpace();
220 }
221 
setContentTypeParameter(const SubstringRange & key,const SubstringRange & value)222 void ParsedContentType::setContentTypeParameter(const SubstringRange& key, const SubstringRange& value)
223 {
224     m_parameters.set(substringForRange(m_contentType, key), substringForRange(m_contentType, value));
225 }
226 
227 }
228