blob: 41182e4eb494a9ad2f916f6794862f439abdcbc1 [file] [log] [blame]
#pragma once
#include <openssl/crypto.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <array>
#include <chrono>
#include <cstdint>
#include <ctime>
#include <functional>
#include <limits>
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
namespace crow
{
namespace black_magic
{
constexpr unsigned findClosingTag(std::string_view s, unsigned p)
{
return s[p] == '>' ? p : findClosingTag(s, p + 1);
}
constexpr bool isInt(std::string_view s, unsigned i)
{
return s.substr(i, 5) == "<int>";
}
constexpr bool isUint(std::string_view s, unsigned i)
{
return s.substr(i, 6) == "<uint>";
}
constexpr bool isFloat(std::string_view s, unsigned i)
{
return s.substr(i, 7) == "<float>" || s.substr(i, 8) == "<double>";
}
constexpr bool isStr(std::string_view s, unsigned i)
{
return s.substr(i, 5) == "<str>" || s.substr(i, 8) == "<string>";
}
constexpr bool isPath(std::string_view s, unsigned i)
{
return s.substr(i, 6) == "<path>";
}
template <typename T>
constexpr int getParameterTag()
{
if constexpr (std::is_same_v<int, T>)
{
return 1;
}
if constexpr (std::is_same_v<char, T>)
{
return 1;
}
if constexpr (std::is_same_v<short, T>)
{
return 1;
}
if constexpr (std::is_same_v<long, T>)
{
return 1;
}
if constexpr (std::is_same_v<long long, T>)
{
return 1;
}
if constexpr (std::is_same_v<unsigned int, T>)
{
return 2;
}
if constexpr (std::is_same_v<unsigned char, T>)
{
return 2;
}
if constexpr (std::is_same_v<unsigned short, T>)
{
return 2;
}
if constexpr (std::is_same_v<unsigned long, T>)
{
return 2;
}
if constexpr (std::is_same_v<unsigned long long, T>)
{
return 2;
}
if constexpr (std::is_same_v<double, T>)
{
return 3;
}
if constexpr (std::is_same_v<std::string, T>)
{
return 4;
}
return 0;
}
template <typename... Args>
struct computeParameterTagFromArgsList;
template <>
struct computeParameterTagFromArgsList<>
{
static constexpr int value = 0;
};
template <typename Arg, typename... Args>
struct computeParameterTagFromArgsList<Arg, Args...>
{
static constexpr int subValue =
computeParameterTagFromArgsList<Args...>::value;
static constexpr int value =
getParameterTag<typename std::decay<Arg>::type>()
? subValue * 6 + getParameterTag<typename std::decay<Arg>::type>()
: subValue;
};
inline bool isParameterTagCompatible(uint64_t a, uint64_t b)
{
if (a == 0)
{
return b == 0;
}
if (b == 0)
{
return a == 0;
}
uint64_t sa = a % 6;
uint64_t sb = a % 6;
if (sa == 5)
{
sa = 4;
}
if (sb == 5)
{
sb = 4;
}
if (sa != sb)
{
return false;
}
return isParameterTagCompatible(a / 6, b / 6);
}
constexpr uint64_t getParameterTag(std::string_view s, unsigned p = 0)
{
if (p == s.size())
{
return 0;
}
if (s[p] != '<')
{
return getParameterTag(s, p + 1);
}
if (isInt(s, p))
{
return getParameterTag(s, findClosingTag(s, p)) * 6 + 1;
}
if (isUint(s, p))
{
return getParameterTag(s, findClosingTag(s, p)) * 6 + 2;
}
if (isFloat(s, p))
{
return getParameterTag(s, findClosingTag(s, p)) * 6 + 3;
}
if (isStr(s, p))
{
return getParameterTag(s, findClosingTag(s, p)) * 6 + 4;
}
if (isPath(s, p))
{
return getParameterTag(s, findClosingTag(s, p)) * 6 + 5;
}
throw std::runtime_error("invalid parameter type");
}
template <typename... T>
struct S
{
template <typename U>
using push = S<U, T...>;
template <typename U>
using push_back = S<T..., U>;
template <template <typename... Args> class U>
using rebind = U<T...>;
};
template <typename F, typename Set>
struct CallHelper;
template <typename F, typename... Args>
struct CallHelper<F, S<Args...>>
{
template <typename F1, typename... Args1,
typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
static char test(int);
template <typename...>
static int test(...);
static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char);
};
template <uint64_t N>
struct SingleTagToType
{};
template <>
struct SingleTagToType<1>
{
using type = int64_t;
};
template <>
struct SingleTagToType<2>
{
using type = uint64_t;
};
template <>
struct SingleTagToType<3>
{
using type = double;
};
template <>
struct SingleTagToType<4>
{
using type = std::string;
};
template <>
struct SingleTagToType<5>
{
using type = std::string;
};
template <uint64_t Tag>
struct Arguments
{
using subarguments = typename Arguments<Tag / 6>::type;
using type = typename subarguments::template push<
typename SingleTagToType<Tag % 6>::type>;
};
template <>
struct Arguments<0>
{
using type = S<>;
};
template <typename T>
struct Promote
{
using type = T;
};
template <typename T>
using PromoteT = typename Promote<T>::type;
template <>
struct Promote<char>
{
using type = int64_t;
};
template <>
struct Promote<short>
{
using type = int64_t;
};
template <>
struct Promote<int>
{
using type = int64_t;
};
template <>
struct Promote<long>
{
using type = int64_t;
};
template <>
struct Promote<long long>
{
using type = int64_t;
};
template <>
struct Promote<unsigned char>
{
using type = uint64_t;
};
template <>
struct Promote<unsigned short>
{
using type = uint64_t;
};
template <>
struct Promote<unsigned int>
{
using type = uint64_t;
};
template <>
struct Promote<unsigned long>
{
using type = uint64_t;
};
template <>
struct Promote<unsigned long long>
{
using type = uint64_t;
};
} // namespace black_magic
namespace detail
{
template <class T, std::size_t N, class... Args>
struct GetIndexOfElementFromTupleByTypeImpl
{
static constexpr std::size_t value = N;
};
template <class T, std::size_t N, class... Args>
struct GetIndexOfElementFromTupleByTypeImpl<T, N, T, Args...>
{
static constexpr std::size_t value = N;
};
template <class T, std::size_t N, class U, class... Args>
struct GetIndexOfElementFromTupleByTypeImpl<T, N, U, Args...>
{
static constexpr std::size_t value =
GetIndexOfElementFromTupleByTypeImpl<T, N + 1, Args...>::value;
};
} // namespace detail
namespace utility
{
template <class T, class... Args>
T& getElementByType(std::tuple<Args...>& t)
{
return std::get<
detail::GetIndexOfElementFromTupleByTypeImpl<T, 0, Args...>::value>(t);
}
template <typename T>
struct function_traits;
template <typename T>
struct function_traits : public function_traits<decltype(&T::operator())>
{
using parent_t = function_traits<decltype(&T::operator())>;
static const size_t arity = parent_t::arity;
using result_type = typename parent_t::result_type;
template <size_t i>
using arg = typename parent_t::template arg<i>;
};
template <typename ClassType, typename r, typename... Args>
struct function_traits<r (ClassType::*)(Args...) const>
{
static const size_t arity = sizeof...(Args);
using result_type = r;
template <size_t i>
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
template <typename ClassType, typename r, typename... Args>
struct function_traits<r (ClassType::*)(Args...)>
{
static const size_t arity = sizeof...(Args);
using result_type = r;
template <size_t i>
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
template <typename r, typename... Args>
struct function_traits<std::function<r(Args...)>>
{
static const size_t arity = sizeof...(Args);
using result_type = r;
template <size_t i>
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type;
};
inline std::string base64encode(const std::string_view data)
{
const std::array<char, 64> key = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
size_t size = data.size();
std::string ret;
ret.resize((size + 2) / 3 * 4);
auto it = ret.begin();
size_t i = 0;
while (i < size)
{
size_t keyIndex = 0;
keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
*it++ = key[keyIndex];
if (i + 1 < size)
{
keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
*it++ = key[keyIndex];
if (i + 2 < size)
{
keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
*it++ = key[keyIndex];
keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
*it++ = key[keyIndex];
}
else
{
keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
*it++ = key[keyIndex];
*it++ = '=';
}
}
else
{
keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
*it++ = key[keyIndex];
*it++ = '=';
*it++ = '=';
}
i += 3;
}
return ret;
}
// TODO this is temporary and should be deleted once base64 is refactored out of
// crow
inline bool base64Decode(const std::string_view input, std::string& output)
{
static const char nop = static_cast<char>(-1);
// See note on encoding_data[] in above function
static const std::array<char, 256> decodingData = {
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59,
60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26,
27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
nop, nop, nop, nop};
size_t inputLength = input.size();
// allocate space for output string
output.clear();
output.reserve(((inputLength + 2) / 3) * 4);
auto getCodeValue = [](char c) {
auto code = static_cast<unsigned char>(c);
// Ensure we cannot index outside the bounds of the decoding array
static_assert(std::numeric_limits<decltype(code)>::max() <
decodingData.size());
return decodingData[code];
};
// for each 4-bytes sequence from the input, extract 4 6-bits sequences by
// dropping first two bits
// and regenerate into 3 8-bits sequences
for (size_t i = 0; i < inputLength; i++)
{
char base64code0 = 0;
char base64code1 = 0;
char base64code2 = 0; // initialized to 0 to suppress warnings
char base64code3 = 0;
base64code0 = getCodeValue(input[i]);
if (base64code0 == nop)
{ // non base64 character
return false;
}
if (!(++i < inputLength))
{ // we need at least two input bytes for first
// byte output
return false;
}
base64code1 = getCodeValue(input[i]);
if (base64code1 == nop)
{ // non base64 character
return false;
}
output +=
static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
if (++i < inputLength)
{
char c = input[i];
if (c == '=')
{ // padding , end of input
return (base64code1 & 0x0f) == 0;
}
base64code2 = getCodeValue(input[i]);
if (base64code2 == nop)
{ // non base64 character
return false;
}
output += static_cast<char>(((base64code1 << 4) & 0xf0) |
((base64code2 >> 2) & 0x0f));
}
if (++i < inputLength)
{
char c = input[i];
if (c == '=')
{ // padding , end of input
return (base64code2 & 0x03) == 0;
}
base64code3 = getCodeValue(input[i]);
if (base64code3 == nop)
{ // non base64 character
return false;
}
output +=
static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
}
}
return true;
}
namespace details
{
inline std::string getDateTime(boost::posix_time::milliseconds timeSinceEpoch)
{
boost::posix_time::ptime epoch(boost::gregorian::date(1970, 1, 1));
boost::posix_time::ptime time = epoch + timeSinceEpoch;
// append zero offset to the end according to the Redfish spec for Date-Time
return boost::posix_time::to_iso_extended_string(time) + "+00:00";
}
} // namespace details
inline std::string getDateTimeUint(uint64_t secondsSinceEpoch)
{
boost::posix_time::seconds boostSeconds(secondsSinceEpoch);
return details::getDateTime(
boost::posix_time::milliseconds(boostSeconds.total_milliseconds()));
}
inline std::string getDateTimeUintMs(uint64_t millisSecondsSinceEpoch)
{
return details::getDateTime(
boost::posix_time::milliseconds(millisSecondsSinceEpoch));
}
inline std::string getDateTimeStdtime(std::time_t secondsSinceEpoch)
{
boost::posix_time::ptime time =
boost::posix_time::from_time_t(secondsSinceEpoch);
return boost::posix_time::to_iso_extended_string(time) + "+00:00";
}
/**
* Returns the current Date, Time & the local Time Offset
* infromation in a pair
*
* @param[in] None
*
* @return std::pair<std::string, std::string>, which consist
* of current DateTime & the TimeOffset strings respectively.
*/
inline std::pair<std::string, std::string> getDateTimeOffsetNow()
{
std::time_t time = std::time(nullptr);
std::string dateTime = getDateTimeStdtime(time);
/* extract the local Time Offset value from the
* recevied dateTime string.
*/
std::string timeOffset("Z00:00");
std::size_t lastPos = dateTime.size();
std::size_t len = timeOffset.size();
if (lastPos > len)
{
timeOffset = dateTime.substr(lastPos - len);
}
return std::make_pair(dateTime, timeOffset);
}
inline bool constantTimeStringCompare(const std::string_view a,
const std::string_view b)
{
// Important note, this function is ONLY constant time if the two input
// sizes are the same
if (a.size() != b.size())
{
return false;
}
return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
}
struct ConstantTimeCompare
{
bool operator()(const std::string_view a, const std::string_view b) const
{
return constantTimeStringCompare(a, b);
}
};
inline std::time_t getTimestamp(uint64_t millisTimeStamp)
{
// Retrieve Created property with format:
// yyyy-mm-ddThh:mm:ss
std::chrono::milliseconds chronoTimeStamp(millisTimeStamp);
return std::chrono::duration_cast<std::chrono::duration<int>>(
chronoTimeStamp)
.count();
}
} // namespace utility
} // namespace crow