#include "config.h"
#include "FormData.h"
#include "Blob.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "DOMFormData.h"
#include "Document.h"
#include "File.h"
#include "FileSystem.h"
#include "FormDataBuilder.h"
#include "MIMETypeRegistry.h"
#include "Page.h"
#include "TextEncoding.h"
#include "UUID.h"
namespace WebCore {
inline FormData::FormData()
: m_identifier(0)
, m_hasGeneratedFiles(false)
, m_alwaysStream(false)
{
}
inline FormData::FormData(const FormData& data)
: RefCounted<FormData>()
, m_elements(data.m_elements)
, m_identifier(data.m_identifier)
, m_hasGeneratedFiles(false)
, m_alwaysStream(false)
{
if (data.m_hasGeneratedFiles) {
size_t n = m_elements.size();
for (size_t i = 0; i < n; ++i) {
FormDataElement& e = m_elements[i];
if (e.m_type == FormDataElement::encodedFile)
e.m_generatedFilename = String();
}
}
}
FormData::~FormData()
{
ASSERT(!m_hasGeneratedFiles);
removeGeneratedFilesIfNeeded();
}
PassRefPtr<FormData> FormData::create()
{
return adoptRef(new FormData);
}
PassRefPtr<FormData> FormData::create(const void* data, size_t size)
{
RefPtr<FormData> result = create();
result->appendData(data, size);
return result.release();
}
PassRefPtr<FormData> FormData::create(const CString& string)
{
RefPtr<FormData> result = create();
result->appendData(string.data(), string.length());
return result.release();
}
PassRefPtr<FormData> FormData::create(const Vector<char>& vector)
{
RefPtr<FormData> result = create();
result->appendData(vector.data(), vector.size());
return result.release();
}
PassRefPtr<FormData> FormData::create(const DOMFormData& domFormData)
{
RefPtr<FormData> result = create();
result->appendDOMFormData(domFormData, false, 0);
return result.release();
}
PassRefPtr<FormData> FormData::createMultiPart(const DOMFormData& domFormData, Document* document)
{
RefPtr<FormData> result = create();
result->appendDOMFormData(domFormData, true, document);
return result.release();
}
PassRefPtr<FormData> FormData::copy() const
{
return adoptRef(new FormData(*this));
}
PassRefPtr<FormData> FormData::deepCopy() const
{
RefPtr<FormData> formData(create());
formData->m_alwaysStream = m_alwaysStream;
size_t n = m_elements.size();
formData->m_elements.reserveInitialCapacity(n);
for (size_t i = 0; i < n; ++i) {
const FormDataElement& e = m_elements[i];
switch (e.m_type) {
case FormDataElement::data:
formData->m_elements.append(FormDataElement(e.m_data));
break;
case FormDataElement::encodedFile:
#if ENABLE(BLOB_SLICE)
formData->m_elements.append(FormDataElement(e.m_filename, e.m_fileStart, e.m_fileLength, e.m_expectedFileModificationTime, e.m_shouldGenerateFile));
#else
formData->m_elements.append(FormDataElement(e.m_filename, e.m_shouldGenerateFile));
#endif
break;
}
}
return formData.release();
}
void FormData::appendData(const void* data, size_t size)
{
if (m_elements.isEmpty() || m_elements.last().m_type != FormDataElement::data)
m_elements.append(FormDataElement());
FormDataElement& e = m_elements.last();
size_t oldSize = e.m_data.size();
e.m_data.grow(oldSize + size);
memcpy(e.m_data.data() + oldSize, data, size);
}
void FormData::appendFile(const String& filename, bool shouldGenerateFile)
{
#if ENABLE(BLOB_SLICE)
m_elements.append(FormDataElement(filename, 0, Blob::toEndOfFile, Blob::doNotCheckFileChange, shouldGenerateFile));
#else
m_elements.append(FormDataElement(filename, shouldGenerateFile));
#endif
}
#if ENABLE(BLOB_SLICE)
void FormData::appendFileRange(const String& filename, long long start, long long length, double expectedModificationTime, bool shouldGenerateFile)
{
m_elements.append(FormDataElement(filename, start, length, expectedModificationTime, shouldGenerateFile));
}
#endif
void FormData::appendDOMFormData(const DOMFormData& domFormData, bool isMultiPartForm, Document* document)
{
FormDataBuilder formDataBuilder;
if (isMultiPartForm)
m_boundary = formDataBuilder.generateUniqueBoundaryString();
Vector<char> encodedData;
TextEncoding encoding = domFormData.encoding();
const Vector<FormDataList::Item>& list = domFormData.list();
size_t formDataListSize = list.size();
ASSERT(!(formDataListSize % 2));
for (size_t i = 0; i < formDataListSize; i += 2) {
const FormDataList::Item& key = list[i];
const FormDataList::Item& value = list[i + 1];
if (isMultiPartForm) {
Vector<char> header;
formDataBuilder.beginMultiPartHeader(header, m_boundary.data(), key.data());
bool shouldGenerateFile = false;
if (value.blob()) {
const String& path = value.blob()->path();
#if ENABLE(BLOB_SLICE)
String fileName;
if (value.blob()->isFile())
fileName = static_cast<File*>(value.blob())->fileName();
else {
fileName = "Blob" + createCanonicalUUIDString();
fileName.replace("-", ""); }
#else
ASSERT(value.blob()->isFile());
String fileName = static_cast<File*>(value.blob())->fileName();
#endif
if (!path.isEmpty()) {
if (Page* page = document->page()) {
String generatedFileName;
shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFileName);
if (shouldGenerateFile)
fileName = generatedFileName;
}
}
formDataBuilder.addFilenameToMultiPartHeader(header, encoding, fileName);
#if ENABLE(BLOB_SLICE)
if (!fileName.isEmpty() && value.blob()->isFile()) {
#else
if (!fileName.isEmpty()) {
#endif
String mimeType = MIMETypeRegistry::getMIMETypeForPath(fileName);
if (!mimeType.isEmpty())
formDataBuilder.addContentTypeToMultiPartHeader(header, mimeType.latin1());
}
}
formDataBuilder.finishMultiPartHeader(header);
appendData(header.data(), header.size());
if (size_t dataSize = value.data().length())
appendData(value.data().data(), dataSize);
else if (value.blob() && !value.blob()->path().isEmpty())
#if ENABLE(BLOB_SLICE)
appendFileRange(value.blob()->path(), value.blob()->start(), value.blob()->length(), value.blob()->modificationTime(), shouldGenerateFile);
#else
appendFile(value.blob()->path(), shouldGenerateFile);
#endif
appendData("\r\n", 2);
} else {
if (encodedData.isEmpty() && key.data() == "isindex")
FormDataBuilder::encodeStringAsFormData(encodedData, value.data());
else
formDataBuilder.addKeyValuePairAsFormData(encodedData, key.data(), value.data());
}
}
if (isMultiPartForm)
formDataBuilder.addBoundaryToMultiPartHeader(encodedData, m_boundary.data(), true);
appendData(encodedData.data(), encodedData.size());
}
void FormData::flatten(Vector<char>& data) const
{
data.clear();
size_t n = m_elements.size();
for (size_t i = 0; i < n; ++i) {
const FormDataElement& e = m_elements[i];
if (e.m_type == FormDataElement::data) {
size_t oldSize = data.size();
size_t delta = e.m_data.size();
data.grow(oldSize + delta);
memcpy(data.data() + oldSize, e.m_data.data(), delta);
}
}
}
String FormData::flattenToString() const
{
Vector<char> bytes;
flatten(bytes);
return Latin1Encoding().decode(bytes.data(), bytes.size());
}
void FormData::generateFiles(Document* document)
{
ASSERT(!m_hasGeneratedFiles);
if (m_hasGeneratedFiles)
return;
Page* page = document->page();
if (!page)
return;
ChromeClient* client = page->chrome()->client();
size_t n = m_elements.size();
for (size_t i = 0; i < n; ++i) {
FormDataElement& e = m_elements[i];
if (e.m_type == FormDataElement::encodedFile && e.m_shouldGenerateFile) {
e.m_generatedFilename = client->generateReplacementFile(e.m_filename);
m_hasGeneratedFiles = true;
}
}
}
void FormData::removeGeneratedFilesIfNeeded()
{
if (!m_hasGeneratedFiles)
return;
size_t n = m_elements.size();
for (size_t i = 0; i < n; ++i) {
FormDataElement& e = m_elements[i];
if (e.m_type == FormDataElement::encodedFile && !e.m_generatedFilename.isEmpty()) {
ASSERT(e.m_shouldGenerateFile);
String directory = directoryName(e.m_generatedFilename);
deleteFile(e.m_generatedFilename);
deleteEmptyDirectory(directory);
e.m_generatedFilename = String();
}
}
m_hasGeneratedFiles = false;
}
}