blob: 75c84c2fc84e9efcf7a347b4923d7b900a89885d [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
Ed Tanous1abe55e2018-09-05 08:30:59 -07002
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "bmcweb_config.h"
4
Ed Tanous51dae672018-09-05 16:07:32 -07005#include <openssl/crypto.h>
6
Ed Tanousc867a832022-03-10 14:17:00 -08007#include <boost/callable_traits.hpp>
Ed Tanous079360a2022-06-29 10:05:19 -07008#include <boost/url/parse.hpp>
Ed Tanouseae855c2021-10-26 11:26:02 -07009#include <boost/url/url.hpp>
Ed Tanous079360a2022-06-29 10:05:19 -070010#include <boost/url/url_view.hpp>
Ed Tanous71f2db72022-05-25 12:28:09 -070011#include <nlohmann/json.hpp>
Nan Zhou1d8782e2021-11-29 22:23:18 -080012
Ed Tanous9ea15c32022-01-04 14:18:22 -080013#include <array>
Ed Tanous74849be2021-02-05 09:47:47 -080014#include <chrono>
Ed Tanousc715ec22022-03-10 15:38:01 -080015#include <cstddef>
Ed Tanous7045c8d2017-04-03 10:04:37 -070016#include <cstdint>
Ed Tanous9ea15c32022-01-04 14:18:22 -080017#include <ctime>
Ed Tanous7045c8d2017-04-03 10:04:37 -070018#include <functional>
Ed Tanous9896eae2022-07-23 15:07:33 -070019#include <iomanip>
Ed Tanous9ea15c32022-01-04 14:18:22 -080020#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070021#include <stdexcept>
22#include <string>
Ed Tanous9ea15c32022-01-04 14:18:22 -080023#include <string_view>
Ed Tanous7045c8d2017-04-03 10:04:37 -070024#include <tuple>
Ed Tanous9ea15c32022-01-04 14:18:22 -080025#include <type_traits>
26#include <utility>
Szymon Dompkeca1600c2022-03-03 14:42:52 +010027#include <variant>
Ed Tanous7045c8d2017-04-03 10:04:37 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
31namespace black_magic
32{
Ed Tanous7045c8d2017-04-03 10:04:37 -070033
Ed Tanousc715ec22022-03-10 15:38:01 -080034enum class TypeCode : uint8_t
35{
36 Unspecified = 0,
Ed Tanous15a42df2023-02-09 18:08:23 -080037 String = 1,
38 Path = 2,
39 Max = 3,
Ed Tanousc715ec22022-03-10 15:38:01 -080040};
41
42// Remove when we have c++23
43template <typename E>
44constexpr typename std::underlying_type<E>::type toUnderlying(E e) noexcept
45{
46 return static_cast<typename std::underlying_type<E>::type>(e);
47}
48
Gunnar Mills1214b7e2020-06-04 10:11:30 -050049template <typename... Args>
Ed Tanous988403c2020-08-24 11:29:49 -070050struct computeParameterTagFromArgsList;
Ed Tanous7045c8d2017-04-03 10:04:37 -070051
Gunnar Mills1214b7e2020-06-04 10:11:30 -050052template <>
Ed Tanous988403c2020-08-24 11:29:49 -070053struct computeParameterTagFromArgsList<>
Ed Tanous1abe55e2018-09-05 08:30:59 -070054{
Ed Tanous69509012019-10-24 16:53:05 -070055 static constexpr int value = 0;
Ed Tanous7045c8d2017-04-03 10:04:37 -070056};
57
58template <typename Arg, typename... Args>
Ed Tanous988403c2020-08-24 11:29:49 -070059struct computeParameterTagFromArgsList<Arg, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -070060{
Ed Tanous15a42df2023-02-09 18:08:23 -080061 static_assert(std::is_same_v<std::string, std::decay_t<Arg>>);
Ed Tanous69509012019-10-24 16:53:05 -070062 static constexpr int subValue =
Ed Tanous988403c2020-08-24 11:29:49 -070063 computeParameterTagFromArgsList<Args...>::value;
Ed Tanous15a42df2023-02-09 18:08:23 -080064 static constexpr int value = subValue * toUnderlying(TypeCode::String);
Ed Tanous7045c8d2017-04-03 10:04:37 -070065};
66
Ed Tanous988403c2020-08-24 11:29:49 -070067inline bool isParameterTagCompatible(uint64_t a, uint64_t b)
Ed Tanous1abe55e2018-09-05 08:30:59 -070068{
Ed Tanous1c30e502022-03-08 18:02:24 -080069 while (true)
Ed Tanous1abe55e2018-09-05 08:30:59 -070070 {
Ed Tanousef641b62022-06-28 19:53:24 -070071 if (a == 0 && b == 0)
Ed Tanous1c30e502022-03-08 18:02:24 -080072 {
Ed Tanousef641b62022-06-28 19:53:24 -070073 // Both tags were equivalent, parameters are compatible
74 return true;
Ed Tanous1c30e502022-03-08 18:02:24 -080075 }
Ed Tanousef641b62022-06-28 19:53:24 -070076 if (a == 0 || b == 0)
Ed Tanous1c30e502022-03-08 18:02:24 -080077 {
Ed Tanousef641b62022-06-28 19:53:24 -070078 // one of the tags had more parameters than the other
79 return false;
Ed Tanous1c30e502022-03-08 18:02:24 -080080 }
Ed Tanousc715ec22022-03-10 15:38:01 -080081 TypeCode sa = static_cast<TypeCode>(a % toUnderlying(TypeCode::Max));
82 TypeCode sb = static_cast<TypeCode>(b % toUnderlying(TypeCode::Max));
83
84 if (sa == TypeCode::Path)
Ed Tanous1c30e502022-03-08 18:02:24 -080085 {
Ed Tanousc715ec22022-03-10 15:38:01 -080086 sa = TypeCode::String;
Ed Tanous1c30e502022-03-08 18:02:24 -080087 }
Ed Tanousc715ec22022-03-10 15:38:01 -080088 if (sb == TypeCode::Path)
Ed Tanous1c30e502022-03-08 18:02:24 -080089 {
Ed Tanousc715ec22022-03-10 15:38:01 -080090 sb = TypeCode::String;
Ed Tanous1c30e502022-03-08 18:02:24 -080091 }
92 if (sa != sb)
93 {
94 return false;
95 }
Ed Tanousc715ec22022-03-10 15:38:01 -080096 a /= toUnderlying(TypeCode::Max);
97 b /= toUnderlying(TypeCode::Max);
Ed Tanous1abe55e2018-09-05 08:30:59 -070098 }
Ed Tanous1c30e502022-03-08 18:02:24 -080099 return false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700100}
101
Ed Tanous1c30e502022-03-08 18:02:24 -0800102constexpr inline uint64_t getParameterTag(std::string_view url)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103{
Ed Tanous1c30e502022-03-08 18:02:24 -0800104 uint64_t tagValue = 0;
105 size_t urlSegmentIndex = std::string_view::npos;
Ed Tanousb00dcc22021-02-23 12:52:50 -0800106
Ed Tanous1c30e502022-03-08 18:02:24 -0800107 size_t paramIndex = 0;
108
109 for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
110 {
111 char character = url[urlIndex];
112 if (character == '<')
113 {
114 if (urlSegmentIndex != std::string_view::npos)
115 {
116 return 0;
117 }
118 urlSegmentIndex = urlIndex;
119 }
120 if (character == '>')
121 {
122 if (urlSegmentIndex == std::string_view::npos)
123 {
124 return 0;
125 }
126 std::string_view tag =
127 url.substr(urlSegmentIndex, urlIndex + 1 - urlSegmentIndex);
128
129 // Note, this is a really lame way to do std::pow(6, paramIndex)
130 // std::pow doesn't work in constexpr in clang.
131 // Ideally in the future we'd move this to use a power of 2 packing
132 // (probably 8 instead of 6) so that these just become bit shifts
133 uint64_t insertIndex = 1;
134 for (size_t unused = 0; unused < paramIndex; unused++)
135 {
Ed Tanous15a42df2023-02-09 18:08:23 -0800136 insertIndex *= 3;
Ed Tanous1c30e502022-03-08 18:02:24 -0800137 }
138
Ed Tanous1c30e502022-03-08 18:02:24 -0800139 if (tag == "<str>" || tag == "<string>")
140 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800141 tagValue += insertIndex * toUnderlying(TypeCode::String);
Ed Tanous1c30e502022-03-08 18:02:24 -0800142 }
143 if (tag == "<path>")
144 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800145 tagValue += insertIndex * toUnderlying(TypeCode::Path);
Ed Tanous1c30e502022-03-08 18:02:24 -0800146 }
147 paramIndex++;
148 urlSegmentIndex = std::string_view::npos;
149 }
150 }
151 if (urlSegmentIndex != std::string_view::npos)
Ed Tanous988403c2020-08-24 11:29:49 -0700152 {
153 return 0;
154 }
Ed Tanous1c30e502022-03-08 18:02:24 -0800155 return tagValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700156}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700157
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500158template <typename... T>
159struct S
Ed Tanous1abe55e2018-09-05 08:30:59 -0700160{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500161 template <typename U>
162 using push = S<U, T...>;
163 template <typename U>
164 using push_back = S<T..., U>;
165 template <template <typename... Args> class U>
166 using rebind = U<T...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700167};
Ed Tanous988403c2020-08-24 11:29:49 -0700168
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500169template <typename F, typename Set>
170struct CallHelper;
Ed Tanous988403c2020-08-24 11:29:49 -0700171
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500172template <typename F, typename... Args>
173struct CallHelper<F, S<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174{
175 template <typename F1, typename... Args1,
176 typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
Ed Tanous2c70f802020-09-28 14:29:23 -0700177 static char test(int);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700178
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500179 template <typename...>
Ed Tanous2c70f802020-09-28 14:29:23 -0700180 static int test(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700181
Ed Tanous2c70f802020-09-28 14:29:23 -0700182 static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700183};
184
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500185template <uint64_t Tag>
186struct Arguments
Ed Tanous1abe55e2018-09-05 08:30:59 -0700187{
Ed Tanous15a42df2023-02-09 18:08:23 -0800188 using subarguments = typename Arguments<Tag / 3>::type;
189 using type = typename subarguments::template push<std::string>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700190};
191
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500192template <>
193struct Arguments<0>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700194{
195 using type = S<>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700196};
197
Ed Tanous1abe55e2018-09-05 08:30:59 -0700198} // namespace black_magic
Ed Tanous7045c8d2017-04-03 10:04:37 -0700199
Ed Tanous1abe55e2018-09-05 08:30:59 -0700200namespace utility
201{
Ed Tanous7045c8d2017-04-03 10:04:37 -0700202
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500203template <typename T>
Ed Tanousc867a832022-03-10 14:17:00 -0800204struct FunctionTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700205{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500206 template <size_t i>
Ed Tanousc867a832022-03-10 14:17:00 -0800207 using arg = std::tuple_element_t<i, boost::callable_traits::args_t<T>>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700208};
209
Ed Tanous26ccae32023-02-16 10:28:44 -0800210inline std::string base64encode(std::string_view data)
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600211{
212 const std::array<char, 64> key = {
213 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
214 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
215 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
216 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
217 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
218
219 size_t size = data.size();
220 std::string ret;
221 ret.resize((size + 2) / 3 * 4);
222 auto it = ret.begin();
223
224 size_t i = 0;
225 while (i < size)
226 {
Ed Tanous543f4402022-01-06 13:12:53 -0800227 size_t keyIndex = 0;
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600228
229 keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
230 *it++ = key[keyIndex];
231
232 if (i + 1 < size)
233 {
234 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
235 keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
236 *it++ = key[keyIndex];
237
238 if (i + 2 < size)
239 {
240 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
241 keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
242 *it++ = key[keyIndex];
243
244 keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
245 *it++ = key[keyIndex];
246 }
247 else
248 {
249 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
250 *it++ = key[keyIndex];
251 *it++ = '=';
252 }
253 }
254 else
255 {
256 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
257 *it++ = key[keyIndex];
258 *it++ = '=';
259 *it++ = '=';
260 }
261
262 i += 3;
263 }
264
265 return ret;
266}
267
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100268// TODO this is temporary and should be deleted once base64 is refactored out of
269// crow
Ed Tanous26ccae32023-02-16 10:28:44 -0800270inline bool base64Decode(std::string_view input, std::string& output)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700271{
Ed Tanous271584a2019-07-09 16:24:22 -0700272 static const char nop = static_cast<char>(-1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 // See note on encoding_data[] in above function
Jonathan Doman5beaf842020-08-14 11:23:33 -0700274 static const std::array<char, 256> decodingData = {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700275 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
276 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
277 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
278 nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59,
279 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4,
280 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
281 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26,
282 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
283 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop,
284 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
285 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
286 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
287 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
288 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
289 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
290 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
291 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
292 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
293 nop, nop, nop, nop};
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100294
Ed Tanous1abe55e2018-09-05 08:30:59 -0700295 size_t inputLength = input.size();
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100296
Ed Tanous1abe55e2018-09-05 08:30:59 -0700297 // allocate space for output string
298 output.clear();
299 output.reserve(((inputLength + 2) / 3) * 4);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100300
Jonathan Doman5beaf842020-08-14 11:23:33 -0700301 auto getCodeValue = [](char c) {
302 auto code = static_cast<unsigned char>(c);
303 // Ensure we cannot index outside the bounds of the decoding array
304 static_assert(std::numeric_limits<decltype(code)>::max() <
305 decodingData.size());
306 return decodingData[code];
307 };
308
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309 // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500310 // dropping first two bits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311 // and regenerate into 3 8-bits sequences
James Feist5a806642020-07-31 16:40:33 +0000312
Ed Tanous1abe55e2018-09-05 08:30:59 -0700313 for (size_t i = 0; i < inputLength; i++)
314 {
Ed Tanous543f4402022-01-06 13:12:53 -0800315 char base64code0 = 0;
316 char base64code1 = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700317 char base64code2 = 0; // initialized to 0 to suppress warnings
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100318
Jonathan Doman5beaf842020-08-14 11:23:33 -0700319 base64code0 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700320 if (base64code0 == nop)
321 { // non base64 character
322 return false;
323 }
324 if (!(++i < inputLength))
325 { // we need at least two input bytes for first
326 // byte output
327 return false;
328 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700329 base64code1 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700330 if (base64code1 == nop)
331 { // non base64 character
332 return false;
333 }
334 output +=
335 static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100336
Ed Tanous1abe55e2018-09-05 08:30:59 -0700337 if (++i < inputLength)
338 {
339 char c = input[i];
340 if (c == '=')
341 { // padding , end of input
342 return (base64code1 & 0x0f) == 0;
343 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700344 base64code2 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700345 if (base64code2 == nop)
346 { // non base64 character
347 return false;
348 }
349 output += static_cast<char>(((base64code1 << 4) & 0xf0) |
350 ((base64code2 >> 2) & 0x0f));
351 }
352
353 if (++i < inputLength)
354 {
355 char c = input[i];
356 if (c == '=')
357 { // padding , end of input
358 return (base64code2 & 0x03) == 0;
359 }
Ed Tanousf8fe53e2022-06-30 15:55:45 -0700360 char base64code3 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700361 if (base64code3 == nop)
362 { // non base64 character
363 return false;
364 }
365 output +=
366 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
367 }
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100368 }
369
Ed Tanous1abe55e2018-09-05 08:30:59 -0700370 return true;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100371}
372
Ed Tanous26ccae32023-02-16 10:28:44 -0800373inline bool constantTimeStringCompare(std::string_view a, std::string_view b)
Ed Tanous51dae672018-09-05 16:07:32 -0700374{
375 // Important note, this function is ONLY constant time if the two input
376 // sizes are the same
377 if (a.size() != b.size())
378 {
379 return false;
380 }
381 return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
382}
383
384struct ConstantTimeCompare
385{
Ed Tanous26ccae32023-02-16 10:28:44 -0800386 bool operator()(std::string_view a, std::string_view b) const
Ed Tanous51dae672018-09-05 16:07:32 -0700387 {
388 return constantTimeStringCompare(a, b);
389 }
390};
391
Ed Tanouseae855c2021-10-26 11:26:02 -0700392namespace details
393{
394inline boost::urls::url
Willy Tuc6bcedc2022-09-27 05:36:59 +0000395 appendUrlPieces(boost::urls::url& url,
396 const std::initializer_list<std::string_view> args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700397{
Ed Tanous26ccae32023-02-16 10:28:44 -0800398 for (std::string_view arg : args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700399 {
400 url.segments().push_back(arg);
401 }
402 return url;
403}
Willy Tuc6bcedc2022-09-27 05:36:59 +0000404
405inline boost::urls::url
406 urlFromPiecesDetail(const std::initializer_list<std::string_view> args)
407{
408 boost::urls::url url("/");
409 appendUrlPieces(url, args);
410 return url;
411}
Ed Tanouseae855c2021-10-26 11:26:02 -0700412} // namespace details
413
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700414class OrMorePaths
415{};
416
Ed Tanouseae855c2021-10-26 11:26:02 -0700417template <typename... AV>
418inline boost::urls::url urlFromPieces(const AV... args)
419{
420 return details::urlFromPiecesDetail({args...});
421}
422
Willy Tuc6bcedc2022-09-27 05:36:59 +0000423template <typename... AV>
424inline void appendUrlPieces(boost::urls::url& url, const AV... args)
425{
426 details::appendUrlPieces(url, {args...});
427}
428
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100429namespace details
430{
431
432// std::reference_wrapper<std::string> - extracts segment to variable
433// std::string_view - checks if segment is equal to variable
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700434using UrlSegment = std::variant<std::reference_wrapper<std::string>,
435 std::string_view, OrMorePaths>;
436
437enum class UrlParseResult
438{
439 Continue,
440 Fail,
441 Done,
442};
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100443
444class UrlSegmentMatcherVisitor
445{
446 public:
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700447 UrlParseResult operator()(std::string& output)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100448 {
Ed Tanous079360a2022-06-29 10:05:19 -0700449 output = segment;
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700450 return UrlParseResult::Continue;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100451 }
452
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700453 UrlParseResult operator()(std::string_view expected)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100454 {
Ed Tanous079360a2022-06-29 10:05:19 -0700455 if (segment == expected)
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700456 {
457 return UrlParseResult::Continue;
458 }
459 return UrlParseResult::Fail;
460 }
461
462 UrlParseResult operator()(OrMorePaths /*unused*/)
463 {
464 return UrlParseResult::Done;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100465 }
466
Ed Tanous079360a2022-06-29 10:05:19 -0700467 explicit UrlSegmentMatcherVisitor(std::string_view segmentIn) :
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100468 segment(segmentIn)
469 {}
470
471 private:
Ed Tanous079360a2022-06-29 10:05:19 -0700472 std::string_view segment;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100473};
474
Ed Tanousd9f466b2023-03-06 15:04:25 -0800475inline bool readUrlSegments(boost::urls::url_view url,
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100476 std::initializer_list<UrlSegment>&& segments)
477{
Ed Tanousd9f466b2023-03-06 15:04:25 -0800478 boost::urls::segments_view urlSegments = url.segments();
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100479
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700480 if (!urlSegments.is_absolute())
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100481 {
482 return false;
483 }
484
485 boost::urls::segments_view::iterator it = urlSegments.begin();
486 boost::urls::segments_view::iterator end = urlSegments.end();
487
488 for (const auto& segment : segments)
489 {
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700490 if (it == end)
491 {
492 // If the request ends with an "any" path, this was successful
493 return std::holds_alternative<OrMorePaths>(segment);
494 }
495 UrlParseResult res = std::visit(UrlSegmentMatcherVisitor(*it), segment);
496 if (res == UrlParseResult::Done)
497 {
498 return true;
499 }
500 if (res == UrlParseResult::Fail)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100501 {
502 return false;
503 }
504 it++;
505 }
Carson Labrado4c30e222022-06-24 22:16:00 +0000506
507 // There will be an empty segment at the end if the URI ends with a "/"
508 // e.g. /redfish/v1/Chassis/
509 if ((it != end) && urlSegments.back().empty())
510 {
511 it++;
512 }
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700513 return it == end;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100514}
515
516} // namespace details
517
518template <typename... Args>
Ed Tanousd9f466b2023-03-06 15:04:25 -0800519inline bool readUrlSegments(boost::urls::url_view url, Args&&... args)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100520{
Ed Tanous39662a32023-02-06 15:09:46 -0800521 return details::readUrlSegments(url, {std::forward<Args>(args)...});
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100522}
523
Ed Tanousd9f466b2023-03-06 15:04:25 -0800524inline boost::urls::url replaceUrlSegment(boost::urls::url_view urlView,
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000525 const uint replaceLoc,
Ed Tanous26ccae32023-02-16 10:28:44 -0800526 std::string_view newSegment)
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000527{
Ed Tanousd9f466b2023-03-06 15:04:25 -0800528 boost::urls::segments_view urlSegments = urlView.segments();
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000529 boost::urls::url url("/");
530
531 if (!urlSegments.is_absolute())
532 {
533 return url;
534 }
535
536 boost::urls::segments_view::iterator it = urlSegments.begin();
537 boost::urls::segments_view::iterator end = urlSegments.end();
538
539 for (uint idx = 0; it != end; it++, idx++)
540 {
541 if (idx == replaceLoc)
542 {
543 url.segments().push_back(newSegment);
544 }
545 else
546 {
547 url.segments().push_back(*it);
548 }
549 }
550
551 return url;
552}
553
Ed Tanousd9f466b2023-03-06 15:04:25 -0800554inline std::string setProtocolDefaults(boost::urls::url_view urlView)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800555{
Ed Tanous39662a32023-02-06 15:09:46 -0800556 if (urlView.scheme() == "https")
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800557 {
558 return "https";
559 }
Ed Tanous39662a32023-02-06 15:09:46 -0800560 if (urlView.scheme() == "http")
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800561 {
562 if (bmcwebInsecureEnableHttpPushStyleEventing)
563 {
564 return "http";
565 }
566 return "";
567 }
568 return "";
569}
570
Ed Tanousd9f466b2023-03-06 15:04:25 -0800571inline uint16_t setPortDefaults(boost::urls::url_view url)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800572{
573 uint16_t port = url.port_number();
574 if (port != 0)
575 {
576 // user picked a port already.
577 return port;
578 }
579
580 // If the user hasn't explicitly stated a port, pick one explicitly for them
581 // based on the protocol defaults
582 if (url.scheme() == "http")
583 {
584 return 80;
585 }
586 if (url.scheme() == "https")
587 {
588 return 443;
589 }
590 return 0;
591}
592
Ed Tanous11baefe2022-02-09 12:14:12 -0800593inline bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto,
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800594 std::string& host, uint16_t& port,
Ed Tanous11baefe2022-02-09 12:14:12 -0800595 std::string& path)
596{
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800597 boost::urls::result<boost::urls::url_view> url =
Ed Tanous079360a2022-06-29 10:05:19 -0700598 boost::urls::parse_uri(destUrl);
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800599 if (!url)
600 {
601 return false;
602 }
603 urlProto = setProtocolDefaults(url.value());
604 if (urlProto.empty())
Ed Tanous11baefe2022-02-09 12:14:12 -0800605 {
606 return false;
607 }
608
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800609 port = setPortDefaults(url.value());
Ed Tanous11baefe2022-02-09 12:14:12 -0800610
Ed Tanous079360a2022-06-29 10:05:19 -0700611 host = url->encoded_host();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800612
Ed Tanous079360a2022-06-29 10:05:19 -0700613 path = url->encoded_path();
Ed Tanous11baefe2022-02-09 12:14:12 -0800614 if (path.empty())
615 {
616 path = "/";
617 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800618 if (url->has_fragment())
619 {
620 path += '#';
Ed Tanous079360a2022-06-29 10:05:19 -0700621 path += url->encoded_fragment();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800622 }
623
624 if (url->has_query())
625 {
626 path += '?';
Ed Tanous079360a2022-06-29 10:05:19 -0700627 path += url->encoded_query();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800628 }
629
Ed Tanous11baefe2022-02-09 12:14:12 -0800630 return true;
631}
632
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633} // namespace utility
634} // namespace crow
Ed Tanous71f2db72022-05-25 12:28:09 -0700635
636namespace nlohmann
637{
638template <>
639struct adl_serializer<boost::urls::url>
640{
641 // nlohmann requires a specific casing to look these up in adl
642 // NOLINTNEXTLINE(readability-identifier-naming)
643 static void to_json(json& j, const boost::urls::url& url)
644 {
Ed Tanous079360a2022-06-29 10:05:19 -0700645 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700646 }
647};
648
649template <>
650struct adl_serializer<boost::urls::url_view>
651{
652 // NOLINTNEXTLINE(readability-identifier-naming)
Ed Tanousd9f466b2023-03-06 15:04:25 -0800653 static void to_json(json& j, boost::urls::url_view url)
Ed Tanous71f2db72022-05-25 12:28:09 -0700654 {
Ed Tanous079360a2022-06-29 10:05:19 -0700655 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700656 }
657};
658} // namespace nlohmann