blob: c9728f5b6d7411a3108ecd8b453525263107fed4 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
Ed Tanous1abe55e2018-09-05 08:30:59 -07002
Ed Tanouseb1c47d2022-02-09 11:47:27 -08003#include <bmcweb_config.h>
Ed Tanous51dae672018-09-05 16:07:32 -07004#include <openssl/crypto.h>
5
Ed Tanousc867a832022-03-10 14:17:00 -08006#include <boost/callable_traits.hpp>
Ed Tanouseae855c2021-10-26 11:26:02 -07007#include <boost/url/url.hpp>
Ed Tanous71f2db72022-05-25 12:28:09 -07008#include <nlohmann/json.hpp>
Nan Zhou1d8782e2021-11-29 22:23:18 -08009
Ed Tanous9ea15c32022-01-04 14:18:22 -080010#include <array>
Ed Tanous74849be2021-02-05 09:47:47 -080011#include <chrono>
Ed Tanousc715ec22022-03-10 15:38:01 -080012#include <cstddef>
Ed Tanous7045c8d2017-04-03 10:04:37 -070013#include <cstdint>
Ed Tanous9ea15c32022-01-04 14:18:22 -080014#include <ctime>
Ed Tanous7045c8d2017-04-03 10:04:37 -070015#include <functional>
Ed Tanous9896eae2022-07-23 15:07:33 -070016#include <iomanip>
Ed Tanous9ea15c32022-01-04 14:18:22 -080017#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070018#include <stdexcept>
19#include <string>
Ed Tanous9ea15c32022-01-04 14:18:22 -080020#include <string_view>
Ed Tanous7045c8d2017-04-03 10:04:37 -070021#include <tuple>
Ed Tanous9ea15c32022-01-04 14:18:22 -080022#include <type_traits>
23#include <utility>
Szymon Dompkeca1600c2022-03-03 14:42:52 +010024#include <variant>
Ed Tanous7045c8d2017-04-03 10:04:37 -070025
Ed Tanous1abe55e2018-09-05 08:30:59 -070026namespace crow
27{
28namespace black_magic
29{
Ed Tanous7045c8d2017-04-03 10:04:37 -070030
Ed Tanousc715ec22022-03-10 15:38:01 -080031enum class TypeCode : uint8_t
32{
33 Unspecified = 0,
34 Integer = 1,
35 UnsignedInteger = 2,
36 Float = 3,
37 String = 4,
38 Path = 5,
39 Max = 6,
40};
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 T>
Ed Tanousc715ec22022-03-10 15:38:01 -080050constexpr TypeCode getParameterTag()
Ed Tanous1abe55e2018-09-05 08:30:59 -070051{
Ed Tanous69509012019-10-24 16:53:05 -070052 if constexpr (std::is_same_v<int, T>)
53 {
Ed Tanousc715ec22022-03-10 15:38:01 -080054 return TypeCode::Integer;
Ed Tanous1abe55e2018-09-05 08:30:59 -070055 }
Ed Tanous69509012019-10-24 16:53:05 -070056 if constexpr (std::is_same_v<char, T>)
57 {
Ed Tanousc715ec22022-03-10 15:38:01 -080058 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070059 }
60 if constexpr (std::is_same_v<short, T>)
61 {
Ed Tanousc715ec22022-03-10 15:38:01 -080062 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070063 }
64 if constexpr (std::is_same_v<long, T>)
65 {
Ed Tanousc715ec22022-03-10 15:38:01 -080066 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070067 }
68 if constexpr (std::is_same_v<long long, T>)
69 {
Ed Tanousc715ec22022-03-10 15:38:01 -080070 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070071 }
72 if constexpr (std::is_same_v<unsigned int, T>)
73 {
Ed Tanousc715ec22022-03-10 15:38:01 -080074 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070075 }
76 if constexpr (std::is_same_v<unsigned char, T>)
77 {
Ed Tanousc715ec22022-03-10 15:38:01 -080078 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070079 }
80 if constexpr (std::is_same_v<unsigned short, T>)
81 {
Ed Tanousc715ec22022-03-10 15:38:01 -080082 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070083 }
84 if constexpr (std::is_same_v<unsigned long, T>)
85 {
Ed Tanousc715ec22022-03-10 15:38:01 -080086 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070087 }
88 if constexpr (std::is_same_v<unsigned long long, T>)
89 {
Ed Tanousc715ec22022-03-10 15:38:01 -080090 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070091 }
92 if constexpr (std::is_same_v<double, T>)
93 {
Ed Tanousc715ec22022-03-10 15:38:01 -080094 return TypeCode::Float;
Ed Tanous69509012019-10-24 16:53:05 -070095 }
96 if constexpr (std::is_same_v<std::string, T>)
97 {
Ed Tanousc715ec22022-03-10 15:38:01 -080098 return TypeCode::String;
Ed Tanous69509012019-10-24 16:53:05 -070099 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800100 return TypeCode::Unspecified;
Ed Tanous69509012019-10-24 16:53:05 -0700101}
102
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500103template <typename... Args>
Ed Tanous988403c2020-08-24 11:29:49 -0700104struct computeParameterTagFromArgsList;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700105
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500106template <>
Ed Tanous988403c2020-08-24 11:29:49 -0700107struct computeParameterTagFromArgsList<>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700108{
Ed Tanous69509012019-10-24 16:53:05 -0700109 static constexpr int value = 0;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700110};
111
112template <typename Arg, typename... Args>
Ed Tanous988403c2020-08-24 11:29:49 -0700113struct computeParameterTagFromArgsList<Arg, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700114{
Ed Tanous69509012019-10-24 16:53:05 -0700115 static constexpr int subValue =
Ed Tanous988403c2020-08-24 11:29:49 -0700116 computeParameterTagFromArgsList<Args...>::value;
Ed Tanous69509012019-10-24 16:53:05 -0700117 static constexpr int value =
Ed Tanousc715ec22022-03-10 15:38:01 -0800118 getParameterTag<typename std::decay<Arg>::type>() !=
119 TypeCode::Unspecified
120 ? static_cast<unsigned long>(subValue *
121 toUnderlying(TypeCode::Max)) +
122 static_cast<uint64_t>(
123 getParameterTag<typename std::decay<Arg>::type>())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700124 : subValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700125};
126
Ed Tanous988403c2020-08-24 11:29:49 -0700127inline bool isParameterTagCompatible(uint64_t a, uint64_t b)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700128{
Ed Tanous1c30e502022-03-08 18:02:24 -0800129 while (true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130 {
Ed Tanousef641b62022-06-28 19:53:24 -0700131 if (a == 0 && b == 0)
Ed Tanous1c30e502022-03-08 18:02:24 -0800132 {
Ed Tanousef641b62022-06-28 19:53:24 -0700133 // Both tags were equivalent, parameters are compatible
134 return true;
Ed Tanous1c30e502022-03-08 18:02:24 -0800135 }
Ed Tanousef641b62022-06-28 19:53:24 -0700136 if (a == 0 || b == 0)
Ed Tanous1c30e502022-03-08 18:02:24 -0800137 {
Ed Tanousef641b62022-06-28 19:53:24 -0700138 // one of the tags had more parameters than the other
139 return false;
Ed Tanous1c30e502022-03-08 18:02:24 -0800140 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800141 TypeCode sa = static_cast<TypeCode>(a % toUnderlying(TypeCode::Max));
142 TypeCode sb = static_cast<TypeCode>(b % toUnderlying(TypeCode::Max));
143
144 if (sa == TypeCode::Path)
Ed Tanous1c30e502022-03-08 18:02:24 -0800145 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800146 sa = TypeCode::String;
Ed Tanous1c30e502022-03-08 18:02:24 -0800147 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800148 if (sb == TypeCode::Path)
Ed Tanous1c30e502022-03-08 18:02:24 -0800149 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800150 sb = TypeCode::String;
Ed Tanous1c30e502022-03-08 18:02:24 -0800151 }
152 if (sa != sb)
153 {
154 return false;
155 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800156 a /= toUnderlying(TypeCode::Max);
157 b /= toUnderlying(TypeCode::Max);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700158 }
Ed Tanous1c30e502022-03-08 18:02:24 -0800159 return false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700160}
161
Ed Tanous1c30e502022-03-08 18:02:24 -0800162constexpr inline uint64_t getParameterTag(std::string_view url)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700163{
Ed Tanous1c30e502022-03-08 18:02:24 -0800164 uint64_t tagValue = 0;
165 size_t urlSegmentIndex = std::string_view::npos;
Ed Tanousb00dcc22021-02-23 12:52:50 -0800166
Ed Tanous1c30e502022-03-08 18:02:24 -0800167 size_t paramIndex = 0;
168
169 for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
170 {
171 char character = url[urlIndex];
172 if (character == '<')
173 {
174 if (urlSegmentIndex != std::string_view::npos)
175 {
176 return 0;
177 }
178 urlSegmentIndex = urlIndex;
179 }
180 if (character == '>')
181 {
182 if (urlSegmentIndex == std::string_view::npos)
183 {
184 return 0;
185 }
186 std::string_view tag =
187 url.substr(urlSegmentIndex, urlIndex + 1 - urlSegmentIndex);
188
189 // Note, this is a really lame way to do std::pow(6, paramIndex)
190 // std::pow doesn't work in constexpr in clang.
191 // Ideally in the future we'd move this to use a power of 2 packing
192 // (probably 8 instead of 6) so that these just become bit shifts
193 uint64_t insertIndex = 1;
194 for (size_t unused = 0; unused < paramIndex; unused++)
195 {
196 insertIndex *= 6;
197 }
198
199 if (tag == "<int>")
200 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800201 tagValue += insertIndex * toUnderlying(TypeCode::Integer);
Ed Tanous1c30e502022-03-08 18:02:24 -0800202 }
203 if (tag == "<uint>")
204 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800205 tagValue +=
206 insertIndex * toUnderlying(TypeCode::UnsignedInteger);
Ed Tanous1c30e502022-03-08 18:02:24 -0800207 }
208 if (tag == "<float>" || tag == "<double>")
209 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800210 tagValue += insertIndex * toUnderlying(TypeCode::Float);
Ed Tanous1c30e502022-03-08 18:02:24 -0800211 }
212 if (tag == "<str>" || tag == "<string>")
213 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800214 tagValue += insertIndex * toUnderlying(TypeCode::String);
Ed Tanous1c30e502022-03-08 18:02:24 -0800215 }
216 if (tag == "<path>")
217 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800218 tagValue += insertIndex * toUnderlying(TypeCode::Path);
Ed Tanous1c30e502022-03-08 18:02:24 -0800219 }
220 paramIndex++;
221 urlSegmentIndex = std::string_view::npos;
222 }
223 }
224 if (urlSegmentIndex != std::string_view::npos)
Ed Tanous988403c2020-08-24 11:29:49 -0700225 {
226 return 0;
227 }
Ed Tanous1c30e502022-03-08 18:02:24 -0800228 return tagValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700229}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700230
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500231template <typename... T>
232struct S
Ed Tanous1abe55e2018-09-05 08:30:59 -0700233{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500234 template <typename U>
235 using push = S<U, T...>;
236 template <typename U>
237 using push_back = S<T..., U>;
238 template <template <typename... Args> class U>
239 using rebind = U<T...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700240};
Ed Tanous988403c2020-08-24 11:29:49 -0700241
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500242template <typename F, typename Set>
243struct CallHelper;
Ed Tanous988403c2020-08-24 11:29:49 -0700244
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500245template <typename F, typename... Args>
246struct CallHelper<F, S<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700247{
248 template <typename F1, typename... Args1,
249 typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
Ed Tanous2c70f802020-09-28 14:29:23 -0700250 static char test(int);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700251
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500252 template <typename...>
Ed Tanous2c70f802020-09-28 14:29:23 -0700253 static int test(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700254
Ed Tanous2c70f802020-09-28 14:29:23 -0700255 static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700256};
257
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500258template <uint64_t N>
259struct SingleTagToType
260{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700261
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500262template <>
263struct SingleTagToType<1>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700264{
265 using type = int64_t;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700266};
267
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500268template <>
269struct SingleTagToType<2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700270{
271 using type = uint64_t;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700272};
273
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500274template <>
275struct SingleTagToType<3>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700276{
277 using type = double;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700278};
279
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500280template <>
281struct SingleTagToType<4>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700282{
283 using type = std::string;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700284};
285
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500286template <>
287struct SingleTagToType<5>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288{
289 using type = std::string;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700290};
291
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500292template <uint64_t Tag>
293struct Arguments
Ed Tanous1abe55e2018-09-05 08:30:59 -0700294{
295 using subarguments = typename Arguments<Tag / 6>::type;
296 using type = typename subarguments::template push<
297 typename SingleTagToType<Tag % 6>::type>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700298};
299
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500300template <>
301struct Arguments<0>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700302{
303 using type = S<>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700304};
305
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500306template <typename T>
Ed Tanous988403c2020-08-24 11:29:49 -0700307struct Promote
Ed Tanous1abe55e2018-09-05 08:30:59 -0700308{
309 using type = T;
310};
311
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500312template <typename T>
Ed Tanous988403c2020-08-24 11:29:49 -0700313using PromoteT = typename Promote<T>::type;
314
315template <>
316struct Promote<char>
317{
318 using type = int64_t;
319};
320template <>
321struct Promote<short>
322{
323 using type = int64_t;
324};
325template <>
326struct Promote<int>
327{
328 using type = int64_t;
329};
330template <>
331struct Promote<long>
332{
333 using type = int64_t;
334};
335template <>
336struct Promote<long long>
337{
338 using type = int64_t;
339};
340template <>
341struct Promote<unsigned char>
342{
343 using type = uint64_t;
344};
345template <>
346struct Promote<unsigned short>
347{
348 using type = uint64_t;
349};
350template <>
351struct Promote<unsigned int>
352{
353 using type = uint64_t;
354};
355template <>
356struct Promote<unsigned long>
357{
358 using type = uint64_t;
359};
360template <>
361struct Promote<unsigned long long>
362{
363 using type = uint64_t;
364};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700365
Ed Tanous1abe55e2018-09-05 08:30:59 -0700366} // namespace black_magic
Ed Tanous7045c8d2017-04-03 10:04:37 -0700367
Ed Tanous1abe55e2018-09-05 08:30:59 -0700368namespace utility
369{
Ed Tanous7045c8d2017-04-03 10:04:37 -0700370
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500371template <typename T>
Ed Tanousc867a832022-03-10 14:17:00 -0800372struct FunctionTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700373{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500374 template <size_t i>
Ed Tanousc867a832022-03-10 14:17:00 -0800375 using arg = std::tuple_element_t<i, boost::callable_traits::args_t<T>>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700376};
377
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600378inline std::string base64encode(const std::string_view data)
379{
380 const std::array<char, 64> key = {
381 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
382 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
383 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
384 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
385 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
386
387 size_t size = data.size();
388 std::string ret;
389 ret.resize((size + 2) / 3 * 4);
390 auto it = ret.begin();
391
392 size_t i = 0;
393 while (i < size)
394 {
Ed Tanous543f4402022-01-06 13:12:53 -0800395 size_t keyIndex = 0;
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600396
397 keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
398 *it++ = key[keyIndex];
399
400 if (i + 1 < size)
401 {
402 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
403 keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
404 *it++ = key[keyIndex];
405
406 if (i + 2 < size)
407 {
408 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
409 keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
410 *it++ = key[keyIndex];
411
412 keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
413 *it++ = key[keyIndex];
414 }
415 else
416 {
417 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
418 *it++ = key[keyIndex];
419 *it++ = '=';
420 }
421 }
422 else
423 {
424 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
425 *it++ = key[keyIndex];
426 *it++ = '=';
427 *it++ = '=';
428 }
429
430 i += 3;
431 }
432
433 return ret;
434}
435
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100436// TODO this is temporary and should be deleted once base64 is refactored out of
437// crow
Ed Tanous39e77502019-03-04 17:35:53 -0800438inline bool base64Decode(const std::string_view input, std::string& output)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700439{
Ed Tanous271584a2019-07-09 16:24:22 -0700440 static const char nop = static_cast<char>(-1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700441 // See note on encoding_data[] in above function
Jonathan Doman5beaf842020-08-14 11:23:33 -0700442 static const std::array<char, 256> decodingData = {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700443 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
444 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
445 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
446 nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59,
447 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4,
448 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
449 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26,
450 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
451 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop,
452 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
453 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
454 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 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};
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100462
Ed Tanous1abe55e2018-09-05 08:30:59 -0700463 size_t inputLength = input.size();
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100464
Ed Tanous1abe55e2018-09-05 08:30:59 -0700465 // allocate space for output string
466 output.clear();
467 output.reserve(((inputLength + 2) / 3) * 4);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100468
Jonathan Doman5beaf842020-08-14 11:23:33 -0700469 auto getCodeValue = [](char c) {
470 auto code = static_cast<unsigned char>(c);
471 // Ensure we cannot index outside the bounds of the decoding array
472 static_assert(std::numeric_limits<decltype(code)>::max() <
473 decodingData.size());
474 return decodingData[code];
475 };
476
Ed Tanous1abe55e2018-09-05 08:30:59 -0700477 // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500478 // dropping first two bits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700479 // and regenerate into 3 8-bits sequences
James Feist5a806642020-07-31 16:40:33 +0000480
Ed Tanous1abe55e2018-09-05 08:30:59 -0700481 for (size_t i = 0; i < inputLength; i++)
482 {
Ed Tanous543f4402022-01-06 13:12:53 -0800483 char base64code0 = 0;
484 char base64code1 = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700485 char base64code2 = 0; // initialized to 0 to suppress warnings
Ed Tanous543f4402022-01-06 13:12:53 -0800486 char base64code3 = 0;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100487
Jonathan Doman5beaf842020-08-14 11:23:33 -0700488 base64code0 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700489 if (base64code0 == nop)
490 { // non base64 character
491 return false;
492 }
493 if (!(++i < inputLength))
494 { // we need at least two input bytes for first
495 // byte output
496 return false;
497 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700498 base64code1 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700499 if (base64code1 == nop)
500 { // non base64 character
501 return false;
502 }
503 output +=
504 static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100505
Ed Tanous1abe55e2018-09-05 08:30:59 -0700506 if (++i < inputLength)
507 {
508 char c = input[i];
509 if (c == '=')
510 { // padding , end of input
511 return (base64code1 & 0x0f) == 0;
512 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700513 base64code2 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700514 if (base64code2 == nop)
515 { // non base64 character
516 return false;
517 }
518 output += static_cast<char>(((base64code1 << 4) & 0xf0) |
519 ((base64code2 >> 2) & 0x0f));
520 }
521
522 if (++i < inputLength)
523 {
524 char c = input[i];
525 if (c == '=')
526 { // padding , end of input
527 return (base64code2 & 0x03) == 0;
528 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700529 base64code3 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700530 if (base64code3 == nop)
531 { // non base64 character
532 return false;
533 }
534 output +=
535 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
536 }
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100537 }
538
Ed Tanous1abe55e2018-09-05 08:30:59 -0700539 return true;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100540}
541
Krzysztof Grobelny45c367e2021-12-29 10:28:53 +0100542namespace details
543{
Ed Tanous9896eae2022-07-23 15:07:33 -0700544// Returns year/month/day triple in civil calendar
545// Preconditions: z is number of days since 1970-01-01 and is in the range:
546// [numeric_limits<Int>::min(),
547// numeric_limits<Int>::max()-719468].
548// Algorithm sourced from
549// https://howardhinnant.github.io/date_algorithms.html#civil_from_days
550// All constants are explained in the above
551template <class IntType>
552constexpr std::tuple<IntType, unsigned, unsigned>
553 civilFromDays(IntType z) noexcept
Andrew Geisslercb92c032018-08-17 07:56:14 -0700554{
Ed Tanous9896eae2022-07-23 15:07:33 -0700555 z += 719468;
556 IntType era = (z >= 0 ? z : z - 146096) / 146097;
557 unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
558 unsigned yoe =
559 (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399]
560 IntType y = static_cast<IntType>(yoe) + era * 400;
561 unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365]
562 unsigned mp = (5 * doy + 2) / 153; // [0, 11]
563 unsigned d = doy - (153 * mp + 2) / 5 + 1; // [1, 31]
564 unsigned m = mp < 10 ? mp + 3 : mp - 9; // [1, 12]
565
566 return std::tuple<IntType, unsigned, unsigned>(y + (m <= 2), m, d);
567}
568
569// Creates a string from an integer in the most efficient way possible without
570// using std::locale. Adds an exact zero pad based on the pad input parameter.
571// Does not handle negative numbers.
572inline std::string padZeros(int value, size_t pad)
573{
574 std::string result(pad, '0');
575 for (int val = value; pad > 0; pad--)
576 {
577 result[pad - 1] = static_cast<char>('0' + val % 10);
578 val /= 10;
579 }
580 return result;
581}
582
583template <typename IntType, typename Period>
584std::string toISO8061ExtendedStr(std::chrono::duration<IntType, Period> t)
585{
586 using seconds = std::chrono::duration<int>;
587 using minutes = std::chrono::duration<int, std::ratio<60>>;
588 using hours = std::chrono::duration<int, std::ratio<3600>>;
589 using days = std::chrono::duration<
590 IntType, std::ratio_multiply<hours::period, std::ratio<24>>>;
591
592 // d is days since 1970-01-01
593 days d = std::chrono::duration_cast<days>(t);
594
595 // t is now time duration since midnight of day d
596 t -= d;
597
598 // break d down into year/month/day
599 int year = 0;
600 int month = 0;
601 int day = 0;
602 std::tie(year, month, day) = details::civilFromDays(d.count());
603 // Check against limits. Can't go above year 9999, and can't go below epoch
604 // (1970)
605 if (year >= 10000)
606 {
607 year = 9999;
608 month = 12;
609 day = 31;
610 t = days(1) - std::chrono::duration<IntType, Period>(1);
611 }
612 else if (year < 1970)
613 {
614 year = 1970;
615 month = 1;
616 day = 1;
617 t = std::chrono::duration<IntType, Period>::zero();
618 }
619 std::string out;
620 out += details::padZeros(year, 4);
621 out += '-';
622 out += details::padZeros(month, 2);
623 out += '-';
624 out += details::padZeros(day, 2);
625
626 out += 'T';
627 hours hr = duration_cast<hours>(t);
628 out += details::padZeros(hr.count(), 2);
629 t -= hr;
630 out += ':';
631
632 minutes mt = duration_cast<minutes>(t);
633 out += details::padZeros(mt.count(), 2);
634 t -= mt;
635 out += ':';
636
637 seconds se = duration_cast<seconds>(t);
638 out += details::padZeros(se.count(), 2);
Ed Tanous2c986762022-07-23 17:32:15 -0700639 t -= se;
640
641 if constexpr (std::is_same_v<typename decltype(t)::period, std::milli>)
642 {
643 out += '.';
644 using MilliDuration = std::chrono::duration<int, std::milli>;
645 MilliDuration subsec = duration_cast<MilliDuration>(t);
646 out += details::padZeros(subsec.count(), 3);
647 }
648 else if constexpr (std::is_same_v<typename decltype(t)::period, std::micro>)
649 {
650 out += '.';
651 using MicroDuration = std::chrono::duration<int, std::micro>;
652 MicroDuration subsec = duration_cast<MicroDuration>(t);
653 out += details::padZeros(subsec.count(), 6);
654 }
Ed Tanous9896eae2022-07-23 15:07:33 -0700655
656 out += "+00:00";
657 return out;
Nan Zhou1d8782e2021-11-29 22:23:18 -0800658}
Krzysztof Grobelny45c367e2021-12-29 10:28:53 +0100659} // namespace details
Andrew Geisslercb92c032018-08-17 07:56:14 -0700660
Ed Tanous22ce5452022-01-11 10:50:23 -0800661// Returns the formatted date time string.
662// Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if
663// the given |secondsSinceEpoch| is too large, we return the maximum supported
Ed Tanous9896eae2022-07-23 15:07:33 -0700664// date.
Nan Zhou1d8782e2021-11-29 22:23:18 -0800665inline std::string getDateTimeUint(uint64_t secondsSinceEpoch)
666{
Ed Tanous9896eae2022-07-23 15:07:33 -0700667 using DurationType = std::chrono::duration<uint64_t>;
668 DurationType sinceEpoch(secondsSinceEpoch);
669 return details::toISO8061ExtendedStr(sinceEpoch);
Krzysztof Grobelny45c367e2021-12-29 10:28:53 +0100670}
671
Ed Tanous2c986762022-07-23 17:32:15 -0700672// Returns the formatted date time string with millisecond precision
Ed Tanous9896eae2022-07-23 15:07:33 -0700673// Note that the maximum supported date is 9999-12-31T23:59:59+00:00, if
674// the given |secondsSinceEpoch| is too large, we return the maximum supported
675// date.
Ed Tanous22ce5452022-01-11 10:50:23 -0800676inline std::string getDateTimeUintMs(uint64_t milliSecondsSinceEpoch)
Krzysztof Grobelny45c367e2021-12-29 10:28:53 +0100677{
Ed Tanous9896eae2022-07-23 15:07:33 -0700678 using DurationType = std::chrono::duration<uint64_t, std::milli>;
679 DurationType sinceEpoch(milliSecondsSinceEpoch);
680 return details::toISO8061ExtendedStr(sinceEpoch);
Nan Zhou1d8782e2021-11-29 22:23:18 -0800681}
Andrew Geisslercb92c032018-08-17 07:56:14 -0700682
Ed Tanous2c986762022-07-23 17:32:15 -0700683// Returns the formatted date time string with microsecond precision
684inline std::string getDateTimeUintUs(uint64_t microSecondsSinceEpoch)
685{
686 using DurationType = std::chrono::duration<uint64_t, std::micro>;
687 DurationType sinceEpoch(microSecondsSinceEpoch);
688 return details::toISO8061ExtendedStr(sinceEpoch);
689}
690
Nan Zhou1d8782e2021-11-29 22:23:18 -0800691inline std::string getDateTimeStdtime(std::time_t secondsSinceEpoch)
692{
Ed Tanous9896eae2022-07-23 15:07:33 -0700693 using DurationType = std::chrono::duration<std::time_t>;
694 DurationType sinceEpoch(secondsSinceEpoch);
695 return details::toISO8061ExtendedStr(sinceEpoch);
Andrew Geisslercb92c032018-08-17 07:56:14 -0700696}
697
Tejas Patil7c8c4052021-06-04 17:43:14 +0530698/**
699 * Returns the current Date, Time & the local Time Offset
700 * infromation in a pair
701 *
702 * @param[in] None
703 *
704 * @return std::pair<std::string, std::string>, which consist
705 * of current DateTime & the TimeOffset strings respectively.
706 */
707inline std::pair<std::string, std::string> getDateTimeOffsetNow()
Andrew Geisslercb92c032018-08-17 07:56:14 -0700708{
709 std::time_t time = std::time(nullptr);
Nan Zhou1d8782e2021-11-29 22:23:18 -0800710 std::string dateTime = getDateTimeStdtime(time);
Tejas Patil7c8c4052021-06-04 17:43:14 +0530711
712 /* extract the local Time Offset value from the
713 * recevied dateTime string.
714 */
715 std::string timeOffset("Z00:00");
716 std::size_t lastPos = dateTime.size();
717 std::size_t len = timeOffset.size();
718 if (lastPos > len)
719 {
720 timeOffset = dateTime.substr(lastPos - len);
721 }
722
723 return std::make_pair(dateTime, timeOffset);
Andrew Geisslercb92c032018-08-17 07:56:14 -0700724}
725
Ed Tanous51dae672018-09-05 16:07:32 -0700726inline bool constantTimeStringCompare(const std::string_view a,
727 const std::string_view b)
728{
729 // Important note, this function is ONLY constant time if the two input
730 // sizes are the same
731 if (a.size() != b.size())
732 {
733 return false;
734 }
735 return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
736}
737
738struct ConstantTimeCompare
739{
740 bool operator()(const std::string_view a, const std::string_view b) const
741 {
742 return constantTimeStringCompare(a, b);
743 }
744};
745
Ed Tanouseae855c2021-10-26 11:26:02 -0700746namespace details
747{
748inline boost::urls::url
749 urlFromPiecesDetail(const std::initializer_list<std::string_view> args)
750{
751 boost::urls::url url("/");
752 for (const std::string_view& arg : args)
753 {
754 url.segments().push_back(arg);
755 }
756 return url;
757}
758} // namespace details
759
760template <typename... AV>
761inline boost::urls::url urlFromPieces(const AV... args)
762{
763 return details::urlFromPiecesDetail({args...});
764}
765
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100766namespace details
767{
768
769// std::reference_wrapper<std::string> - extracts segment to variable
770// std::string_view - checks if segment is equal to variable
771using UrlSegment =
772 std::variant<std::reference_wrapper<std::string>, std::string_view>;
773
774class UrlSegmentMatcherVisitor
775{
776 public:
777 bool operator()(std::string& output)
778 {
779 output = std::string_view(segment.data(), segment.size());
780 return true;
781 }
782
783 bool operator()(std::string_view expected)
784 {
785 return std::string_view(segment.data(), segment.size()) == expected;
786 }
787
Ed Tanous4e23a442022-06-06 09:57:26 -0700788 explicit UrlSegmentMatcherVisitor(
789 const boost::urls::string_value& segmentIn) :
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100790 segment(segmentIn)
791 {}
792
793 private:
794 const boost::urls::string_value& segment;
795};
796
797inline bool readUrlSegments(const boost::urls::url_view& urlView,
798 std::initializer_list<UrlSegment>&& segments)
799{
800 const boost::urls::segments_view& urlSegments = urlView.segments();
801
802 if (!urlSegments.is_absolute() || segments.size() != urlSegments.size())
803 {
804 return false;
805 }
806
807 boost::urls::segments_view::iterator it = urlSegments.begin();
808 boost::urls::segments_view::iterator end = urlSegments.end();
809
810 for (const auto& segment : segments)
811 {
812 if (!std::visit(UrlSegmentMatcherVisitor(*it), segment))
813 {
814 return false;
815 }
816 it++;
817 }
818 return true;
819}
820
821} // namespace details
822
823template <typename... Args>
824inline bool readUrlSegments(const boost::urls::url_view& urlView,
825 Args&&... args)
826{
827 return details::readUrlSegments(urlView, {std::forward<Args>(args)...});
828}
829
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800830inline std::string setProtocolDefaults(const boost::urls::url_view& url)
831{
832 if (url.scheme() == "https")
833 {
834 return "https";
835 }
836 if (url.scheme() == "http")
837 {
838 if (bmcwebInsecureEnableHttpPushStyleEventing)
839 {
840 return "http";
841 }
842 return "";
843 }
844 return "";
845}
846
847inline uint16_t setPortDefaults(const boost::urls::url_view& url)
848{
849 uint16_t port = url.port_number();
850 if (port != 0)
851 {
852 // user picked a port already.
853 return port;
854 }
855
856 // If the user hasn't explicitly stated a port, pick one explicitly for them
857 // based on the protocol defaults
858 if (url.scheme() == "http")
859 {
860 return 80;
861 }
862 if (url.scheme() == "https")
863 {
864 return 443;
865 }
866 return 0;
867}
868
Ed Tanous11baefe2022-02-09 12:14:12 -0800869inline bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto,
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800870 std::string& host, uint16_t& port,
Ed Tanous11baefe2022-02-09 12:14:12 -0800871 std::string& path)
872{
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800873 boost::string_view urlBoost(destUrl.data(), destUrl.size());
874 boost::urls::result<boost::urls::url_view> url =
875 boost::urls::parse_uri(urlBoost);
876 if (!url)
877 {
878 return false;
879 }
880 urlProto = setProtocolDefaults(url.value());
881 if (urlProto.empty())
Ed Tanous11baefe2022-02-09 12:14:12 -0800882 {
883 return false;
884 }
885
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800886 port = setPortDefaults(url.value());
Ed Tanous11baefe2022-02-09 12:14:12 -0800887
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800888 host = std::string_view(url->encoded_host().data(),
889 url->encoded_host().size());
890
891 path = std::string_view(url->encoded_path().data(),
892 url->encoded_path().size());
Ed Tanous11baefe2022-02-09 12:14:12 -0800893 if (path.empty())
894 {
895 path = "/";
896 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800897 if (url->has_fragment())
898 {
899 path += '#';
900 path += std::string_view(url->encoded_fragment().data(),
901 url->encoded_fragment().size());
902 }
903
904 if (url->has_query())
905 {
906 path += '?';
907 path += std::string_view(url->encoded_query().data(),
908 url->encoded_query().size());
909 }
910
Ed Tanous11baefe2022-02-09 12:14:12 -0800911 return true;
912}
913
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914} // namespace utility
915} // namespace crow
Ed Tanous71f2db72022-05-25 12:28:09 -0700916
917namespace nlohmann
918{
919template <>
920struct adl_serializer<boost::urls::url>
921{
922 // nlohmann requires a specific casing to look these up in adl
923 // NOLINTNEXTLINE(readability-identifier-naming)
924 static void to_json(json& j, const boost::urls::url& url)
925 {
926 j = url.string();
927 }
928};
929
930template <>
931struct adl_serializer<boost::urls::url_view>
932{
933 // NOLINTNEXTLINE(readability-identifier-naming)
934 static void to_json(json& j, const boost::urls::url_view& url)
935 {
936 j = url.string();
937 }
938};
939} // namespace nlohmann