• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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