1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25 #include "config.h"
26 #include "FormDataBuilder.h"
27
28 #include "CString.h"
29 #include "Document.h"
30 #include "Frame.h"
31 #include "FrameLoader.h"
32 #include "TextEncoding.h"
33
34 #include <limits>
35 #include <wtf/Assertions.h>
36 #include <wtf/RandomNumber.h>
37
38 namespace WebCore {
39
FormDataBuilder()40 FormDataBuilder::FormDataBuilder()
41 : m_isPostMethod(false)
42 , m_isMultiPartForm(false)
43 , m_encodingType("application/x-www-form-urlencoded")
44 {
45 }
46
~FormDataBuilder()47 FormDataBuilder::~FormDataBuilder()
48 {
49 }
50
parseEncodingType(const String & type)51 void FormDataBuilder::parseEncodingType(const String& type)
52 {
53 if (type.contains("multipart", false) || type.contains("form-data", false)) {
54 m_encodingType = "multipart/form-data";
55 m_isMultiPartForm = true;
56 } else if (type.contains("text", false) || type.contains("plain", false)) {
57 m_encodingType = "text/plain";
58 m_isMultiPartForm = false;
59 } else {
60 m_encodingType = "application/x-www-form-urlencoded";
61 m_isMultiPartForm = false;
62 }
63 }
64
parseMethodType(const String & type)65 void FormDataBuilder::parseMethodType(const String& type)
66 {
67 if (equalIgnoringCase(type, "post"))
68 m_isPostMethod = true;
69 else if (equalIgnoringCase(type, "get"))
70 m_isPostMethod = false;
71 }
72
dataEncoding(Document * document) const73 TextEncoding FormDataBuilder::dataEncoding(Document* document) const
74 {
75 String acceptCharset = m_acceptCharset;
76 acceptCharset.replace(',', ' ');
77
78 Vector<String> charsets;
79 acceptCharset.split(' ', charsets);
80
81 TextEncoding encoding;
82
83 Vector<String>::const_iterator end = charsets.end();
84 for (Vector<String>::const_iterator it = charsets.begin(); it != end; ++it) {
85 if ((encoding = TextEncoding(*it)).isValid())
86 return encoding;
87 }
88
89 if (Frame* frame = document->frame())
90 return frame->loader()->encoding();
91
92 return Latin1Encoding();
93 }
94
95 // Helper functions
append(Vector<char> & buffer,char string)96 static inline void append(Vector<char>& buffer, char string)
97 {
98 buffer.append(string);
99 }
100
append(Vector<char> & buffer,const char * string)101 static inline void append(Vector<char>& buffer, const char* string)
102 {
103 buffer.append(string, strlen(string));
104 }
105
append(Vector<char> & buffer,const CString & string)106 static inline void append(Vector<char>& buffer, const CString& string)
107 {
108 buffer.append(string.data(), string.length());
109 }
110
appendQuotedString(Vector<char> & buffer,const CString & string)111 static void appendQuotedString(Vector<char>& buffer, const CString& string)
112 {
113 // Append a string as a quoted value, escaping quotes and line breaks.
114 // FIXME: Is it correct to use percent escaping here? Other browsers do not encode these characters yet,
115 // so we should test popular servers to find out if there is an encoding form they can handle.
116 unsigned length = string.length();
117 for (unsigned i = 0; i < length; ++i) {
118 unsigned char c = string.data()[i];
119
120 switch (c) {
121 case 0x0a:
122 append(buffer, "%0A");
123 break;
124 case 0x0d:
125 append(buffer, "%0D");
126 break;
127 case '"':
128 append(buffer, "%22");
129 break;
130 default:
131 append(buffer, c);
132 }
133 }
134 }
135
generateUniqueBoundaryString()136 Vector<char> FormDataBuilder::generateUniqueBoundaryString()
137 {
138 Vector<char> boundary;
139
140 // The RFC 2046 spec says the alphanumeric characters plus the
141 // following characters are legal for boundaries: '()+_,-./:=?
142 // However the following characters, though legal, cause some sites
143 // to fail: (),./:=+
144 // Note that our algorithm makes it twice as much likely for 'A' or 'B'
145 // to appear in the boundary string, because 0x41 and 0x42 are present in
146 // the below array twice.
147 static const char alphaNumericEncodingMap[64] = {
148 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
149 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
150 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
151 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
152 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
153 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
154 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33,
155 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42
156 };
157
158 // Start with an informative prefix.
159 append(boundary, "----WebKitFormBoundary");
160
161 // Append 16 random 7bit ascii AlphaNumeric characters.
162 Vector<char> randomBytes;
163
164 for (unsigned i = 0; i < 4; ++i) {
165 unsigned randomness = static_cast<unsigned>(WTF::randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0));
166 randomBytes.append(alphaNumericEncodingMap[(randomness >> 24) & 0x3F]);
167 randomBytes.append(alphaNumericEncodingMap[(randomness >> 16) & 0x3F]);
168 randomBytes.append(alphaNumericEncodingMap[(randomness >> 8) & 0x3F]);
169 randomBytes.append(alphaNumericEncodingMap[randomness & 0x3F]);
170 }
171
172 boundary.append(randomBytes);
173 boundary.append(0); // Add a 0 at the end so we can use this as a C-style string.
174 return boundary;
175 }
176
beginMultiPartHeader(Vector<char> & buffer,const CString & boundary,const CString & name)177 void FormDataBuilder::beginMultiPartHeader(Vector<char>& buffer, const CString& boundary, const CString& name)
178 {
179 addBoundaryToMultiPartHeader(buffer, boundary);
180
181 // FIXME: This loses data irreversibly if the input name includes characters you can't encode
182 // in the website's character set.
183 append(buffer, "Content-Disposition: form-data; name=\"");
184 appendQuotedString(buffer, name);
185 append(buffer, '"');
186 }
187
addBoundaryToMultiPartHeader(Vector<char> & buffer,const CString & boundary,bool isLastBoundary)188 void FormDataBuilder::addBoundaryToMultiPartHeader(Vector<char>& buffer, const CString& boundary, bool isLastBoundary)
189 {
190 append(buffer, "--");
191 append(buffer, boundary);
192
193 if (isLastBoundary)
194 append(buffer, "--");
195
196 append(buffer, "\r\n");
197 }
198
addFilenameToMultiPartHeader(Vector<char> & buffer,const TextEncoding & encoding,const String & filename)199 void FormDataBuilder::addFilenameToMultiPartHeader(Vector<char>& buffer, const TextEncoding& encoding, const String& filename)
200 {
201 // FIXME: This loses data irreversibly if the filename includes characters you can't encode
202 // in the website's character set.
203 append(buffer, "; filename=\"");
204 appendQuotedString(buffer, encoding.encode(filename.characters(), filename.length(), QuestionMarksForUnencodables));
205 append(buffer, '"');
206 }
207
addContentTypeToMultiPartHeader(Vector<char> & buffer,const CString & mimeType)208 void FormDataBuilder::addContentTypeToMultiPartHeader(Vector<char>& buffer, const CString& mimeType)
209 {
210 append(buffer, "\r\nContent-Type: ");
211 append(buffer, mimeType);
212 }
213
finishMultiPartHeader(Vector<char> & buffer)214 void FormDataBuilder::finishMultiPartHeader(Vector<char>& buffer)
215 {
216 append(buffer, "\r\n\r\n");
217 }
218
addKeyValuePairAsFormData(Vector<char> & buffer,const CString & key,const CString & value)219 void FormDataBuilder::addKeyValuePairAsFormData(Vector<char>& buffer, const CString& key, const CString& value)
220 {
221 if (!buffer.isEmpty())
222 append(buffer, '&');
223
224 encodeStringAsFormData(buffer, key);
225 append(buffer, '=');
226 encodeStringAsFormData(buffer, value);
227 }
228
encodeStringAsFormData(Vector<char> & buffer,const CString & string)229 void FormDataBuilder::encodeStringAsFormData(Vector<char>& buffer, const CString& string)
230 {
231 static const char hexDigits[17] = "0123456789ABCDEF";
232
233 // Same safe characters as Netscape for compatibility.
234 static const char safeCharacters[] = "-._*";
235
236 // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
237 unsigned length = string.length();
238 for (unsigned i = 0; i < length; ++i) {
239 unsigned char c = string.data()[i];
240
241 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || strchr(safeCharacters, c))
242 append(buffer, c);
243 else if (c == ' ')
244 append(buffer, '+');
245 else if (c == '\n' || (c == '\r' && (i + 1 >= length || string.data()[i + 1] != '\n')))
246 append(buffer, "%0D%0A");
247 else if (c != '\r') {
248 append(buffer, '%');
249 append(buffer, hexDigits[c >> 4]);
250 append(buffer, hexDigits[c & 0xF]);
251 }
252 }
253 }
254
255 }
256