Remove QueryString

QueryString is an error-prone library that was
leftover from crow. Replace it with boost::url,
a header only library based and written by the
one of the authors of boost beast.

Tested: Verified logging paging still worked
as expected

Change-Id: I47c225089aa7d0f7d2299142f91806294f879381
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2886438..50483ad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -280,6 +280,8 @@
 add_definitions (-DBOOST_NO_RTTI)
 add_definitions (-DBOOST_NO_TYPEID)
 add_definitions (-DBOOST_COROUTINES_NO_DEPRECATION_WARNING)
+add_definitions (-DBOOST_URL_STANDALONE)
+add_definitions (-DBOOST_URL_HEADER_ONLY)
 
 # sdbusplus
 if (NOT ${YOCTO_DEPENDENCIES})
diff --git a/CMakeLists.txt.in b/CMakeLists.txt.in
index d14910f..5cd73f6 100644
--- a/CMakeLists.txt.in
+++ b/CMakeLists.txt.in
@@ -53,3 +53,13 @@
     cp -r "${CMAKE_BINARY_DIR}/nlohmann-json-src/include/nlohmann"
     "${CMAKE_BINARY_DIR}/prefix/include"
 )
+
+externalproject_add (
+    Boost-URL GIT_REPOSITORY "https://github.com/CPPAlliance/url.git" GIT_TAG
+    a56ae0df6d3078319755fbaa67822b4fa7fd352b SOURCE_DIR
+    "${CMAKE_BINARY_DIR}/boost-url-src" BINARY_DIR
+    "${CMAKE_BINARY_DIR}/boost-url-build" CONFIGURE_COMMAND "" BUILD_COMMAND
+    "" INSTALL_COMMAND mkdir -p "${CMAKE_BINARY_DIR}/prefix/include" &&
+    cp -r "${CMAKE_BINARY_DIR}/boost-url-src/include/boost"
+    "${CMAKE_BINARY_DIR}/prefix/include"
+)
diff --git a/http/http_connection.h b/http/http_connection.h
index 35bf99c..8dba3d6 100644
--- a/http/http_connection.h
+++ b/http/http_connection.h
@@ -728,13 +728,9 @@
                     return;
                 }
 
-                // Compute the url parameters for the request
-                req->url = req->target();
-                std::size_t index = req->url.find("?");
-                if (index != std::string_view::npos)
-                {
-                    req->url = req->url.substr(0, index);
-                }
+                req->urlView = boost::urls::url_view(req->target());
+                req->url = req->urlView.encoded_path();
+
                 crow::authorization::authenticate(*req, res, session);
 
                 bool loggedIn = req && req->session;
@@ -743,7 +739,16 @@
                     startDeadline(loggedInAttempts);
                     BMCWEB_LOG_DEBUG << "Starting slow deadline";
 
-                    req->urlParams = QueryString(std::string(req->target()));
+                    req->urlParams = req->urlView.params();
+
+#ifdef BMCWEB_ENABLE_DEBUG
+                    std::string paramList = "";
+                    for (const auto param : req->urlParams)
+                    {
+                        paramList += param->key() + " " + param->value() + " ";
+                    }
+                    BMCWEB_LOG_DEBUG << "QueryParams: " << paramList;
+#endif
                 }
                 else
                 {
diff --git a/http/http_request.h b/http/http_request.h
index 0691465..95f88c7 100644
--- a/http/http_request.h
+++ b/http/http_request.h
@@ -1,7 +1,6 @@
 #pragma once
 
 #include "common.h"
-#include "query_string.h"
 
 #include "sessions.hpp"
 
@@ -9,6 +8,7 @@
 #include <boost/beast/http.hpp>
 #include <boost/beast/ssl/ssl_stream.hpp>
 #include <boost/beast/websocket.hpp>
+#include <boost/url/url_view.hpp>
 
 namespace crow
 {
@@ -24,7 +24,8 @@
     boost::beast::http::request<boost::beast::http::string_body>& req;
     boost::beast::http::fields& fields;
     std::string_view url{};
-    QueryString urlParams{};
+    boost::urls::url_view urlView{};
+    boost::urls::url_view::params_type urlParams{};
     bool isSecure{false};
 
     const std::string& body;
diff --git a/http/query_string.h b/http/query_string.h
deleted file mode 100644
index e980280..0000000
--- a/http/query_string.h
+++ /dev/null
@@ -1,421 +0,0 @@
-#pragma once
-
-#include <cstdio>
-#include <cstring>
-#include <iostream>
-#include <string>
-#include <vector>
-
-namespace crow
-{
-// ----------------------------------------------------------------------------
-// qs_parse (modified)
-// https://github.com/bartgrantham/qs_parse
-// ----------------------------------------------------------------------------
-/*  Similar to strncmp, but handles URL-encoding for either string  */
-int qsStrncmp(const char* s, const char* qs, size_t n);
-
-/*  Finds the beginning of each key/value pair and stores a pointer in qs_kv.
- *  Also decodes the value portion of the k/v pair *in-place*.  In a future
- *  enhancement it will also have a compile-time option of sorting qs_kv
- *  alphabetically by key.  */
-size_t qsParse(char* qs, char* qs_kv[], size_t qs_kv_size);
-
-/*  Used by qs_parse to decode the value portion of a k/v pair  */
-int qsDecode(char* qs);
-
-/*  Looks up the value according to the key on a pre-processed query string
- *  A future enhancement will be a compile-time option to look up the key
- *  in a pre-sorted qs_kv array via a binary search.  */
-// char * qs_k2v(const char * key, char * qs_kv[], int qs_kv_size);
-char* qsK2v(const char* key, char* const* qs_kv, int qs_kv_size, int nth);
-
-/*  Non-destructive lookup of value, based on key.  User provides the
- *  destinaton string and length.  */
-char* qsScanvalue(const char* key, const char* qs, char* val, size_t val_len);
-
-// TODO: implement sorting of the qs_kv array; for now ensure it's not compiled
-#undef _qsSORTING
-
-// isxdigit _is_ available in <ctype.h>, but let's avoid another header instead
-#define BMCWEB_QS_ISHEX(x)                                                     \
-    ((((x) >= '0' && (x) <= '9') || ((x) >= 'A' && (x) <= 'F') ||              \
-      ((x) >= 'a' && (x) <= 'f'))                                              \
-         ? 1                                                                   \
-         : 0)
-#define BMCWEB_QS_HEX2DEC(x)                                                   \
-    (((x) >= '0' && (x) <= '9')                                                \
-         ? (x)-48                                                              \
-         : ((x) >= 'A' && (x) <= 'F')                                          \
-               ? (x)-55                                                        \
-               : ((x) >= 'a' && (x) <= 'f') ? (x)-87 : 0)
-#define BMCWEB_QS_ISQSCHR(x)                                                   \
-    ((((x) == '=') || ((x) == '#') || ((x) == '&') || ((x) == '\0')) ? 0 : 1)
-
-inline int qsStrncmp(const char* s, const char* qs, size_t n)
-{
-    int i = 0;
-    char u1, u2;
-    char unyb, lnyb;
-
-    while (n-- > 0)
-    {
-        u1 = *s++;
-        u2 = *qs++;
-
-        if (!BMCWEB_QS_ISQSCHR(u1))
-        {
-            u1 = '\0';
-        }
-        if (!BMCWEB_QS_ISQSCHR(u2))
-        {
-            u2 = '\0';
-        }
-
-        if (u1 == '+')
-        {
-            u1 = ' ';
-        }
-        if (u1 == '%') // easier/safer than scanf
-        {
-            unyb = static_cast<char>(*s++);
-            lnyb = static_cast<char>(*s++);
-            if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb))
-            {
-                u1 = static_cast<char>((BMCWEB_QS_HEX2DEC(unyb) * 16) +
-                                       BMCWEB_QS_HEX2DEC(lnyb));
-            }
-            else
-            {
-                u1 = '\0';
-            }
-        }
-
-        if (u2 == '+')
-        {
-            u2 = ' ';
-        }
-        if (u2 == '%') // easier/safer than scanf
-        {
-            unyb = static_cast<char>(*qs++);
-            lnyb = static_cast<char>(*qs++);
-            if (BMCWEB_QS_ISHEX(unyb) && BMCWEB_QS_ISHEX(lnyb))
-            {
-                u2 = static_cast<char>((BMCWEB_QS_HEX2DEC(unyb) * 16) +
-                                       BMCWEB_QS_HEX2DEC(lnyb));
-            }
-            else
-            {
-                u2 = '\0';
-            }
-        }
-
-        if (u1 != u2)
-        {
-            return u1 - u2;
-        }
-        if (u1 == '\0')
-        {
-            return 0;
-        }
-        i++;
-    }
-    if (BMCWEB_QS_ISQSCHR(*qs))
-    {
-        return -1;
-    }
-    else
-    {
-        return 0;
-    }
-}
-
-inline size_t qsParse(char* qs, char* qs_kv[], size_t qs_kv_size)
-{
-    size_t i;
-    size_t j;
-    char* substrPtr;
-
-    for (i = 0; i < qs_kv_size; i++)
-    {
-        qs_kv[i] = nullptr;
-    }
-
-    // find the beginning of the k/v substrings or the fragment
-    substrPtr = qs + strcspn(qs, "?#");
-    if (substrPtr[0] != '\0')
-    {
-        substrPtr++;
-    }
-    else
-    {
-        return 0; // no query or fragment
-    }
-
-    i = 0;
-    while (i < qs_kv_size)
-    {
-        qs_kv[i++] = substrPtr;
-        j = strcspn(substrPtr, "&");
-        if (substrPtr[j] == '\0')
-        {
-            break;
-        }
-        substrPtr += j + 1;
-    }
-
-    // we only decode the values in place, the keys could have '='s in them
-    // which will hose our ability to distinguish keys from values later
-    for (j = 0; j < i; j++)
-    {
-        substrPtr = qs_kv[j] + strcspn(qs_kv[j], "=&#");
-        if (substrPtr[0] == '&' || substrPtr[0] == '\0')
-        { // blank value: skip decoding
-            substrPtr[0] = '\0';
-        }
-        else
-        {
-            qsDecode(++substrPtr);
-        }
-    }
-
-#ifdef _qsSORTING
-// TODO: qsort qs_kv, using qs_strncmp() for the comparison
-#endif
-
-    return i;
-}
-
-inline int qsDecode(char* qs)
-{
-    int i = 0, j = 0;
-
-    while (BMCWEB_QS_ISQSCHR(qs[j]))
-    {
-        if (qs[j] == '+')
-        {
-            qs[i] = ' ';
-        }
-        else if (qs[j] == '%') // easier/safer than scanf
-        {
-            if (!BMCWEB_QS_ISHEX(qs[j + 1]) || !BMCWEB_QS_ISHEX(qs[j + 2]))
-            {
-                qs[i] = '\0';
-                return i;
-            }
-            qs[i] = static_cast<char>((BMCWEB_QS_HEX2DEC(qs[j + 1]) * 16) +
-                                      BMCWEB_QS_HEX2DEC(qs[j + 2]));
-            j += 2;
-        }
-        else
-        {
-            qs[i] = qs[j];
-        }
-        i++;
-        j++;
-    }
-    qs[i] = '\0';
-
-    return i;
-}
-
-inline char* qsK2v(const char* key, char* const* qs_kv, int qs_kv_size,
-                   int nth = 0)
-{
-    int i;
-    size_t keyLen, skip;
-
-    keyLen = strlen(key);
-
-#ifdef _qsSORTING
-// TODO: binary search for key in the sorted qs_kv
-#else  // _qsSORTING
-    for (i = 0; i < qs_kv_size; i++)
-    {
-        // we rely on the unambiguous '=' to find the value in our k/v pair
-        if (qsStrncmp(key, qs_kv[i], keyLen) == 0)
-        {
-            skip = strcspn(qs_kv[i], "=");
-            if (qs_kv[i][skip] == '=')
-            {
-                skip++;
-            }
-            // return (zero-char value) ? ptr to trailing '\0' : ptr to value
-            if (nth == 0)
-            {
-                return qs_kv[i] + skip;
-            }
-            else
-            {
-                --nth;
-            }
-        }
-    }
-#endif // _qsSORTING
-
-    return nullptr;
-}
-
-inline char* qsScanvalue(const char* key, const char* qs, char* val,
-                         size_t val_len)
-{
-    size_t i, keyLen;
-    const char* tmp;
-
-    // find the beginning of the k/v substrings
-    if ((tmp = strchr(qs, '?')) != nullptr)
-    {
-        qs = tmp + 1;
-    }
-
-    keyLen = strlen(key);
-    while (qs[0] != '#' && qs[0] != '\0')
-    {
-        if (qsStrncmp(key, qs, keyLen) == 0)
-        {
-            break;
-        }
-        qs += strcspn(qs, "&") + 1;
-    }
-
-    if (qs[0] == '\0')
-    {
-        return nullptr;
-    }
-
-    qs += strcspn(qs, "=&#");
-    if (qs[0] == '=')
-    {
-        qs++;
-        i = strcspn(qs, "&=#");
-        strncpy(val, qs, (val_len - 1) < (i + 1) ? (val_len - 1) : (i + 1));
-        qsDecode(val);
-    }
-    else
-    {
-        if (val_len > 0)
-        {
-            val[0] = '\0';
-        }
-    }
-
-    return val;
-}
-} // namespace crow
-// ----------------------------------------------------------------------------
-
-namespace crow
-{
-class QueryString
-{
-  public:
-    static const size_t maxKeyValuePairsCount = 256;
-
-    QueryString() = default;
-
-    QueryString(const QueryString& qs) : url(qs.url)
-    {
-        for (auto p : qs.keyValuePairs)
-        {
-            keyValuePairs.push_back(
-                const_cast<char*>(p - qs.url.c_str() + url.c_str()));
-        }
-    }
-
-    QueryString& operator=(const QueryString& qs)
-    {
-        if (this == &qs)
-        {
-            return *this;
-        }
-
-        url = qs.url;
-        keyValuePairs.clear();
-        for (auto p : qs.keyValuePairs)
-        {
-            keyValuePairs.push_back(
-                const_cast<char*>(p - qs.url.c_str() + url.c_str()));
-        }
-        return *this;
-    }
-
-    QueryString& operator=(QueryString&& qs)
-    {
-        keyValuePairs = std::move(qs.keyValuePairs);
-        auto* oldData = const_cast<char*>(qs.url.c_str());
-        url = std::move(qs.url);
-        for (auto& p : keyValuePairs)
-        {
-            p += const_cast<char*>(url.c_str()) - oldData;
-        }
-        return *this;
-    }
-
-    explicit QueryString(std::string newUrl) : url(std::move(newUrl))
-    {
-        if (url.empty())
-        {
-            return;
-        }
-
-        keyValuePairs.resize(maxKeyValuePairsCount);
-
-        size_t count =
-            qsParse(&url[0], &keyValuePairs[0], maxKeyValuePairsCount);
-        keyValuePairs.resize(count);
-    }
-
-    void clear()
-    {
-        keyValuePairs.clear();
-        url.clear();
-    }
-
-    friend std::ostream& operator<<(std::ostream& os, const QueryString& qs)
-    {
-        os << "[ ";
-        for (size_t i = 0; i < qs.keyValuePairs.size(); ++i)
-        {
-            if (i != 0u)
-            {
-                os << ", ";
-            }
-            os << qs.keyValuePairs[i];
-        }
-        os << " ]";
-        return os;
-    }
-
-    char* get(const std::string& name) const
-    {
-        char* ret = qsK2v(name.c_str(), keyValuePairs.data(),
-                          static_cast<int>(keyValuePairs.size()));
-        return ret;
-    }
-
-    std::vector<char*> getList(const std::string& name) const
-    {
-        std::vector<char*> ret;
-        std::string plus = name + "[]";
-        char* element = nullptr;
-
-        int count = 0;
-        while (true)
-        {
-            element = qsK2v(plus.c_str(), keyValuePairs.data(),
-                            static_cast<int>(keyValuePairs.size()), count++);
-            if (element == nullptr)
-            {
-                break;
-            }
-            ret.push_back(element);
-        }
-        return ret;
-    }
-
-  private:
-    std::string url;
-    std::vector<char*> keyValuePairs;
-};
-
-} // namespace crow
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
index d3ed416..4d9d4d9 100644
--- a/redfish-core/lib/event_service.hpp
+++ b/redfish-core/lib/event_service.hpp
@@ -471,13 +471,16 @@
         subValue->protocol = "Redfish";
         subValue->retryPolicy = "TerminateAfterRetries";
 
-        char* filters = req.urlParams.get("$filter");
-        if (filters == nullptr)
+        boost::urls::url_view::params_type::iterator it =
+            req.urlParams.find("$filter");
+        if (it == req.urlParams.end())
         {
             subValue->eventFormatType = "Event";
         }
+
         else
         {
+            std::string filters = it->value();
             // Reading from query params.
             bool status = readSSEQueryParams(
                 filters, subValue->eventFormatType, subValue->registryMsgIds,
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index c4d4e89..b572bbf 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -198,12 +198,14 @@
 static bool getSkipParam(crow::Response& res, const crow::Request& req,
                          uint64_t& skip)
 {
-    char* skipParam = req.urlParams.get("$skip");
-    if (skipParam != nullptr)
+    boost::urls::url_view::params_type::iterator it =
+        req.urlParams.find("$skip");
+    if (it != req.urlParams.end())
     {
+        std::string skipParam = it->value();
         char* ptr = nullptr;
-        skip = std::strtoul(skipParam, &ptr, 10);
-        if (*skipParam == '\0' || *ptr != '\0')
+        skip = std::strtoul(skipParam.c_str(), &ptr, 10);
+        if (skipParam.empty() || *ptr != '\0')
         {
 
             messages::queryParameterValueTypeError(res, std::string(skipParam),
@@ -218,12 +220,14 @@
 static bool getTopParam(crow::Response& res, const crow::Request& req,
                         uint64_t& top)
 {
-    char* topParam = req.urlParams.get("$top");
-    if (topParam != nullptr)
+    boost::urls::url_view::params_type::iterator it =
+        req.urlParams.find("$top");
+    if (it != req.urlParams.end())
     {
+        std::string topParam = it->value();
         char* ptr = nullptr;
-        top = std::strtoul(topParam, &ptr, 10);
-        if (*topParam == '\0' || *ptr != '\0')
+        top = std::strtoul(topParam.c_str(), &ptr, 10);
+        if (topParam.empty() || *ptr != '\0')
         {
             messages::queryParameterValueTypeError(res, std::string(topParam),
                                                    "$top");