1// Copyright 2014 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <brillo/http/http_form_data.h> 6 7#include <limits> 8 9#include <base/format_macros.h> 10#include <base/rand_util.h> 11#include <base/strings/stringprintf.h> 12 13#include <brillo/errors/error_codes.h> 14#include <brillo/http/http_transport.h> 15#include <brillo/mime_utils.h> 16#include <brillo/streams/file_stream.h> 17#include <brillo/streams/input_stream_set.h> 18#include <brillo/streams/memory_stream.h> 19 20namespace brillo { 21namespace http { 22 23namespace form_header { 24const char kContentDisposition[] = "Content-Disposition"; 25const char kContentTransferEncoding[] = "Content-Transfer-Encoding"; 26const char kContentType[] = "Content-Type"; 27} // namespace form_header 28 29const char content_disposition::kFile[] = "file"; 30const char content_disposition::kFormData[] = "form-data"; 31 32FormField::FormField(const std::string& name, 33 const std::string& content_disposition, 34 const std::string& content_type, 35 const std::string& transfer_encoding) 36 : name_{name}, 37 content_disposition_{content_disposition}, 38 content_type_{content_type}, 39 transfer_encoding_{transfer_encoding} { 40} 41 42std::string FormField::GetContentDisposition() const { 43 std::string disposition = content_disposition_; 44 if (!name_.empty()) 45 base::StringAppendF(&disposition, "; name=\"%s\"", name_.c_str()); 46 return disposition; 47} 48 49std::string FormField::GetContentType() const { 50 return content_type_; 51} 52 53std::string FormField::GetContentHeader() const { 54 HeaderList headers{ 55 {form_header::kContentDisposition, GetContentDisposition()} 56 }; 57 58 if (!content_type_.empty()) 59 headers.emplace_back(form_header::kContentType, GetContentType()); 60 61 if (!transfer_encoding_.empty()) { 62 headers.emplace_back(form_header::kContentTransferEncoding, 63 transfer_encoding_); 64 } 65 66 std::string result; 67 for (const auto& pair : headers) { 68 base::StringAppendF( 69 &result, "%s: %s\r\n", pair.first.c_str(), pair.second.c_str()); 70 } 71 result += "\r\n"; 72 return result; 73} 74 75TextFormField::TextFormField(const std::string& name, 76 const std::string& data, 77 const std::string& content_type, 78 const std::string& transfer_encoding) 79 : FormField{name, 80 content_disposition::kFormData, 81 content_type, 82 transfer_encoding}, 83 data_{data} { 84} 85 86bool TextFormField::ExtractDataStreams(std::vector<StreamPtr>* streams) { 87 streams->push_back(MemoryStream::OpenCopyOf(data_, nullptr)); 88 return true; 89} 90 91FileFormField::FileFormField(const std::string& name, 92 StreamPtr stream, 93 const std::string& file_name, 94 const std::string& content_disposition, 95 const std::string& content_type, 96 const std::string& transfer_encoding) 97 : FormField{name, content_disposition, content_type, transfer_encoding}, 98 stream_{std::move(stream)}, 99 file_name_{file_name} { 100} 101 102std::string FileFormField::GetContentDisposition() const { 103 std::string disposition = FormField::GetContentDisposition(); 104 base::StringAppendF(&disposition, "; filename=\"%s\"", file_name_.c_str()); 105 return disposition; 106} 107 108bool FileFormField::ExtractDataStreams(std::vector<StreamPtr>* streams) { 109 if (!stream_) 110 return false; 111 streams->push_back(std::move(stream_)); 112 return true; 113} 114 115MultiPartFormField::MultiPartFormField(const std::string& name, 116 const std::string& content_type, 117 const std::string& boundary) 118 : FormField{name, 119 content_disposition::kFormData, 120 content_type.empty() ? mime::multipart::kMixed : content_type, 121 {}}, 122 boundary_{boundary} { 123 if (boundary_.empty()) 124 boundary_ = base::StringPrintf("%016" PRIx64, base::RandUint64()); 125} 126 127bool MultiPartFormField::ExtractDataStreams(std::vector<StreamPtr>* streams) { 128 for (auto& part : parts_) { 129 std::string data = GetBoundaryStart() + part->GetContentHeader(); 130 streams->push_back(MemoryStream::OpenCopyOf(data, nullptr)); 131 if (!part->ExtractDataStreams(streams)) 132 return false; 133 134 streams->push_back(MemoryStream::OpenRef("\r\n", nullptr)); 135 } 136 if (!parts_.empty()) { 137 std::string data = GetBoundaryEnd(); 138 streams->push_back(MemoryStream::OpenCopyOf(data, nullptr)); 139 } 140 return true; 141} 142 143std::string MultiPartFormField::GetContentType() const { 144 return base::StringPrintf( 145 "%s; boundary=\"%s\"", content_type_.c_str(), boundary_.c_str()); 146} 147 148void MultiPartFormField::AddCustomField(std::unique_ptr<FormField> field) { 149 parts_.push_back(std::move(field)); 150} 151 152void MultiPartFormField::AddTextField(const std::string& name, 153 const std::string& data) { 154 AddCustomField(std::unique_ptr<FormField>{new TextFormField{name, data}}); 155} 156 157bool MultiPartFormField::AddFileField(const std::string& name, 158 const base::FilePath& file_path, 159 const std::string& content_disposition, 160 const std::string& content_type, 161 brillo::ErrorPtr* error) { 162 StreamPtr stream = FileStream::Open(file_path, Stream::AccessMode::READ, 163 FileStream::Disposition::OPEN_EXISTING, 164 error); 165 if (!stream) 166 return false; 167 std::string file_name = file_path.BaseName().value(); 168 std::unique_ptr<FormField> file_field{new FileFormField{name, 169 std::move(stream), 170 file_name, 171 content_disposition, 172 content_type, 173 "binary"}}; 174 AddCustomField(std::move(file_field)); 175 return true; 176} 177 178std::string MultiPartFormField::GetBoundaryStart() const { 179 return base::StringPrintf("--%s\r\n", boundary_.c_str()); 180} 181 182std::string MultiPartFormField::GetBoundaryEnd() const { 183 return base::StringPrintf("--%s--", boundary_.c_str()); 184} 185 186FormData::FormData() : FormData{std::string{}} { 187} 188 189FormData::FormData(const std::string& boundary) 190 : form_data_{"", mime::multipart::kFormData, boundary} { 191} 192 193void FormData::AddCustomField(std::unique_ptr<FormField> field) { 194 form_data_.AddCustomField(std::move(field)); 195} 196 197void FormData::AddTextField(const std::string& name, const std::string& data) { 198 form_data_.AddTextField(name, data); 199} 200 201bool FormData::AddFileField(const std::string& name, 202 const base::FilePath& file_path, 203 const std::string& content_type, 204 brillo::ErrorPtr* error) { 205 return form_data_.AddFileField( 206 name, file_path, content_disposition::kFormData, content_type, error); 207} 208 209std::string FormData::GetContentType() const { 210 return form_data_.GetContentType(); 211} 212 213StreamPtr FormData::ExtractDataStream() { 214 std::vector<StreamPtr> source_streams; 215 if (form_data_.ExtractDataStreams(&source_streams)) 216 return InputStreamSet::Create(std::move(source_streams), nullptr); 217 return {}; 218} 219 220} // namespace http 221} // namespace brillo 222