6888b2cceb4b97cca9551837504e51cf6631088a
[WebKit-https.git] / Source / WebCore / platform / network / FormDataBuilder.cpp
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 "Blob.h"
29 #include "TextEncoding.h"
30 #include <limits>
31 #include <wtf/Assertions.h>
32 #include <wtf/HexNumber.h>
33 #include <wtf/RandomNumber.h>
34 #include <wtf/text/CString.h>
35 #include <wtf/text/StringView.h>
36
37 namespace WebCore {
38
39 // Helper functions
40 static inline void append(Vector<char>& buffer, char string)
41 {
42     buffer.append(string);
43 }
44
45 static inline void append(Vector<char>& buffer, const char* string)
46 {
47     buffer.append(string, strlen(string));
48 }
49
50 static inline void append(Vector<char>& buffer, const CString& string)
51 {
52     buffer.append(string.data(), string.length());
53 }
54
55 static void appendQuotedString(Vector<char>& buffer, const CString& string)
56 {
57     // Append a string as a quoted value, escaping quotes and line breaks.
58     // FIXME: Is it correct to use percent escaping here? Other browsers do not encode these characters yet,
59     // so we should test popular servers to find out if there is an encoding form they can handle.
60     size_t length = string.length();
61     for (size_t i = 0; i < length; ++i) {
62         char c = string.data()[i];
63         switch (c) {
64         case 0xA:
65             append(buffer, "%0A");
66             break;
67         case 0xD:
68             append(buffer, "%0D");
69             break;
70         case '"':
71             append(buffer, "%22");
72             break;
73         default:
74             append(buffer, c);
75         }
76     }
77 }
78
79 Vector<char> FormDataBuilder::generateUniqueBoundaryString()
80 {
81     Vector<char> boundary;
82
83     // The RFC 2046 spec says the alphanumeric characters plus the
84     // following characters are legal for boundaries:  '()+_,-./:=?
85     // However the following characters, though legal, cause some sites
86     // to fail: (),./:=+
87     // Note that our algorithm makes it twice as much likely for 'A' or 'B'
88     // to appear in the boundary string, because 0x41 and 0x42 are present in
89     // the below array twice.
90     static const char alphaNumericEncodingMap[64] = {
91         0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
92         0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
93         0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
94         0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
95         0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
96         0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
97         0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33,
98         0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42
99     };
100
101     // Start with an informative prefix.
102     append(boundary, "----WebKitFormBoundary");
103
104     // Append 16 random 7bit ascii AlphaNumeric characters.
105     Vector<char> randomBytes;
106
107     for (unsigned i = 0; i < 4; ++i) {
108         unsigned randomness = static_cast<unsigned>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0));
109         randomBytes.append(alphaNumericEncodingMap[(randomness >> 24) & 0x3F]);
110         randomBytes.append(alphaNumericEncodingMap[(randomness >> 16) & 0x3F]);
111         randomBytes.append(alphaNumericEncodingMap[(randomness >> 8) & 0x3F]);
112         randomBytes.append(alphaNumericEncodingMap[randomness & 0x3F]);
113     }
114
115     boundary.appendVector(randomBytes);
116     boundary.append(0); // Add a 0 at the end so we can use this as a C-style string.
117     return boundary;
118 }
119
120 void FormDataBuilder::beginMultiPartHeader(Vector<char>& buffer, const CString& boundary, const CString& name)
121 {
122     addBoundaryToMultiPartHeader(buffer, boundary);
123
124     // FIXME: This loses data irreversibly if the input name includes characters you can't encode
125     // in the website's character set.
126     append(buffer, "Content-Disposition: form-data; name=\"");
127     appendQuotedString(buffer, name);
128     append(buffer, '"');
129 }
130
131 void FormDataBuilder::addBoundaryToMultiPartHeader(Vector<char>& buffer, const CString& boundary, bool isLastBoundary)
132 {
133     append(buffer, "--");
134     append(buffer, boundary);
135
136     if (isLastBoundary)
137         append(buffer, "--");
138
139     append(buffer, "\r\n");
140 }
141
142 void FormDataBuilder::addFilenameToMultiPartHeader(Vector<char>& buffer, const TextEncoding& encoding, const String& filename)
143 {
144     // FIXME: This loses data irreversibly if the filename includes characters you can't encode
145     // in the website's character set.
146     append(buffer, "; filename=\"");
147     appendQuotedString(buffer, encoding.encode(filename, QuestionMarksForUnencodables));
148     append(buffer, '"');
149 }
150
151 void FormDataBuilder::addContentTypeToMultiPartHeader(Vector<char>& buffer, const CString& mimeType)
152 {
153     ASSERT(Blob::isNormalizedContentType(mimeType));
154     append(buffer, "\r\nContent-Type: ");
155     append(buffer, mimeType);
156 }
157
158 void FormDataBuilder::finishMultiPartHeader(Vector<char>& buffer)
159 {
160     append(buffer, "\r\n\r\n");
161 }
162
163 void FormDataBuilder::addKeyValuePairAsFormData(Vector<char>& buffer, const CString& key, const CString& value, FormData::EncodingType encodingType)
164 {
165     if (encodingType == FormData::TextPlain) {
166         if (!buffer.isEmpty())
167             append(buffer, "\r\n");
168         append(buffer, key);
169         append(buffer, '=');
170         append(buffer, value);
171     } else {
172         if (!buffer.isEmpty())
173             append(buffer, '&');
174         encodeStringAsFormData(buffer, key);
175         append(buffer, '=');
176         encodeStringAsFormData(buffer, value);
177     }
178 }
179
180 void FormDataBuilder::encodeStringAsFormData(Vector<char>& buffer, const CString& string)
181 {
182     // Same safe characters as Netscape for compatibility.
183     static const char safeCharacters[] = "-._*";
184
185     // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
186     unsigned length = string.length();
187     for (unsigned i = 0; i < length; ++i) {
188         unsigned char c = string.data()[i];
189
190         if (isASCIIAlphanumeric(c) || strchr(safeCharacters, c))
191             append(buffer, c);
192         else if (c == ' ')
193             append(buffer, '+');
194         else if (c == '\n' || (c == '\r' && (i + 1 >= length || string.data()[i + 1] != '\n')))
195             append(buffer, "%0D%0A");
196         else if (c != '\r') {
197             append(buffer, '%');
198             appendByteAsHex(c, buffer);
199         }
200     }
201 }
202
203 }