Home | History | Annotate | Download | only in http
      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