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
11namespace brillo {
12
13// ***************************************************************************
14// ******************************* MIME types ********************************
15// ***************************************************************************
16const char mime::types::kApplication[]             = "application";
17const char mime::types::kAudio[]                   = "audio";
18const char mime::types::kImage[]                   = "image";
19const char mime::types::kMessage[]                 = "message";
20const char mime::types::kMultipart[]               = "multipart";
21const char mime::types::kText[]                    = "text";
22const char mime::types::kVideo[]                   = "video";
23
24const char mime::parameters::kCharset[]            = "charset";
25
26const char mime::image::kJpeg[]                    = "image/jpeg";
27const char mime::image::kPng[]                     = "image/png";
28const char mime::image::kBmp[]                     = "image/bmp";
29const char mime::image::kTiff[]                    = "image/tiff";
30const char mime::image::kGif[]                     = "image/gif";
31
32const char mime::text::kPlain[]                    = "text/plain";
33const char mime::text::kHtml[]                     = "text/html";
34const char mime::text::kXml[]                      = "text/xml";
35
36const char mime::application::kOctet_stream[]      = "application/octet-stream";
37const char mime::application::kJson[]              = "application/json";
38const char mime::application::kWwwFormUrlEncoded[] =
39    "application/x-www-form-urlencoded";
40const char mime::application::kProtobuf[]          = "application/x-protobuf";
41
42const char mime::multipart::kFormData[]            = "multipart/form-data";
43const char mime::multipart::kMixed[]               = "multipart/mixed";
44
45// ***************************************************************************
46// **************************** Utility Functions ****************************
47// ***************************************************************************
48static 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
57static 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
68bool 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
92bool 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
107std::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
119std::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
124std::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
129mime::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
140std::string mime::RemoveParameters(const std::string& mime_string) {
141  return brillo::string_utils::SplitAtFirst(mime_string, ";").first;
142}
143
144std::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
153std::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