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