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/mime_utils.h>
6 
7 #include <algorithm>
8 #include <base/strings/string_util.h>
9 #include <brillo/strings/string_utils.h>
10 
11 namespace brillo {
12 
13 // ***************************************************************************
14 // ******************************* MIME types ********************************
15 // ***************************************************************************
16 const char mime::types::kApplication[]             = "application";
17 const char mime::types::kAudio[]                   = "audio";
18 const char mime::types::kImage[]                   = "image";
19 const char mime::types::kMessage[]                 = "message";
20 const char mime::types::kMultipart[]               = "multipart";
21 const char mime::types::kText[]                    = "text";
22 const char mime::types::kVideo[]                   = "video";
23 
24 const char mime::parameters::kCharset[]            = "charset";
25 
26 const char mime::image::kJpeg[]                    = "image/jpeg";
27 const char mime::image::kPng[]                     = "image/png";
28 const char mime::image::kBmp[]                     = "image/bmp";
29 const char mime::image::kTiff[]                    = "image/tiff";
30 const char mime::image::kGif[]                     = "image/gif";
31 
32 const char mime::text::kPlain[]                    = "text/plain";
33 const char mime::text::kHtml[]                     = "text/html";
34 const char mime::text::kXml[]                      = "text/xml";
35 
36 const char mime::application::kOctet_stream[]      = "application/octet-stream";
37 const char mime::application::kJson[]              = "application/json";
38 const char mime::application::kWwwFormUrlEncoded[] =
39     "application/x-www-form-urlencoded";
40 const char mime::application::kProtobuf[]          = "application/x-protobuf";
41 
42 const char mime::multipart::kFormData[]            = "multipart/form-data";
43 const char mime::multipart::kMixed[]               = "multipart/mixed";
44 
45 // ***************************************************************************
46 // **************************** Utility Functions ****************************
47 // ***************************************************************************
EncodeParam(const std::string & param)48 static std::string EncodeParam(const std::string& param) {
49   // If the string contains one of "tspecials" characters as
50   // specified in RFC 1521, enclose it in quotes.
51   if (param.find_first_of("()<>@,;:\\\"/[]?=") != std::string::npos) {
52     return '"' + param + '"';
53   }
54   return param;
55 }
56 
DecodeParam(const std::string & param)57 static std::string DecodeParam(const std::string& param) {
58   if (param.size() > 1 && param.front() == '"' && param.back() == '"') {
59     return param.substr(1, param.size() - 2);
60   }
61   return param;
62 }
63 
64 // ***************************************************************************
65 // ******************** Main MIME manipulation functions *********************
66 // ***************************************************************************
67 
Split(const std::string & mime_string,std::string * type,std::string * subtype,mime::Parameters * parameters)68 bool mime::Split(const std::string& mime_string,
69                  std::string* type,
70                  std::string* subtype,
71                  mime::Parameters* parameters) {
72   std::vector<std::string> parts =
73       brillo::string_utils::Split(mime_string, ";");
74   if (parts.empty())
75     return false;
76 
77   if (!mime::Split(parts.front(), type, subtype))
78     return false;
79 
80   if (parameters) {
81     parameters->clear();
82     parameters->reserve(parts.size() - 1);
83     for (size_t i = 1; i < parts.size(); i++) {
84       auto pair = brillo::string_utils::SplitAtFirst(parts[i], "=");
85       pair.second = DecodeParam(pair.second);
86       parameters->push_back(pair);
87     }
88   }
89   return true;
90 }
91 
Split(const std::string & mime_string,std::string * type,std::string * subtype)92 bool mime::Split(const std::string& mime_string,
93                  std::string* type,
94                  std::string* subtype) {
95   std::string mime = mime::RemoveParameters(mime_string);
96   auto types = brillo::string_utils::SplitAtFirst(mime, "/");
97 
98   if (type)
99     *type = types.first;
100 
101   if (subtype)
102     *subtype = types.second;
103 
104   return !types.first.empty() && !types.second.empty();
105 }
106 
Combine(const std::string & type,const std::string & subtype,const mime::Parameters & parameters)107 std::string mime::Combine(const std::string& type,
108                           const std::string& subtype,
109                           const mime::Parameters& parameters) {
110   std::vector<std::string> parts;
111   parts.push_back(brillo::string_utils::Join("/", type, subtype));
112   for (const auto& pair : parameters) {
113     parts.push_back(
114         brillo::string_utils::Join("=", pair.first, EncodeParam(pair.second)));
115   }
116   return brillo::string_utils::Join("; ", parts);
117 }
118 
GetType(const std::string & mime_string)119 std::string mime::GetType(const std::string& mime_string) {
120   std::string mime = mime::RemoveParameters(mime_string);
121   return brillo::string_utils::SplitAtFirst(mime, "/").first;
122 }
123 
GetSubtype(const std::string & mime_string)124 std::string mime::GetSubtype(const std::string& mime_string) {
125   std::string mime = mime::RemoveParameters(mime_string);
126   return brillo::string_utils::SplitAtFirst(mime, "/").second;
127 }
128 
GetParameters(const std::string & mime_string)129 mime::Parameters mime::GetParameters(const std::string& mime_string) {
130   std::string type;
131   std::string subtype;
132   mime::Parameters parameters;
133 
134   if (mime::Split(mime_string, &type, &subtype, &parameters))
135     return parameters;
136 
137   return mime::Parameters();
138 }
139 
RemoveParameters(const std::string & mime_string)140 std::string mime::RemoveParameters(const std::string& mime_string) {
141   return brillo::string_utils::SplitAtFirst(mime_string, ";").first;
142 }
143 
AppendParameter(const std::string & mime_string,const std::string & paramName,const std::string & paramValue)144 std::string mime::AppendParameter(const std::string& mime_string,
145                                   const std::string& paramName,
146                                   const std::string& paramValue) {
147   std::string mime(mime_string);
148   mime += "; ";
149   mime += brillo::string_utils::Join("=", paramName, EncodeParam(paramValue));
150   return mime;
151 }
152 
GetParameterValue(const std::string & mime_string,const std::string & paramName)153 std::string mime::GetParameterValue(const std::string& mime_string,
154                                     const std::string& paramName) {
155   mime::Parameters params = mime::GetParameters(mime_string);
156   for (const auto& pair : params) {
157     if (base::EqualsCaseInsensitiveASCII(pair.first.c_str(), paramName.c_str()))
158       return pair.second;
159   }
160   return std::string();
161 }
162 
163 }  // namespace brillo
164