• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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