blob: aa640431325a0904f9669c8f7fb437c54552cd3b [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
Ed Tanouseae855c2021-10-26 11:26:02 -0700404} // namespace details
405
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700406class OrMorePaths
407{};
408
Ed Tanouseae855c2021-10-26 11:26:02 -0700409template <typename... AV>
Willy Tuc6bcedc2022-09-27 05:36:59 +0000410inline void appendUrlPieces(boost::urls::url& url, const AV... args)
411{
412 details::appendUrlPieces(url, {args...});
413}
414
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100415namespace details
416{
417
418// std::reference_wrapper<std::string> - extracts segment to variable
419// std::string_view - checks if segment is equal to variable
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700420using UrlSegment = std::variant<std::reference_wrapper<std::string>,
421 std::string_view, OrMorePaths>;
422
423enum class UrlParseResult
424{
425 Continue,
426 Fail,
427 Done,
428};
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100429
430class UrlSegmentMatcherVisitor
431{
432 public:
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700433 UrlParseResult operator()(std::string& output)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100434 {
Ed Tanous079360a2022-06-29 10:05:19 -0700435 output = segment;
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700436 return UrlParseResult::Continue;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100437 }
438
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700439 UrlParseResult operator()(std::string_view expected)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100440 {
Ed Tanous079360a2022-06-29 10:05:19 -0700441 if (segment == expected)
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700442 {
443 return UrlParseResult::Continue;
444 }
445 return UrlParseResult::Fail;
446 }
447
448 UrlParseResult operator()(OrMorePaths /*unused*/)
449 {
450 return UrlParseResult::Done;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100451 }
452
Ed Tanous079360a2022-06-29 10:05:19 -0700453 explicit UrlSegmentMatcherVisitor(std::string_view segmentIn) :
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100454 segment(segmentIn)
455 {}
456
457 private:
Ed Tanous079360a2022-06-29 10:05:19 -0700458 std::string_view segment;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100459};
460
Ed Tanousd9f466b2023-03-06 15:04:25 -0800461inline bool readUrlSegments(boost::urls::url_view url,
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100462 std::initializer_list<UrlSegment>&& segments)
463{
Ed Tanousd9f466b2023-03-06 15:04:25 -0800464 boost::urls::segments_view urlSegments = url.segments();
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100465
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700466 if (!urlSegments.is_absolute())
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100467 {
468 return false;
469 }
470
471 boost::urls::segments_view::iterator it = urlSegments.begin();
472 boost::urls::segments_view::iterator end = urlSegments.end();
473
474 for (const auto& segment : segments)
475 {
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700476 if (it == end)
477 {
478 // If the request ends with an "any" path, this was successful
479 return std::holds_alternative<OrMorePaths>(segment);
480 }
481 UrlParseResult res = std::visit(UrlSegmentMatcherVisitor(*it), segment);
482 if (res == UrlParseResult::Done)
483 {
484 return true;
485 }
486 if (res == UrlParseResult::Fail)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100487 {
488 return false;
489 }
490 it++;
491 }
Carson Labrado4c30e222022-06-24 22:16:00 +0000492
493 // There will be an empty segment at the end if the URI ends with a "/"
494 // e.g. /redfish/v1/Chassis/
495 if ((it != end) && urlSegments.back().empty())
496 {
497 it++;
498 }
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700499 return it == end;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100500}
501
502} // namespace details
503
504template <typename... Args>
Ed Tanousd9f466b2023-03-06 15:04:25 -0800505inline bool readUrlSegments(boost::urls::url_view url, Args&&... args)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100506{
Ed Tanous39662a32023-02-06 15:09:46 -0800507 return details::readUrlSegments(url, {std::forward<Args>(args)...});
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100508}
509
Ed Tanousd9f466b2023-03-06 15:04:25 -0800510inline boost::urls::url replaceUrlSegment(boost::urls::url_view urlView,
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000511 const uint replaceLoc,
Ed Tanous26ccae32023-02-16 10:28:44 -0800512 std::string_view newSegment)
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000513{
Ed Tanousd9f466b2023-03-06 15:04:25 -0800514 boost::urls::segments_view urlSegments = urlView.segments();
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000515 boost::urls::url url("/");
516
517 if (!urlSegments.is_absolute())
518 {
519 return url;
520 }
521
522 boost::urls::segments_view::iterator it = urlSegments.begin();
523 boost::urls::segments_view::iterator end = urlSegments.end();
524
525 for (uint idx = 0; it != end; it++, idx++)
526 {
527 if (idx == replaceLoc)
528 {
529 url.segments().push_back(newSegment);
530 }
531 else
532 {
533 url.segments().push_back(*it);
534 }
535 }
536
537 return url;
538}
539
Ed Tanousd9f466b2023-03-06 15:04:25 -0800540inline std::string setProtocolDefaults(boost::urls::url_view urlView)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800541{
Ed Tanous39662a32023-02-06 15:09:46 -0800542 if (urlView.scheme() == "https")
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800543 {
544 return "https";
545 }
Ed Tanous39662a32023-02-06 15:09:46 -0800546 if (urlView.scheme() == "http")
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800547 {
548 if (bmcwebInsecureEnableHttpPushStyleEventing)
549 {
550 return "http";
551 }
552 return "";
553 }
554 return "";
555}
556
Ed Tanousd9f466b2023-03-06 15:04:25 -0800557inline uint16_t setPortDefaults(boost::urls::url_view url)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800558{
559 uint16_t port = url.port_number();
560 if (port != 0)
561 {
562 // user picked a port already.
563 return port;
564 }
565
566 // If the user hasn't explicitly stated a port, pick one explicitly for them
567 // based on the protocol defaults
568 if (url.scheme() == "http")
569 {
570 return 80;
571 }
572 if (url.scheme() == "https")
573 {
574 return 443;
575 }
576 return 0;
577}
578
Ed Tanous11baefe2022-02-09 12:14:12 -0800579inline bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto,
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800580 std::string& host, uint16_t& port,
Ed Tanous11baefe2022-02-09 12:14:12 -0800581 std::string& path)
582{
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800583 boost::urls::result<boost::urls::url_view> url =
Ed Tanous079360a2022-06-29 10:05:19 -0700584 boost::urls::parse_uri(destUrl);
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800585 if (!url)
586 {
587 return false;
588 }
589 urlProto = setProtocolDefaults(url.value());
590 if (urlProto.empty())
Ed Tanous11baefe2022-02-09 12:14:12 -0800591 {
592 return false;
593 }
594
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800595 port = setPortDefaults(url.value());
Ed Tanous11baefe2022-02-09 12:14:12 -0800596
Ed Tanous079360a2022-06-29 10:05:19 -0700597 host = url->encoded_host();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800598
Ed Tanous079360a2022-06-29 10:05:19 -0700599 path = url->encoded_path();
Ed Tanous11baefe2022-02-09 12:14:12 -0800600 if (path.empty())
601 {
602 path = "/";
603 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800604 if (url->has_fragment())
605 {
606 path += '#';
Ed Tanous079360a2022-06-29 10:05:19 -0700607 path += url->encoded_fragment();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800608 }
609
610 if (url->has_query())
611 {
612 path += '?';
Ed Tanous079360a2022-06-29 10:05:19 -0700613 path += url->encoded_query();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800614 }
615
Ed Tanous11baefe2022-02-09 12:14:12 -0800616 return true;
617}
618
Ed Tanous1abe55e2018-09-05 08:30:59 -0700619} // namespace utility
620} // namespace crow
Ed Tanous71f2db72022-05-25 12:28:09 -0700621
622namespace nlohmann
623{
624template <>
625struct adl_serializer<boost::urls::url>
626{
627 // nlohmann requires a specific casing to look these up in adl
628 // NOLINTNEXTLINE(readability-identifier-naming)
629 static void to_json(json& j, const boost::urls::url& url)
630 {
Ed Tanous079360a2022-06-29 10:05:19 -0700631 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700632 }
633};
634
635template <>
636struct adl_serializer<boost::urls::url_view>
637{
638 // NOLINTNEXTLINE(readability-identifier-naming)
Ed Tanousd9f466b2023-03-06 15:04:25 -0800639 static void to_json(json& j, boost::urls::url_view url)
Ed Tanous71f2db72022-05-25 12:28:09 -0700640 {
Ed Tanous079360a2022-06-29 10:05:19 -0700641 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700642 }
643};
644} // namespace nlohmann