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