1 #pragma once 2 3 #include "nlohmann/json.hpp" 4 5 #include <openssl/crypto.h> 6 7 #include <chrono> 8 #include <cstdint> 9 #include <cstring> 10 #include <functional> 11 #include <regex> 12 #include <stdexcept> 13 #include <string> 14 #include <tuple> 15 16 namespace crow 17 { 18 namespace black_magic 19 { 20 21 constexpr unsigned findClosingTag(std::string_view s, unsigned p) 22 { 23 return s[p] == '>' ? p : findClosingTag(s, p + 1); 24 } 25 26 constexpr bool isInt(std::string_view s, unsigned i) 27 { 28 return s.substr(i, 5) == "<int>"; 29 } 30 31 constexpr bool isUint(std::string_view s, unsigned i) 32 { 33 return s.substr(i, 6) == "<uint>"; 34 } 35 36 constexpr bool isFloat(std::string_view s, unsigned i) 37 { 38 return s.substr(i, 7) == "<float>" || s.substr(i, 8) == "<double>"; 39 } 40 41 constexpr bool isStr(std::string_view s, unsigned i) 42 { 43 return s.substr(i, 5) == "<str>" || s.substr(i, 8) == "<string>"; 44 } 45 46 constexpr bool isPath(std::string_view s, unsigned i) 47 { 48 return s.substr(i, 6) == "<path>"; 49 } 50 51 template <typename T> 52 constexpr int getParameterTag() 53 { 54 if constexpr (std::is_same_v<int, T>) 55 { 56 return 1; 57 } 58 if constexpr (std::is_same_v<char, T>) 59 { 60 return 1; 61 } 62 if constexpr (std::is_same_v<short, T>) 63 { 64 return 1; 65 } 66 if constexpr (std::is_same_v<long, T>) 67 { 68 return 1; 69 } 70 if constexpr (std::is_same_v<long long, T>) 71 { 72 return 1; 73 } 74 if constexpr (std::is_same_v<unsigned int, T>) 75 { 76 return 2; 77 } 78 if constexpr (std::is_same_v<unsigned char, T>) 79 { 80 return 2; 81 } 82 if constexpr (std::is_same_v<unsigned short, T>) 83 { 84 return 2; 85 } 86 if constexpr (std::is_same_v<unsigned long, T>) 87 { 88 return 2; 89 } 90 if constexpr (std::is_same_v<unsigned long long, T>) 91 { 92 return 2; 93 } 94 if constexpr (std::is_same_v<double, T>) 95 { 96 return 3; 97 } 98 if constexpr (std::is_same_v<std::string, T>) 99 { 100 return 4; 101 } 102 return 0; 103 } 104 105 template <typename... Args> 106 struct computeParameterTagFromArgsList; 107 108 template <> 109 struct computeParameterTagFromArgsList<> 110 { 111 static constexpr int value = 0; 112 }; 113 114 template <typename Arg, typename... Args> 115 struct computeParameterTagFromArgsList<Arg, Args...> 116 { 117 static constexpr int subValue = 118 computeParameterTagFromArgsList<Args...>::value; 119 static constexpr int value = 120 getParameterTag<typename std::decay<Arg>::type>() 121 ? subValue * 6 + getParameterTag<typename std::decay<Arg>::type>() 122 : subValue; 123 }; 124 125 inline bool isParameterTagCompatible(uint64_t a, uint64_t b) 126 { 127 if (a == 0) 128 { 129 return b == 0; 130 } 131 if (b == 0) 132 { 133 return a == 0; 134 } 135 uint64_t sa = a % 6; 136 uint64_t sb = a % 6; 137 if (sa == 5) 138 { 139 sa = 4; 140 } 141 if (sb == 5) 142 { 143 sb = 4; 144 } 145 if (sa != sb) 146 { 147 return false; 148 } 149 return isParameterTagCompatible(a / 6, b / 6); 150 } 151 152 constexpr uint64_t getParameterTag(std::string_view s, unsigned p = 0) 153 { 154 if (p == s.size()) 155 { 156 return 0; 157 } 158 159 if (s[p] != '<') 160 { 161 return getParameterTag(s, p + 1); 162 } 163 164 if (isInt(s, p)) 165 { 166 return getParameterTag(s, findClosingTag(s, p)) * 6 + 1; 167 } 168 169 if (isUint(s, p)) 170 { 171 return getParameterTag(s, findClosingTag(s, p)) * 6 + 2; 172 } 173 174 if (isFloat(s, p)) 175 { 176 return getParameterTag(s, findClosingTag(s, p)) * 6 + 3; 177 } 178 179 if (isStr(s, p)) 180 { 181 return getParameterTag(s, findClosingTag(s, p)) * 6 + 4; 182 } 183 184 if (isPath(s, p)) 185 { 186 return getParameterTag(s, findClosingTag(s, p)) * 6 + 5; 187 } 188 189 throw std::runtime_error("invalid parameter type"); 190 } 191 192 template <typename... T> 193 struct S 194 { 195 template <typename U> 196 using push = S<U, T...>; 197 template <typename U> 198 using push_back = S<T..., U>; 199 template <template <typename... Args> class U> 200 using rebind = U<T...>; 201 }; 202 203 template <typename F, typename Set> 204 struct CallHelper; 205 206 template <typename F, typename... Args> 207 struct CallHelper<F, S<Args...>> 208 { 209 template <typename F1, typename... Args1, 210 typename = decltype(std::declval<F1>()(std::declval<Args1>()...))> 211 static char test(int); 212 213 template <typename...> 214 static int test(...); 215 216 static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char); 217 }; 218 219 template <uint64_t N> 220 struct SingleTagToType 221 {}; 222 223 template <> 224 struct SingleTagToType<1> 225 { 226 using type = int64_t; 227 }; 228 229 template <> 230 struct SingleTagToType<2> 231 { 232 using type = uint64_t; 233 }; 234 235 template <> 236 struct SingleTagToType<3> 237 { 238 using type = double; 239 }; 240 241 template <> 242 struct SingleTagToType<4> 243 { 244 using type = std::string; 245 }; 246 247 template <> 248 struct SingleTagToType<5> 249 { 250 using type = std::string; 251 }; 252 253 template <uint64_t Tag> 254 struct Arguments 255 { 256 using subarguments = typename Arguments<Tag / 6>::type; 257 using type = typename subarguments::template push< 258 typename SingleTagToType<Tag % 6>::type>; 259 }; 260 261 template <> 262 struct Arguments<0> 263 { 264 using type = S<>; 265 }; 266 267 template <typename T> 268 struct Promote 269 { 270 using type = T; 271 }; 272 273 template <typename T> 274 using PromoteT = typename Promote<T>::type; 275 276 template <> 277 struct Promote<char> 278 { 279 using type = int64_t; 280 }; 281 template <> 282 struct Promote<short> 283 { 284 using type = int64_t; 285 }; 286 template <> 287 struct Promote<int> 288 { 289 using type = int64_t; 290 }; 291 template <> 292 struct Promote<long> 293 { 294 using type = int64_t; 295 }; 296 template <> 297 struct Promote<long long> 298 { 299 using type = int64_t; 300 }; 301 template <> 302 struct Promote<unsigned char> 303 { 304 using type = uint64_t; 305 }; 306 template <> 307 struct Promote<unsigned short> 308 { 309 using type = uint64_t; 310 }; 311 template <> 312 struct Promote<unsigned int> 313 { 314 using type = uint64_t; 315 }; 316 template <> 317 struct Promote<unsigned long> 318 { 319 using type = uint64_t; 320 }; 321 template <> 322 struct Promote<unsigned long long> 323 { 324 using type = uint64_t; 325 }; 326 327 } // namespace black_magic 328 329 namespace detail 330 { 331 332 template <class T, std::size_t N, class... Args> 333 struct GetIndexOfElementFromTupleByTypeImpl 334 { 335 static constexpr std::size_t value = N; 336 }; 337 338 template <class T, std::size_t N, class... Args> 339 struct GetIndexOfElementFromTupleByTypeImpl<T, N, T, Args...> 340 { 341 static constexpr std::size_t value = N; 342 }; 343 344 template <class T, std::size_t N, class U, class... Args> 345 struct GetIndexOfElementFromTupleByTypeImpl<T, N, U, Args...> 346 { 347 static constexpr std::size_t value = 348 GetIndexOfElementFromTupleByTypeImpl<T, N + 1, Args...>::value; 349 }; 350 351 } // namespace detail 352 353 namespace utility 354 { 355 template <class T, class... Args> 356 T& getElementByType(std::tuple<Args...>& t) 357 { 358 return std::get< 359 detail::GetIndexOfElementFromTupleByTypeImpl<T, 0, Args...>::value>(t); 360 } 361 362 template <typename T> 363 struct function_traits; 364 365 template <typename T> 366 struct function_traits : public function_traits<decltype(&T::operator())> 367 { 368 using parent_t = function_traits<decltype(&T::operator())>; 369 static const size_t arity = parent_t::arity; 370 using result_type = typename parent_t::result_type; 371 template <size_t i> 372 using arg = typename parent_t::template arg<i>; 373 }; 374 375 template <typename ClassType, typename r, typename... Args> 376 struct function_traits<r (ClassType::*)(Args...) const> 377 { 378 static const size_t arity = sizeof...(Args); 379 380 using result_type = r; 381 382 template <size_t i> 383 using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; 384 }; 385 386 template <typename ClassType, typename r, typename... Args> 387 struct function_traits<r (ClassType::*)(Args...)> 388 { 389 static const size_t arity = sizeof...(Args); 390 391 using result_type = r; 392 393 template <size_t i> 394 using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; 395 }; 396 397 template <typename r, typename... Args> 398 struct function_traits<std::function<r(Args...)>> 399 { 400 static const size_t arity = sizeof...(Args); 401 402 using result_type = r; 403 404 template <size_t i> 405 using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; 406 }; 407 408 inline std::string base64encode(const std::string_view data) 409 { 410 const std::array<char, 64> key = { 411 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 412 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 413 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 414 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 415 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; 416 417 size_t size = data.size(); 418 std::string ret; 419 ret.resize((size + 2) / 3 * 4); 420 auto it = ret.begin(); 421 422 size_t i = 0; 423 while (i < size) 424 { 425 size_t keyIndex; 426 427 keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2; 428 *it++ = key[keyIndex]; 429 430 if (i + 1 < size) 431 { 432 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4; 433 keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4; 434 *it++ = key[keyIndex]; 435 436 if (i + 2 < size) 437 { 438 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2; 439 keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6; 440 *it++ = key[keyIndex]; 441 442 keyIndex = static_cast<size_t>(data[i + 2] & 0x3F); 443 *it++ = key[keyIndex]; 444 } 445 else 446 { 447 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2; 448 *it++ = key[keyIndex]; 449 *it++ = '='; 450 } 451 } 452 else 453 { 454 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4; 455 *it++ = key[keyIndex]; 456 *it++ = '='; 457 *it++ = '='; 458 } 459 460 i += 3; 461 } 462 463 return ret; 464 } 465 466 // TODO this is temporary and should be deleted once base64 is refactored out of 467 // crow 468 inline bool base64Decode(const std::string_view input, std::string& output) 469 { 470 static const char nop = static_cast<char>(-1); 471 // See note on encoding_data[] in above function 472 static const std::array<char, 256> decodingData = { 473 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 474 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 475 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 476 nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59, 477 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4, 478 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 479 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26, 480 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 481 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop, 482 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 483 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 484 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 485 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 486 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 487 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 488 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 489 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 490 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, 491 nop, nop, nop, nop}; 492 493 size_t inputLength = input.size(); 494 495 // allocate space for output string 496 output.clear(); 497 output.reserve(((inputLength + 2) / 3) * 4); 498 499 auto getCodeValue = [](char c) { 500 auto code = static_cast<unsigned char>(c); 501 // Ensure we cannot index outside the bounds of the decoding array 502 static_assert(std::numeric_limits<decltype(code)>::max() < 503 decodingData.size()); 504 return decodingData[code]; 505 }; 506 507 // for each 4-bytes sequence from the input, extract 4 6-bits sequences by 508 // dropping first two bits 509 // and regenerate into 3 8-bits sequences 510 511 for (size_t i = 0; i < inputLength; i++) 512 { 513 char base64code0; 514 char base64code1; 515 char base64code2 = 0; // initialized to 0 to suppress warnings 516 char base64code3; 517 518 base64code0 = getCodeValue(input[i]); 519 if (base64code0 == nop) 520 { // non base64 character 521 return false; 522 } 523 if (!(++i < inputLength)) 524 { // we need at least two input bytes for first 525 // byte output 526 return false; 527 } 528 base64code1 = getCodeValue(input[i]); 529 if (base64code1 == nop) 530 { // non base64 character 531 return false; 532 } 533 output += 534 static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3)); 535 536 if (++i < inputLength) 537 { 538 char c = input[i]; 539 if (c == '=') 540 { // padding , end of input 541 return (base64code1 & 0x0f) == 0; 542 } 543 base64code2 = getCodeValue(input[i]); 544 if (base64code2 == nop) 545 { // non base64 character 546 return false; 547 } 548 output += static_cast<char>(((base64code1 << 4) & 0xf0) | 549 ((base64code2 >> 2) & 0x0f)); 550 } 551 552 if (++i < inputLength) 553 { 554 char c = input[i]; 555 if (c == '=') 556 { // padding , end of input 557 return (base64code2 & 0x03) == 0; 558 } 559 base64code3 = getCodeValue(input[i]); 560 if (base64code3 == nop) 561 { // non base64 character 562 return false; 563 } 564 output += 565 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3)); 566 } 567 } 568 569 return true; 570 } 571 572 /** 573 * Method returns Date Time information according to requested format 574 * 575 * @param[in] time time in second since the Epoch 576 * 577 * @return Date Time according to requested format 578 */ 579 inline std::string getDateTime(const std::time_t& time) 580 { 581 std::array<char, 128> dateTime; 582 std::string redfishDateTime("0000-00-00T00:00:00Z00:00"); 583 584 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z", 585 std::localtime(&time))) 586 { 587 // insert the colon required by the ISO 8601 standard 588 redfishDateTime = std::string(dateTime.data()); 589 redfishDateTime.insert(redfishDateTime.end() - 2, ':'); 590 } 591 592 return redfishDateTime; 593 } 594 595 inline std::string dateTimeNow() 596 { 597 std::time_t time = std::time(nullptr); 598 return getDateTime(time); 599 } 600 601 inline bool constantTimeStringCompare(const std::string_view a, 602 const std::string_view b) 603 { 604 // Important note, this function is ONLY constant time if the two input 605 // sizes are the same 606 if (a.size() != b.size()) 607 { 608 return false; 609 } 610 return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0; 611 } 612 613 struct ConstantTimeCompare 614 { 615 bool operator()(const std::string_view a, const std::string_view b) const 616 { 617 return constantTimeStringCompare(a, b); 618 } 619 }; 620 621 inline std::time_t getTimestamp(uint64_t millisTimeStamp) 622 { 623 // Retrieve Created property with format: 624 // yyyy-mm-ddThh:mm:ss 625 std::chrono::milliseconds chronoTimeStamp(millisTimeStamp); 626 return std::chrono::duration_cast<std::chrono::duration<int>>( 627 chronoTimeStamp) 628 .count(); 629 } 630 631 } // namespace utility 632 } // namespace crow 633