Home | History | Annotate | Download | only in network
      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