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