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 #ifndef LIBBRILLO_BRILLO_HTTP_HTTP_FORM_DATA_H_
6 #define LIBBRILLO_BRILLO_HTTP_HTTP_FORM_DATA_H_
7 
8 #include <memory>
9 #include <string>
10 #include <vector>
11 
12 #include <base/files/file_path.h>
13 #include <base/macros.h>
14 #include <brillo/brillo_export.h>
15 #include <brillo/errors/error.h>
16 #include <brillo/streams/stream.h>
17 
18 namespace brillo {
19 namespace http {
20 
21 namespace content_disposition {
22 BRILLO_EXPORT extern const char kFormData[];
23 BRILLO_EXPORT extern const char kFile[];
24 }  // namespace content_disposition
25 
26 // An abstract base class for all types of form fields used by FormData class.
27 // This class represents basic information about a form part in
28 // multipart/form-data and multipart/mixed content.
29 // For more details on multipart content, see the following RFC:
30 //    http://www.ietf.org/rfc/rfc2388
31 // For more details on MIME and content headers, see the following RFC:
32 //    http://www.ietf.org/rfc/rfc2045
33 class BRILLO_EXPORT FormField {
34  public:
35   // The constructor that takes the basic data part information common to
36   // all part types. An example of part's headers could include:
37   //
38   //    Content-Disposition: form-data; name="field1"
39   //    Content-Type: text/plain;charset=windows-1250
40   //    Content-Transfer-Encoding: quoted-printable
41   //
42   // The constructor parameters correspond to the basic part attributes:
43   //  |name| = the part name ("name" parameter of Content-Disposition header;
44   //           "field1" in the example above)
45   //  |content_disposition| = the part disposition ("form-data" in the example)
46   //  |content_type| = the content type ("text/plain;charset=windows-1250")
47   //  |transfer_encoding| = the encoding type for transport ("quoted-printable")
48   //                        See http://www.ietf.org/rfc/rfc2045, section 6.1
49   FormField(const std::string& name,
50             const std::string& content_disposition,
51             const std::string& content_type,
52             const std::string& transfer_encoding);
53   virtual ~FormField() = default;
54 
55   // Returns the full Content-Disposition header value. This might include the
56   // disposition type itself as well as the field "name" and/or "filename"
57   // parameters.
58   virtual std::string GetContentDisposition() const;
59 
60   // Returns the full content type of field data. MultiPartFormField overloads
61   // this method to append "boundary" parameter to it.
62   virtual std::string GetContentType() const;
63 
64   // Returns a string with all of the field headers, delimited by CRLF
65   // characters ("\r\n").
66   std::string GetContentHeader() const;
67 
68   // Adds the data stream(s) to the list of streams to read from.
69   // This is a potentially destructive operation and can be guaranteed to
70   // succeed only on the first try. Subsequent calls will fail for certain
71   // types of form fields.
72   virtual bool ExtractDataStreams(std::vector<StreamPtr>* streams) = 0;
73 
74  protected:
75   // Form field name. If not empty, it will be appended to Content-Disposition
76   // field header using "name" attribute.
77   std::string name_;
78 
79   // Form field disposition. Most of the time this will be "form-data". But for
80   // nested file uploads inside "multipart/mixed" sections, this can be "file".
81   std::string content_disposition_;
82 
83   // Content type. If omitted (empty), "plain/text" assumed.
84   std::string content_type_;
85 
86   // Transfer encoding for field data. If omitted, "7bit" is assumed. For most
87   // binary contents (e.g. for file content), use "binary".
88   std::string transfer_encoding_;
89 
90  private:
91   DISALLOW_COPY_AND_ASSIGN(FormField);
92 };
93 
94 // Simple text form field.
95 class BRILLO_EXPORT TextFormField : public FormField {
96  public:
97   // Constructor. Parameters:
98   //  name: field name
99   //  data: field text data
100   //  content_type: the data content type. Empty if not specified.
101   //  transfer_encoding: the encoding type of data. If omitted, no encoding
102   //      is specified (and "7bit" is assumed).
103   TextFormField(const std::string& name,
104                 const std::string& data,
105                 const std::string& content_type = {},
106                 const std::string& transfer_encoding = {});
107 
108   bool ExtractDataStreams(std::vector<StreamPtr>* streams) override;
109 
110  private:
111   std::string data_;  // Buffer/reader for field data.
112 
113   DISALLOW_COPY_AND_ASSIGN(TextFormField);
114 };
115 
116 // File upload form field.
117 class BRILLO_EXPORT FileFormField : public FormField {
118  public:
119   // Constructor. Parameters:
120   //  name: field name
121   //  stream: open stream with the contents of the file.
122   //  file_name: just the base file name of the file, e.g. "file.txt".
123   //      Used in "filename" parameter of Content-Disposition header.
124   //  content_type: valid content type of the file.
125   //  transfer_encoding: the encoding type of data.
126   //      If omitted, "binary" is used.
127   FileFormField(const std::string& name,
128                 StreamPtr stream,
129                 const std::string& file_name,
130                 const std::string& content_disposition,
131                 const std::string& content_type,
132                 const std::string& transfer_encoding = {});
133 
134   // Override from FormField.
135   // Appends "filename" parameter to Content-Disposition header.
136   std::string GetContentDisposition() const override;
137 
138   bool ExtractDataStreams(std::vector<StreamPtr>* streams) override;
139 
140  private:
141   StreamPtr stream_;
142   std::string file_name_;
143 
144   DISALLOW_COPY_AND_ASSIGN(FileFormField);
145 };
146 
147 // Multipart form field.
148 // This is used directly by FormData class to build the request body for
149 // form upload. It can also be used with multiple file uploads for a single
150 // file field, when the uploaded files should be sent as "multipart/mixed".
151 class BRILLO_EXPORT MultiPartFormField : public FormField {
152  public:
153   // Constructor. Parameters:
154   //  name: field name
155   //  content_type: valid content type. If omitted, "multipart/mixed" is used.
156   //  boundary: multipart boundary separator.
157   //      If omitted/empty, a random string is generated.
158   MultiPartFormField(const std::string& name,
159                      const std::string& content_type = {},
160                      const std::string& boundary = {});
161 
162   // Override from FormField.
163   // Appends "boundary" parameter to Content-Type header.
164   std::string GetContentType() const override;
165 
166   bool ExtractDataStreams(std::vector<StreamPtr>* streams) override;
167 
168   // Adds a form field to the form data. The |field| could be a simple text
169   // field, a file upload field or a multipart form field.
170   void AddCustomField(std::unique_ptr<FormField> field);
171 
172   // Adds a simple text form field.
173   void AddTextField(const std::string& name, const std::string& data);
174 
175   // Adds a file upload form field using a file path.
176   bool AddFileField(const std::string& name,
177                     const base::FilePath& file_path,
178                     const std::string& content_disposition,
179                     const std::string& content_type,
180                     brillo::ErrorPtr* error);
181 
182   // Returns a boundary string used to separate multipart form fields.
GetBoundary()183   const std::string& GetBoundary() const { return boundary_; }
184 
185  private:
186   // Returns the starting boundary string: "--<boundary>".
187   std::string GetBoundaryStart() const;
188   // Returns the ending boundary string: "--<boundary>--".
189   std::string GetBoundaryEnd() const;
190 
191   std::string boundary_;  // Boundary string used as field separator.
192   std::vector<std::unique_ptr<FormField>> parts_;  // Form field list.
193 
194   DISALLOW_COPY_AND_ASSIGN(MultiPartFormField);
195 };
196 
197 // A class representing a multipart form data for sending as HTTP POST request.
198 class BRILLO_EXPORT FormData final {
199  public:
200   FormData();
201   // Allows to specify a custom |boundary| separator string.
202   explicit FormData(const std::string& boundary);
203 
204   // Adds a form field to the form data. The |field| could be a simple text
205   // field, a file upload field or a multipart form field.
206   void AddCustomField(std::unique_ptr<FormField> field);
207 
208   // Adds a simple text form field.
209   void AddTextField(const std::string& name, const std::string& data);
210 
211   // Adds a file upload form field using a file path.
212   bool AddFileField(const std::string& name,
213                     const base::FilePath& file_path,
214                     const std::string& content_type,
215                     brillo::ErrorPtr* error);
216 
217   // Returns the complete content type string to be used in HTTP requests.
218   std::string GetContentType() const;
219 
220   // Returns the data stream for the form data. This is a potentially
221   // destructive operation and can be called only once.
222   StreamPtr ExtractDataStream();
223 
224  private:
225   MultiPartFormField form_data_;
226 
227   DISALLOW_COPY_AND_ASSIGN(FormData);
228 };
229 
230 }  // namespace http
231 }  // namespace brillo
232 
233 #endif  // LIBBRILLO_BRILLO_HTTP_HTTP_FORM_DATA_H_
234