1/*
2 * Copyright (C) 2004, 2006, 2008, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Google Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21#include "config.h"
22
23#include "FormData.h"
24
25#include "BlobData.h"
26#include "BlobURL.h"
27#include "Chrome.h"
28#include "ChromeClient.h"
29#include "Document.h"
30#include "File.h"
31#include "FileSystem.h"
32#include "FormDataBuilder.h"
33#include "FormDataList.h"
34#include "MIMETypeRegistry.h"
35#include "Page.h"
36#include "TextEncoding.h"
37#include <wtf/Decoder.h>
38#include <wtf/Encoder.h>
39
40namespace WebCore {
41
42inline FormData::FormData()
43    : m_identifier(0)
44    , m_hasGeneratedFiles(false)
45    , m_alwaysStream(false)
46{
47}
48
49inline FormData::FormData(const FormData& data)
50    : RefCounted<FormData>()
51    , m_elements(data.m_elements)
52    , m_identifier(data.m_identifier)
53    , m_hasGeneratedFiles(false)
54    , m_alwaysStream(false)
55{
56    // We shouldn't be copying FormData that hasn't already removed its generated files
57    // but just in case, make sure the new FormData is ready to generate its own files.
58    if (data.m_hasGeneratedFiles) {
59        size_t n = m_elements.size();
60        for (size_t i = 0; i < n; ++i) {
61            FormDataElement& e = m_elements[i];
62            if (e.m_type == FormDataElement::encodedFile)
63                e.m_generatedFilename = String();
64        }
65    }
66}
67
68FormData::~FormData()
69{
70    // This cleanup should've happened when the form submission finished.
71    // Just in case, let's assert, and do the cleanup anyway in release builds.
72    ASSERT(!m_hasGeneratedFiles);
73    removeGeneratedFilesIfNeeded();
74}
75
76PassRefPtr<FormData> FormData::create()
77{
78    return adoptRef(new FormData);
79}
80
81PassRefPtr<FormData> FormData::create(const void* data, size_t size)
82{
83    RefPtr<FormData> result = create();
84    result->appendData(data, size);
85    return result.release();
86}
87
88PassRefPtr<FormData> FormData::create(const CString& string)
89{
90    RefPtr<FormData> result = create();
91    result->appendData(string.data(), string.length());
92    return result.release();
93}
94
95PassRefPtr<FormData> FormData::create(const Vector<char>& vector)
96{
97    RefPtr<FormData> result = create();
98    result->appendData(vector.data(), vector.size());
99    return result.release();
100}
101
102PassRefPtr<FormData> FormData::create(const FormDataList& list, const TextEncoding& encoding)
103{
104    RefPtr<FormData> result = create();
105    result->appendKeyValuePairItems(list, encoding, false, 0);
106    return result.release();
107}
108
109PassRefPtr<FormData> FormData::createMultiPart(const FormDataList& list, const TextEncoding& encoding, Document* document)
110{
111    RefPtr<FormData> result = create();
112    result->appendKeyValuePairItems(list, encoding, true, document);
113    return result.release();
114}
115
116PassRefPtr<FormData> FormData::copy() const
117{
118    return adoptRef(new FormData(*this));
119}
120
121PassRefPtr<FormData> FormData::deepCopy() const
122{
123    RefPtr<FormData> formData(create());
124
125    formData->m_alwaysStream = m_alwaysStream;
126
127    size_t n = m_elements.size();
128    formData->m_elements.reserveInitialCapacity(n);
129    for (size_t i = 0; i < n; ++i) {
130        const FormDataElement& e = m_elements[i];
131        switch (e.m_type) {
132        case FormDataElement::data:
133            formData->m_elements.append(FormDataElement(e.m_data));
134            break;
135        case FormDataElement::encodedFile:
136#if ENABLE(BLOB)
137            formData->m_elements.append(FormDataElement(e.m_filename, e.m_fileStart, e.m_fileLength, e.m_expectedFileModificationTime, e.m_shouldGenerateFile));
138#else
139            formData->m_elements.append(FormDataElement(e.m_filename, e.m_shouldGenerateFile));
140#endif
141            break;
142#if ENABLE(BLOB)
143        case FormDataElement::encodedBlob:
144            formData->m_elements.append(FormDataElement(e.m_blobURL));
145            break;
146#endif
147        }
148    }
149    return formData.release();
150}
151
152void FormData::appendData(const void* data, size_t size)
153{
154    if (m_elements.isEmpty() || m_elements.last().m_type != FormDataElement::data)
155        m_elements.append(FormDataElement());
156    FormDataElement& e = m_elements.last();
157    size_t oldSize = e.m_data.size();
158    e.m_data.grow(oldSize + size);
159    memcpy(e.m_data.data() + oldSize, data, size);
160}
161
162void FormData::appendFile(const String& filename, bool shouldGenerateFile)
163{
164#if ENABLE(BLOB)
165    m_elements.append(FormDataElement(filename, 0, BlobDataItem::toEndOfFile, BlobDataItem::doNotCheckFileChange, shouldGenerateFile));
166#else
167    m_elements.append(FormDataElement(filename, shouldGenerateFile));
168#endif
169}
170
171#if ENABLE(BLOB)
172void FormData::appendFileRange(const String& filename, long long start, long long length, double expectedModificationTime, bool shouldGenerateFile)
173{
174    m_elements.append(FormDataElement(filename, start, length, expectedModificationTime, shouldGenerateFile));
175}
176
177void FormData::appendBlob(const KURL& blobURL)
178{
179    m_elements.append(FormDataElement(blobURL));
180}
181#endif
182
183void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncoding& encoding, bool isMultiPartForm, Document* document)
184{
185    if (isMultiPartForm)
186        m_boundary = FormDataBuilder::generateUniqueBoundaryString();
187
188    Vector<char> encodedData;
189
190    const Vector<FormDataList::Item>& items = list.items();
191    size_t formDataListSize = items.size();
192    ASSERT(!(formDataListSize % 2));
193    for (size_t i = 0; i < formDataListSize; i += 2) {
194        const FormDataList::Item& key = items[i];
195        const FormDataList::Item& value = items[i + 1];
196        if (isMultiPartForm) {
197            Vector<char> header;
198            FormDataBuilder::beginMultiPartHeader(header, m_boundary.data(), key.data());
199
200            bool shouldGenerateFile = false;
201
202            // If the current type is blob, then we also need to include the filename
203            if (value.blob()) {
204                String name;
205                if (value.blob()->isFile()) {
206                    // For file blob, use the filename (or relative path if it is present) as the name.
207                    File* file = static_cast<File*>(value.blob());
208#if ENABLE(DIRECTORY_UPLOAD)
209                    name = file->webkitRelativePath().isEmpty() ? file->name() : file->webkitRelativePath();
210#else
211                    name = file->name();
212#endif
213                    // Let the application specify a filename if it's going to generate a replacement file for the upload.
214                    const String& path = file->path();
215                    if (!path.isEmpty()) {
216                        if (Page* page = document->page()) {
217                            String generatedFileName;
218                            shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFileName);
219                            if (shouldGenerateFile)
220                                name = generatedFileName;
221                        }
222                    }
223                } else {
224                    // For non-file blob, use the identifier part of the URL as the name.
225                    name = "Blob" + BlobURL::getIdentifier(value.blob()->url());
226                    name = name.replace("-", ""); // For safety, remove '-' from the filename since some servers may not like it.
227                }
228
229                // We have to include the filename=".." part in the header, even if the filename is empty
230                FormDataBuilder::addFilenameToMultiPartHeader(header, encoding, name);
231
232                // Add the content type if available, or "application/octet-stream" otherwise (RFC 1867).
233                String contentType;
234                if (value.blob()->type().isEmpty())
235                    contentType = "application/octet-stream";
236                else
237                    contentType = value.blob()->type();
238                FormDataBuilder::addContentTypeToMultiPartHeader(header, contentType.latin1());
239            }
240
241            FormDataBuilder::finishMultiPartHeader(header);
242
243            // Append body
244            appendData(header.data(), header.size());
245            if (value.blob()) {
246                if (value.blob()->isFile()) {
247                    // Do not add the file if the path is empty.
248                    if (!static_cast<File*>(value.blob())->path().isEmpty())
249                        appendFile(static_cast<File*>(value.blob())->path(), shouldGenerateFile);
250                }
251#if ENABLE(BLOB)
252                else
253                    appendBlob(value.blob()->url());
254#endif
255            } else
256                appendData(value.data().data(), value.data().length());
257            appendData("\r\n", 2);
258        } else {
259            // Omit the name "isindex" if it's the first form data element.
260            // FIXME: Why is this a good rule? Is this obsolete now?
261            if (encodedData.isEmpty() && key.data() == "isindex")
262                FormDataBuilder::encodeStringAsFormData(encodedData, value.data());
263            else
264                FormDataBuilder::addKeyValuePairAsFormData(encodedData, key.data(), value.data());
265        }
266    }
267
268    if (isMultiPartForm)
269        FormDataBuilder::addBoundaryToMultiPartHeader(encodedData, m_boundary.data(), true);
270
271    appendData(encodedData.data(), encodedData.size());
272}
273
274void FormData::flatten(Vector<char>& data) const
275{
276    // Concatenate all the byte arrays, but omit any files.
277    data.clear();
278    size_t n = m_elements.size();
279    for (size_t i = 0; i < n; ++i) {
280        const FormDataElement& e = m_elements[i];
281        if (e.m_type == FormDataElement::data)
282            data.append(e.m_data.data(), static_cast<size_t>(e.m_data.size()));
283    }
284}
285
286String FormData::flattenToString() const
287{
288    Vector<char> bytes;
289    flatten(bytes);
290    return Latin1Encoding().decode(reinterpret_cast<const char*>(bytes.data()), bytes.size());
291}
292
293void FormData::generateFiles(Document* document)
294{
295    ASSERT(!m_hasGeneratedFiles);
296
297    if (m_hasGeneratedFiles)
298        return;
299
300    Page* page = document->page();
301    if (!page)
302        return;
303    ChromeClient* client = page->chrome()->client();
304
305    size_t n = m_elements.size();
306    for (size_t i = 0; i < n; ++i) {
307        FormDataElement& e = m_elements[i];
308        if (e.m_type == FormDataElement::encodedFile && e.m_shouldGenerateFile) {
309            e.m_generatedFilename = client->generateReplacementFile(e.m_filename);
310            m_hasGeneratedFiles = true;
311        }
312    }
313}
314
315void FormData::removeGeneratedFilesIfNeeded()
316{
317    if (!m_hasGeneratedFiles)
318        return;
319
320    size_t n = m_elements.size();
321    for (size_t i = 0; i < n; ++i) {
322        FormDataElement& e = m_elements[i];
323        if (e.m_type == FormDataElement::encodedFile && !e.m_generatedFilename.isEmpty()) {
324            ASSERT(e.m_shouldGenerateFile);
325            String directory = directoryName(e.m_generatedFilename);
326            deleteFile(e.m_generatedFilename);
327            deleteEmptyDirectory(directory);
328            e.m_generatedFilename = String();
329        }
330    }
331    m_hasGeneratedFiles = false;
332}
333
334static void encode(Encoder& encoder, const FormDataElement& element)
335{
336    encoder.encodeUInt32(element.m_type);
337
338    switch (element.m_type) {
339    case FormDataElement::data:
340        encoder.encodeBytes(reinterpret_cast<const uint8_t*>(element.m_data.data()), element.m_data.size());
341        return;
342
343    case FormDataElement::encodedFile:
344        encoder.encodeString(element.m_filename);
345        encoder.encodeBool(element.m_shouldGenerateFile);
346#if ENABLE(BLOB)
347        encoder.encodeInt64(element.m_fileStart);
348        encoder.encodeInt64(element.m_fileLength);
349        encoder.encodeDouble(element.m_expectedFileModificationTime);
350#else
351        encoder.encodeInt64(0);
352        encoder.encodeInt64(0);
353        encoder.encodeDouble(0);
354#endif
355        return;
356
357#if ENABLE(BLOB)
358    case FormDataElement::encodedBlob:
359        encoder.encodeString(element.m_blobURL.string());
360        return;
361#endif
362    }
363
364    ASSERT_NOT_REACHED();
365}
366
367static bool decode(Decoder& decoder, FormDataElement& element)
368{
369    uint32_t type;
370    if (!decoder.decodeUInt32(type))
371        return false;
372
373    switch (type) {
374    case FormDataElement::data: {
375        element.m_type = FormDataElement::data;
376        Vector<uint8_t> data;
377        if (!decoder.decodeBytes(data))
378            return false;
379        size_t size = data.size();
380        element.m_data.resize(size);
381        memcpy(element.m_data.data(), data.data(), size);
382        return true;
383    }
384
385    case FormDataElement::encodedFile: {
386        element.m_type = FormDataElement::encodedFile;
387        if (!decoder.decodeString(element.m_filename))
388            return false;
389        if (!decoder.decodeBool(element.m_shouldGenerateFile))
390            return false;
391        int64_t fileStart;
392        if (!decoder.decodeInt64(fileStart))
393            return false;
394        if (fileStart < 0)
395            return false;
396        int64_t fileLength;
397        if (!decoder.decodeInt64(fileLength))
398            return false;
399        if (fileLength < fileStart)
400            return false;
401        double expectedFileModificationTime;
402        if (!decoder.decodeDouble(expectedFileModificationTime))
403            return false;
404#if ENABLE(BLOB)
405        element.m_fileStart = fileStart;
406        element.m_fileLength = fileLength;
407        element.m_expectedFileModificationTime = expectedFileModificationTime;
408#endif
409        return true;
410    }
411
412#if ENABLE(BLOB)
413    case FormDataElement::encodedBlob:
414        element.m_type = FormDataElement::encodedBlob;
415        String blobURLString;
416        if (!decoder.decodeString(blobURLString))
417            return false;
418        element.m_blobURL = KURL(KURL(), blobURLString);
419        return true;
420#endif
421    }
422
423    return false;
424}
425
426void FormData::encodeForBackForward(Encoder& encoder) const
427{
428    encoder.encodeBool(m_alwaysStream);
429
430    encoder.encodeBytes(reinterpret_cast<const uint8_t*>(m_boundary.data()), m_boundary.size());
431
432    size_t size = m_elements.size();
433    encoder.encodeUInt64(size);
434    for (size_t i = 0; i < size; ++i)
435        encode(encoder, m_elements[i]);
436
437    encoder.encodeBool(m_hasGeneratedFiles);
438
439    encoder.encodeInt64(m_identifier);
440}
441
442PassRefPtr<FormData> FormData::decodeForBackForward(Decoder& decoder)
443{
444    RefPtr<FormData> data = FormData::create();
445
446    if (!decoder.decodeBool(data->m_alwaysStream))
447        return 0;
448
449    Vector<uint8_t> boundary;
450    if (!decoder.decodeBytes(boundary))
451        return 0;
452    size_t size = boundary.size();
453    data->m_boundary.resize(size);
454    memcpy(data->m_boundary.data(), boundary.data(), size);
455
456    uint64_t elementsSize;
457    if (!decoder.decodeUInt64(elementsSize))
458        return 0;
459    for (size_t i = 0; i < elementsSize; ++i) {
460        FormDataElement element;
461        if (!decode(decoder, element))
462            return 0;
463        data->m_elements.append(element);
464    }
465
466    if (!decoder.decodeBool(data->m_hasGeneratedFiles))
467        return 0;
468
469    if (!decoder.decodeInt64(data->m_identifier))
470        return 0;
471
472    return data.release();
473}
474
475} // namespace WebCore
476