blob: f49aa9b859e88af6003e3338b4c7e1faa3943830 [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 Tanous079360a2022-06-29 10:05:19 -07007#include <boost/url/parse.hpp>
Ed Tanouseae855c2021-10-26 11:26:02 -07008#include <boost/url/url.hpp>
Ed Tanous079360a2022-06-29 10:05:19 -07009#include <boost/url/url_view.hpp>
Ed Tanous71f2db72022-05-25 12:28:09 -070010#include <nlohmann/json.hpp>
Nan Zhou1d8782e2021-11-29 22:23:18 -080011
Ed Tanous9ea15c32022-01-04 14:18:22 -080012#include <array>
Ed Tanous74849be2021-02-05 09:47:47 -080013#include <chrono>
Ed Tanousc715ec22022-03-10 15:38:01 -080014#include <cstddef>
Ed Tanous7045c8d2017-04-03 10:04:37 -070015#include <cstdint>
Ed Tanous9ea15c32022-01-04 14:18:22 -080016#include <ctime>
Ed Tanous7045c8d2017-04-03 10:04:37 -070017#include <functional>
Ed Tanous9896eae2022-07-23 15:07:33 -070018#include <iomanip>
Ed Tanous9ea15c32022-01-04 14:18:22 -080019#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070020#include <stdexcept>
21#include <string>
Ed Tanous9ea15c32022-01-04 14:18:22 -080022#include <string_view>
Ed Tanous7045c8d2017-04-03 10:04:37 -070023#include <tuple>
Ed Tanous9ea15c32022-01-04 14:18:22 -080024#include <type_traits>
25#include <utility>
Szymon Dompkeca1600c2022-03-03 14:42:52 +010026#include <variant>
Ed Tanous7045c8d2017-04-03 10:04:37 -070027
Ed Tanous1abe55e2018-09-05 08:30:59 -070028namespace crow
29{
30namespace black_magic
31{
Ed Tanous7045c8d2017-04-03 10:04:37 -070032
Ed Tanousc715ec22022-03-10 15:38:01 -080033enum class TypeCode : uint8_t
34{
35 Unspecified = 0,
36 Integer = 1,
37 UnsignedInteger = 2,
38 Float = 3,
39 String = 4,
40 Path = 5,
41 Max = 6,
42};
43
44// Remove when we have c++23
45template <typename E>
46constexpr typename std::underlying_type<E>::type toUnderlying(E e) noexcept
47{
48 return static_cast<typename std::underlying_type<E>::type>(e);
49}
50
Gunnar Mills1214b7e2020-06-04 10:11:30 -050051template <typename T>
Ed Tanousc715ec22022-03-10 15:38:01 -080052constexpr TypeCode getParameterTag()
Ed Tanous1abe55e2018-09-05 08:30:59 -070053{
Ed Tanous69509012019-10-24 16:53:05 -070054 if constexpr (std::is_same_v<int, T>)
55 {
Ed Tanousc715ec22022-03-10 15:38:01 -080056 return TypeCode::Integer;
Ed Tanous1abe55e2018-09-05 08:30:59 -070057 }
Ed Tanous69509012019-10-24 16:53:05 -070058 if constexpr (std::is_same_v<char, T>)
59 {
Ed Tanousc715ec22022-03-10 15:38:01 -080060 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070061 }
62 if constexpr (std::is_same_v<short, T>)
63 {
Ed Tanousc715ec22022-03-10 15:38:01 -080064 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070065 }
66 if constexpr (std::is_same_v<long, T>)
67 {
Ed Tanousc715ec22022-03-10 15:38:01 -080068 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070069 }
70 if constexpr (std::is_same_v<long long, T>)
71 {
Ed Tanousc715ec22022-03-10 15:38:01 -080072 return TypeCode::Integer;
Ed Tanous69509012019-10-24 16:53:05 -070073 }
74 if constexpr (std::is_same_v<unsigned int, T>)
75 {
Ed Tanousc715ec22022-03-10 15:38:01 -080076 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070077 }
78 if constexpr (std::is_same_v<unsigned char, T>)
79 {
Ed Tanousc715ec22022-03-10 15:38:01 -080080 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070081 }
82 if constexpr (std::is_same_v<unsigned short, T>)
83 {
Ed Tanousc715ec22022-03-10 15:38:01 -080084 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070085 }
86 if constexpr (std::is_same_v<unsigned long, T>)
87 {
Ed Tanousc715ec22022-03-10 15:38:01 -080088 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070089 }
90 if constexpr (std::is_same_v<unsigned long long, T>)
91 {
Ed Tanousc715ec22022-03-10 15:38:01 -080092 return TypeCode::UnsignedInteger;
Ed Tanous69509012019-10-24 16:53:05 -070093 }
94 if constexpr (std::is_same_v<double, T>)
95 {
Ed Tanousc715ec22022-03-10 15:38:01 -080096 return TypeCode::Float;
Ed Tanous69509012019-10-24 16:53:05 -070097 }
98 if constexpr (std::is_same_v<std::string, T>)
99 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800100 return TypeCode::String;
Ed Tanous69509012019-10-24 16:53:05 -0700101 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800102 return TypeCode::Unspecified;
Ed Tanous69509012019-10-24 16:53:05 -0700103}
104
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500105template <typename... Args>
Ed Tanous988403c2020-08-24 11:29:49 -0700106struct computeParameterTagFromArgsList;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700107
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500108template <>
Ed Tanous988403c2020-08-24 11:29:49 -0700109struct computeParameterTagFromArgsList<>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700110{
Ed Tanous69509012019-10-24 16:53:05 -0700111 static constexpr int value = 0;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700112};
113
114template <typename Arg, typename... Args>
Ed Tanous988403c2020-08-24 11:29:49 -0700115struct computeParameterTagFromArgsList<Arg, Args...>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116{
Ed Tanous69509012019-10-24 16:53:05 -0700117 static constexpr int subValue =
Ed Tanous988403c2020-08-24 11:29:49 -0700118 computeParameterTagFromArgsList<Args...>::value;
Ed Tanous69509012019-10-24 16:53:05 -0700119 static constexpr int value =
Ed Tanousc715ec22022-03-10 15:38:01 -0800120 getParameterTag<typename std::decay<Arg>::type>() !=
121 TypeCode::Unspecified
122 ? static_cast<unsigned long>(subValue *
123 toUnderlying(TypeCode::Max)) +
124 static_cast<uint64_t>(
125 getParameterTag<typename std::decay<Arg>::type>())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700126 : subValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700127};
128
Ed Tanous988403c2020-08-24 11:29:49 -0700129inline bool isParameterTagCompatible(uint64_t a, uint64_t b)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700130{
Ed Tanous1c30e502022-03-08 18:02:24 -0800131 while (true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700132 {
Ed Tanousef641b62022-06-28 19:53:24 -0700133 if (a == 0 && b == 0)
Ed Tanous1c30e502022-03-08 18:02:24 -0800134 {
Ed Tanousef641b62022-06-28 19:53:24 -0700135 // Both tags were equivalent, parameters are compatible
136 return true;
Ed Tanous1c30e502022-03-08 18:02:24 -0800137 }
Ed Tanousef641b62022-06-28 19:53:24 -0700138 if (a == 0 || b == 0)
Ed Tanous1c30e502022-03-08 18:02:24 -0800139 {
Ed Tanousef641b62022-06-28 19:53:24 -0700140 // one of the tags had more parameters than the other
141 return false;
Ed Tanous1c30e502022-03-08 18:02:24 -0800142 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800143 TypeCode sa = static_cast<TypeCode>(a % toUnderlying(TypeCode::Max));
144 TypeCode sb = static_cast<TypeCode>(b % toUnderlying(TypeCode::Max));
145
146 if (sa == TypeCode::Path)
Ed Tanous1c30e502022-03-08 18:02:24 -0800147 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800148 sa = TypeCode::String;
Ed Tanous1c30e502022-03-08 18:02:24 -0800149 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800150 if (sb == TypeCode::Path)
Ed Tanous1c30e502022-03-08 18:02:24 -0800151 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800152 sb = TypeCode::String;
Ed Tanous1c30e502022-03-08 18:02:24 -0800153 }
154 if (sa != sb)
155 {
156 return false;
157 }
Ed Tanousc715ec22022-03-10 15:38:01 -0800158 a /= toUnderlying(TypeCode::Max);
159 b /= toUnderlying(TypeCode::Max);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700160 }
Ed Tanous1c30e502022-03-08 18:02:24 -0800161 return false;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700162}
163
Ed Tanous1c30e502022-03-08 18:02:24 -0800164constexpr inline uint64_t getParameterTag(std::string_view url)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700165{
Ed Tanous1c30e502022-03-08 18:02:24 -0800166 uint64_t tagValue = 0;
167 size_t urlSegmentIndex = std::string_view::npos;
Ed Tanousb00dcc22021-02-23 12:52:50 -0800168
Ed Tanous1c30e502022-03-08 18:02:24 -0800169 size_t paramIndex = 0;
170
171 for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
172 {
173 char character = url[urlIndex];
174 if (character == '<')
175 {
176 if (urlSegmentIndex != std::string_view::npos)
177 {
178 return 0;
179 }
180 urlSegmentIndex = urlIndex;
181 }
182 if (character == '>')
183 {
184 if (urlSegmentIndex == std::string_view::npos)
185 {
186 return 0;
187 }
188 std::string_view tag =
189 url.substr(urlSegmentIndex, urlIndex + 1 - urlSegmentIndex);
190
191 // Note, this is a really lame way to do std::pow(6, paramIndex)
192 // std::pow doesn't work in constexpr in clang.
193 // Ideally in the future we'd move this to use a power of 2 packing
194 // (probably 8 instead of 6) so that these just become bit shifts
195 uint64_t insertIndex = 1;
196 for (size_t unused = 0; unused < paramIndex; unused++)
197 {
198 insertIndex *= 6;
199 }
200
201 if (tag == "<int>")
202 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800203 tagValue += insertIndex * toUnderlying(TypeCode::Integer);
Ed Tanous1c30e502022-03-08 18:02:24 -0800204 }
205 if (tag == "<uint>")
206 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800207 tagValue +=
208 insertIndex * toUnderlying(TypeCode::UnsignedInteger);
Ed Tanous1c30e502022-03-08 18:02:24 -0800209 }
210 if (tag == "<float>" || tag == "<double>")
211 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800212 tagValue += insertIndex * toUnderlying(TypeCode::Float);
Ed Tanous1c30e502022-03-08 18:02:24 -0800213 }
214 if (tag == "<str>" || tag == "<string>")
215 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800216 tagValue += insertIndex * toUnderlying(TypeCode::String);
Ed Tanous1c30e502022-03-08 18:02:24 -0800217 }
218 if (tag == "<path>")
219 {
Ed Tanousc715ec22022-03-10 15:38:01 -0800220 tagValue += insertIndex * toUnderlying(TypeCode::Path);
Ed Tanous1c30e502022-03-08 18:02:24 -0800221 }
222 paramIndex++;
223 urlSegmentIndex = std::string_view::npos;
224 }
225 }
226 if (urlSegmentIndex != std::string_view::npos)
Ed Tanous988403c2020-08-24 11:29:49 -0700227 {
228 return 0;
229 }
Ed Tanous1c30e502022-03-08 18:02:24 -0800230 return tagValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700231}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700232
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500233template <typename... T>
234struct S
Ed Tanous1abe55e2018-09-05 08:30:59 -0700235{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500236 template <typename U>
237 using push = S<U, T...>;
238 template <typename U>
239 using push_back = S<T..., U>;
240 template <template <typename... Args> class U>
241 using rebind = U<T...>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700242};
Ed Tanous988403c2020-08-24 11:29:49 -0700243
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500244template <typename F, typename Set>
245struct CallHelper;
Ed Tanous988403c2020-08-24 11:29:49 -0700246
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500247template <typename F, typename... Args>
248struct CallHelper<F, S<Args...>>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700249{
250 template <typename F1, typename... Args1,
251 typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
Ed Tanous2c70f802020-09-28 14:29:23 -0700252 static char test(int);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700253
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500254 template <typename...>
Ed Tanous2c70f802020-09-28 14:29:23 -0700255 static int test(...);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700256
Ed Tanous2c70f802020-09-28 14:29:23 -0700257 static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700258};
259
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500260template <uint64_t N>
261struct SingleTagToType
262{};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700263
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500264template <>
265struct SingleTagToType<1>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700266{
267 using type = int64_t;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700268};
269
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500270template <>
271struct SingleTagToType<2>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272{
273 using type = uint64_t;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700274};
275
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500276template <>
277struct SingleTagToType<3>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700278{
279 using type = double;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700280};
281
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500282template <>
283struct SingleTagToType<4>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284{
285 using type = std::string;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700286};
287
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500288template <>
289struct SingleTagToType<5>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700290{
291 using type = std::string;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700292};
293
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500294template <uint64_t Tag>
295struct Arguments
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296{
297 using subarguments = typename Arguments<Tag / 6>::type;
298 using type = typename subarguments::template push<
299 typename SingleTagToType<Tag % 6>::type>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700300};
301
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500302template <>
303struct Arguments<0>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304{
305 using type = S<>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700306};
307
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500308template <typename T>
Ed Tanous988403c2020-08-24 11:29:49 -0700309struct Promote
Ed Tanous1abe55e2018-09-05 08:30:59 -0700310{
311 using type = T;
312};
313
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500314template <typename T>
Ed Tanous988403c2020-08-24 11:29:49 -0700315using PromoteT = typename Promote<T>::type;
316
317template <>
318struct Promote<char>
319{
320 using type = int64_t;
321};
322template <>
323struct Promote<short>
324{
325 using type = int64_t;
326};
327template <>
328struct Promote<int>
329{
330 using type = int64_t;
331};
332template <>
333struct Promote<long>
334{
335 using type = int64_t;
336};
337template <>
338struct Promote<long long>
339{
340 using type = int64_t;
341};
342template <>
343struct Promote<unsigned char>
344{
345 using type = uint64_t;
346};
347template <>
348struct Promote<unsigned short>
349{
350 using type = uint64_t;
351};
352template <>
353struct Promote<unsigned int>
354{
355 using type = uint64_t;
356};
357template <>
358struct Promote<unsigned long>
359{
360 using type = uint64_t;
361};
362template <>
363struct Promote<unsigned long long>
364{
365 using type = uint64_t;
366};
Ed Tanous7045c8d2017-04-03 10:04:37 -0700367
Ed Tanous1abe55e2018-09-05 08:30:59 -0700368} // namespace black_magic
Ed Tanous7045c8d2017-04-03 10:04:37 -0700369
Ed Tanous1abe55e2018-09-05 08:30:59 -0700370namespace utility
371{
Ed Tanous7045c8d2017-04-03 10:04:37 -0700372
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500373template <typename T>
Ed Tanousc867a832022-03-10 14:17:00 -0800374struct FunctionTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700375{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500376 template <size_t i>
Ed Tanousc867a832022-03-10 14:17:00 -0800377 using arg = std::tuple_element_t<i, boost::callable_traits::args_t<T>>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700378};
379
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600380inline std::string base64encode(const std::string_view data)
381{
382 const std::array<char, 64> key = {
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 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
386 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
387 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
388
389 size_t size = data.size();
390 std::string ret;
391 ret.resize((size + 2) / 3 * 4);
392 auto it = ret.begin();
393
394 size_t i = 0;
395 while (i < size)
396 {
Ed Tanous543f4402022-01-06 13:12:53 -0800397 size_t keyIndex = 0;
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600398
399 keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
400 *it++ = key[keyIndex];
401
402 if (i + 1 < size)
403 {
404 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
405 keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
406 *it++ = key[keyIndex];
407
408 if (i + 2 < size)
409 {
410 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
411 keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
412 *it++ = key[keyIndex];
413
414 keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
415 *it++ = key[keyIndex];
416 }
417 else
418 {
419 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
420 *it++ = key[keyIndex];
421 *it++ = '=';
422 }
423 }
424 else
425 {
426 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
427 *it++ = key[keyIndex];
428 *it++ = '=';
429 *it++ = '=';
430 }
431
432 i += 3;
433 }
434
435 return ret;
436}
437
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100438// TODO this is temporary and should be deleted once base64 is refactored out of
439// crow
Ed Tanous39e77502019-03-04 17:35:53 -0800440inline bool base64Decode(const std::string_view input, std::string& output)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700441{
Ed Tanous271584a2019-07-09 16:24:22 -0700442 static const char nop = static_cast<char>(-1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700443 // See note on encoding_data[] in above function
Jonathan Doman5beaf842020-08-14 11:23:33 -0700444 static const std::array<char, 256> decodingData = {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
446 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, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59,
449 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4,
450 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
451 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26,
452 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
453 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 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, 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};
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100464
Ed Tanous1abe55e2018-09-05 08:30:59 -0700465 size_t inputLength = input.size();
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100466
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 // allocate space for output string
468 output.clear();
469 output.reserve(((inputLength + 2) / 3) * 4);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100470
Jonathan Doman5beaf842020-08-14 11:23:33 -0700471 auto getCodeValue = [](char c) {
472 auto code = static_cast<unsigned char>(c);
473 // Ensure we cannot index outside the bounds of the decoding array
474 static_assert(std::numeric_limits<decltype(code)>::max() <
475 decodingData.size());
476 return decodingData[code];
477 };
478
Ed Tanous1abe55e2018-09-05 08:30:59 -0700479 // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500480 // dropping first two bits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700481 // and regenerate into 3 8-bits sequences
James Feist5a806642020-07-31 16:40:33 +0000482
Ed Tanous1abe55e2018-09-05 08:30:59 -0700483 for (size_t i = 0; i < inputLength; i++)
484 {
Ed Tanous543f4402022-01-06 13:12:53 -0800485 char base64code0 = 0;
486 char base64code1 = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700487 char base64code2 = 0; // initialized to 0 to suppress warnings
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100488
Jonathan Doman5beaf842020-08-14 11:23:33 -0700489 base64code0 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700490 if (base64code0 == nop)
491 { // non base64 character
492 return false;
493 }
494 if (!(++i < inputLength))
495 { // we need at least two input bytes for first
496 // byte output
497 return false;
498 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700499 base64code1 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700500 if (base64code1 == nop)
501 { // non base64 character
502 return false;
503 }
504 output +=
505 static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100506
Ed Tanous1abe55e2018-09-05 08:30:59 -0700507 if (++i < inputLength)
508 {
509 char c = input[i];
510 if (c == '=')
511 { // padding , end of input
512 return (base64code1 & 0x0f) == 0;
513 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700514 base64code2 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700515 if (base64code2 == nop)
516 { // non base64 character
517 return false;
518 }
519 output += static_cast<char>(((base64code1 << 4) & 0xf0) |
520 ((base64code2 >> 2) & 0x0f));
521 }
522
523 if (++i < inputLength)
524 {
525 char c = input[i];
526 if (c == '=')
527 { // padding , end of input
528 return (base64code2 & 0x03) == 0;
529 }
Ed Tanousf8fe53e2022-06-30 15:55:45 -0700530 char base64code3 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700531 if (base64code3 == nop)
532 { // non base64 character
533 return false;
534 }
535 output +=
536 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
537 }
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100538 }
539
Ed Tanous1abe55e2018-09-05 08:30:59 -0700540 return true;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100541}
542
Ed Tanous51dae672018-09-05 16:07:32 -0700543inline bool constantTimeStringCompare(const std::string_view a,
544 const std::string_view b)
545{
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{
557 bool operator()(const std::string_view a, const std::string_view b) const
558 {
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 Tanouseae855c2021-10-26 11:26:02 -0700569 for (const std::string_view& arg : args)
570 {
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
646inline bool readUrlSegments(const boost::urls::url_view& urlView,
647 std::initializer_list<UrlSegment>&& segments)
648{
649 const boost::urls::segments_view& urlSegments = urlView.segments();
650
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>
690inline bool readUrlSegments(const boost::urls::url_view& urlView,
691 Args&&... args)
692{
693 return details::readUrlSegments(urlView, {std::forward<Args>(args)...});
694}
695
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000696inline boost::urls::url replaceUrlSegment(const boost::urls::url_view& urlView,
697 const uint replaceLoc,
698 const std::string_view newSegment)
699{
700 const boost::urls::segments_view& urlSegments = urlView.segments();
701 boost::urls::url url("/");
702
703 if (!urlSegments.is_absolute())
704 {
705 return url;
706 }
707
708 boost::urls::segments_view::iterator it = urlSegments.begin();
709 boost::urls::segments_view::iterator end = urlSegments.end();
710
711 for (uint idx = 0; it != end; it++, idx++)
712 {
713 if (idx == replaceLoc)
714 {
715 url.segments().push_back(newSegment);
716 }
717 else
718 {
719 url.segments().push_back(*it);
720 }
721 }
722
723 return url;
724}
725
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800726inline std::string setProtocolDefaults(const boost::urls::url_view& url)
727{
728 if (url.scheme() == "https")
729 {
730 return "https";
731 }
732 if (url.scheme() == "http")
733 {
734 if (bmcwebInsecureEnableHttpPushStyleEventing)
735 {
736 return "http";
737 }
738 return "";
739 }
740 return "";
741}
742
743inline uint16_t setPortDefaults(const boost::urls::url_view& url)
744{
745 uint16_t port = url.port_number();
746 if (port != 0)
747 {
748 // user picked a port already.
749 return port;
750 }
751
752 // If the user hasn't explicitly stated a port, pick one explicitly for them
753 // based on the protocol defaults
754 if (url.scheme() == "http")
755 {
756 return 80;
757 }
758 if (url.scheme() == "https")
759 {
760 return 443;
761 }
762 return 0;
763}
764
Ed Tanous11baefe2022-02-09 12:14:12 -0800765inline bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto,
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800766 std::string& host, uint16_t& port,
Ed Tanous11baefe2022-02-09 12:14:12 -0800767 std::string& path)
768{
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800769 boost::urls::result<boost::urls::url_view> url =
Ed Tanous079360a2022-06-29 10:05:19 -0700770 boost::urls::parse_uri(destUrl);
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800771 if (!url)
772 {
773 return false;
774 }
775 urlProto = setProtocolDefaults(url.value());
776 if (urlProto.empty())
Ed Tanous11baefe2022-02-09 12:14:12 -0800777 {
778 return false;
779 }
780
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800781 port = setPortDefaults(url.value());
Ed Tanous11baefe2022-02-09 12:14:12 -0800782
Ed Tanous079360a2022-06-29 10:05:19 -0700783 host = url->encoded_host();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800784
Ed Tanous079360a2022-06-29 10:05:19 -0700785 path = url->encoded_path();
Ed Tanous11baefe2022-02-09 12:14:12 -0800786 if (path.empty())
787 {
788 path = "/";
789 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800790 if (url->has_fragment())
791 {
792 path += '#';
Ed Tanous079360a2022-06-29 10:05:19 -0700793 path += url->encoded_fragment();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800794 }
795
796 if (url->has_query())
797 {
798 path += '?';
Ed Tanous079360a2022-06-29 10:05:19 -0700799 path += url->encoded_query();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800800 }
801
Ed Tanous11baefe2022-02-09 12:14:12 -0800802 return true;
803}
804
Ed Tanous1abe55e2018-09-05 08:30:59 -0700805} // namespace utility
806} // namespace crow
Ed Tanous71f2db72022-05-25 12:28:09 -0700807
808namespace nlohmann
809{
810template <>
811struct adl_serializer<boost::urls::url>
812{
813 // nlohmann requires a specific casing to look these up in adl
814 // NOLINTNEXTLINE(readability-identifier-naming)
815 static void to_json(json& j, const boost::urls::url& url)
816 {
Ed Tanous079360a2022-06-29 10:05:19 -0700817 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700818 }
819};
820
821template <>
822struct adl_serializer<boost::urls::url_view>
823{
824 // NOLINTNEXTLINE(readability-identifier-naming)
825 static void to_json(json& j, const boost::urls::url_view& url)
826 {
Ed Tanous079360a2022-06-29 10:05:19 -0700827 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700828 }
829};
830} // namespace nlohmann