blob: 4e6ed716759e02c0683e4d5b68bbd82391ce44e0 [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,
37 Integer = 1,
38 UnsignedInteger = 2,
39 Float = 3,
40 String = 4,
41 Path = 5,
42 Max = 6,
43};
44
45// Remove when we have c++23
46template <typename E>
47constexpr typename std::underlying_type<E>::type toUnderlying(E e) noexcept
48{
49 return static_cast<typename std::underlying_type<E>::type>(e);
50}
51
Gunnar Mills1214b7e2020-06-04 10:11:30 -050052template <typename T>
Ed Tanousc715ec22022-03-10 15:38:01 -080053constexpr TypeCode getParameterTag()
Ed Tanous1abe55e2018-09-05 08:30:59 -070054{
Ed Tanous69509012019-10-24 16:53:05 -070055 if constexpr (std::is_same_v<int, T>)
56 {
Ed Tanousc715ec22022-03-10 15:38:01 -080057 return TypeCode::Integer;
Ed Tanous1abe55e2018-09-05 08:30:59 -070058 }
Ed Tanous69509012019-10-24 16:53:05 -070059 if constexpr (std::is_same_v<char, T>)
60 {
Ed Tanousc715ec22022-03-10 15:38:01 -080061 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070062 }
63 if constexpr (std::is_same_v<short, T>)
64 {
Ed Tanousc715ec22022-03-10 15:38:01 -080065 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070066 }
67 if constexpr (std::is_same_v<long, T>)
68 {
Ed Tanousc715ec22022-03-10 15:38:01 -080069 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070070 }
71 if constexpr (std::is_same_v<long long, T>)
72 {
Ed Tanousc715ec22022-03-10 15:38:01 -080073 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070074 }
75 if constexpr (std::is_same_v<unsigned int, T>)
76 {
Ed Tanousc715ec22022-03-10 15:38:01 -080077 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070078 }
79 if constexpr (std::is_same_v<unsigned char, T>)
80 {
Ed Tanousc715ec22022-03-10 15:38:01 -080081 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070082 }
83 if constexpr (std::is_same_v<unsigned short, T>)
84 {
Ed Tanousc715ec22022-03-10 15:38:01 -080085 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070086 }
87 if constexpr (std::is_same_v<unsigned long, T>)
88 {
Ed Tanousc715ec22022-03-10 15:38:01 -080089 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070090 }
91 if constexpr (std::is_same_v<unsigned long long, T>)
92 {
Ed Tanousc715ec22022-03-10 15:38:01 -080093 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070094 }
95 if constexpr (std::is_same_v<double, T>)
96 {
Ed Tanousc715ec22022-03-10 15:38:01 -080097 return TypeCode::Float;
Ed Tanous69509012019-10-24 16:53:05 -070098 }
99 if constexpr (std::is_same_v<std::string, T>)
100 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800101 return TypeCode::String;
Ed Tanous69509012019-10-24 16:53:05 -0700102 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800103 return TypeCode::Unspecified;
Ed Tanous69509012019-10-24 16:53:05 -0700104}
105
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500106template <typename... Args>
Ed Tanous988403c2020-08-24 11:29:49 -0700107struct computeParameterTagFromArgsList;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700108
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500109template <>
Ed Tanous988403c2020-08-24 11:29:49 -0700110struct computeParameterTagFromArgsList<>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111{
Ed Tanous69509012019-10-24 16:53:05 -0700112 static constexpr int value = 0;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700113};
114
115template <typename Arg, typename... Args>
Ed Tanous988403c2020-08-24 11:29:49 -0700116struct computeParameterTagFromArgsList<Arg, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700117{
Ed Tanous69509012019-10-24 16:53:05 -0700118 static constexpr int subValue =
Ed Tanous988403c2020-08-24 11:29:49 -0700119 computeParameterTagFromArgsList<Args...>::value;
Ed Tanous69509012019-10-24 16:53:05 -0700120 static constexpr int value =
Ed Tanousc715ec22022-03-10 15:38:01 -0800121 getParameterTag<typename std::decay<Arg>::type>() !=
122 TypeCode::Unspecified
123 ? static_cast<unsigned long>(subValue *
124 toUnderlying(TypeCode::Max)) +
125 static_cast<uint64_t>(
126 getParameterTag<typename std::decay<Arg>::type>())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700127 : subValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700128};
129
Ed Tanous988403c2020-08-24 11:29:49 -0700130inline bool isParameterTagCompatible(uint64_t a, uint64_t b)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700131{
Ed Tanous1c30e502022-03-08 18:02:24 -0800132 while (true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700133 {
Ed Tanousef641b62022-06-28 19:53:24 -0700134 if (a == 0 && b == 0)
Ed Tanous1c30e502022-03-08 18:02:24 -0800135 {
Ed Tanousef641b62022-06-28 19:53:24 -0700136 // Both tags were equivalent, parameters are compatible
137 return true;
Ed Tanous1c30e502022-03-08 18:02:24 -0800138 }
Ed Tanousef641b62022-06-28 19:53:24 -0700139 if (a == 0 || b == 0)
Ed Tanous1c30e502022-03-08 18:02:24 -0800140 {
Ed Tanousef641b62022-06-28 19:53:24 -0700141 // one of the tags had more parameters than the other
142 return false;
Ed Tanous1c30e502022-03-08 18:02:24 -0800143 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800144 TypeCode sa = static_cast<TypeCode>(a % toUnderlying(TypeCode::Max));
145 TypeCode sb = static_cast<TypeCode>(b % toUnderlying(TypeCode::Max));
146
147 if (sa == TypeCode::Path)
Ed Tanous1c30e502022-03-08 18:02:24 -0800148 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800149 sa = TypeCode::String;
Ed Tanous1c30e502022-03-08 18:02:24 -0800150 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800151 if (sb == TypeCode::Path)
Ed Tanous1c30e502022-03-08 18:02:24 -0800152 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800153 sb = TypeCode::String;
Ed Tanous1c30e502022-03-08 18:02:24 -0800154 }
155 if (sa != sb)
156 {
157 return false;
158 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800159 a /= toUnderlying(TypeCode::Max);
160 b /= toUnderlying(TypeCode::Max);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700161 }
Ed Tanous1c30e502022-03-08 18:02:24 -0800162 return false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700163}
164
Ed Tanous1c30e502022-03-08 18:02:24 -0800165constexpr inline uint64_t getParameterTag(std::string_view url)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166{
Ed Tanous1c30e502022-03-08 18:02:24 -0800167 uint64_t tagValue = 0;
168 size_t urlSegmentIndex = std::string_view::npos;
Ed Tanousb00dcc22021-02-23 12:52:50 -0800169
Ed Tanous1c30e502022-03-08 18:02:24 -0800170 size_t paramIndex = 0;
171
172 for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
173 {
174 char character = url[urlIndex];
175 if (character == '<')
176 {
177 if (urlSegmentIndex != std::string_view::npos)
178 {
179 return 0;
180 }
181 urlSegmentIndex = urlIndex;
182 }
183 if (character == '>')
184 {
185 if (urlSegmentIndex == std::string_view::npos)
186 {
187 return 0;
188 }
189 std::string_view tag =
190 url.substr(urlSegmentIndex, urlIndex + 1 - urlSegmentIndex);
191
192 // Note, this is a really lame way to do std::pow(6, paramIndex)
193 // std::pow doesn't work in constexpr in clang.
194 // Ideally in the future we'd move this to use a power of 2 packing
195 // (probably 8 instead of 6) so that these just become bit shifts
196 uint64_t insertIndex = 1;
197 for (size_t unused = 0; unused < paramIndex; unused++)
198 {
199 insertIndex *= 6;
200 }
201
202 if (tag == "<int>")
203 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800204 tagValue += insertIndex * toUnderlying(TypeCode::Integer);
Ed Tanous1c30e502022-03-08 18:02:24 -0800205 }
206 if (tag == "<uint>")
207 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800208 tagValue +=
209 insertIndex * toUnderlying(TypeCode::UnsignedInteger);
Ed Tanous1c30e502022-03-08 18:02:24 -0800210 }
211 if (tag == "<float>" || tag == "<double>")
212 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800213 tagValue += insertIndex * toUnderlying(TypeCode::Float);
Ed Tanous1c30e502022-03-08 18:02:24 -0800214 }
215 if (tag == "<str>" || tag == "<string>")
216 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800217 tagValue += insertIndex * toUnderlying(TypeCode::String);
Ed Tanous1c30e502022-03-08 18:02:24 -0800218 }
219 if (tag == "<path>")
220 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800221 tagValue += insertIndex * toUnderlying(TypeCode::Path);
Ed Tanous1c30e502022-03-08 18:02:24 -0800222 }
223 paramIndex++;
224 urlSegmentIndex = std::string_view::npos;
225 }
226 }
227 if (urlSegmentIndex != std::string_view::npos)
Ed Tanous988403c2020-08-24 11:29:49 -0700228 {
229 return 0;
230 }
Ed Tanous1c30e502022-03-08 18:02:24 -0800231 return tagValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700232}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700233
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500234template <typename... T>
235struct S
Ed Tanous1abe55e2018-09-05 08:30:59 -0700236{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500237 template <typename U>
238 using push = S<U, T...>;
239 template <typename U>
240 using push_back = S<T..., U>;
241 template <template <typename... Args> class U>
242 using rebind = U<T...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700243};
Ed Tanous988403c2020-08-24 11:29:49 -0700244
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500245template <typename F, typename Set>
246struct CallHelper;
Ed Tanous988403c2020-08-24 11:29:49 -0700247
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500248template <typename F, typename... Args>
249struct CallHelper<F, S<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250{
251 template <typename F1, typename... Args1,
252 typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
Ed Tanous2c70f802020-09-28 14:29:23 -0700253 static char test(int);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700254
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500255 template <typename...>
Ed Tanous2c70f802020-09-28 14:29:23 -0700256 static int test(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700257
Ed Tanous2c70f802020-09-28 14:29:23 -0700258 static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700259};
260
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500261template <uint64_t N>
262struct SingleTagToType
263{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700264
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500265template <>
266struct SingleTagToType<1>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700267{
268 using type = int64_t;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700269};
270
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500271template <>
272struct SingleTagToType<2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273{
274 using type = uint64_t;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700275};
276
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500277template <>
278struct SingleTagToType<3>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279{
280 using type = double;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700281};
282
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500283template <>
284struct SingleTagToType<4>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700285{
286 using type = std::string;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700287};
288
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500289template <>
290struct SingleTagToType<5>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700291{
292 using type = std::string;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700293};
294
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500295template <uint64_t Tag>
296struct Arguments
Ed Tanous1abe55e2018-09-05 08:30:59 -0700297{
298 using subarguments = typename Arguments<Tag / 6>::type;
299 using type = typename subarguments::template push<
300 typename SingleTagToType<Tag % 6>::type>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700301};
302
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500303template <>
304struct Arguments<0>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700305{
306 using type = S<>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700307};
308
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500309template <typename T>
Ed Tanous988403c2020-08-24 11:29:49 -0700310struct Promote
Ed Tanous1abe55e2018-09-05 08:30:59 -0700311{
312 using type = T;
313};
314
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500315template <typename T>
Ed Tanous988403c2020-08-24 11:29:49 -0700316using PromoteT = typename Promote<T>::type;
317
318template <>
319struct Promote<char>
320{
321 using type = int64_t;
322};
323template <>
324struct Promote<short>
325{
326 using type = int64_t;
327};
328template <>
329struct Promote<int>
330{
331 using type = int64_t;
332};
333template <>
334struct Promote<long>
335{
336 using type = int64_t;
337};
338template <>
339struct Promote<long long>
340{
341 using type = int64_t;
342};
343template <>
344struct Promote<unsigned char>
345{
346 using type = uint64_t;
347};
348template <>
349struct Promote<unsigned short>
350{
351 using type = uint64_t;
352};
353template <>
354struct Promote<unsigned int>
355{
356 using type = uint64_t;
357};
358template <>
359struct Promote<unsigned long>
360{
361 using type = uint64_t;
362};
363template <>
364struct Promote<unsigned long long>
365{
366 using type = uint64_t;
367};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700368
Ed Tanous1abe55e2018-09-05 08:30:59 -0700369} // namespace black_magic
Ed Tanous7045c8d2017-04-03 10:04:37 -0700370
Ed Tanous1abe55e2018-09-05 08:30:59 -0700371namespace utility
372{
Ed Tanous7045c8d2017-04-03 10:04:37 -0700373
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500374template <typename T>
Ed Tanousc867a832022-03-10 14:17:00 -0800375struct FunctionTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700376{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500377 template <size_t i>
Ed Tanousc867a832022-03-10 14:17:00 -0800378 using arg = std::tuple_element_t<i, boost::callable_traits::args_t<T>>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700379};
380
Ed Tanous26ccae32023-02-16 10:28:44 -0800381inline std::string base64encode(std::string_view data)
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600382{
383 const std::array<char, 64> key = {
384 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
385 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
386 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
387 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
388 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
389
390 size_t size = data.size();
391 std::string ret;
392 ret.resize((size + 2) / 3 * 4);
393 auto it = ret.begin();
394
395 size_t i = 0;
396 while (i < size)
397 {
Ed Tanous543f4402022-01-06 13:12:53 -0800398 size_t keyIndex = 0;
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600399
400 keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
401 *it++ = key[keyIndex];
402
403 if (i + 1 < size)
404 {
405 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
406 keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
407 *it++ = key[keyIndex];
408
409 if (i + 2 < size)
410 {
411 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
412 keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
413 *it++ = key[keyIndex];
414
415 keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
416 *it++ = key[keyIndex];
417 }
418 else
419 {
420 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
421 *it++ = key[keyIndex];
422 *it++ = '=';
423 }
424 }
425 else
426 {
427 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
428 *it++ = key[keyIndex];
429 *it++ = '=';
430 *it++ = '=';
431 }
432
433 i += 3;
434 }
435
436 return ret;
437}
438
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100439// TODO this is temporary and should be deleted once base64 is refactored out of
440// crow
Ed Tanous26ccae32023-02-16 10:28:44 -0800441inline bool base64Decode(std::string_view input, std::string& output)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700442{
Ed Tanous271584a2019-07-09 16:24:22 -0700443 static const char nop = static_cast<char>(-1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700444 // See note on encoding_data[] in above function
Jonathan Doman5beaf842020-08-14 11:23:33 -0700445 static const std::array<char, 256> decodingData = {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
447 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
448 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
449 nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59,
450 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4,
451 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
452 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26,
453 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
454 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop,
455 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
456 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
457 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
458 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
459 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
460 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
461 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
462 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
463 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
464 nop, nop, nop, nop};
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100465
Ed Tanous1abe55e2018-09-05 08:30:59 -0700466 size_t inputLength = input.size();
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100467
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468 // allocate space for output string
469 output.clear();
470 output.reserve(((inputLength + 2) / 3) * 4);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100471
Jonathan Doman5beaf842020-08-14 11:23:33 -0700472 auto getCodeValue = [](char c) {
473 auto code = static_cast<unsigned char>(c);
474 // Ensure we cannot index outside the bounds of the decoding array
475 static_assert(std::numeric_limits<decltype(code)>::max() <
476 decodingData.size());
477 return decodingData[code];
478 };
479
Ed Tanous1abe55e2018-09-05 08:30:59 -0700480 // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500481 // dropping first two bits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700482 // and regenerate into 3 8-bits sequences
James Feist5a806642020-07-31 16:40:33 +0000483
Ed Tanous1abe55e2018-09-05 08:30:59 -0700484 for (size_t i = 0; i < inputLength; i++)
485 {
Ed Tanous543f4402022-01-06 13:12:53 -0800486 char base64code0 = 0;
487 char base64code1 = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700488 char base64code2 = 0; // initialized to 0 to suppress warnings
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100489
Jonathan Doman5beaf842020-08-14 11:23:33 -0700490 base64code0 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700491 if (base64code0 == nop)
492 { // non base64 character
493 return false;
494 }
495 if (!(++i < inputLength))
496 { // we need at least two input bytes for first
497 // byte output
498 return false;
499 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700500 base64code1 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700501 if (base64code1 == nop)
502 { // non base64 character
503 return false;
504 }
505 output +=
506 static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100507
Ed Tanous1abe55e2018-09-05 08:30:59 -0700508 if (++i < inputLength)
509 {
510 char c = input[i];
511 if (c == '=')
512 { // padding , end of input
513 return (base64code1 & 0x0f) == 0;
514 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700515 base64code2 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700516 if (base64code2 == nop)
517 { // non base64 character
518 return false;
519 }
520 output += static_cast<char>(((base64code1 << 4) & 0xf0) |
521 ((base64code2 >> 2) & 0x0f));
522 }
523
524 if (++i < inputLength)
525 {
526 char c = input[i];
527 if (c == '=')
528 { // padding , end of input
529 return (base64code2 & 0x03) == 0;
530 }
Ed Tanousf8fe53e2022-06-30 15:55:45 -0700531 char base64code3 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700532 if (base64code3 == nop)
533 { // non base64 character
534 return false;
535 }
536 output +=
537 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
538 }
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100539 }
540
Ed Tanous1abe55e2018-09-05 08:30:59 -0700541 return true;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100542}
543
Ed Tanous26ccae32023-02-16 10:28:44 -0800544inline bool constantTimeStringCompare(std::string_view a, std::string_view b)
Ed Tanous51dae672018-09-05 16:07:32 -0700545{
546 // Important note, this function is ONLY constant time if the two input
547 // sizes are the same
548 if (a.size() != b.size())
549 {
550 return false;
551 }
552 return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
553}
554
555struct ConstantTimeCompare
556{
Ed Tanous26ccae32023-02-16 10:28:44 -0800557 bool operator()(std::string_view a, std::string_view b) const
Ed Tanous51dae672018-09-05 16:07:32 -0700558 {
559 return constantTimeStringCompare(a, b);
560 }
561};
562
Ed Tanouseae855c2021-10-26 11:26:02 -0700563namespace details
564{
565inline boost::urls::url
Willy Tuc6bcedc2022-09-27 05:36:59 +0000566 appendUrlPieces(boost::urls::url& url,
567 const std::initializer_list<std::string_view> args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700568{
Ed Tanous26ccae32023-02-16 10:28:44 -0800569 for (std::string_view arg : args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700570 {
571 url.segments().push_back(arg);
572 }
573 return url;
574}
Willy Tuc6bcedc2022-09-27 05:36:59 +0000575
576inline boost::urls::url
577 urlFromPiecesDetail(const std::initializer_list<std::string_view> args)
578{
579 boost::urls::url url("/");
580 appendUrlPieces(url, args);
581 return url;
582}
Ed Tanouseae855c2021-10-26 11:26:02 -0700583} // namespace details
584
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700585class OrMorePaths
586{};
587
Ed Tanouseae855c2021-10-26 11:26:02 -0700588template <typename... AV>
589inline boost::urls::url urlFromPieces(const AV... args)
590{
591 return details::urlFromPiecesDetail({args...});
592}
593
Willy Tuc6bcedc2022-09-27 05:36:59 +0000594template <typename... AV>
595inline void appendUrlPieces(boost::urls::url& url, const AV... args)
596{
597 details::appendUrlPieces(url, {args...});
598}
599
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100600namespace details
601{
602
603// std::reference_wrapper<std::string> - extracts segment to variable
604// std::string_view - checks if segment is equal to variable
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700605using UrlSegment = std::variant<std::reference_wrapper<std::string>,
606 std::string_view, OrMorePaths>;
607
608enum class UrlParseResult
609{
610 Continue,
611 Fail,
612 Done,
613};
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100614
615class UrlSegmentMatcherVisitor
616{
617 public:
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700618 UrlParseResult operator()(std::string& output)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100619 {
Ed Tanous079360a2022-06-29 10:05:19 -0700620 output = segment;
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700621 return UrlParseResult::Continue;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100622 }
623
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700624 UrlParseResult operator()(std::string_view expected)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100625 {
Ed Tanous079360a2022-06-29 10:05:19 -0700626 if (segment == expected)
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700627 {
628 return UrlParseResult::Continue;
629 }
630 return UrlParseResult::Fail;
631 }
632
633 UrlParseResult operator()(OrMorePaths /*unused*/)
634 {
635 return UrlParseResult::Done;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100636 }
637
Ed Tanous079360a2022-06-29 10:05:19 -0700638 explicit UrlSegmentMatcherVisitor(std::string_view segmentIn) :
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100639 segment(segmentIn)
640 {}
641
642 private:
Ed Tanous079360a2022-06-29 10:05:19 -0700643 std::string_view segment;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100644};
645
Ed Tanous39662a32023-02-06 15:09:46 -0800646inline bool readUrlSegments(const boost::urls::url_view& url,
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100647 std::initializer_list<UrlSegment>&& segments)
648{
Ed Tanous39662a32023-02-06 15:09:46 -0800649 const boost::urls::segments_view& urlSegments = url.segments();
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100650
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700651 if (!urlSegments.is_absolute())
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100652 {
653 return false;
654 }
655
656 boost::urls::segments_view::iterator it = urlSegments.begin();
657 boost::urls::segments_view::iterator end = urlSegments.end();
658
659 for (const auto& segment : segments)
660 {
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700661 if (it == end)
662 {
663 // If the request ends with an "any" path, this was successful
664 return std::holds_alternative<OrMorePaths>(segment);
665 }
666 UrlParseResult res = std::visit(UrlSegmentMatcherVisitor(*it), segment);
667 if (res == UrlParseResult::Done)
668 {
669 return true;
670 }
671 if (res == UrlParseResult::Fail)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100672 {
673 return false;
674 }
675 it++;
676 }
Carson Labrado4c30e222022-06-24 22:16:00 +0000677
678 // There will be an empty segment at the end if the URI ends with a "/"
679 // e.g. /redfish/v1/Chassis/
680 if ((it != end) && urlSegments.back().empty())
681 {
682 it++;
683 }
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700684 return it == end;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100685}
686
687} // namespace details
688
689template <typename... Args>
Ed Tanous39662a32023-02-06 15:09:46 -0800690inline bool readUrlSegments(const boost::urls::url_view& url, Args&&... args)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100691{
Ed Tanous39662a32023-02-06 15:09:46 -0800692 return details::readUrlSegments(url, {std::forward<Args>(args)...});
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100693}
694
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000695inline boost::urls::url replaceUrlSegment(const boost::urls::url_view& urlView,
696 const uint replaceLoc,
Ed Tanous26ccae32023-02-16 10:28:44 -0800697 std::string_view newSegment)
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000698{
699 const boost::urls::segments_view& urlSegments = urlView.segments();
700 boost::urls::url url("/");
701
702 if (!urlSegments.is_absolute())
703 {
704 return url;
705 }
706
707 boost::urls::segments_view::iterator it = urlSegments.begin();
708 boost::urls::segments_view::iterator end = urlSegments.end();
709
710 for (uint idx = 0; it != end; it++, idx++)
711 {
712 if (idx == replaceLoc)
713 {
714 url.segments().push_back(newSegment);
715 }
716 else
717 {
718 url.segments().push_back(*it);
719 }
720 }
721
722 return url;
723}
724
Ed Tanous39662a32023-02-06 15:09:46 -0800725inline std::string setProtocolDefaults(const boost::urls::url_view& urlView)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800726{
Ed Tanous39662a32023-02-06 15:09:46 -0800727 if (urlView.scheme() == "https")
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800728 {
729 return "https";
730 }
Ed Tanous39662a32023-02-06 15:09:46 -0800731 if (urlView.scheme() == "http")
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800732 {
733 if (bmcwebInsecureEnableHttpPushStyleEventing)
734 {
735 return "http";
736 }
737 return "";
738 }
739 return "";
740}
741
742inline uint16_t setPortDefaults(const boost::urls::url_view& url)
743{
744 uint16_t port = url.port_number();
745 if (port != 0)
746 {
747 // user picked a port already.
748 return port;
749 }
750
751 // If the user hasn't explicitly stated a port, pick one explicitly for them
752 // based on the protocol defaults
753 if (url.scheme() == "http")
754 {
755 return 80;
756 }
757 if (url.scheme() == "https")
758 {
759 return 443;
760 }
761 return 0;
762}
763
Ed Tanous11baefe2022-02-09 12:14:12 -0800764inline bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto,
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800765 std::string& host, uint16_t& port,
Ed Tanous11baefe2022-02-09 12:14:12 -0800766 std::string& path)
767{
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800768 boost::urls::result<boost::urls::url_view> url =
Ed Tanous079360a2022-06-29 10:05:19 -0700769 boost::urls::parse_uri(destUrl);
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800770 if (!url)
771 {
772 return false;
773 }
774 urlProto = setProtocolDefaults(url.value());
775 if (urlProto.empty())
Ed Tanous11baefe2022-02-09 12:14:12 -0800776 {
777 return false;
778 }
779
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800780 port = setPortDefaults(url.value());
Ed Tanous11baefe2022-02-09 12:14:12 -0800781
Ed Tanous079360a2022-06-29 10:05:19 -0700782 host = url->encoded_host();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800783
Ed Tanous079360a2022-06-29 10:05:19 -0700784 path = url->encoded_path();
Ed Tanous11baefe2022-02-09 12:14:12 -0800785 if (path.empty())
786 {
787 path = "/";
788 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800789 if (url->has_fragment())
790 {
791 path += '#';
Ed Tanous079360a2022-06-29 10:05:19 -0700792 path += url->encoded_fragment();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800793 }
794
795 if (url->has_query())
796 {
797 path += '?';
Ed Tanous079360a2022-06-29 10:05:19 -0700798 path += url->encoded_query();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800799 }
800
Ed Tanous11baefe2022-02-09 12:14:12 -0800801 return true;
802}
803
Ed Tanous1abe55e2018-09-05 08:30:59 -0700804} // namespace utility
805} // namespace crow
Ed Tanous71f2db72022-05-25 12:28:09 -0700806
807namespace nlohmann
808{
809template <>
810struct adl_serializer<boost::urls::url>
811{
812 // nlohmann requires a specific casing to look these up in adl
813 // NOLINTNEXTLINE(readability-identifier-naming)
814 static void to_json(json& j, const boost::urls::url& url)
815 {
Ed Tanous079360a2022-06-29 10:05:19 -0700816 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700817 }
818};
819
820template <>
821struct adl_serializer<boost::urls::url_view>
822{
823 // NOLINTNEXTLINE(readability-identifier-naming)
824 static void to_json(json& j, const boost::urls::url_view& url)
825 {
Ed Tanous079360a2022-06-29 10:05:19 -0700826 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700827 }
828};
829} // namespace nlohmann