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