blob: 4ea44d58632a61d45b592eb354ea3d3276700b62 [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
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600381inline std::string base64encode(const std::string_view data)
382{
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 Tanous39e77502019-03-04 17:35:53 -0800441inline bool base64Decode(const 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 Tanous51dae672018-09-05 16:07:32 -0700544inline bool constantTimeStringCompare(const std::string_view a,
545 const std::string_view b)
546{
547 // Important note, this function is ONLY constant time if the two input
548 // sizes are the same
549 if (a.size() != b.size())
550 {
551 return false;
552 }
553 return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
554}
555
556struct ConstantTimeCompare
557{
558 bool operator()(const std::string_view a, const std::string_view b) const
559 {
560 return constantTimeStringCompare(a, b);
561 }
562};
563
Ed Tanouseae855c2021-10-26 11:26:02 -0700564namespace details
565{
566inline boost::urls::url
Willy Tuc6bcedc2022-09-27 05:36:59 +0000567 appendUrlPieces(boost::urls::url& url,
568 const std::initializer_list<std::string_view> args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700569{
Ed Tanouseae855c2021-10-26 11:26:02 -0700570 for (const std::string_view& arg : args)
571 {
572 url.segments().push_back(arg);
573 }
574 return url;
575}
Willy Tuc6bcedc2022-09-27 05:36:59 +0000576
577inline boost::urls::url
578 urlFromPiecesDetail(const std::initializer_list<std::string_view> args)
579{
580 boost::urls::url url("/");
581 appendUrlPieces(url, args);
582 return url;
583}
Ed Tanouseae855c2021-10-26 11:26:02 -0700584} // namespace details
585
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700586class OrMorePaths
587{};
588
Ed Tanouseae855c2021-10-26 11:26:02 -0700589template <typename... AV>
590inline boost::urls::url urlFromPieces(const AV... args)
591{
592 return details::urlFromPiecesDetail({args...});
593}
594
Willy Tuc6bcedc2022-09-27 05:36:59 +0000595template <typename... AV>
596inline void appendUrlPieces(boost::urls::url& url, const AV... args)
597{
598 details::appendUrlPieces(url, {args...});
599}
600
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100601namespace details
602{
603
604// std::reference_wrapper<std::string> - extracts segment to variable
605// std::string_view - checks if segment is equal to variable
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700606using UrlSegment = std::variant<std::reference_wrapper<std::string>,
607 std::string_view, OrMorePaths>;
608
609enum class UrlParseResult
610{
611 Continue,
612 Fail,
613 Done,
614};
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100615
616class UrlSegmentMatcherVisitor
617{
618 public:
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700619 UrlParseResult operator()(std::string& output)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100620 {
Ed Tanous079360a2022-06-29 10:05:19 -0700621 output = segment;
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700622 return UrlParseResult::Continue;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100623 }
624
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700625 UrlParseResult operator()(std::string_view expected)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100626 {
Ed Tanous079360a2022-06-29 10:05:19 -0700627 if (segment == expected)
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700628 {
629 return UrlParseResult::Continue;
630 }
631 return UrlParseResult::Fail;
632 }
633
634 UrlParseResult operator()(OrMorePaths /*unused*/)
635 {
636 return UrlParseResult::Done;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100637 }
638
Ed Tanous079360a2022-06-29 10:05:19 -0700639 explicit UrlSegmentMatcherVisitor(std::string_view segmentIn) :
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100640 segment(segmentIn)
641 {}
642
643 private:
Ed Tanous079360a2022-06-29 10:05:19 -0700644 std::string_view segment;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100645};
646
647inline bool readUrlSegments(const boost::urls::url_view& urlView,
648 std::initializer_list<UrlSegment>&& segments)
649{
650 const boost::urls::segments_view& urlSegments = urlView.segments();
651
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700652 if (!urlSegments.is_absolute())
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100653 {
654 return false;
655 }
656
657 boost::urls::segments_view::iterator it = urlSegments.begin();
658 boost::urls::segments_view::iterator end = urlSegments.end();
659
660 for (const auto& segment : segments)
661 {
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700662 if (it == end)
663 {
664 // If the request ends with an "any" path, this was successful
665 return std::holds_alternative<OrMorePaths>(segment);
666 }
667 UrlParseResult res = std::visit(UrlSegmentMatcherVisitor(*it), segment);
668 if (res == UrlParseResult::Done)
669 {
670 return true;
671 }
672 if (res == UrlParseResult::Fail)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100673 {
674 return false;
675 }
676 it++;
677 }
Carson Labrado4c30e222022-06-24 22:16:00 +0000678
679 // There will be an empty segment at the end if the URI ends with a "/"
680 // e.g. /redfish/v1/Chassis/
681 if ((it != end) && urlSegments.back().empty())
682 {
683 it++;
684 }
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700685 return it == end;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100686}
687
688} // namespace details
689
690template <typename... Args>
691inline bool readUrlSegments(const boost::urls::url_view& urlView,
692 Args&&... args)
693{
694 return details::readUrlSegments(urlView, {std::forward<Args>(args)...});
695}
696
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000697inline boost::urls::url replaceUrlSegment(const boost::urls::url_view& urlView,
698 const uint replaceLoc,
699 const std::string_view newSegment)
700{
701 const boost::urls::segments_view& urlSegments = urlView.segments();
702 boost::urls::url url("/");
703
704 if (!urlSegments.is_absolute())
705 {
706 return url;
707 }
708
709 boost::urls::segments_view::iterator it = urlSegments.begin();
710 boost::urls::segments_view::iterator end = urlSegments.end();
711
712 for (uint idx = 0; it != end; it++, idx++)
713 {
714 if (idx == replaceLoc)
715 {
716 url.segments().push_back(newSegment);
717 }
718 else
719 {
720 url.segments().push_back(*it);
721 }
722 }
723
724 return url;
725}
726
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800727inline std::string setProtocolDefaults(const boost::urls::url_view& url)
728{
729 if (url.scheme() == "https")
730 {
731 return "https";
732 }
733 if (url.scheme() == "http")
734 {
735 if (bmcwebInsecureEnableHttpPushStyleEventing)
736 {
737 return "http";
738 }
739 return "";
740 }
741 return "";
742}
743
744inline uint16_t setPortDefaults(const boost::urls::url_view& url)
745{
746 uint16_t port = url.port_number();
747 if (port != 0)
748 {
749 // user picked a port already.
750 return port;
751 }
752
753 // If the user hasn't explicitly stated a port, pick one explicitly for them
754 // based on the protocol defaults
755 if (url.scheme() == "http")
756 {
757 return 80;
758 }
759 if (url.scheme() == "https")
760 {
761 return 443;
762 }
763 return 0;
764}
765
Ed Tanous11baefe2022-02-09 12:14:12 -0800766inline bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto,
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800767 std::string& host, uint16_t& port,
Ed Tanous11baefe2022-02-09 12:14:12 -0800768 std::string& path)
769{
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800770 boost::urls::result<boost::urls::url_view> url =
Ed Tanous079360a2022-06-29 10:05:19 -0700771 boost::urls::parse_uri(destUrl);
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800772 if (!url)
773 {
774 return false;
775 }
776 urlProto = setProtocolDefaults(url.value());
777 if (urlProto.empty())
Ed Tanous11baefe2022-02-09 12:14:12 -0800778 {
779 return false;
780 }
781
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800782 port = setPortDefaults(url.value());
Ed Tanous11baefe2022-02-09 12:14:12 -0800783
Ed Tanous079360a2022-06-29 10:05:19 -0700784 host = url->encoded_host();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800785
Ed Tanous079360a2022-06-29 10:05:19 -0700786 path = url->encoded_path();
Ed Tanous11baefe2022-02-09 12:14:12 -0800787 if (path.empty())
788 {
789 path = "/";
790 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800791 if (url->has_fragment())
792 {
793 path += '#';
Ed Tanous079360a2022-06-29 10:05:19 -0700794 path += url->encoded_fragment();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800795 }
796
797 if (url->has_query())
798 {
799 path += '?';
Ed Tanous079360a2022-06-29 10:05:19 -0700800 path += url->encoded_query();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800801 }
802
Ed Tanous11baefe2022-02-09 12:14:12 -0800803 return true;
804}
805
Ed Tanous1abe55e2018-09-05 08:30:59 -0700806} // namespace utility
807} // namespace crow
Ed Tanous71f2db72022-05-25 12:28:09 -0700808
809namespace nlohmann
810{
811template <>
812struct adl_serializer<boost::urls::url>
813{
814 // nlohmann requires a specific casing to look these up in adl
815 // NOLINTNEXTLINE(readability-identifier-naming)
816 static void to_json(json& j, const boost::urls::url& url)
817 {
Ed Tanous079360a2022-06-29 10:05:19 -0700818 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700819 }
820};
821
822template <>
823struct adl_serializer<boost::urls::url_view>
824{
825 // NOLINTNEXTLINE(readability-identifier-naming)
826 static void to_json(json& j, const boost::urls::url_view& url)
827 {
Ed Tanous079360a2022-06-29 10:05:19 -0700828 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700829 }
830};
831} // namespace nlohmann