1 // Copyright 2015 The Weave 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 "src/data_encoding.h"
6
7 #include <memory>
8
9 #include <base/logging.h>
10 #include <base/strings/string_util.h>
11 #include <base/strings/stringprintf.h>
12
13 #include "src/string_utils.h"
14 #include "third_party/modp_b64/modp_b64/modp_b64.h"
15
16 namespace weave {
17
18 namespace {
19
HexToDec(int hex)20 inline int HexToDec(int hex) {
21 int dec = -1;
22 if (hex >= '0' && hex <= '9') {
23 dec = hex - '0';
24 } else if (hex >= 'A' && hex <= 'F') {
25 dec = hex - 'A' + 10;
26 } else if (hex >= 'a' && hex <= 'f') {
27 dec = hex - 'a' + 10;
28 }
29 return dec;
30 }
31
32 // Helper for Base64Encode() and Base64EncodeWrapLines().
Base64EncodeHelper(const void * data,size_t size)33 std::string Base64EncodeHelper(const void* data, size_t size) {
34 std::vector<char> buffer;
35 buffer.resize(modp_b64_encode_len(size));
36 size_t out_size =
37 modp_b64_encode(buffer.data(), static_cast<const char*>(data), size);
38 return std::string{buffer.begin(), buffer.begin() + out_size};
39 }
40
41 } // namespace
42
UrlEncode(const char * data,bool encodeSpaceAsPlus)43 std::string UrlEncode(const char* data, bool encodeSpaceAsPlus) {
44 std::string result;
45
46 while (*data) {
47 char c = *data++;
48 // According to RFC3986 (http://www.faqs.org/rfcs/rfc3986.html),
49 // section 2.3. - Unreserved Characters
50 if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') ||
51 (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' ||
52 c == '~') {
53 result += c;
54 } else if (c == ' ' && encodeSpaceAsPlus) {
55 // For historical reasons, some URLs have spaces encoded as '+',
56 // this also applies to form data encoded as
57 // 'application/x-www-form-urlencoded'
58 result += '+';
59 } else {
60 base::StringAppendF(&result, "%%%02X",
61 static_cast<unsigned char>(c)); // Encode as %NN
62 }
63 }
64 return result;
65 }
66
UrlDecode(const char * data)67 std::string UrlDecode(const char* data) {
68 std::string result;
69 while (*data) {
70 char c = *data++;
71 int part1 = 0, part2 = 0;
72 // HexToDec would return -1 even for character 0 (end of string),
73 // so it is safe to access data[0] and data[1] without overrunning the buf.
74 if (c == '%' && (part1 = HexToDec(data[0])) >= 0 &&
75 (part2 = HexToDec(data[1])) >= 0) {
76 c = static_cast<char>((part1 << 4) | part2);
77 data += 2;
78 } else if (c == '+') {
79 c = ' ';
80 }
81 result += c;
82 }
83 return result;
84 }
85
WebParamsEncode(const WebParamList & params,bool encodeSpaceAsPlus)86 std::string WebParamsEncode(const WebParamList& params,
87 bool encodeSpaceAsPlus) {
88 std::vector<std::string> pairs;
89 pairs.reserve(params.size());
90 for (const auto& p : params) {
91 std::string key = UrlEncode(p.first.c_str(), encodeSpaceAsPlus);
92 std::string value = UrlEncode(p.second.c_str(), encodeSpaceAsPlus);
93 pairs.push_back(Join("=", key, value));
94 }
95
96 return Join("&", pairs);
97 }
98
WebParamsDecode(const std::string & data)99 WebParamList WebParamsDecode(const std::string& data) {
100 WebParamList result;
101 for (const auto& p : Split(data, "&", true, true)) {
102 auto pair = SplitAtFirst(p, "=", true);
103 result.emplace_back(UrlDecode(pair.first.c_str()),
104 UrlDecode(pair.second.c_str()));
105 }
106 return result;
107 }
108
Base64Encode(const void * data,size_t size)109 std::string Base64Encode(const void* data, size_t size) {
110 return Base64EncodeHelper(data, size);
111 }
112
Base64EncodeWrapLines(const void * data,size_t size)113 std::string Base64EncodeWrapLines(const void* data, size_t size) {
114 std::string unwrapped = Base64EncodeHelper(data, size);
115 std::string wrapped;
116
117 for (size_t i = 0; i < unwrapped.size(); i += 64) {
118 wrapped.append(unwrapped, i, 64);
119 wrapped.append("\n");
120 }
121 return wrapped;
122 }
123
Base64Decode(const std::string & input,std::vector<uint8_t> * output)124 bool Base64Decode(const std::string& input, std::vector<uint8_t>* output) {
125 std::string temp_buffer;
126 const std::string* data = &input;
127 if (input.find_first_of("\r\n") != std::string::npos) {
128 base::ReplaceChars(input, "\n", "", &temp_buffer);
129 base::ReplaceChars(temp_buffer, "\r", "", &temp_buffer);
130 data = &temp_buffer;
131 }
132 // base64 decoded data has 25% fewer bytes than the original (since every
133 // 3 source octets are encoded as 4 characters in base64).
134 // modp_b64_decode_len provides an upper estimate of the size of the output
135 // data.
136 output->resize(modp_b64_decode_len(data->size()));
137
138 size_t size_read = modp_b64_decode(reinterpret_cast<char*>(output->data()),
139 data->data(), data->size());
140 if (size_read == MODP_B64_ERROR) {
141 output->resize(0);
142 return false;
143 }
144 output->resize(size_read);
145
146 return true;
147 }
148
149 } // namespace weave
150