blob: 0476d73c74769abdd51b97828bab3e1530cbcbc9 [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
Ed Tanous1abe55e2018-09-05 08:30:59 -07002
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "bmcweb_config.h"
4
Ed Tanous51dae672018-09-05 16:07:32 -07005#include <openssl/crypto.h>
6
Ed Tanousc867a832022-03-10 14:17:00 -08007#include <boost/callable_traits.hpp>
Ed Tanous079360a2022-06-29 10:05:19 -07008#include <boost/url/parse.hpp>
Ed Tanouseae855c2021-10-26 11:26:02 -07009#include <boost/url/url.hpp>
Ed Tanous079360a2022-06-29 10:05:19 -070010#include <boost/url/url_view.hpp>
Ed Tanous71f2db72022-05-25 12:28:09 -070011#include <nlohmann/json.hpp>
Nan Zhou1d8782e2021-11-29 22:23:18 -080012
Ed Tanous9ea15c32022-01-04 14:18:22 -080013#include <array>
Ed Tanous74849be2021-02-05 09:47:47 -080014#include <chrono>
Ed Tanousc715ec22022-03-10 15:38:01 -080015#include <cstddef>
Ed Tanous7045c8d2017-04-03 10:04:37 -070016#include <cstdint>
Ed Tanous9ea15c32022-01-04 14:18:22 -080017#include <ctime>
Ed Tanous7045c8d2017-04-03 10:04:37 -070018#include <functional>
Ed Tanous9896eae2022-07-23 15:07:33 -070019#include <iomanip>
Ed Tanous9ea15c32022-01-04 14:18:22 -080020#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070021#include <stdexcept>
22#include <string>
Ed Tanous9ea15c32022-01-04 14:18:22 -080023#include <string_view>
Ed Tanous7045c8d2017-04-03 10:04:37 -070024#include <tuple>
Ed Tanous9ea15c32022-01-04 14:18:22 -080025#include <type_traits>
26#include <utility>
Szymon Dompkeca1600c2022-03-03 14:42:52 +010027#include <variant>
Ed Tanous7045c8d2017-04-03 10:04:37 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
Ed Tanous47488a92023-06-26 18:19:33 -070031namespace utility
Ed Tanous1abe55e2018-09-05 08:30:59 -070032{
Ed Tanous7045c8d2017-04-03 10:04:37 -070033
Ed Tanousc715ec22022-03-10 15:38:01 -080034enum class TypeCode : uint8_t
35{
36 Unspecified = 0,
Ed Tanous15a42df2023-02-09 18:08:23 -080037 String = 1,
38 Path = 2,
39 Max = 3,
Ed Tanousc715ec22022-03-10 15:38:01 -080040};
41
42// Remove when we have c++23
43template <typename E>
44constexpr typename std::underlying_type<E>::type toUnderlying(E e) noexcept
45{
46 return static_cast<typename std::underlying_type<E>::type>(e);
47}
48
Ed Tanous9de65b32024-03-27 13:34:40 -070049constexpr uint64_t getParameterTag(std::string_view url)
Ed Tanous1abe55e2018-09-05 08:30:59 -070050{
Ed Tanous1c30e502022-03-08 18:02:24 -080051 uint64_t tagValue = 0;
52 size_t urlSegmentIndex = std::string_view::npos;
Ed Tanousb00dcc22021-02-23 12:52:50 -080053
Ed Tanous1c30e502022-03-08 18:02:24 -080054 size_t paramIndex = 0;
55
56 for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
57 {
58 char character = url[urlIndex];
59 if (character == '<')
60 {
61 if (urlSegmentIndex != std::string_view::npos)
62 {
63 return 0;
64 }
65 urlSegmentIndex = urlIndex;
66 }
67 if (character == '>')
68 {
69 if (urlSegmentIndex == std::string_view::npos)
70 {
71 return 0;
72 }
Patrick Williams89492a12023-05-10 07:51:34 -050073 std::string_view tag = url.substr(urlSegmentIndex,
74 urlIndex + 1 - urlSegmentIndex);
Ed Tanous1c30e502022-03-08 18:02:24 -080075
76 // Note, this is a really lame way to do std::pow(6, paramIndex)
77 // std::pow doesn't work in constexpr in clang.
78 // Ideally in the future we'd move this to use a power of 2 packing
79 // (probably 8 instead of 6) so that these just become bit shifts
80 uint64_t insertIndex = 1;
81 for (size_t unused = 0; unused < paramIndex; unused++)
82 {
Ed Tanous15a42df2023-02-09 18:08:23 -080083 insertIndex *= 3;
Ed Tanous1c30e502022-03-08 18:02:24 -080084 }
85
Ed Tanous1c30e502022-03-08 18:02:24 -080086 if (tag == "<str>" || tag == "<string>")
87 {
Ed Tanousc715ec22022-03-10 15:38:01 -080088 tagValue += insertIndex * toUnderlying(TypeCode::String);
Ed Tanous1c30e502022-03-08 18:02:24 -080089 }
90 if (tag == "<path>")
91 {
Ed Tanousc715ec22022-03-10 15:38:01 -080092 tagValue += insertIndex * toUnderlying(TypeCode::Path);
Ed Tanous1c30e502022-03-08 18:02:24 -080093 }
94 paramIndex++;
95 urlSegmentIndex = std::string_view::npos;
96 }
97 }
98 if (urlSegmentIndex != std::string_view::npos)
Ed Tanous988403c2020-08-24 11:29:49 -070099 {
100 return 0;
101 }
Ed Tanous1c30e502022-03-08 18:02:24 -0800102 return tagValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700103}
Ed Tanous7045c8d2017-04-03 10:04:37 -0700104
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700105constexpr size_t numArgsFromTag(int tag)
106{
107 size_t ret = 0;
108 while (tag > 0)
109 {
110 // Move to the next tag by removing the bottom bits from the number
Ed Tanous47488a92023-06-26 18:19:33 -0700111 tag /= toUnderlying(TypeCode::Max);
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700112 ret++;
113 }
114 return ret;
115};
116
Ed Tanousee192c02023-12-13 10:49:58 -0800117class Base64Encoder
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600118{
Ed Tanousee192c02023-12-13 10:49:58 -0800119 char overflow1 = '\0';
120 char overflow2 = '\0';
121 uint8_t overflowCount = 0;
122
123 constexpr static std::array<char, 64> key = {
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600124 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
125 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
126 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
127 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
128 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
129
Ed Tanousee192c02023-12-13 10:49:58 -0800130 // Takes 3 ascii chars, and encodes them as 4 base64 chars
131 static void encodeTriple(char first, char second, char third,
132 std::string& output)
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600133 {
Ed Tanous543f4402022-01-06 13:12:53 -0800134 size_t keyIndex = 0;
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600135
Ed Tanousee192c02023-12-13 10:49:58 -0800136 keyIndex = static_cast<size_t>(first & 0xFC) >> 2;
137 output += key[keyIndex];
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600138
Ed Tanousee192c02023-12-13 10:49:58 -0800139 keyIndex = static_cast<size_t>(first & 0x03) << 4;
140 keyIndex += static_cast<size_t>(second & 0xF0) >> 4;
141 output += key[keyIndex];
142
143 keyIndex = static_cast<size_t>(second & 0x0F) << 2;
144 keyIndex += static_cast<size_t>(third & 0xC0) >> 6;
145 output += key[keyIndex];
146
147 keyIndex = static_cast<size_t>(third & 0x3F);
148 output += key[keyIndex];
149 }
150
151 public:
152 // Accepts a partial string to encode, and writes the encoded characters to
153 // the output stream. requires subsequently calling finalize to complete
154 // stream.
155 void encode(std::string_view data, std::string& output)
156 {
157 // Encode the last round of overflow chars first
158 if (overflowCount == 2)
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600159 {
Ed Tanousee192c02023-12-13 10:49:58 -0800160 if (!data.empty())
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600161 {
Ed Tanousee192c02023-12-13 10:49:58 -0800162 encodeTriple(overflow1, overflow2, data[0], output);
163 overflowCount = 0;
164 data.remove_prefix(1);
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600165 }
Ed Tanousee192c02023-12-13 10:49:58 -0800166 }
167 else if (overflowCount == 1)
168 {
169 if (data.size() >= 2)
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600170 {
Ed Tanousee192c02023-12-13 10:49:58 -0800171 encodeTriple(overflow1, data[0], data[1], output);
172 overflowCount = 0;
173 data.remove_prefix(2);
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600174 }
175 }
Ed Tanousee192c02023-12-13 10:49:58 -0800176
177 while (data.size() >= 3)
178 {
179 encodeTriple(data[0], data[1], data[2], output);
180 data.remove_prefix(3);
181 }
182
183 if (!data.empty() && overflowCount == 0)
184 {
185 overflow1 = data[0];
186 overflowCount++;
187 data.remove_prefix(1);
188 }
189
190 if (!data.empty() && overflowCount == 1)
191 {
192 overflow2 = data[0];
193 overflowCount++;
194 data.remove_prefix(1);
195 }
196 }
197
198 // Completes a base64 output, by writing any MOD(3) characters to the
199 // output, as well as any required trailing =
200 void finalize(std::string& output)
201 {
202 if (overflowCount == 0)
203 {
204 return;
205 }
206 size_t keyIndex = static_cast<size_t>(overflow1 & 0xFC) >> 2;
207 output += key[keyIndex];
208
209 keyIndex = static_cast<size_t>(overflow1 & 0x03) << 4;
210 if (overflowCount == 2)
211 {
212 keyIndex += static_cast<size_t>(overflow2 & 0xF0) >> 4;
213 output += key[keyIndex];
214 keyIndex = static_cast<size_t>(overflow2 & 0x0F) << 2;
215 output += key[keyIndex];
216 }
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600217 else
218 {
Ed Tanousee192c02023-12-13 10:49:58 -0800219 output += key[keyIndex];
220 output += '=';
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600221 }
Ed Tanousee192c02023-12-13 10:49:58 -0800222 output += '=';
223 overflowCount = 0;
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600224 }
225
Ed Tanousee192c02023-12-13 10:49:58 -0800226 // Returns the required output buffer in characters for an input of size
227 // inputSize
228 static size_t constexpr encodedSize(size_t inputSize)
229 {
230 // Base64 encodes 3 character blocks as 4 character blocks
231 // With a possibility of 2 trailing = characters
232 return (inputSize + 2) / 3 * 4;
233 }
234};
235
236inline std::string base64encode(std::string_view data)
237{
238 // Encodes a 3 character stream into a 4 character stream
239 std::string out;
240 Base64Encoder base64;
241 out.reserve(Base64Encoder::encodedSize(data.size()));
242 base64.encode(data, out);
243 base64.finalize(out);
244 return out;
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600245}
246
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100247// TODO this is temporary and should be deleted once base64 is refactored out of
248// crow
Ed Tanous26ccae32023-02-16 10:28:44 -0800249inline bool base64Decode(std::string_view input, std::string& output)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700250{
Ed Tanous271584a2019-07-09 16:24:22 -0700251 static const char nop = static_cast<char>(-1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700252 // See note on encoding_data[] in above function
Jonathan Doman5beaf842020-08-14 11:23:33 -0700253 static const std::array<char, 256> decodingData = {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700254 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
255 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
256 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
257 nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59,
258 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4,
259 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
260 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26,
261 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
262 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop,
263 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
264 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
265 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
266 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
267 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
268 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
269 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
270 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
271 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
272 nop, nop, nop, nop};
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100273
Ed Tanous1abe55e2018-09-05 08:30:59 -0700274 size_t inputLength = input.size();
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100275
Ed Tanous1abe55e2018-09-05 08:30:59 -0700276 // allocate space for output string
277 output.clear();
278 output.reserve(((inputLength + 2) / 3) * 4);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100279
Jonathan Doman5beaf842020-08-14 11:23:33 -0700280 auto getCodeValue = [](char c) {
281 auto code = static_cast<unsigned char>(c);
282 // Ensure we cannot index outside the bounds of the decoding array
283 static_assert(std::numeric_limits<decltype(code)>::max() <
284 decodingData.size());
285 return decodingData[code];
286 };
287
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500289 // dropping first two bits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700290 // and regenerate into 3 8-bits sequences
James Feist5a806642020-07-31 16:40:33 +0000291
Ed Tanous1abe55e2018-09-05 08:30:59 -0700292 for (size_t i = 0; i < inputLength; i++)
293 {
Ed Tanous543f4402022-01-06 13:12:53 -0800294 char base64code0 = 0;
295 char base64code1 = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700296 char base64code2 = 0; // initialized to 0 to suppress warnings
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100297
Jonathan Doman5beaf842020-08-14 11:23:33 -0700298 base64code0 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700299 if (base64code0 == nop)
300 { // non base64 character
301 return false;
302 }
303 if (!(++i < inputLength))
304 { // we need at least two input bytes for first
305 // byte output
306 return false;
307 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700308 base64code1 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700309 if (base64code1 == nop)
310 { // non base64 character
311 return false;
312 }
313 output +=
314 static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100315
Ed Tanous1abe55e2018-09-05 08:30:59 -0700316 if (++i < inputLength)
317 {
318 char c = input[i];
319 if (c == '=')
320 { // padding , end of input
321 return (base64code1 & 0x0f) == 0;
322 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700323 base64code2 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700324 if (base64code2 == nop)
325 { // non base64 character
326 return false;
327 }
328 output += static_cast<char>(((base64code1 << 4) & 0xf0) |
329 ((base64code2 >> 2) & 0x0f));
330 }
331
332 if (++i < inputLength)
333 {
334 char c = input[i];
335 if (c == '=')
336 { // padding , end of input
337 return (base64code2 & 0x03) == 0;
338 }
Ed Tanousf8fe53e2022-06-30 15:55:45 -0700339 char base64code3 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700340 if (base64code3 == nop)
341 { // non base64 character
342 return false;
343 }
344 output +=
345 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
346 }
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100347 }
348
Ed Tanous1abe55e2018-09-05 08:30:59 -0700349 return true;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100350}
351
Ed Tanous26ccae32023-02-16 10:28:44 -0800352inline bool constantTimeStringCompare(std::string_view a, std::string_view b)
Ed Tanous51dae672018-09-05 16:07:32 -0700353{
354 // Important note, this function is ONLY constant time if the two input
355 // sizes are the same
356 if (a.size() != b.size())
357 {
358 return false;
359 }
360 return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
361}
362
363struct ConstantTimeCompare
364{
Ed Tanous26ccae32023-02-16 10:28:44 -0800365 bool operator()(std::string_view a, std::string_view b) const
Ed Tanous51dae672018-09-05 16:07:32 -0700366 {
367 return constantTimeStringCompare(a, b);
368 }
369};
370
Ed Tanouseae855c2021-10-26 11:26:02 -0700371namespace details
372{
373inline boost::urls::url
Willy Tuc6bcedc2022-09-27 05:36:59 +0000374 appendUrlPieces(boost::urls::url& url,
375 const std::initializer_list<std::string_view> args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700376{
Ed Tanous26ccae32023-02-16 10:28:44 -0800377 for (std::string_view arg : args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700378 {
379 url.segments().push_back(arg);
380 }
381 return url;
382}
Willy Tuc6bcedc2022-09-27 05:36:59 +0000383
Ed Tanouseae855c2021-10-26 11:26:02 -0700384} // namespace details
385
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700386class OrMorePaths
387{};
388
Ed Tanouseae855c2021-10-26 11:26:02 -0700389template <typename... AV>
Willy Tuc6bcedc2022-09-27 05:36:59 +0000390inline void appendUrlPieces(boost::urls::url& url, const AV... args)
391{
392 details::appendUrlPieces(url, {args...});
393}
394
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100395namespace details
396{
397
398// std::reference_wrapper<std::string> - extracts segment to variable
399// std::string_view - checks if segment is equal to variable
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700400using UrlSegment = std::variant<std::reference_wrapper<std::string>,
401 std::string_view, OrMorePaths>;
402
403enum class UrlParseResult
404{
405 Continue,
406 Fail,
407 Done,
408};
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100409
410class UrlSegmentMatcherVisitor
411{
412 public:
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700413 UrlParseResult operator()(std::string& output)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100414 {
Ed Tanous079360a2022-06-29 10:05:19 -0700415 output = segment;
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700416 return UrlParseResult::Continue;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100417 }
418
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700419 UrlParseResult operator()(std::string_view expected)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100420 {
Ed Tanous079360a2022-06-29 10:05:19 -0700421 if (segment == expected)
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700422 {
423 return UrlParseResult::Continue;
424 }
425 return UrlParseResult::Fail;
426 }
427
428 UrlParseResult operator()(OrMorePaths /*unused*/)
429 {
430 return UrlParseResult::Done;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100431 }
432
Ed Tanous079360a2022-06-29 10:05:19 -0700433 explicit UrlSegmentMatcherVisitor(std::string_view segmentIn) :
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100434 segment(segmentIn)
435 {}
436
437 private:
Ed Tanous079360a2022-06-29 10:05:19 -0700438 std::string_view segment;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100439};
440
Ed Tanousd9f466b2023-03-06 15:04:25 -0800441inline bool readUrlSegments(boost::urls::url_view url,
Ed Tanous5be2b142024-03-27 15:27:04 -0700442 std::initializer_list<UrlSegment> segments)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100443{
Ed Tanousd9f466b2023-03-06 15:04:25 -0800444 boost::urls::segments_view urlSegments = url.segments();
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100445
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700446 if (!urlSegments.is_absolute())
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100447 {
448 return false;
449 }
450
451 boost::urls::segments_view::iterator it = urlSegments.begin();
452 boost::urls::segments_view::iterator end = urlSegments.end();
453
454 for (const auto& segment : segments)
455 {
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700456 if (it == end)
457 {
458 // If the request ends with an "any" path, this was successful
459 return std::holds_alternative<OrMorePaths>(segment);
460 }
461 UrlParseResult res = std::visit(UrlSegmentMatcherVisitor(*it), segment);
462 if (res == UrlParseResult::Done)
463 {
464 return true;
465 }
466 if (res == UrlParseResult::Fail)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100467 {
468 return false;
469 }
470 it++;
471 }
Carson Labrado4c30e222022-06-24 22:16:00 +0000472
473 // There will be an empty segment at the end if the URI ends with a "/"
474 // e.g. /redfish/v1/Chassis/
475 if ((it != end) && urlSegments.back().empty())
476 {
477 it++;
478 }
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700479 return it == end;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100480}
481
482} // namespace details
483
484template <typename... Args>
Ed Tanousd9f466b2023-03-06 15:04:25 -0800485inline bool readUrlSegments(boost::urls::url_view url, Args&&... args)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100486{
Ed Tanous39662a32023-02-06 15:09:46 -0800487 return details::readUrlSegments(url, {std::forward<Args>(args)...});
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100488}
489
Ed Tanousd9f466b2023-03-06 15:04:25 -0800490inline boost::urls::url replaceUrlSegment(boost::urls::url_view urlView,
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000491 const uint replaceLoc,
Ed Tanous26ccae32023-02-16 10:28:44 -0800492 std::string_view newSegment)
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000493{
Ed Tanousd9f466b2023-03-06 15:04:25 -0800494 boost::urls::segments_view urlSegments = urlView.segments();
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000495 boost::urls::url url("/");
496
497 if (!urlSegments.is_absolute())
498 {
499 return url;
500 }
501
502 boost::urls::segments_view::iterator it = urlSegments.begin();
503 boost::urls::segments_view::iterator end = urlSegments.end();
504
505 for (uint idx = 0; it != end; it++, idx++)
506 {
507 if (idx == replaceLoc)
508 {
509 url.segments().push_back(newSegment);
510 }
511 else
512 {
513 url.segments().push_back(*it);
514 }
515 }
516
517 return url;
518}
519
Ed Tanousa716aa72023-08-01 11:35:53 -0700520inline void setProtocolDefaults(boost::urls::url& url,
521 std::string_view protocol)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800522{
Ed Tanousa716aa72023-08-01 11:35:53 -0700523 if (url.has_scheme())
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800524 {
Ed Tanousa716aa72023-08-01 11:35:53 -0700525 return;
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800526 }
Ed Tanousa716aa72023-08-01 11:35:53 -0700527 if (protocol == "Redfish" || protocol.empty())
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800528 {
Ed Tanousa716aa72023-08-01 11:35:53 -0700529 if (url.port_number() == 443)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800530 {
Ed Tanousa716aa72023-08-01 11:35:53 -0700531 url.set_scheme("https");
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800532 }
Ed Tanousa716aa72023-08-01 11:35:53 -0700533 if (url.port_number() == 80)
534 {
535 if (bmcwebInsecureEnableHttpPushStyleEventing)
536 {
537 url.set_scheme("http");
538 }
539 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800540 }
Ed Tanousa716aa72023-08-01 11:35:53 -0700541 else if (protocol == "SNMPv2c")
Chicago Duan3d307082020-11-26 14:12:12 +0800542 {
Ed Tanousa716aa72023-08-01 11:35:53 -0700543 url.set_scheme("snmp");
Chicago Duan3d307082020-11-26 14:12:12 +0800544 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800545}
546
Ed Tanousa716aa72023-08-01 11:35:53 -0700547inline void setPortDefaults(boost::urls::url& url)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800548{
549 uint16_t port = url.port_number();
550 if (port != 0)
551 {
Ed Tanousa716aa72023-08-01 11:35:53 -0700552 return;
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800553 }
554
555 // If the user hasn't explicitly stated a port, pick one explicitly for them
556 // based on the protocol defaults
557 if (url.scheme() == "http")
558 {
Ed Tanousa716aa72023-08-01 11:35:53 -0700559 url.set_port_number(80);
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800560 }
561 if (url.scheme() == "https")
562 {
Ed Tanousa716aa72023-08-01 11:35:53 -0700563 url.set_port_number(443);
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800564 }
Chicago Duan3d307082020-11-26 14:12:12 +0800565 if (url.scheme() == "snmp")
566 {
Ed Tanousa716aa72023-08-01 11:35:53 -0700567 url.set_port_number(162);
Chicago Duan3d307082020-11-26 14:12:12 +0800568 }
Ed Tanous11baefe2022-02-09 12:14:12 -0800569}
570
Ed Tanous1abe55e2018-09-05 08:30:59 -0700571} // namespace utility
572} // namespace crow
Ed Tanous71f2db72022-05-25 12:28:09 -0700573
574namespace nlohmann
575{
576template <>
577struct adl_serializer<boost::urls::url>
578{
579 // nlohmann requires a specific casing to look these up in adl
580 // NOLINTNEXTLINE(readability-identifier-naming)
581 static void to_json(json& j, const boost::urls::url& url)
582 {
Ed Tanous079360a2022-06-29 10:05:19 -0700583 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700584 }
585};
586
587template <>
588struct adl_serializer<boost::urls::url_view>
589{
590 // NOLINTNEXTLINE(readability-identifier-naming)
Ed Tanousd9f466b2023-03-06 15:04:25 -0800591 static void to_json(json& j, boost::urls::url_view url)
Ed Tanous71f2db72022-05-25 12:28:09 -0700592 {
Ed Tanous079360a2022-06-29 10:05:19 -0700593 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700594 }
595};
596} // namespace nlohmann