blob: 6fed49a894e4f89b6aebff7c82e9a2e754eae52a [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
2
Ed Tanous911ac312017-08-15 09:37:42 -07003#include <cstdio>
4#include <cstring>
Ed Tanous7045c8d2017-04-03 10:04:37 -07005#include <iostream>
6#include <string>
7#include <vector>
8
Ed Tanous1abe55e2018-09-05 08:30:59 -07009namespace crow
10{
Ed Tanous7045c8d2017-04-03 10:04:37 -070011// ----------------------------------------------------------------------------
12// qs_parse (modified)
13// https://github.com/bartgrantham/qs_parse
14// ----------------------------------------------------------------------------
15/* Similar to strncmp, but handles URL-encoding for either string */
Ed Tanous55c7b7a2018-05-22 15:27:24 -070016int qsStrncmp(const char* s, const char* qs, size_t n);
Ed Tanous7045c8d2017-04-03 10:04:37 -070017
18/* Finds the beginning of each key/value pair and stores a pointer in qs_kv.
19 * Also decodes the value portion of the k/v pair *in-place*. In a future
20 * enhancement it will also have a compile-time option of sorting qs_kv
21 * alphabetically by key. */
Ed Tanous271584a2019-07-09 16:24:22 -070022size_t qsParse(char* qs, char* qs_kv[], size_t qs_kv_size);
Ed Tanous7045c8d2017-04-03 10:04:37 -070023
24/* Used by qs_parse to decode the value portion of a k/v pair */
Ed Tanousb01bf292019-03-25 19:25:26 +000025int qsDecode(char* qs);
Ed Tanous7045c8d2017-04-03 10:04:37 -070026
27/* Looks up the value according to the key on a pre-processed query string
28 * A future enhancement will be a compile-time option to look up the key
29 * in a pre-sorted qs_kv array via a binary search. */
30// char * qs_k2v(const char * key, char * qs_kv[], int qs_kv_size);
Ed Tanousb01bf292019-03-25 19:25:26 +000031char* qsK2v(const char* key, char* const* qs_kv, int qs_kv_size, int nth);
Ed Tanous7045c8d2017-04-03 10:04:37 -070032
33/* Non-destructive lookup of value, based on key. User provides the
34 * destinaton string and length. */
Ed Tanous55c7b7a2018-05-22 15:27:24 -070035char* qsScanvalue(const char* key, const char* qs, char* val, size_t val_len);
Ed Tanous7045c8d2017-04-03 10:04:37 -070036
37// TODO: implement sorting of the qs_kv array; for now ensure it's not compiled
38#undef _qsSORTING
39
40// isxdigit _is_ available in <ctype.h>, but let's avoid another header instead
Ed Tanous1abe55e2018-09-05 08:30:59 -070041#define BMCWEB_QS_ISHEX(x) \
42 ((((x) >= '0' && (x) <= '9') || ((x) >= 'A' && (x) <= 'F') || \
43 ((x) >= 'a' && (x) <= 'f')) \
44 ? 1 \
45 : 0)
46#define BMCWEB_QS_HEX2DEC(x) \
47 (((x) >= '0' && (x) <= '9') \
48 ? (x)-48 \
49 : ((x) >= 'A' && (x) <= 'F') \
50 ? (x)-55 \
51 : ((x) >= 'a' && (x) <= 'f') ? (x)-87 : 0)
52#define BMCWEB_QS_ISQSCHR(x) \
53 ((((x) == '=') || ((x) == '#') || ((x) == '&') || ((x) == '\0')) ? 0 : 1)
Ed Tanous7045c8d2017-04-03 10:04:37 -070054
Ed Tanous1abe55e2018-09-05 08:30:59 -070055inline int qsStrncmp(const char* s, const char* qs, size_t n)
56{
57 int i = 0;
Ed Tanous271584a2019-07-09 16:24:22 -070058 char u1, u2;
59 char unyb, lnyb;
Ed Tanous7045c8d2017-04-03 10:04:37 -070060
Ed Tanous1abe55e2018-09-05 08:30:59 -070061 while (n-- > 0)
Ed Tanous7045c8d2017-04-03 10:04:37 -070062 {
Ed Tanous271584a2019-07-09 16:24:22 -070063 u1 = *s++;
64 u2 = *qs++;
Ed Tanous7045c8d2017-04-03 10:04:37 -070065
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 if (!BMCWEB_QS_ISQSCHR(u1))
67 {
68 u1 = '\0';
69 }
70 if (!BMCWEB_QS_ISQSCHR(u2))
71 {
72 u2 = '\0';
73 }
74
75 if (u1 == '+')
76 {
77 u1 = ' ';
78 }
79 if (u1 == '%') // easier/safer than scanf
80 {
Ed Tanous271584a2019-07-09 16:24:22 -070081 unyb = static_cast<char>(*s++);
82 lnyb = static_cast<char>(*s++);
Ed Tanous1abe55e2018-09-05 08:30:59 -070083 if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb))
84 {
Ed Tanous271584a2019-07-09 16:24:22 -070085 u1 = static_cast<char>((BMCWEB_QS_HEX2DEC(unyb) * 16) +
86 BMCWEB_QS_HEX2DEC(lnyb));
Ed Tanous1abe55e2018-09-05 08:30:59 -070087 }
88 else
89 {
90 u1 = '\0';
91 }
92 }
93
94 if (u2 == '+')
95 {
96 u2 = ' ';
97 }
98 if (u2 == '%') // easier/safer than scanf
99 {
Ed Tanous271584a2019-07-09 16:24:22 -0700100 unyb = static_cast<char>(*qs++);
101 lnyb = static_cast<char>(*qs++);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700102 if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb))
103 {
Ed Tanous271584a2019-07-09 16:24:22 -0700104 u2 = static_cast<char>((BMCWEB_QS_HEX2DEC(unyb) * 16) +
105 BMCWEB_QS_HEX2DEC(lnyb));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 }
107 else
108 {
109 u2 = '\0';
110 }
111 }
112
113 if (u1 != u2)
114 {
115 return u1 - u2;
116 }
117 if (u1 == '\0')
118 {
119 return 0;
120 }
121 i++;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700122 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700123 if (BMCWEB_QS_ISQSCHR(*qs))
Ed Tanous7045c8d2017-04-03 10:04:37 -0700124 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700125 return -1;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700126 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700127 else
128 {
129 return 0;
Ed Tanous911ac312017-08-15 09:37:42 -0700130 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700131}
132
Ed Tanous271584a2019-07-09 16:24:22 -0700133inline size_t qsParse(char* qs, char* qs_kv[], size_t qs_kv_size)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700134{
Ed Tanous271584a2019-07-09 16:24:22 -0700135 size_t i;
136 size_t j;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700137 char* substrPtr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700138
Ed Tanous1abe55e2018-09-05 08:30:59 -0700139 for (i = 0; i < qs_kv_size; i++)
140 {
Ed Tanous99131cd2019-10-24 11:12:47 -0700141 qs_kv[i] = nullptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700142 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700143
Ed Tanous1abe55e2018-09-05 08:30:59 -0700144 // find the beginning of the k/v substrings or the fragment
145 substrPtr = qs + strcspn(qs, "?#");
146 if (substrPtr[0] != '\0')
147 {
148 substrPtr++;
Ed Tanous911ac312017-08-15 09:37:42 -0700149 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700150 else
151 {
152 return 0; // no query or fragment
153 }
154
155 i = 0;
156 while (i < qs_kv_size)
157 {
James Feist45b1b132020-02-13 16:14:13 -0800158 qs_kv[i++] = substrPtr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700159 j = strcspn(substrPtr, "&");
160 if (substrPtr[j] == '\0')
161 {
162 break;
163 }
164 substrPtr += j + 1;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700165 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166
167 // we only decode the values in place, the keys could have '='s in them
168 // which will hose our ability to distinguish keys from values later
169 for (j = 0; j < i; j++)
170 {
171 substrPtr = qs_kv[j] + strcspn(qs_kv[j], "=&#");
172 if (substrPtr[0] == '&' || substrPtr[0] == '\0')
173 { // blank value: skip decoding
174 substrPtr[0] = '\0';
175 }
176 else
177 {
178 qsDecode(++substrPtr);
179 }
180 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700181
182#ifdef _qsSORTING
183// TODO: qsort qs_kv, using qs_strncmp() for the comparison
184#endif
185
Ed Tanous1abe55e2018-09-05 08:30:59 -0700186 return i;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700187}
188
Ed Tanousb01bf292019-03-25 19:25:26 +0000189inline int qsDecode(char* qs)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700190{
Ed Tanousb01bf292019-03-25 19:25:26 +0000191 int i = 0, j = 0;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700192
Ed Tanous1abe55e2018-09-05 08:30:59 -0700193 while (BMCWEB_QS_ISQSCHR(qs[j]))
Ed Tanous7045c8d2017-04-03 10:04:37 -0700194 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700195 if (qs[j] == '+')
196 {
197 qs[i] = ' ';
198 }
199 else if (qs[j] == '%') // easier/safer than scanf
200 {
201 if (!BMCWEB_QS_ISHEX(qs[j + 1]) || !BMCWEB_QS_ISHEX(qs[j + 2]))
202 {
203 qs[i] = '\0';
204 return i;
205 }
Ed Tanous271584a2019-07-09 16:24:22 -0700206 qs[i] = static_cast<char>(BMCWEB_QS_HEX2DEC(qs[j + 1] * 16) +
207 BMCWEB_QS_HEX2DEC(qs[j + 2]));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700208 j += 2;
209 }
210 else
211 {
212 qs[i] = qs[j];
213 }
214 i++;
215 j++;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700216 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700217 qs[i] = '\0';
Ed Tanous7045c8d2017-04-03 10:04:37 -0700218
Ed Tanous1abe55e2018-09-05 08:30:59 -0700219 return i;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700220}
221
Ed Tanousb01bf292019-03-25 19:25:26 +0000222inline char* qsK2v(const char* key, char* const* qs_kv, int qs_kv_size,
223 int nth = 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700224{
Ed Tanousb01bf292019-03-25 19:25:26 +0000225 int i;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700226 size_t keyLen, skip;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700227
Ed Tanous1abe55e2018-09-05 08:30:59 -0700228 keyLen = strlen(key);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700229
230#ifdef _qsSORTING
231// TODO: binary search for key in the sorted qs_kv
Ed Tanous1abe55e2018-09-05 08:30:59 -0700232#else // _qsSORTING
233 for (i = 0; i < qs_kv_size; i++)
234 {
235 // we rely on the unambiguous '=' to find the value in our k/v pair
236 if (qsStrncmp(key, qs_kv[i], keyLen) == 0)
237 {
238 skip = strcspn(qs_kv[i], "=");
239 if (qs_kv[i][skip] == '=')
240 {
241 skip++;
242 }
243 // return (zero-char value) ? ptr to trailing '\0' : ptr to value
244 if (nth == 0)
245 {
246 return qs_kv[i] + skip;
247 }
248 else
249 {
250 --nth;
251 }
252 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700253 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700254#endif // _qsSORTING
Ed Tanous7045c8d2017-04-03 10:04:37 -0700255
Ed Tanous99131cd2019-10-24 11:12:47 -0700256 return nullptr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700257}
258
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700259inline char* qsScanvalue(const char* key, const char* qs, char* val,
Ed Tanous1abe55e2018-09-05 08:30:59 -0700260 size_t val_len)
261{
262 size_t i, keyLen;
263 const char* tmp;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700264
Ed Tanous1abe55e2018-09-05 08:30:59 -0700265 // find the beginning of the k/v substrings
Ed Tanous99131cd2019-10-24 11:12:47 -0700266 if ((tmp = strchr(qs, '?')) != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700267 {
268 qs = tmp + 1;
Ed Tanous911ac312017-08-15 09:37:42 -0700269 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700270
Ed Tanous1abe55e2018-09-05 08:30:59 -0700271 keyLen = strlen(key);
272 while (qs[0] != '#' && qs[0] != '\0')
273 {
274 if (qsStrncmp(key, qs, keyLen) == 0)
275 {
276 break;
277 }
278 qs += strcspn(qs, "&") + 1;
Ed Tanous911ac312017-08-15 09:37:42 -0700279 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700280
Ed Tanous1abe55e2018-09-05 08:30:59 -0700281 if (qs[0] == '\0')
282 {
Ed Tanous99131cd2019-10-24 11:12:47 -0700283 return nullptr;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 }
285
286 qs += strcspn(qs, "=&#");
287 if (qs[0] == '=')
288 {
289 qs++;
290 i = strcspn(qs, "&=#");
291 strncpy(val, qs, (val_len - 1) < (i + 1) ? (val_len - 1) : (i + 1));
292 qsDecode(val);
293 }
294 else
295 {
296 if (val_len > 0)
297 {
298 val[0] = '\0';
299 }
300 }
301
302 return val;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700303}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700304} // namespace crow
Ed Tanous7045c8d2017-04-03 10:04:37 -0700305// ----------------------------------------------------------------------------
306
Ed Tanous1abe55e2018-09-05 08:30:59 -0700307namespace crow
308{
309class QueryString
310{
311 public:
Ed Tanous271584a2019-07-09 16:24:22 -0700312 static const size_t maxKeyValuePairsCount = 256;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700313
Ed Tanous1abe55e2018-09-05 08:30:59 -0700314 QueryString() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700315
Ed Tanous1abe55e2018-09-05 08:30:59 -0700316 QueryString(const QueryString& qs) : url(qs.url)
317 {
318 for (auto p : qs.keyValuePairs)
319 {
320 keyValuePairs.push_back(
321 const_cast<char*>(p - qs.url.c_str() + url.c_str()));
322 }
Ed Tanous911ac312017-08-15 09:37:42 -0700323 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700324
Ed Tanous1abe55e2018-09-05 08:30:59 -0700325 QueryString& operator=(const QueryString& qs)
326 {
Ed Tanous173d1812019-10-24 10:03:41 -0700327 if (this == &qs)
328 {
329 return *this;
330 }
331
Ed Tanous1abe55e2018-09-05 08:30:59 -0700332 url = qs.url;
333 keyValuePairs.clear();
334 for (auto p : qs.keyValuePairs)
335 {
336 keyValuePairs.push_back(
337 const_cast<char*>(p - qs.url.c_str() + url.c_str()));
338 }
339 return *this;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700340 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700341
Ed Tanous1abe55e2018-09-05 08:30:59 -0700342 QueryString& operator=(QueryString&& qs)
343 {
344 keyValuePairs = std::move(qs.keyValuePairs);
345 auto* oldData = const_cast<char*>(qs.url.c_str());
346 url = std::move(qs.url);
347 for (auto& p : keyValuePairs)
348 {
349 p += const_cast<char*>(url.c_str()) - oldData;
350 }
351 return *this;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700352 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700353
Jason M. Bills43fcbe52018-10-16 15:19:20 -0700354 explicit QueryString(std::string newUrl) : url(std::move(newUrl))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700355 {
356 if (url.empty())
357 {
358 return;
359 }
360
361 keyValuePairs.resize(maxKeyValuePairsCount);
362
Ed Tanous271584a2019-07-09 16:24:22 -0700363 size_t count =
364 qsParse(&url[0], &keyValuePairs[0], maxKeyValuePairsCount);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700365 keyValuePairs.resize(count);
366 }
367
368 void clear()
369 {
370 keyValuePairs.clear();
371 url.clear();
372 }
373
374 friend std::ostream& operator<<(std::ostream& os, const QueryString& qs)
375 {
376 os << "[ ";
377 for (size_t i = 0; i < qs.keyValuePairs.size(); ++i)
378 {
379 if (i != 0u)
380 {
381 os << ", ";
382 }
383 os << qs.keyValuePairs[i];
384 }
385 os << " ]";
386 return os;
387 }
388
389 char* get(const std::string& name) const
390 {
Ed Tanous271584a2019-07-09 16:24:22 -0700391 char* ret = qsK2v(name.c_str(), keyValuePairs.data(),
392 static_cast<int>(keyValuePairs.size()));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700393 return ret;
394 }
395
396 std::vector<char*> getList(const std::string& name) const
397 {
398 std::vector<char*> ret;
399 std::string plus = name + "[]";
400 char* element = nullptr;
401
Ed Tanousb01bf292019-03-25 19:25:26 +0000402 int count = 0;
Ed Tanousf12a13b2019-10-24 11:29:41 -0700403 while (true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404 {
405 element = qsK2v(plus.c_str(), keyValuePairs.data(),
Ed Tanous271584a2019-07-09 16:24:22 -0700406 static_cast<int>(keyValuePairs.size()), count++);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 if (element == nullptr)
408 {
409 break;
410 }
411 ret.push_back(element);
412 }
413 return ret;
414 }
415
416 private:
417 std::string url;
418 std::vector<char*> keyValuePairs;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700419};
420
Ed Tanous1abe55e2018-09-05 08:30:59 -0700421} // namespace crow