blob: 6717327bea85e226b3b5f554314efeacda41bb0d [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 Tanous7045c8d2017-04-03 10:04:37 -070099}
100
Ed Tanous1c30e502022-03-08 18:02:24 -0800101constexpr inline uint64_t getParameterTag(std::string_view url)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700102{
Ed Tanous1c30e502022-03-08 18:02:24 -0800103 uint64_t tagValue = 0;
104 size_t urlSegmentIndex = std::string_view::npos;
Ed Tanousb00dcc22021-02-23 12:52:50 -0800105
Ed Tanous1c30e502022-03-08 18:02:24 -0800106 size_t paramIndex = 0;
107
108 for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
109 {
110 char character = url[urlIndex];
111 if (character == '<')
112 {
113 if (urlSegmentIndex != std::string_view::npos)
114 {
115 return 0;
116 }
117 urlSegmentIndex = urlIndex;
118 }
119 if (character == '>')
120 {
121 if (urlSegmentIndex == std::string_view::npos)
122 {
123 return 0;
124 }
Patrick Williams89492a12023-05-10 07:51:34 -0500125 std::string_view tag = url.substr(urlSegmentIndex,
126 urlIndex + 1 - urlSegmentIndex);
Ed Tanous1c30e502022-03-08 18:02:24 -0800127
128 // Note, this is a really lame way to do std::pow(6, paramIndex)
129 // std::pow doesn't work in constexpr in clang.
130 // Ideally in the future we'd move this to use a power of 2 packing
131 // (probably 8 instead of 6) so that these just become bit shifts
132 uint64_t insertIndex = 1;
133 for (size_t unused = 0; unused < paramIndex; unused++)
134 {
Ed Tanous15a42df2023-02-09 18:08:23 -0800135 insertIndex *= 3;
Ed Tanous1c30e502022-03-08 18:02:24 -0800136 }
137
Ed Tanous1c30e502022-03-08 18:02:24 -0800138 if (tag == "<str>" || tag == "<string>")
139 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800140 tagValue += insertIndex * toUnderlying(TypeCode::String);
Ed Tanous1c30e502022-03-08 18:02:24 -0800141 }
142 if (tag == "<path>")
143 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800144 tagValue += insertIndex * toUnderlying(TypeCode::Path);
Ed Tanous1c30e502022-03-08 18:02:24 -0800145 }
146 paramIndex++;
147 urlSegmentIndex = std::string_view::npos;
148 }
149 }
150 if (urlSegmentIndex != std::string_view::npos)
Ed Tanous988403c2020-08-24 11:29:49 -0700151 {
152 return 0;
153 }
Ed Tanous1c30e502022-03-08 18:02:24 -0800154 return tagValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700155}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700156
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500157template <typename... T>
158struct S
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500160 template <typename U>
161 using push = S<U, T...>;
162 template <typename U>
163 using push_back = S<T..., U>;
164 template <template <typename... Args> class U>
165 using rebind = U<T...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700166};
Ed Tanous988403c2020-08-24 11:29:49 -0700167
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500168template <typename F, typename Set>
169struct CallHelper;
Ed Tanous988403c2020-08-24 11:29:49 -0700170
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500171template <typename F, typename... Args>
172struct CallHelper<F, S<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700173{
174 template <typename F1, typename... Args1,
175 typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
Ed Tanous2c70f802020-09-28 14:29:23 -0700176 static char test(int);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700177
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500178 template <typename...>
Ed Tanous2c70f802020-09-28 14:29:23 -0700179 static int test(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700180
Ed Tanous2c70f802020-09-28 14:29:23 -0700181 static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700182};
183
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500184template <uint64_t Tag>
185struct Arguments
Ed Tanous1abe55e2018-09-05 08:30:59 -0700186{
Ed Tanous15a42df2023-02-09 18:08:23 -0800187 using subarguments = typename Arguments<Tag / 3>::type;
188 using type = typename subarguments::template push<std::string>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700189};
190
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500191template <>
192struct Arguments<0>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700193{
194 using type = S<>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700195};
196
Ed Tanous1abe55e2018-09-05 08:30:59 -0700197} // namespace black_magic
Ed Tanous7045c8d2017-04-03 10:04:37 -0700198
Ed Tanous1abe55e2018-09-05 08:30:59 -0700199namespace utility
200{
Ed Tanous7045c8d2017-04-03 10:04:37 -0700201
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500202template <typename T>
Ed Tanousc867a832022-03-10 14:17:00 -0800203struct FunctionTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700204{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500205 template <size_t i>
Ed Tanousc867a832022-03-10 14:17:00 -0800206 using arg = std::tuple_element_t<i, boost::callable_traits::args_t<T>>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700207};
208
Ed Tanous26ccae32023-02-16 10:28:44 -0800209inline std::string base64encode(std::string_view data)
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600210{
211 const std::array<char, 64> key = {
212 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
213 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
214 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
215 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
216 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
217
218 size_t size = data.size();
219 std::string ret;
220 ret.resize((size + 2) / 3 * 4);
221 auto it = ret.begin();
222
223 size_t i = 0;
224 while (i < size)
225 {
Ed Tanous543f4402022-01-06 13:12:53 -0800226 size_t keyIndex = 0;
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600227
228 keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
229 *it++ = key[keyIndex];
230
231 if (i + 1 < size)
232 {
233 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
234 keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
235 *it++ = key[keyIndex];
236
237 if (i + 2 < size)
238 {
239 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
240 keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
241 *it++ = key[keyIndex];
242
243 keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
244 *it++ = key[keyIndex];
245 }
246 else
247 {
248 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
249 *it++ = key[keyIndex];
250 *it++ = '=';
251 }
252 }
253 else
254 {
255 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
256 *it++ = key[keyIndex];
257 *it++ = '=';
258 *it++ = '=';
259 }
260
261 i += 3;
262 }
263
264 return ret;
265}
266
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100267// TODO this is temporary and should be deleted once base64 is refactored out of
268// crow
Ed Tanous26ccae32023-02-16 10:28:44 -0800269inline bool base64Decode(std::string_view input, std::string& output)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700270{
Ed Tanous271584a2019-07-09 16:24:22 -0700271 static const char nop = static_cast<char>(-1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 // See note on encoding_data[] in above function
Jonathan Doman5beaf842020-08-14 11:23:33 -0700273 static const std::array<char, 256> decodingData = {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
275 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, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59,
278 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4,
279 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
280 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26,
281 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
282 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop,
283 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 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};
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100293
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294 size_t inputLength = input.size();
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100295
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296 // allocate space for output string
297 output.clear();
298 output.reserve(((inputLength + 2) / 3) * 4);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100299
Jonathan Doman5beaf842020-08-14 11:23:33 -0700300 auto getCodeValue = [](char c) {
301 auto code = static_cast<unsigned char>(c);
302 // Ensure we cannot index outside the bounds of the decoding array
303 static_assert(std::numeric_limits<decltype(code)>::max() <
304 decodingData.size());
305 return decodingData[code];
306 };
307
Ed Tanous1abe55e2018-09-05 08:30:59 -0700308 // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500309 // dropping first two bits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310 // and regenerate into 3 8-bits sequences
James Feist5a806642020-07-31 16:40:33 +0000311
Ed Tanous1abe55e2018-09-05 08:30:59 -0700312 for (size_t i = 0; i < inputLength; i++)
313 {
Ed Tanous543f4402022-01-06 13:12:53 -0800314 char base64code0 = 0;
315 char base64code1 = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700316 char base64code2 = 0; // initialized to 0 to suppress warnings
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100317
Jonathan Doman5beaf842020-08-14 11:23:33 -0700318 base64code0 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700319 if (base64code0 == nop)
320 { // non base64 character
321 return false;
322 }
323 if (!(++i < inputLength))
324 { // we need at least two input bytes for first
325 // byte output
326 return false;
327 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700328 base64code1 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700329 if (base64code1 == nop)
330 { // non base64 character
331 return false;
332 }
333 output +=
334 static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100335
Ed Tanous1abe55e2018-09-05 08:30:59 -0700336 if (++i < inputLength)
337 {
338 char c = input[i];
339 if (c == '=')
340 { // padding , end of input
341 return (base64code1 & 0x0f) == 0;
342 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700343 base64code2 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700344 if (base64code2 == nop)
345 { // non base64 character
346 return false;
347 }
348 output += static_cast<char>(((base64code1 << 4) & 0xf0) |
349 ((base64code2 >> 2) & 0x0f));
350 }
351
352 if (++i < inputLength)
353 {
354 char c = input[i];
355 if (c == '=')
356 { // padding , end of input
357 return (base64code2 & 0x03) == 0;
358 }
Ed Tanousf8fe53e2022-06-30 15:55:45 -0700359 char base64code3 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700360 if (base64code3 == nop)
361 { // non base64 character
362 return false;
363 }
364 output +=
365 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
366 }
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100367 }
368
Ed Tanous1abe55e2018-09-05 08:30:59 -0700369 return true;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100370}
371
Ed Tanous26ccae32023-02-16 10:28:44 -0800372inline bool constantTimeStringCompare(std::string_view a, std::string_view b)
Ed Tanous51dae672018-09-05 16:07:32 -0700373{
374 // Important note, this function is ONLY constant time if the two input
375 // sizes are the same
376 if (a.size() != b.size())
377 {
378 return false;
379 }
380 return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
381}
382
383struct ConstantTimeCompare
384{
Ed Tanous26ccae32023-02-16 10:28:44 -0800385 bool operator()(std::string_view a, std::string_view b) const
Ed Tanous51dae672018-09-05 16:07:32 -0700386 {
387 return constantTimeStringCompare(a, b);
388 }
389};
390
Ed Tanouseae855c2021-10-26 11:26:02 -0700391namespace details
392{
393inline boost::urls::url
Willy Tuc6bcedc2022-09-27 05:36:59 +0000394 appendUrlPieces(boost::urls::url& url,
395 const std::initializer_list<std::string_view> args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700396{
Ed Tanous26ccae32023-02-16 10:28:44 -0800397 for (std::string_view arg : args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700398 {
399 url.segments().push_back(arg);
400 }
401 return url;
402}
Willy Tuc6bcedc2022-09-27 05:36:59 +0000403
404inline boost::urls::url
405 urlFromPiecesDetail(const std::initializer_list<std::string_view> args)
406{
407 boost::urls::url url("/");
408 appendUrlPieces(url, args);
409 return url;
410}
Ed Tanouseae855c2021-10-26 11:26:02 -0700411} // namespace details
412
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700413class OrMorePaths
414{};
415
Ed Tanouseae855c2021-10-26 11:26:02 -0700416template <typename... AV>
417inline boost::urls::url urlFromPieces(const AV... args)
418{
419 return details::urlFromPiecesDetail({args...});
420}
421
Willy Tuc6bcedc2022-09-27 05:36:59 +0000422template <typename... AV>
423inline void appendUrlPieces(boost::urls::url& url, const AV... args)
424{
425 details::appendUrlPieces(url, {args...});
426}
427
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100428namespace details
429{
430
431// std::reference_wrapper<std::string> - extracts segment to variable
432// std::string_view - checks if segment is equal to variable
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700433using UrlSegment = std::variant<std::reference_wrapper<std::string>,
434 std::string_view, OrMorePaths>;
435
436enum class UrlParseResult
437{
438 Continue,
439 Fail,
440 Done,
441};
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100442
443class UrlSegmentMatcherVisitor
444{
445 public:
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700446 UrlParseResult operator()(std::string& output)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100447 {
Ed Tanous079360a2022-06-29 10:05:19 -0700448 output = segment;
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700449 return UrlParseResult::Continue;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100450 }
451
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700452 UrlParseResult operator()(std::string_view expected)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100453 {
Ed Tanous079360a2022-06-29 10:05:19 -0700454 if (segment == expected)
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700455 {
456 return UrlParseResult::Continue;
457 }
458 return UrlParseResult::Fail;
459 }
460
461 UrlParseResult operator()(OrMorePaths /*unused*/)
462 {
463 return UrlParseResult::Done;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100464 }
465
Ed Tanous079360a2022-06-29 10:05:19 -0700466 explicit UrlSegmentMatcherVisitor(std::string_view segmentIn) :
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100467 segment(segmentIn)
468 {}
469
470 private:
Ed Tanous079360a2022-06-29 10:05:19 -0700471 std::string_view segment;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100472};
473
Ed Tanousd9f466b2023-03-06 15:04:25 -0800474inline bool readUrlSegments(boost::urls::url_view url,
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100475 std::initializer_list<UrlSegment>&& segments)
476{
Ed Tanousd9f466b2023-03-06 15:04:25 -0800477 boost::urls::segments_view urlSegments = url.segments();
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100478
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700479 if (!urlSegments.is_absolute())
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100480 {
481 return false;
482 }
483
484 boost::urls::segments_view::iterator it = urlSegments.begin();
485 boost::urls::segments_view::iterator end = urlSegments.end();
486
487 for (const auto& segment : segments)
488 {
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700489 if (it == end)
490 {
491 // If the request ends with an "any" path, this was successful
492 return std::holds_alternative<OrMorePaths>(segment);
493 }
494 UrlParseResult res = std::visit(UrlSegmentMatcherVisitor(*it), segment);
495 if (res == UrlParseResult::Done)
496 {
497 return true;
498 }
499 if (res == UrlParseResult::Fail)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100500 {
501 return false;
502 }
503 it++;
504 }
Carson Labrado4c30e222022-06-24 22:16:00 +0000505
506 // There will be an empty segment at the end if the URI ends with a "/"
507 // e.g. /redfish/v1/Chassis/
508 if ((it != end) && urlSegments.back().empty())
509 {
510 it++;
511 }
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700512 return it == end;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100513}
514
515} // namespace details
516
517template <typename... Args>
Ed Tanousd9f466b2023-03-06 15:04:25 -0800518inline bool readUrlSegments(boost::urls::url_view url, Args&&... args)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100519{
Ed Tanous39662a32023-02-06 15:09:46 -0800520 return details::readUrlSegments(url, {std::forward<Args>(args)...});
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100521}
522
Ed Tanousd9f466b2023-03-06 15:04:25 -0800523inline boost::urls::url replaceUrlSegment(boost::urls::url_view urlView,
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000524 const uint replaceLoc,
Ed Tanous26ccae32023-02-16 10:28:44 -0800525 std::string_view newSegment)
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000526{
Ed Tanousd9f466b2023-03-06 15:04:25 -0800527 boost::urls::segments_view urlSegments = urlView.segments();
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000528 boost::urls::url url("/");
529
530 if (!urlSegments.is_absolute())
531 {
532 return url;
533 }
534
535 boost::urls::segments_view::iterator it = urlSegments.begin();
536 boost::urls::segments_view::iterator end = urlSegments.end();
537
538 for (uint idx = 0; it != end; it++, idx++)
539 {
540 if (idx == replaceLoc)
541 {
542 url.segments().push_back(newSegment);
543 }
544 else
545 {
546 url.segments().push_back(*it);
547 }
548 }
549
550 return url;
551}
552
Ed Tanousd9f466b2023-03-06 15:04:25 -0800553inline std::string setProtocolDefaults(boost::urls::url_view urlView)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800554{
Ed Tanous39662a32023-02-06 15:09:46 -0800555 if (urlView.scheme() == "https")
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800556 {
557 return "https";
558 }
Ed Tanous39662a32023-02-06 15:09:46 -0800559 if (urlView.scheme() == "http")
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800560 {
561 if (bmcwebInsecureEnableHttpPushStyleEventing)
562 {
563 return "http";
564 }
565 return "";
566 }
567 return "";
568}
569
Ed Tanousd9f466b2023-03-06 15:04:25 -0800570inline uint16_t setPortDefaults(boost::urls::url_view url)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800571{
572 uint16_t port = url.port_number();
573 if (port != 0)
574 {
575 // user picked a port already.
576 return port;
577 }
578
579 // If the user hasn't explicitly stated a port, pick one explicitly for them
580 // based on the protocol defaults
581 if (url.scheme() == "http")
582 {
583 return 80;
584 }
585 if (url.scheme() == "https")
586 {
587 return 443;
588 }
589 return 0;
590}
591
Ed Tanous11baefe2022-02-09 12:14:12 -0800592inline bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto,
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800593 std::string& host, uint16_t& port,
Ed Tanous11baefe2022-02-09 12:14:12 -0800594 std::string& path)
595{
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800596 boost::urls::result<boost::urls::url_view> url =
Ed Tanous079360a2022-06-29 10:05:19 -0700597 boost::urls::parse_uri(destUrl);
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800598 if (!url)
599 {
600 return false;
601 }
602 urlProto = setProtocolDefaults(url.value());
603 if (urlProto.empty())
Ed Tanous11baefe2022-02-09 12:14:12 -0800604 {
605 return false;
606 }
607
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800608 port = setPortDefaults(url.value());
Ed Tanous11baefe2022-02-09 12:14:12 -0800609
Ed Tanous079360a2022-06-29 10:05:19 -0700610 host = url->encoded_host();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800611
Ed Tanous079360a2022-06-29 10:05:19 -0700612 path = url->encoded_path();
Ed Tanous11baefe2022-02-09 12:14:12 -0800613 if (path.empty())
614 {
615 path = "/";
616 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800617 if (url->has_fragment())
618 {
619 path += '#';
Ed Tanous079360a2022-06-29 10:05:19 -0700620 path += url->encoded_fragment();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800621 }
622
623 if (url->has_query())
624 {
625 path += '?';
Ed Tanous079360a2022-06-29 10:05:19 -0700626 path += url->encoded_query();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800627 }
628
Ed Tanous11baefe2022-02-09 12:14:12 -0800629 return true;
630}
631
Ed Tanous1abe55e2018-09-05 08:30:59 -0700632} // namespace utility
633} // namespace crow
Ed Tanous71f2db72022-05-25 12:28:09 -0700634
635namespace nlohmann
636{
637template <>
638struct adl_serializer<boost::urls::url>
639{
640 // nlohmann requires a specific casing to look these up in adl
641 // NOLINTNEXTLINE(readability-identifier-naming)
642 static void to_json(json& j, const boost::urls::url& url)
643 {
Ed Tanous079360a2022-06-29 10:05:19 -0700644 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700645 }
646};
647
648template <>
649struct adl_serializer<boost::urls::url_view>
650{
651 // NOLINTNEXTLINE(readability-identifier-naming)
Ed Tanousd9f466b2023-03-06 15:04:25 -0800652 static void to_json(json& j, boost::urls::url_view url)
Ed Tanous71f2db72022-05-25 12:28:09 -0700653 {
Ed Tanous079360a2022-06-29 10:05:19 -0700654 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700655 }
656};
657} // namespace nlohmann