diff options
Diffstat (limited to 'brillo/http/http_form_data.cc')
-rw-r--r-- | brillo/http/http_form_data.cc | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/brillo/http/http_form_data.cc b/brillo/http/http_form_data.cc new file mode 100644 index 0000000..4d8f6f0 --- /dev/null +++ b/brillo/http/http_form_data.cc @@ -0,0 +1,221 @@ +// Copyright 2014 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <brillo/http/http_form_data.h> + +#include <limits> + +#include <base/format_macros.h> +#include <base/rand_util.h> +#include <base/strings/stringprintf.h> + +#include <brillo/errors/error_codes.h> +#include <brillo/http/http_transport.h> +#include <brillo/mime_utils.h> +#include <brillo/streams/file_stream.h> +#include <brillo/streams/input_stream_set.h> +#include <brillo/streams/memory_stream.h> + +namespace brillo { +namespace http { + +namespace form_header { +const char kContentDisposition[] = "Content-Disposition"; +const char kContentTransferEncoding[] = "Content-Transfer-Encoding"; +const char kContentType[] = "Content-Type"; +} // namespace form_header + +const char content_disposition::kFile[] = "file"; +const char content_disposition::kFormData[] = "form-data"; + +FormField::FormField(const std::string& name, + const std::string& content_disposition, + const std::string& content_type, + const std::string& transfer_encoding) + : name_{name}, + content_disposition_{content_disposition}, + content_type_{content_type}, + transfer_encoding_{transfer_encoding} { +} + +std::string FormField::GetContentDisposition() const { + std::string disposition = content_disposition_; + if (!name_.empty()) + base::StringAppendF(&disposition, "; name=\"%s\"", name_.c_str()); + return disposition; +} + +std::string FormField::GetContentType() const { + return content_type_; +} + +std::string FormField::GetContentHeader() const { + HeaderList headers{ + {form_header::kContentDisposition, GetContentDisposition()} + }; + + if (!content_type_.empty()) + headers.emplace_back(form_header::kContentType, GetContentType()); + + if (!transfer_encoding_.empty()) { + headers.emplace_back(form_header::kContentTransferEncoding, + transfer_encoding_); + } + + std::string result; + for (const auto& pair : headers) { + base::StringAppendF( + &result, "%s: %s\r\n", pair.first.c_str(), pair.second.c_str()); + } + result += "\r\n"; + return result; +} + +TextFormField::TextFormField(const std::string& name, + const std::string& data, + const std::string& content_type, + const std::string& transfer_encoding) + : FormField{name, + content_disposition::kFormData, + content_type, + transfer_encoding}, + data_{data} { +} + +bool TextFormField::ExtractDataStreams(std::vector<StreamPtr>* streams) { + streams->push_back(MemoryStream::OpenCopyOf(data_, nullptr)); + return true; +} + +FileFormField::FileFormField(const std::string& name, + StreamPtr stream, + const std::string& file_name, + const std::string& content_disposition, + const std::string& content_type, + const std::string& transfer_encoding) + : FormField{name, content_disposition, content_type, transfer_encoding}, + stream_{std::move(stream)}, + file_name_{file_name} { +} + +std::string FileFormField::GetContentDisposition() const { + std::string disposition = FormField::GetContentDisposition(); + base::StringAppendF(&disposition, "; filename=\"%s\"", file_name_.c_str()); + return disposition; +} + +bool FileFormField::ExtractDataStreams(std::vector<StreamPtr>* streams) { + if (!stream_) + return false; + streams->push_back(std::move(stream_)); + return true; +} + +MultiPartFormField::MultiPartFormField(const std::string& name, + const std::string& content_type, + const std::string& boundary) + : FormField{name, + content_disposition::kFormData, + content_type.empty() ? mime::multipart::kMixed : content_type, + {}}, + boundary_{boundary} { + if (boundary_.empty()) + boundary_ = base::StringPrintf("%016" PRIx64, base::RandUint64()); +} + +bool MultiPartFormField::ExtractDataStreams(std::vector<StreamPtr>* streams) { + for (auto& part : parts_) { + std::string data = GetBoundaryStart() + part->GetContentHeader(); + streams->push_back(MemoryStream::OpenCopyOf(data, nullptr)); + if (!part->ExtractDataStreams(streams)) + return false; + + streams->push_back(MemoryStream::OpenRef("\r\n", nullptr)); + } + if (!parts_.empty()) { + std::string data = GetBoundaryEnd(); + streams->push_back(MemoryStream::OpenCopyOf(data, nullptr)); + } + return true; +} + +std::string MultiPartFormField::GetContentType() const { + return base::StringPrintf( + "%s; boundary=\"%s\"", content_type_.c_str(), boundary_.c_str()); +} + +void MultiPartFormField::AddCustomField(std::unique_ptr<FormField> field) { + parts_.push_back(std::move(field)); +} + +void MultiPartFormField::AddTextField(const std::string& name, + const std::string& data) { + AddCustomField(std::unique_ptr<FormField>{new TextFormField{name, data}}); +} + +bool MultiPartFormField::AddFileField(const std::string& name, + const base::FilePath& file_path, + const std::string& content_disposition, + const std::string& content_type, + brillo::ErrorPtr* error) { + StreamPtr stream = FileStream::Open(file_path, Stream::AccessMode::READ, + FileStream::Disposition::OPEN_EXISTING, + error); + if (!stream) + return false; + std::string file_name = file_path.BaseName().value(); + std::unique_ptr<FormField> file_field{new FileFormField{name, + std::move(stream), + file_name, + content_disposition, + content_type, + "binary"}}; + AddCustomField(std::move(file_field)); + return true; +} + +std::string MultiPartFormField::GetBoundaryStart() const { + return base::StringPrintf("--%s\r\n", boundary_.c_str()); +} + +std::string MultiPartFormField::GetBoundaryEnd() const { + return base::StringPrintf("--%s--", boundary_.c_str()); +} + +FormData::FormData() : FormData{std::string{}} { +} + +FormData::FormData(const std::string& boundary) + : form_data_{"", mime::multipart::kFormData, boundary} { +} + +void FormData::AddCustomField(std::unique_ptr<FormField> field) { + form_data_.AddCustomField(std::move(field)); +} + +void FormData::AddTextField(const std::string& name, const std::string& data) { + form_data_.AddTextField(name, data); +} + +bool FormData::AddFileField(const std::string& name, + const base::FilePath& file_path, + const std::string& content_type, + brillo::ErrorPtr* error) { + return form_data_.AddFileField( + name, file_path, content_disposition::kFormData, content_type, error); +} + +std::string FormData::GetContentType() const { + return form_data_.GetContentType(); +} + +StreamPtr FormData::ExtractDataStream() { + std::vector<StreamPtr> source_streams; + if (form_data_.ExtractDataStreams(&source_streams)) + return InputStreamSet::Create(std::move(source_streams), nullptr); + return {}; +} + +} // namespace http +} // namespace brillo |