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
40namespace WebCore {
41
42inline FormData::FormData()
43{
44}
45
46inline 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
63FormData::~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
71Ref<FormData> FormData::create()
72{
73 return adoptRef(*new FormData);
74}
75
76Ref<FormData> FormData::create(const void* data, size_t size)
77{
78 auto result = create();
79 result->appendData(data, size);
80 return result;
81}
82
83Ref<FormData> FormData::create(const CString& string)
84{
85 return create(string.data(), string.length());
86}
87
88Ref<FormData> FormData::create(const Vector<char>& vector)
89{
90 return create(vector.data(), vector.size());
91}
92
93Ref<FormData> FormData::create(Vector<char>&& vector)
94{
95 auto data = create();
96 data->m_elements.append(WTFMove(vector));
97 return data;
98}
99
100Ref<FormData> FormData::create(const Vector<uint8_t>& vector)
101{
102 return create(vector.data(), vector.size());
103}
104
105Ref<FormData> FormData::create(const DOMFormData& formData, EncodingType encodingType)
106{
107 auto result = create();
108 result->appendNonMultiPartKeyValuePairItems(formData, encodingType);
109 return result;
110}
111
112Ref<FormData> FormData::createMultiPart(const DOMFormData& formData, Document* document)
113{
114 auto result = create();
115 result->appendMultiPartKeyValuePairItems(formData, document);
116 return result;
117}
118
119Ref<FormData> FormData::copy() const
120{
121 return adoptRef(*new FormData(*this));
122}
123
124Ref<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
140uint64_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
158FormDataElement 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
173void 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
187void 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
193void 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
199void FormData::appendBlob(const URL& blobURL)
200{
201 m_elements.append(FormDataElement(blobURL));
202 m_lengthInBytes = WTF::nullopt;
203}
204
205static Vector<uint8_t> normalizeStringData(TextEncoding& encoding, const String& value)
206{
207 return normalizeLineEndingsToCRLF(encoding.encode(value, UnencodableHandling::Entities));
208}
209
210void 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
246void 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
255void 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
281void 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
297Vector<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
308String FormData::flattenToString() const
309{
310 auto bytes = flatten();
311 return Latin1Encoding().decode(reinterpret_cast<const char*>(bytes.data()), bytes.size());
312}
313
314static 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
338Ref<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
371void 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
392bool 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
403bool 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
416void 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
433uint64_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
444RefPtr<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
453URL 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
464