| // 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 |