| 1 | /* |
| 2 | * Copyright (C) 2004, 2006, 2008, 2011 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * This library is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU Library General Public |
| 6 | * License as published by the Free Software Foundation; either |
| 7 | * version 2 of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This library is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 12 | * Library General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU Library General Public License |
| 15 | * along with this library; see the file COPYING.LIB. If not, write to |
| 16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 17 | * Boston, MA 02110-1301, USA. |
| 18 | */ |
| 19 | |
| 20 | #pragma once |
| 21 | |
| 22 | #include "BlobData.h" |
| 23 | #include <wtf/Forward.h> |
| 24 | #include <wtf/RefCounted.h> |
| 25 | #include <wtf/URL.h> |
| 26 | #include <wtf/Variant.h> |
| 27 | #include <wtf/Vector.h> |
| 28 | #include <wtf/text/WTFString.h> |
| 29 | |
| 30 | namespace WebCore { |
| 31 | |
| 32 | class BlobRegistry; |
| 33 | class DOMFormData; |
| 34 | class Document; |
| 35 | class File; |
| 36 | class SharedBuffer; |
| 37 | class TextEncoding; |
| 38 | |
| 39 | struct FormDataElement { |
| 40 | struct EncodedFileData; |
| 41 | struct EncodedBlobData; |
| 42 | using Data = Variant<Vector<char>, EncodedFileData, EncodedBlobData>; |
| 43 | |
| 44 | FormDataElement() = default; |
| 45 | explicit FormDataElement(Data&& data) |
| 46 | : data(WTFMove(data)) { } |
| 47 | explicit FormDataElement(Vector<char>&& array) |
| 48 | : data(WTFMove(array)) { } |
| 49 | FormDataElement(const String& filename, int64_t fileStart, int64_t fileLength, Optional<WallTime> expectedFileModificationTime, bool shouldGenerateFile) |
| 50 | : data(EncodedFileData { filename, fileStart, fileLength, expectedFileModificationTime, { }, shouldGenerateFile, false }) { } |
| 51 | explicit FormDataElement(const URL& blobURL) |
| 52 | : data(EncodedBlobData { blobURL }) { } |
| 53 | |
| 54 | uint64_t lengthInBytes() const; |
| 55 | |
| 56 | FormDataElement isolatedCopy() const; |
| 57 | |
| 58 | template<typename Encoder> void encode(Encoder& encoder) const |
| 59 | { |
| 60 | encoder << data; |
| 61 | } |
| 62 | template<typename Decoder> static Optional<FormDataElement> decode(Decoder& decoder) |
| 63 | { |
| 64 | Optional<Data> data; |
| 65 | decoder >> data; |
| 66 | if (!data) |
| 67 | return WTF::nullopt; |
| 68 | return FormDataElement(WTFMove(*data)); |
| 69 | } |
| 70 | |
| 71 | struct EncodedFileData { |
| 72 | String filename; |
| 73 | int64_t fileStart { 0 }; |
| 74 | int64_t fileLength { 0 }; |
| 75 | Optional<WallTime> expectedFileModificationTime; |
| 76 | String generatedFilename; |
| 77 | bool shouldGenerateFile { false }; |
| 78 | bool ownsGeneratedFile { false }; |
| 79 | |
| 80 | // FIXME: Generated file support in FormData is almost identical to Blob, they should be merged. |
| 81 | // We can't just switch to using Blobs for all files because EncodedFile form data elements do not |
| 82 | // have a valid expectedFileModificationTime, meaning we always upload the latest content from disk. |
| 83 | |
| 84 | EncodedFileData isolatedCopy() const |
| 85 | { |
| 86 | return { filename.isolatedCopy(), fileStart, fileLength, expectedFileModificationTime, generatedFilename.isolatedCopy(), shouldGenerateFile, ownsGeneratedFile }; |
| 87 | } |
| 88 | |
| 89 | bool operator==(const EncodedFileData& other) const |
| 90 | { |
| 91 | return filename == other.filename |
| 92 | && fileStart == other.fileStart |
| 93 | && fileLength == other.fileLength |
| 94 | && expectedFileModificationTime == other.expectedFileModificationTime |
| 95 | && generatedFilename == other.generatedFilename |
| 96 | && shouldGenerateFile == other.shouldGenerateFile |
| 97 | && ownsGeneratedFile == other.ownsGeneratedFile; |
| 98 | } |
| 99 | template<typename Encoder> void encode(Encoder& encoder) const |
| 100 | { |
| 101 | encoder << filename << fileStart << fileLength << expectedFileModificationTime << generatedFilename << shouldGenerateFile; |
| 102 | } |
| 103 | template<typename Decoder> static Optional<EncodedFileData> decode(Decoder& decoder) |
| 104 | { |
| 105 | Optional<String> filename; |
| 106 | decoder >> filename; |
| 107 | if (!filename) |
| 108 | return WTF::nullopt; |
| 109 | |
| 110 | Optional<int64_t> fileStart; |
| 111 | decoder >> fileStart; |
| 112 | if (!fileStart) |
| 113 | return WTF::nullopt; |
| 114 | |
| 115 | Optional<int64_t> fileLength; |
| 116 | decoder >> fileLength; |
| 117 | if (!fileLength) |
| 118 | return WTF::nullopt; |
| 119 | |
| 120 | Optional<Optional<WallTime>> expectedFileModificationTime; |
| 121 | decoder >> expectedFileModificationTime; |
| 122 | if (!expectedFileModificationTime) |
| 123 | return WTF::nullopt; |
| 124 | |
| 125 | Optional<String> generatedFilename; |
| 126 | decoder >> generatedFilename; |
| 127 | if (!generatedFilename) |
| 128 | return WTF::nullopt; |
| 129 | |
| 130 | Optional<bool> shouldGenerateFile; |
| 131 | decoder >> shouldGenerateFile; |
| 132 | if (!shouldGenerateFile) |
| 133 | return WTF::nullopt; |
| 134 | |
| 135 | bool ownsGeneratedFile = false; |
| 136 | |
| 137 | return {{ |
| 138 | WTFMove(*filename), |
| 139 | WTFMove(*fileStart), |
| 140 | WTFMove(*fileLength), |
| 141 | WTFMove(*expectedFileModificationTime), |
| 142 | WTFMove(*generatedFilename), |
| 143 | WTFMove(*shouldGenerateFile), |
| 144 | WTFMove(ownsGeneratedFile) |
| 145 | }}; |
| 146 | } |
| 147 | |
| 148 | }; |
| 149 | |
| 150 | struct EncodedBlobData { |
| 151 | URL url; |
| 152 | |
| 153 | bool operator==(const EncodedBlobData& other) const |
| 154 | { |
| 155 | return url == other.url; |
| 156 | } |
| 157 | template<typename Encoder> void encode(Encoder& encoder) const |
| 158 | { |
| 159 | encoder << url; |
| 160 | } |
| 161 | template<typename Decoder> static Optional<EncodedBlobData> decode(Decoder& decoder) |
| 162 | { |
| 163 | Optional<URL> url; |
| 164 | decoder >> url; |
| 165 | if (!url) |
| 166 | return WTF::nullopt; |
| 167 | |
| 168 | return {{ WTFMove(*url) }}; |
| 169 | } |
| 170 | }; |
| 171 | |
| 172 | bool operator==(const FormDataElement& other) const |
| 173 | { |
| 174 | if (&other == this) |
| 175 | return true; |
| 176 | if (data.index() != other.data.index()) |
| 177 | return false; |
| 178 | if (!data.index()) |
| 179 | return WTF::get<0>(data) == WTF::get<0>(other.data); |
| 180 | if (data.index() == 1) |
| 181 | return WTF::get<1>(data) == WTF::get<1>(other.data); |
| 182 | return WTF::get<2>(data) == WTF::get<2>(other.data); |
| 183 | } |
| 184 | bool operator!=(const FormDataElement& other) const |
| 185 | { |
| 186 | return !(*this == other); |
| 187 | } |
| 188 | |
| 189 | Data data; |
| 190 | }; |
| 191 | |
| 192 | class FormData : public RefCounted<FormData> { |
| 193 | public: |
| 194 | enum EncodingType { |
| 195 | FormURLEncoded, // for application/x-www-form-urlencoded |
| 196 | TextPlain, // for text/plain |
| 197 | MultipartFormData // for multipart/form-data |
| 198 | }; |
| 199 | |
| 200 | WEBCORE_EXPORT static Ref<FormData> create(); |
| 201 | WEBCORE_EXPORT static Ref<FormData> create(const void*, size_t); |
| 202 | static Ref<FormData> create(const CString&); |
| 203 | static Ref<FormData> create(Vector<char>&&); |
| 204 | static Ref<FormData> create(const Vector<char>&); |
| 205 | static Ref<FormData> create(const Vector<uint8_t>&); |
| 206 | static Ref<FormData> create(const DOMFormData&, EncodingType = FormURLEncoded); |
| 207 | static Ref<FormData> createMultiPart(const DOMFormData&, Document*); |
| 208 | WEBCORE_EXPORT ~FormData(); |
| 209 | |
| 210 | // FIXME: Both these functions perform a deep copy of m_elements, but differ in handling of other data members. |
| 211 | // How much of that is intentional? We need better names that explain the difference. |
| 212 | Ref<FormData> copy() const; |
| 213 | WEBCORE_EXPORT Ref<FormData> isolatedCopy() const; |
| 214 | |
| 215 | template<typename Encoder> |
| 216 | void encode(Encoder&) const; |
| 217 | template<typename Decoder> |
| 218 | static RefPtr<FormData> decode(Decoder&); |
| 219 | |
| 220 | WEBCORE_EXPORT void appendData(const void* data, size_t); |
| 221 | void appendFile(const String& filePath, bool shouldGenerateFile = false); |
| 222 | WEBCORE_EXPORT void appendFileRange(const String& filename, long long start, long long length, Optional<WallTime> expectedModificationTime, bool shouldGenerateFile = false); |
| 223 | WEBCORE_EXPORT void appendBlob(const URL& blobURL); |
| 224 | |
| 225 | WEBCORE_EXPORT Vector<char> flatten() const; // omits files |
| 226 | String flattenToString() const; // omits files |
| 227 | |
| 228 | // Resolve all blob references so we only have file and data. |
| 229 | // If the FormData has no blob references to resolve, this is returned. |
| 230 | WEBCORE_EXPORT Ref<FormData> resolveBlobReferences(BlobRegistry&); |
| 231 | |
| 232 | bool isEmpty() const { return m_elements.isEmpty(); } |
| 233 | const Vector<FormDataElement>& elements() const { return m_elements; } |
| 234 | const Vector<char>& boundary() const { return m_boundary; } |
| 235 | |
| 236 | RefPtr<SharedBuffer> asSharedBuffer() const; |
| 237 | |
| 238 | void generateFiles(Document*); |
| 239 | void removeGeneratedFilesIfNeeded(); |
| 240 | |
| 241 | bool alwaysStream() const { return m_alwaysStream; } |
| 242 | void setAlwaysStream(bool alwaysStream) { m_alwaysStream = alwaysStream; } |
| 243 | |
| 244 | // Identifies a particular form submission instance. A value of 0 is used |
| 245 | // to indicate an unspecified identifier. |
| 246 | void setIdentifier(int64_t identifier) { m_identifier = identifier; } |
| 247 | int64_t identifier() const { return m_identifier; } |
| 248 | |
| 249 | bool containsPasswordData() const { return m_containsPasswordData; } |
| 250 | void setContainsPasswordData(bool containsPasswordData) { m_containsPasswordData = containsPasswordData; } |
| 251 | |
| 252 | static EncodingType parseEncodingType(const String& type) |
| 253 | { |
| 254 | if (equalLettersIgnoringASCIICase(type, "text/plain" )) |
| 255 | return TextPlain; |
| 256 | if (equalLettersIgnoringASCIICase(type, "multipart/form-data" )) |
| 257 | return MultipartFormData; |
| 258 | return FormURLEncoded; |
| 259 | } |
| 260 | |
| 261 | uint64_t lengthInBytes() const; |
| 262 | |
| 263 | WEBCORE_EXPORT URL asBlobURL() const; |
| 264 | |
| 265 | private: |
| 266 | FormData(); |
| 267 | FormData(const FormData&); |
| 268 | |
| 269 | void appendMultiPartFileValue(const File&, Vector<char>& , TextEncoding&, Document*); |
| 270 | void appendMultiPartStringValue(const String&, Vector<char>& , TextEncoding&); |
| 271 | void appendMultiPartKeyValuePairItems(const DOMFormData&, Document*); |
| 272 | void appendNonMultiPartKeyValuePairItems(const DOMFormData&, EncodingType); |
| 273 | |
| 274 | bool hasGeneratedFiles() const; |
| 275 | bool hasOwnedGeneratedFiles() const; |
| 276 | |
| 277 | Vector<FormDataElement> m_elements; |
| 278 | |
| 279 | int64_t m_identifier { 0 }; |
| 280 | bool m_alwaysStream { false }; |
| 281 | Vector<char> m_boundary; |
| 282 | bool m_containsPasswordData { false }; |
| 283 | mutable Optional<uint64_t> m_lengthInBytes; |
| 284 | }; |
| 285 | |
| 286 | inline bool operator==(const FormData& a, const FormData& b) |
| 287 | { |
| 288 | return a.elements() == b.elements(); |
| 289 | } |
| 290 | |
| 291 | inline bool operator!=(const FormData& a, const FormData& b) |
| 292 | { |
| 293 | return !(a == b); |
| 294 | } |
| 295 | |
| 296 | template<typename Encoder> |
| 297 | void FormData::encode(Encoder& encoder) const |
| 298 | { |
| 299 | encoder << m_alwaysStream; |
| 300 | encoder << m_boundary; |
| 301 | encoder << m_elements; |
| 302 | encoder << m_identifier; |
| 303 | // FIXME: Does not encode m_containsPasswordData. Why is that OK? |
| 304 | } |
| 305 | |
| 306 | template<typename Decoder> |
| 307 | RefPtr<FormData> FormData::decode(Decoder& decoder) |
| 308 | { |
| 309 | auto data = FormData::create(); |
| 310 | |
| 311 | if (!decoder.decode(data->m_alwaysStream)) |
| 312 | return nullptr; |
| 313 | |
| 314 | if (!decoder.decode(data->m_boundary)) |
| 315 | return nullptr; |
| 316 | |
| 317 | if (!decoder.decode(data->m_elements)) |
| 318 | return nullptr; |
| 319 | |
| 320 | if (!decoder.decode(data->m_identifier)) |
| 321 | return nullptr; |
| 322 | |
| 323 | return data; |
| 324 | } |
| 325 | |
| 326 | } // namespace WebCore |
| 327 | |
| 328 | |