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
FormField(const std::string & name,const std::string & content_disposition,const std::string & content_type,const std::string & transfer_encoding)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
GetContentDisposition() const42 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
GetContentType() const49 std::string FormField::GetContentType() const {
50 return content_type_;
51 }
52
GetContentHeader() const53 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
TextFormField(const std::string & name,const std::string & data,const std::string & content_type,const std::string & transfer_encoding)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
ExtractDataStreams(std::vector<StreamPtr> * streams)86 bool TextFormField::ExtractDataStreams(std::vector<StreamPtr>* streams) {
87 streams->push_back(MemoryStream::OpenCopyOf(data_, nullptr));
88 return true;
89 }
90
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)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
GetContentDisposition() const102 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
ExtractDataStreams(std::vector<StreamPtr> * streams)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
MultiPartFormField(const std::string & name,const std::string & content_type,const std::string & boundary)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
ExtractDataStreams(std::vector<StreamPtr> * streams)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
GetContentType() const143 std::string MultiPartFormField::GetContentType() const {
144 return base::StringPrintf(
145 "%s; boundary=\"%s\"", content_type_.c_str(), boundary_.c_str());
146 }
147
AddCustomField(std::unique_ptr<FormField> field)148 void MultiPartFormField::AddCustomField(std::unique_ptr<FormField> field) {
149 parts_.push_back(std::move(field));
150 }
151
AddTextField(const std::string & name,const std::string & data)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
AddFileField(const std::string & name,const base::FilePath & file_path,const std::string & content_disposition,const std::string & content_type,brillo::ErrorPtr * error)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
GetBoundaryStart() const178 std::string MultiPartFormField::GetBoundaryStart() const {
179 return base::StringPrintf("--%s\r\n", boundary_.c_str());
180 }
181
GetBoundaryEnd() const182 std::string MultiPartFormField::GetBoundaryEnd() const {
183 return base::StringPrintf("--%s--", boundary_.c_str());
184 }
185
FormData()186 FormData::FormData() : FormData{std::string{}} {
187 }
188
FormData(const std::string & boundary)189 FormData::FormData(const std::string& boundary)
190 : form_data_{"", mime::multipart::kFormData, boundary} {
191 }
192
AddCustomField(std::unique_ptr<FormField> field)193 void FormData::AddCustomField(std::unique_ptr<FormField> field) {
194 form_data_.AddCustomField(std::move(field));
195 }
196
AddTextField(const std::string & name,const std::string & data)197 void FormData::AddTextField(const std::string& name, const std::string& data) {
198 form_data_.AddTextField(name, data);
199 }
200
AddFileField(const std::string & name,const base::FilePath & file_path,const std::string & content_type,brillo::ErrorPtr * error)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
GetContentType() const209 std::string FormData::GetContentType() const {
210 return form_data_.GetContentType();
211 }
212
ExtractDataStream()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