blob: 060699477c0c49e8102a5d1420e8f618483cfc91 [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
9namespace crow {
10// ----------------------------------------------------------------------------
11// qs_parse (modified)
12// https://github.com/bartgrantham/qs_parse
13// ----------------------------------------------------------------------------
14/* Similar to strncmp, but handles URL-encoding for either string */
Ed Tanous55c7b7a2018-05-22 15:27:24 -070015int qsStrncmp(const char* s, const char* qs, size_t n);
Ed Tanous7045c8d2017-04-03 10:04:37 -070016
17/* Finds the beginning of each key/value pair and stores a pointer in qs_kv.
18 * Also decodes the value portion of the k/v pair *in-place*. In a future
19 * enhancement it will also have a compile-time option of sorting qs_kv
20 * alphabetically by key. */
Ed Tanous55c7b7a2018-05-22 15:27:24 -070021int qsParse(char* qs, char* qs_kv[], int qs_kv_size);
Ed Tanous7045c8d2017-04-03 10:04:37 -070022
23/* Used by qs_parse to decode the value portion of a k/v pair */
Ed Tanous55c7b7a2018-05-22 15:27:24 -070024int qsDecode(char* qs);
Ed Tanous7045c8d2017-04-03 10:04:37 -070025
26/* Looks up the value according to the key on a pre-processed query string
27 * A future enhancement will be a compile-time option to look up the key
28 * in a pre-sorted qs_kv array via a binary search. */
29// char * qs_k2v(const char * key, char * qs_kv[], int qs_kv_size);
Ed Tanous55c7b7a2018-05-22 15:27:24 -070030char* qsK2v(const char* key, char* const* qs_kv, int qs_kv_size, int nth);
Ed Tanous7045c8d2017-04-03 10:04:37 -070031
32/* Non-destructive lookup of value, based on key. User provides the
33 * destinaton string and length. */
Ed Tanous55c7b7a2018-05-22 15:27:24 -070034char* qsScanvalue(const char* key, const char* qs, char* val, size_t val_len);
Ed Tanous7045c8d2017-04-03 10:04:37 -070035
36// TODO: implement sorting of the qs_kv array; for now ensure it's not compiled
37#undef _qsSORTING
38
39// isxdigit _is_ available in <ctype.h>, but let's avoid another header instead
Ed Tanous55c7b7a2018-05-22 15:27:24 -070040#define BMCWEB_QS_ISHEX(x) \
Ed Tanous7045c8d2017-04-03 10:04:37 -070041 ((((x) >= '0' && (x) <= '9') || ((x) >= 'A' && (x) <= 'F') || \
42 ((x) >= 'a' && (x) <= 'f')) \
43 ? 1 \
44 : 0)
Ed Tanous55c7b7a2018-05-22 15:27:24 -070045#define BMCWEB_QS_HEX2DEC(x) \
Ed Tanous7045c8d2017-04-03 10:04:37 -070046 (((x) >= '0' && (x) <= '9') \
47 ? (x)-48 \
48 : ((x) >= 'A' && (x) <= 'F') ? (x)-55 \
49 : ((x) >= 'a' && (x) <= 'f') ? (x)-87 : 0)
Ed Tanous55c7b7a2018-05-22 15:27:24 -070050#define BMCWEB_QS_ISQSCHR(x) \
Ed Tanous7045c8d2017-04-03 10:04:37 -070051 ((((x) == '=') || ((x) == '#') || ((x) == '&') || ((x) == '\0')) ? 0 : 1)
52
Ed Tanous55c7b7a2018-05-22 15:27:24 -070053inline int qsStrncmp(const char* s, const char* qs, size_t n) {
Ed Tanous7045c8d2017-04-03 10:04:37 -070054 int i = 0;
55 unsigned char u1, u2, unyb, lnyb;
56
57 while (n-- > 0) {
Ed Tanous911ac312017-08-15 09:37:42 -070058 u1 = static_cast<unsigned char>(*s++);
59 u2 = static_cast<unsigned char>(*qs++);
Ed Tanous7045c8d2017-04-03 10:04:37 -070060
Ed Tanous55c7b7a2018-05-22 15:27:24 -070061 if (!BMCWEB_QS_ISQSCHR(u1)) {
Ed Tanous7045c8d2017-04-03 10:04:37 -070062 u1 = '\0';
63 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070064 if (!BMCWEB_QS_ISQSCHR(u2)) {
Ed Tanous7045c8d2017-04-03 10:04:37 -070065 u2 = '\0';
66 }
67
68 if (u1 == '+') {
69 u1 = ' ';
70 }
71 if (u1 == '%') // easier/safer than scanf
72 {
Ed Tanous911ac312017-08-15 09:37:42 -070073 unyb = static_cast<unsigned char>(*s++);
74 lnyb = static_cast<unsigned char>(*s++);
Ed Tanous55c7b7a2018-05-22 15:27:24 -070075 if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb)) {
76 u1 = (BMCWEB_QS_HEX2DEC(unyb) * 16) + BMCWEB_QS_HEX2DEC(lnyb);
Ed Tanous911ac312017-08-15 09:37:42 -070077 } else {
Ed Tanous7045c8d2017-04-03 10:04:37 -070078 u1 = '\0';
Ed Tanous911ac312017-08-15 09:37:42 -070079 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070080 }
81
82 if (u2 == '+') {
83 u2 = ' ';
84 }
85 if (u2 == '%') // easier/safer than scanf
86 {
Ed Tanous911ac312017-08-15 09:37:42 -070087 unyb = static_cast<unsigned char>(*qs++);
88 lnyb = static_cast<unsigned char>(*qs++);
Ed Tanous55c7b7a2018-05-22 15:27:24 -070089 if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb)) {
90 u2 = (BMCWEB_QS_HEX2DEC(unyb) * 16) + BMCWEB_QS_HEX2DEC(lnyb);
Ed Tanous911ac312017-08-15 09:37:42 -070091 } else {
Ed Tanous7045c8d2017-04-03 10:04:37 -070092 u2 = '\0';
Ed Tanous911ac312017-08-15 09:37:42 -070093 }
Ed Tanous7045c8d2017-04-03 10:04:37 -070094 }
95
Ed Tanous911ac312017-08-15 09:37:42 -070096 if (u1 != u2) {
97 return u1 - u2;
98 }
99 if (u1 == '\0') {
100 return 0;
101 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700102 i++;
103 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700104 if (BMCWEB_QS_ISQSCHR(*qs)) {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700105 return -1;
Ed Tanous911ac312017-08-15 09:37:42 -0700106 } else {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700107 return 0;
Ed Tanous911ac312017-08-15 09:37:42 -0700108 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700109}
110
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700111inline int qsParse(char* qs, char* qs_kv[], int qs_kv_size) {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700112 int i, j;
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700113 char* substrPtr;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700114
Ed Tanous911ac312017-08-15 09:37:42 -0700115 for (i = 0; i < qs_kv_size; i++) {
116 qs_kv[i] = NULL;
117 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700118
119 // find the beginning of the k/v substrings or the fragment
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700120 substrPtr = qs + strcspn(qs, "?#");
121 if (substrPtr[0] != '\0') {
122 substrPtr++;
Ed Tanous911ac312017-08-15 09:37:42 -0700123 } else {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700124 return 0; // no query or fragment
Ed Tanous911ac312017-08-15 09:37:42 -0700125 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700126
127 i = 0;
128 while (i < qs_kv_size) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700129 qs_kv[i] = substrPtr;
130 j = strcspn(substrPtr, "&");
131 if (substrPtr[j] == '\0') {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700132 break;
133 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700134 substrPtr += j + 1;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700135 i++;
136 }
137 i++; // x &'s -> means x iterations of this loop -> means *x+1* k/v pairs
138
139 // we only decode the values in place, the keys could have '='s in them
140 // which will hose our ability to distinguish keys from values later
141 for (j = 0; j < i; j++) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700142 substrPtr = qs_kv[j] + strcspn(qs_kv[j], "=&#");
143 if (substrPtr[0] == '&' ||
144 substrPtr[0] == '\0') { // blank value: skip decoding
145 substrPtr[0] = '\0';
Ed Tanous911ac312017-08-15 09:37:42 -0700146 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700147 qsDecode(++substrPtr);
Ed Tanous911ac312017-08-15 09:37:42 -0700148 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700149 }
150
151#ifdef _qsSORTING
152// TODO: qsort qs_kv, using qs_strncmp() for the comparison
153#endif
154
155 return i;
156}
157
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700158inline int qsDecode(char* qs) {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700159 int i = 0, j = 0;
160
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700161 while (BMCWEB_QS_ISQSCHR(qs[j])) {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700162 if (qs[j] == '+') {
163 qs[i] = ' ';
164 } else if (qs[j] == '%') // easier/safer than scanf
165 {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700166 if (!BMCWEB_QS_ISHEX(qs[j + 1]) || !BMCWEB_QS_ISHEX(qs[j + 2])) {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700167 qs[i] = '\0';
168 return i;
169 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700170 qs[i] =
171 (BMCWEB_QS_HEX2DEC(qs[j + 1]) * 16) + BMCWEB_QS_HEX2DEC(qs[j + 2]);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700172 j += 2;
173 } else {
174 qs[i] = qs[j];
175 }
176 i++;
177 j++;
178 }
179 qs[i] = '\0';
180
181 return i;
182}
183
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700184inline char* qsK2v(const char* key, char* const* qs_kv, int qs_kv_size,
185 int nth = 0) {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700186 int i;
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700187 size_t keyLen, skip;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700188
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700189 keyLen = strlen(key);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700190
191#ifdef _qsSORTING
192// TODO: binary search for key in the sorted qs_kv
193#else // _qsSORTING
194 for (i = 0; i < qs_kv_size; i++) {
195 // we rely on the unambiguous '=' to find the value in our k/v pair
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700196 if (qsStrncmp(key, qs_kv[i], keyLen) == 0) {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700197 skip = strcspn(qs_kv[i], "=");
Ed Tanous911ac312017-08-15 09:37:42 -0700198 if (qs_kv[i][skip] == '=') {
199 skip++;
200 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700201 // return (zero-char value) ? ptr to trailing '\0' : ptr to value
Ed Tanous911ac312017-08-15 09:37:42 -0700202 if (nth == 0) {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700203 return qs_kv[i] + skip;
Ed Tanous911ac312017-08-15 09:37:42 -0700204 } else {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700205 --nth;
Ed Tanous911ac312017-08-15 09:37:42 -0700206 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700207 }
208 }
209#endif // _qsSORTING
210
211 return NULL;
212}
213
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700214inline char* qsScanvalue(const char* key, const char* qs, char* val,
215 size_t val_len) {
216 size_t i, keyLen;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700217 const char* tmp;
218
219 // find the beginning of the k/v substrings
Ed Tanous911ac312017-08-15 09:37:42 -0700220 if ((tmp = strchr(qs, '?')) != NULL) {
221 qs = tmp + 1;
222 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700223
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700224 keyLen = strlen(key);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700225 while (qs[0] != '#' && qs[0] != '\0') {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700226 if (qsStrncmp(key, qs, keyLen) == 0) {
Ed Tanous911ac312017-08-15 09:37:42 -0700227 break;
228 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700229 qs += strcspn(qs, "&") + 1;
230 }
231
Ed Tanous911ac312017-08-15 09:37:42 -0700232 if (qs[0] == '\0') {
233 return NULL;
234 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700235
236 qs += strcspn(qs, "=&#");
237 if (qs[0] == '=') {
238 qs++;
239 i = strcspn(qs, "&=#");
240 strncpy(val, qs, (val_len - 1) < (i + 1) ? (val_len - 1) : (i + 1));
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700241 qsDecode(val);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700242 } else {
Ed Tanous911ac312017-08-15 09:37:42 -0700243 if (val_len > 0) {
244 val[0] = '\0';
245 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700246 }
247
248 return val;
249}
Ed Tanous911ac312017-08-15 09:37:42 -0700250} // namespace crow
Ed Tanous7045c8d2017-04-03 10:04:37 -0700251// ----------------------------------------------------------------------------
252
253namespace crow {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700254class QueryString {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700255 public:
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700256 static const int maxKeyValuePairsCount = 256;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700257
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700258 QueryString() = default;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700259
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700260 QueryString(const QueryString& qs) : url(qs.url) {
261 for (auto p : qs.keyValuePairs) {
262 keyValuePairs.push_back(
263 const_cast<char*>(p - qs.url.c_str() + url.c_str()));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700264 }
265 }
266
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700267 QueryString& operator=(const QueryString& qs) {
268 url = qs.url;
269 keyValuePairs.clear();
270 for (auto p : qs.keyValuePairs) {
271 keyValuePairs.push_back(
272 const_cast<char*>(p - qs.url.c_str() + url.c_str()));
Ed Tanous7045c8d2017-04-03 10:04:37 -0700273 }
274 return *this;
275 }
276
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700277 QueryString& operator=(QueryString&& qs) {
278 keyValuePairs = std::move(qs.keyValuePairs);
279 auto* oldData = const_cast<char*>(qs.url.c_str());
280 url = std::move(qs.url);
281 for (auto& p : keyValuePairs) {
282 p += const_cast<char*>(url.c_str()) - oldData;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700283 }
284 return *this;
285 }
286
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700287 explicit QueryString(std::string url) : url(std::move(url)) {
288 if (url.empty()) {
Ed Tanous911ac312017-08-15 09:37:42 -0700289 return;
290 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700291
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700292 keyValuePairs.resize(maxKeyValuePairsCount);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700293
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700294 int count = qsParse(&url[0], &keyValuePairs[0], maxKeyValuePairsCount);
295 keyValuePairs.resize(count);
Ed Tanous7045c8d2017-04-03 10:04:37 -0700296 }
297
298 void clear() {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700299 keyValuePairs.clear();
300 url.clear();
Ed Tanous7045c8d2017-04-03 10:04:37 -0700301 }
302
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700303 friend std::ostream& operator<<(std::ostream& os, const QueryString& qs) {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700304 os << "[ ";
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700305 for (size_t i = 0; i < qs.keyValuePairs.size(); ++i) {
Ed Tanous911ac312017-08-15 09:37:42 -0700306 if (i != 0u) {
307 os << ", ";
308 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700309 os << qs.keyValuePairs[i];
Ed Tanous7045c8d2017-04-03 10:04:37 -0700310 }
311 os << " ]";
312 return os;
313 }
314
315 char* get(const std::string& name) const {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700316 char* ret = qsK2v(name.c_str(), keyValuePairs.data(), keyValuePairs.size());
Ed Tanous7045c8d2017-04-03 10:04:37 -0700317 return ret;
318 }
319
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700320 std::vector<char*> getList(const std::string& name) const {
Ed Tanous7045c8d2017-04-03 10:04:37 -0700321 std::vector<char*> ret;
322 std::string plus = name + "[]";
323 char* element = nullptr;
324
325 int count = 0;
326 while (1) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700327 element = qsK2v(plus.c_str(), keyValuePairs.data(), keyValuePairs.size(),
328 count++);
Ed Tanous911ac312017-08-15 09:37:42 -0700329 if (element == nullptr) {
330 break;
331 }
Ed Tanous7045c8d2017-04-03 10:04:37 -0700332 ret.push_back(element);
333 }
334 return ret;
335 }
336
337 private:
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700338 std::string url;
339 std::vector<char*> keyValuePairs;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700340};
341
Ed Tanous911ac312017-08-15 09:37:42 -0700342} // namespace crow