• 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 
generateUniqueBoundaryString()111 Vector<char> FormDataBuilder::generateUniqueBoundaryString()
112 {
113     Vector<char> boundary;
114 
115     // The RFC 2046 spec says the alphanumeric characters plus the
116     // following characters are legal for boundaries:  '()+_,-./:=?
117     // However the following characters, though legal, cause some sites
118     // to fail: (),./:= (http://bugs.webkit.org/show_bug.cgi?id=13352)
119     static const char alphaNumericEncodingMap[64] = {
120         0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
121         0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
122         0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
123         0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
124         0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
125         0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
126         0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33,
127         0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x41
128         // FIXME <rdar://problem/5252577> gmail does not accept legal characters in the form boundary
129         // As stated above, some legal characters cause, sites to fail. Specifically
130         // the / character which was the last character in the above array. I have
131         // replaced the last character with another character already in the array
132         // (notice the first and last values are both 0x41, A). Instead of picking
133         // another unique legal character for boundary strings that, because it has
134         // never been tested, may or may not break other sites, I simply
135         // replaced / with A.  This means A is twice as likely to occur in our boundary
136         // strings than any other character but I think this is fine for the time being.
137         // The FIXME here is about restoring the / character once the aforementioned
138         // radar has been resolved.
139     };
140 
141     // Start with an informative prefix.
142     append(boundary, "----WebKitFormBoundary");
143 
144     // Append 16 random 7bit ascii AlphaNumeric characters.
145     Vector<char> randomBytes;
146 
147     for (unsigned i = 0; i < 4; ++i) {
148         unsigned randomness = static_cast<unsigned>(WTF::randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0));
149         randomBytes.append(alphaNumericEncodingMap[(randomness >> 24) & 0x3F]);
150         randomBytes.append(alphaNumericEncodingMap[(randomness >> 16) & 0x3F]);
151         randomBytes.append(alphaNumericEncodingMap[(randomness >> 8) & 0x3F]);
152         randomBytes.append(alphaNumericEncodingMap[randomness & 0x3F]);
153     }
154 
155     boundary.append(randomBytes);
156     boundary.append(0); // Add a 0 at the end so we can use this as a C-style string.
157     return boundary;
158 }
159 
beginMultiPartHeader(Vector<char> & buffer,const CString & boundary,const CString & name)160 void FormDataBuilder::beginMultiPartHeader(Vector<char>& buffer, const CString& boundary, const CString& name)
161 {
162     addBoundaryToMultiPartHeader(buffer, boundary);
163 
164     append(buffer, "Content-Disposition: form-data; name=\"");
165     append(buffer, name);
166     append(buffer, '"');
167 }
168 
addBoundaryToMultiPartHeader(Vector<char> & buffer,const CString & boundary,bool isLastBoundary)169 void FormDataBuilder::addBoundaryToMultiPartHeader(Vector<char>& buffer, const CString& boundary, bool isLastBoundary)
170 {
171     append(buffer, "--");
172     append(buffer, boundary);
173 
174     if (isLastBoundary)
175         append(buffer, "--");
176 
177     append(buffer, "\r\n");
178 }
179 
addFilenameToMultiPartHeader(Vector<char> & buffer,const TextEncoding & encoding,const String & filename)180 void FormDataBuilder::addFilenameToMultiPartHeader(Vector<char>& buffer, const TextEncoding& encoding, const String& filename)
181 {
182     // FIXME: This won't work if the filename includes a " mark,
183     // or control characters like CR or LF. This also does strange
184     // things if the filename includes characters you can't encode
185     // in the website's character set.
186     append(buffer, "; filename=\"");
187     append(buffer, encoding.encode(filename.characters(), filename.length(), QuestionMarksForUnencodables));
188     append(buffer, '"');
189 }
190 
addContentTypeToMultiPartHeader(Vector<char> & buffer,const CString & mimeType)191 void FormDataBuilder::addContentTypeToMultiPartHeader(Vector<char>& buffer, const CString& mimeType)
192 {
193     append(buffer, "\r\nContent-Type: ");
194     append(buffer, mimeType);
195 }
196 
finishMultiPartHeader(Vector<char> & buffer)197 void FormDataBuilder::finishMultiPartHeader(Vector<char>& buffer)
198 {
199     append(buffer, "\r\n\r\n");
200 }
201 
addKeyValuePairAsFormData(Vector<char> & buffer,const CString & key,const CString & value)202 void FormDataBuilder::addKeyValuePairAsFormData(Vector<char>& buffer, const CString& key, const CString& value)
203 {
204     if (!buffer.isEmpty())
205         append(buffer, '&');
206 
207     encodeStringAsFormData(buffer, key);
208     append(buffer, '=');
209     encodeStringAsFormData(buffer, value);
210 }
211 
encodeStringAsFormData(Vector<char> & buffer,const CString & string)212 void FormDataBuilder::encodeStringAsFormData(Vector<char>& buffer, const CString& string)
213 {
214     static const char hexDigits[17] = "0123456789ABCDEF";
215 
216     // Same safe characters as Netscape for compatibility.
217     static const char safeCharacters[] = "-._*";
218 
219     // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
220     unsigned length = string.length();
221     for (unsigned i = 0; i < length; ++i) {
222         unsigned char c = string.data()[i];
223 
224         if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || strchr(safeCharacters, c))
225             append(buffer, c);
226         else if (c == ' ')
227             append(buffer, '+');
228         else if (c == '\n' || (c == '\r' && (i + 1 >= length || string.data()[i + 1] != '\n')))
229             append(buffer, "%0D%0A");
230         else if (c != '\r') {
231             append(buffer, '%');
232             append(buffer, hexDigits[c >> 4]);
233             append(buffer, hexDigits[c & 0xF]);
234         }
235     }
236 }
237 
238 }
239