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