Add Optional to Forward.h.
[WebKit.git] / Source / WebCore / platform / network / FormData.cpp
1 /*
2  * Copyright (C) 2004, 2006, 2008, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2009 Google Inc. All rights reserved.
4  * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB. If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23 #include "FormData.h"
24
25 #include "BlobRegistryImpl.h"
26 #include "BlobURL.h"
27 #include "Chrome.h"
28 #include "ChromeClient.h"
29 #include "DOMFormData.h"
30 #include "Document.h"
31 #include "File.h"
32 #include "FormDataBuilder.h"
33 #include "Page.h"
34 #include "SharedBuffer.h"
35 #include "TextEncoding.h"
36 #include "ThreadableBlobRegistry.h"
37 #include <wtf/FileSystem.h>
38 #include <wtf/text/LineEnding.h>
39
40 namespace WebCore {
41
42 inline FormData::FormData()
43 {
44 }
45
46 inline FormData::FormData(const FormData& data)
47     : RefCounted<FormData>()
48     , m_elements(data.m_elements)
49     , m_identifier(data.m_identifier)
50     , m_alwaysStream(false)
51     , m_containsPasswordData(data.m_containsPasswordData)
52 {
53     // We shouldn't be copying FormData that hasn't already removed its generated files
54     // but just in case, make sure the new FormData is ready to generate its own files.
55     for (auto& element : m_elements) {
56         if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
57             fileData->generatedFilename = { };
58             fileData->ownsGeneratedFile = false;
59         }
60     }
61 }
62
63 FormData::~FormData()
64 {
65     // This cleanup should've happened when the form submission finished.
66     // Just in case, let's assert, and do the cleanup anyway in release builds.
67     ASSERT(!hasOwnedGeneratedFiles());
68     removeGeneratedFilesIfNeeded();
69 }
70
71 Ref<FormData> FormData::create()
72 {
73     return adoptRef(*new FormData);
74 }
75
76 Ref<FormData> FormData::create(const void* data, size_t size)
77 {
78     auto result = create();
79     result->appendData(data, size);
80     return result;
81 }
82
83 Ref<FormData> FormData::create(const CString& string)
84 {
85     return create(string.data(), string.length());
86 }
87
88 Ref<FormData> FormData::create(const Vector<char>& vector)
89 {
90     return create(vector.data(), vector.size());
91 }
92
93 Ref<FormData> FormData::create(Vector<char>&& vector)
94 {
95     auto data = create();
96     data->m_elements.append(WTFMove(vector));
97     return data;
98 }
99
100 Ref<FormData> FormData::create(const Vector<uint8_t>& vector)
101 {
102     return create(vector.data(), vector.size());
103 }
104
105 Ref<FormData> FormData::create(const DOMFormData& formData, EncodingType encodingType)
106 {
107     auto result = create();
108     result->appendNonMultiPartKeyValuePairItems(formData, encodingType);
109     return result;
110 }
111
112 Ref<FormData> FormData::createMultiPart(const DOMFormData& formData, Document* document)
113 {
114     auto result = create();
115     result->appendMultiPartKeyValuePairItems(formData, document);
116     return result;
117 }
118
119 Ref<FormData> FormData::copy() const
120 {
121     return adoptRef(*new FormData(*this));
122 }
123
124 Ref<FormData> FormData::isolatedCopy() const
125 {
126     // FIXME: isolatedCopy() does not copy m_identifier, m_boundary, or m_containsPasswordData.
127     // Is all of that correct and intentional?
128
129     auto formData = create();
130
131     formData->m_alwaysStream = m_alwaysStream;
132
133     formData->m_elements.reserveInitialCapacity(m_elements.size());
134     for (auto& element : m_elements)
135         formData->m_elements.uncheckedAppend(element.isolatedCopy());
136
137     return formData;
138 }
139
140 uint64_t FormDataElement::lengthInBytes() const
141 {
142     return switchOn(data,
143         [] (const Vector<char>& bytes) {
144             return static_cast<uint64_t>(bytes.size());
145         }, [] (const FormDataElement::EncodedFileData& fileData) {
146             if (fileData.fileLength != BlobDataItem::toEndOfFile)
147                 return static_cast<uint64_t>(fileData.fileLength);
148             long long fileSize;
149             if (FileSystem::getFileSize(fileData.shouldGenerateFile ? fileData.generatedFilename : fileData.filename, fileSize))
150                 return static_cast<uint64_t>(fileSize);
151             return static_cast<uint64_t>(0);
152         }, [] (const FormDataElement::EncodedBlobData& blobData) {
153             return ThreadableBlobRegistry::blobSize(blobData.url);
154         }
155     );
156 }
157
158 FormDataElement FormDataElement::isolatedCopy() const
159 {
160     return switchOn(data,
161         [] (const Vector<char>& bytes) {
162             Vector<char> copy;
163             copy.append(bytes.data(), bytes.size());
164             return FormDataElement(WTFMove(copy));
165         }, [] (const FormDataElement::EncodedFileData& fileData) {
166             return FormDataElement(fileData.isolatedCopy());
167         }, [] (const FormDataElement::EncodedBlobData& blobData) {
168             return FormDataElement(blobData.url.isolatedCopy());
169         }
170     );
171 }
172
173 void FormData::appendData(const void* data, size_t size)
174 {
175     m_lengthInBytes = WTF::nullopt;
176     if (!m_elements.isEmpty()) {
177         if (auto* vector = WTF::get_if<Vector<char>>(m_elements.last().data)) {
178             vector->append(reinterpret_cast<const char*>(data), size);
179             return;
180         }
181     }
182     Vector<char> vector;
183     vector.append(reinterpret_cast<const char*>(data), size);
184     m_elements.append(WTFMove(vector));
185 }
186
187 void FormData::appendFile(const String& filename, bool shouldGenerateFile)
188 {
189     m_elements.append(FormDataElement(filename, 0, BlobDataItem::toEndOfFile, WTF::nullopt, shouldGenerateFile));
190     m_lengthInBytes = WTF::nullopt;
191 }
192
193 void FormData::appendFileRange(const String& filename, long long start, long long length, Optional<WallTime> expectedModificationTime, bool shouldGenerateFile)
194 {
195     m_elements.append(FormDataElement(filename, start, length, expectedModificationTime, shouldGenerateFile));
196     m_lengthInBytes = WTF::nullopt;
197 }
198
199 void FormData::appendBlob(const URL& blobURL)
200 {
201     m_elements.append(FormDataElement(blobURL));
202     m_lengthInBytes = WTF::nullopt;
203 }
204
205 static Vector<uint8_t> normalizeStringData(TextEncoding& encoding, const String& value)
206 {
207     return normalizeLineEndingsToCRLF(encoding.encode(value, UnencodableHandling::Entities));
208 }
209
210 void FormData::appendMultiPartFileValue(const File& file, Vector<char>& header, TextEncoding& encoding, Document* document)
211 {
212     auto name = file.name();
213
214     // Let the application specify a filename if it's going to generate a replacement file for the upload.
215     bool shouldGenerateFile = false;
216     auto& path = file.path();
217     if (!path.isEmpty()) {
218         if (Page* page = document->page()) {
219             String generatedFileName;
220             shouldGenerateFile = page->chrome().client().shouldReplaceWithGeneratedFileForUpload(path, generatedFileName);
221             if (shouldGenerateFile)
222                 name = generatedFileName;
223         }
224     }
225
226     // We have to include the filename=".." part in the header, even if the filename is empty
227     FormDataBuilder::addFilenameToMultiPartHeader(header, encoding, name);
228
229     // Add the content type if available, or "application/octet-stream" otherwise (RFC 1867).
230     auto contentType = file.type();
231     if (contentType.isEmpty())
232         contentType = "application/octet-stream"_s;
233     ASSERT(Blob::isNormalizedContentType(contentType));
234
235     FormDataBuilder::addContentTypeToMultiPartHeader(header, contentType.ascii());
236
237     FormDataBuilder::finishMultiPartHeader(header);
238     appendData(header.data(), header.size());
239
240     if (!file.path().isEmpty())
241         appendFile(file.path(), shouldGenerateFile);
242     else if (file.size())
243         appendBlob(file.url());
244 }
245
246 void FormData::appendMultiPartStringValue(const String& string, Vector<char>& header, TextEncoding& encoding)
247 {
248     FormDataBuilder::finishMultiPartHeader(header);
249     appendData(header.data(), header.size());
250
251     auto normalizedStringData = normalizeStringData(encoding, string);
252     appendData(normalizedStringData.data(), normalizedStringData.size());
253 }
254
255 void FormData::appendMultiPartKeyValuePairItems(const DOMFormData& formData, Document* document)
256 {
257     m_boundary = FormDataBuilder::generateUniqueBoundaryString();
258
259     auto encoding = formData.encoding();
260
261     Vector<char> encodedData;
262     for (auto& item : formData.items()) {
263         auto normalizedName = normalizeStringData(encoding, item.name);
264     
265         Vector<char> header;
266         FormDataBuilder::beginMultiPartHeader(header, m_boundary.data(), normalizedName);
267
268         if (WTF::holds_alternative<RefPtr<File>>(item.data))
269             appendMultiPartFileValue(*WTF::get<RefPtr<File>>(item.data), header, encoding, document);
270         else
271             appendMultiPartStringValue(WTF::get<String>(item.data), header, encoding);
272
273         appendData("\r\n", 2);
274     }
275     
276     FormDataBuilder::addBoundaryToMultiPartHeader(encodedData, m_boundary.data(), true);
277
278     appendData(encodedData.data(), encodedData.size());
279 }
280
281 void FormData::appendNonMultiPartKeyValuePairItems(const DOMFormData& formData, EncodingType encodingType)
282 {
283     auto encoding = formData.encoding();
284
285     Vector<char> encodedData;
286     for (auto& item : formData.items()) {
287         ASSERT(WTF::holds_alternative<String>(item.data));
288
289         auto normalizedName = normalizeStringData(encoding, item.name);
290         auto normalizedStringData = normalizeStringData(encoding, WTF::get<String>(item.data));
291         FormDataBuilder::addKeyValuePairAsFormData(encodedData, normalizedName, normalizedStringData, encodingType);
292     }
293
294     appendData(encodedData.data(), encodedData.size());
295 }
296
297 Vector<char> FormData::flatten() const
298 {
299     // Concatenate all the byte arrays, but omit any files.
300     Vector<char> data;
301     for (auto& element : m_elements) {
302         if (auto* vector = WTF::get_if<Vector<char>>(element.data))
303             data.append(vector->data(), vector->size());
304     }
305     return data;
306 }
307
308 String FormData::flattenToString() const
309 {
310     auto bytes = flatten();
311     return Latin1Encoding().decode(reinterpret_cast<const char*>(bytes.data()), bytes.size());
312 }
313
314 static void appendBlobResolved(BlobRegistry& blobRegistry, FormData& formData, const URL& url)
315 {
316     if (!blobRegistry.isBlobRegistryImpl()) {
317         LOG_ERROR("Tried to resolve a blob without a usable registry");
318         return;
319     }
320
321     auto* blobData = static_cast<BlobRegistryImpl&>(blobRegistry).getBlobDataFromURL(url);
322     if (!blobData) {
323         LOG_ERROR("Could not get blob data from a registry");
324         return;
325     }
326
327     for (const auto& blobItem : blobData->items()) {
328         if (blobItem.type() == BlobDataItem::Type::Data) {
329             ASSERT(blobItem.data().data());
330             formData.appendData(blobItem.data().data()->data() + static_cast<int>(blobItem.offset()), static_cast<int>(blobItem.length()));
331         } else if (blobItem.type() == BlobDataItem::Type::File)
332             formData.appendFileRange(blobItem.file()->path(), blobItem.offset(), blobItem.length(), blobItem.file()->expectedModificationTime());
333         else
334             ASSERT_NOT_REACHED();
335     }
336 }
337
338 Ref<FormData> FormData::resolveBlobReferences(BlobRegistry& blobRegistry)
339 {
340     // First check if any blobs needs to be resolved, or we can take the fast path.
341     bool hasBlob = false;
342     for (auto& element : m_elements) {
343         if (WTF::holds_alternative<FormDataElement::EncodedBlobData>(element.data)) {
344             hasBlob = true;
345             break;
346         }
347     }
348
349     if (!hasBlob)
350         return *this;
351
352     // Create a copy to append the result into.
353     auto newFormData = FormData::create();
354     newFormData->setAlwaysStream(alwaysStream());
355     newFormData->setIdentifier(identifier());
356
357     for (auto& element : m_elements) {
358         switchOn(element.data,
359             [&] (const Vector<char>& bytes) {
360                 newFormData->appendData(bytes.data(), bytes.size());
361             }, [&] (const FormDataElement::EncodedFileData& fileData) {
362                 newFormData->appendFileRange(fileData.filename, fileData.fileStart, fileData.fileLength, fileData.expectedFileModificationTime, fileData.shouldGenerateFile);
363             }, [&] (const FormDataElement::EncodedBlobData& blobData) {
364                 appendBlobResolved(blobRegistry, newFormData.get(), blobData.url);
365             }
366         );
367     }
368     return newFormData;
369 }
370
371 void FormData::generateFiles(Document* document)
372 {
373     Page* page = document->page();
374     if (!page)
375         return;
376
377     for (auto& element : m_elements) {
378         if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
379             if (fileData->shouldGenerateFile) {
380                 ASSERT(!fileData->ownsGeneratedFile);
381                 ASSERT(fileData->generatedFilename.isEmpty());
382                 if (!fileData->generatedFilename.isEmpty())
383                     continue;
384                 fileData->generatedFilename = page->chrome().client().generateReplacementFile(fileData->filename);
385                 if (!fileData->generatedFilename.isEmpty())
386                     fileData->ownsGeneratedFile = true;
387             }
388         }
389     }
390 }
391
392 bool FormData::hasGeneratedFiles() const
393 {
394     for (auto& element : m_elements) {
395         if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
396             if (!fileData->generatedFilename.isEmpty())
397                 return true;
398         }
399     }
400     return false;
401 }
402
403 bool FormData::hasOwnedGeneratedFiles() const
404 {
405     for (auto& element : m_elements) {
406         if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
407             if (fileData->ownsGeneratedFile) {
408                 ASSERT(!fileData->generatedFilename.isEmpty());
409                 return true;
410             }
411         }
412     }
413     return false;
414 }
415
416 void FormData::removeGeneratedFilesIfNeeded()
417 {
418     for (auto& element : m_elements) {
419         if (auto* fileData = WTF::get_if<FormDataElement::EncodedFileData>(element.data)) {
420             if (fileData->ownsGeneratedFile) {
421                 ASSERT(!fileData->generatedFilename.isEmpty());
422                 ASSERT(fileData->shouldGenerateFile);
423                 String directory = FileSystem::directoryName(fileData->generatedFilename);
424                 FileSystem::deleteFile(fileData->generatedFilename);
425                 FileSystem::deleteEmptyDirectory(directory);
426                 fileData->generatedFilename = String();
427                 fileData->ownsGeneratedFile = false;
428             }
429         }
430     }
431 }
432
433 uint64_t FormData::lengthInBytes() const
434 {
435     if (!m_lengthInBytes) {
436         uint64_t length = 0;
437         for (auto& element : m_elements)
438             length += element.lengthInBytes();
439         m_lengthInBytes = length;
440     }
441     return *m_lengthInBytes;
442 }
443
444 RefPtr<SharedBuffer> FormData::asSharedBuffer() const
445 {
446     for (auto& element : m_elements) {
447         if (!WTF::holds_alternative<Vector<char>>(element.data))
448             return nullptr;
449     }
450     return SharedBuffer::create(flatten());
451 }
452
453 URL FormData::asBlobURL() const
454 {
455     if (m_elements.size() != 1)
456         return { };
457
458     if (auto* blobData = WTF::get_if<FormDataElement::EncodedBlobData>(m_elements.first().data))
459         return blobData->url;
460     return { };
461 }
462
463 } // namespace WebCore