aboutsummaryrefslogtreecommitdiffstats
path: root/brillo/http/http_form_data.cc
diff options
context:
space:
mode:
Diffstat (limited to 'brillo/http/http_form_data.cc')
-rw-r--r--brillo/http/http_form_data.cc221
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