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