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 20 namespace brillo { 21 namespace http { 22 23 namespace form_header { 24 const char kContentDisposition[] = "Content-Disposition"; 25 const char kContentTransferEncoding[] = "Content-Transfer-Encoding"; 26 const char kContentType[] = "Content-Type"; 27 } // namespace form_header 28 29 const char content_disposition::kFile[] = "file"; 30 const char content_disposition::kFormData[] = "form-data"; 31 32 FormField::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 42 std::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 49 std::string FormField::GetContentType() const { 50 return content_type_; 51 } 52 53 std::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 75 TextFormField::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 86 bool TextFormField::ExtractDataStreams(std::vector<StreamPtr>* streams) { 87 streams->push_back(MemoryStream::OpenCopyOf(data_, nullptr)); 88 return true; 89 } 90 91 FileFormField::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 102 std::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 108 bool FileFormField::ExtractDataStreams(std::vector<StreamPtr>* streams) { 109 if (!stream_) 110 return false; 111 streams->push_back(std::move(stream_)); 112 return true; 113 } 114 115 MultiPartFormField::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 127 bool 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 143 std::string MultiPartFormField::GetContentType() const { 144 return base::StringPrintf( 145 "%s; boundary=\"%s\"", content_type_.c_str(), boundary_.c_str()); 146 } 147 148 void MultiPartFormField::AddCustomField(std::unique_ptr<FormField> field) { 149 parts_.push_back(std::move(field)); 150 } 151 152 void MultiPartFormField::AddTextField(const std::string& name, 153 const std::string& data) { 154 AddCustomField(std::unique_ptr<FormField>{new TextFormField{name, data}}); 155 } 156 157 bool 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 178 std::string MultiPartFormField::GetBoundaryStart() const { 179 return base::StringPrintf("--%s\r\n", boundary_.c_str()); 180 } 181 182 std::string MultiPartFormField::GetBoundaryEnd() const { 183 return base::StringPrintf("--%s--", boundary_.c_str()); 184 } 185 186 FormData::FormData() : FormData{std::string{}} { 187 } 188 189 FormData::FormData(const std::string& boundary) 190 : form_data_{"", mime::multipart::kFormData, boundary} { 191 } 192 193 void FormData::AddCustomField(std::unique_ptr<FormField> field) { 194 form_data_.AddCustomField(std::move(field)); 195 } 196 197 void FormData::AddTextField(const std::string& name, const std::string& data) { 198 form_data_.AddTextField(name, data); 199 } 200 201 bool 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 209 std::string FormData::GetContentType() const { 210 return form_data_.GetContentType(); 211 } 212 213 StreamPtr 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