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