certificate: Parse URL with readUrlSegments in ReplaceCertificate

readUrlSegments both validates and reads segments from URL, using it is
way better than comparing the URL with boost::start_with now (which
violates common error #12). It also removes the last part that relies
on the incorrect assumption that the certificate id is always a number.

Also removes the boost::convert dependency as it is not longer needed.

Tested:
* Verified calling ReplaceCertificate with malformed CertificateUri
  gives ActionParameterNotSupported error.
* Replacing certificate at a valid but non-existing returns 404 Not
  found
* Replacing an existing certificate operates successfully.

Change-Id: I96ffe7060b3350ea9240ef64624246af0fdd3da0
Signed-off-by: Jiaqing Zhao <jiaqing.zhao@intel.com>
diff --git a/redfish-core/lib/certificate_service.hpp b/redfish-core/lib/certificate_service.hpp
index 53c72e6..6da91ff 100644
--- a/redfish-core/lib/certificate_service.hpp
+++ b/redfish-core/lib/certificate_service.hpp
@@ -1,8 +1,6 @@
 #pragma once
 
 #include <app.hpp>
-#include <boost/convert.hpp>
-#include <boost/convert/strtol.hpp>
 #include <boost/system/linux_error.hpp>
 #include <dbus_utility.hpp>
 #include <query.hpp>
@@ -86,30 +84,6 @@
         });
 } // requestRoutesCertificateService
 
-/**
- * @brief Find the ID specified in the URL
- * Finds the numbers specified after the last "/" in the URL and returns.
- * @param[in] path URL
- * @return -1 on failure and number on success
- */
-inline long getIDFromURL(const std::string_view url)
-{
-    std::size_t found = url.rfind('/');
-    if (found == std::string::npos)
-    {
-        return -1;
-    }
-
-    if ((found + 1) < url.length())
-    {
-        std::string_view str = url.substr(found + 1);
-
-        return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
-    }
-
-    return -1;
-}
-
 inline std::string getCertificateFromReqBody(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
     const crow::Request& req)
@@ -783,40 +757,47 @@
                 asyncResp->res, "ReplaceCertificate", "CertificateUri");
             return;
         }
+        BMCWEB_LOG_INFO << "Certificate URI to replace: " << certURI;
 
-        BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
-        long id = getIDFromURL(certURI);
-        if (id < 0)
+        boost::urls::result<boost::urls::url_view> parsedUrl =
+            boost::urls::parse_relative_ref(certURI);
+        if (!parsedUrl)
         {
             messages::actionParameterValueFormatError(asyncResp->res, certURI,
                                                       "CertificateUri",
                                                       "ReplaceCertificate");
             return;
         }
-        std::string objectPath;
+
+        std::string id;
+        sdbusplus::message::object_path objectPath;
         std::string name;
         std::string service;
-        if (certURI.starts_with(
-                "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
+        if (crow::utility::readUrlSegments(
+                *parsedUrl, "redfish", "v1", "Managers", "bmc",
+                "NetworkProtocol", "HTTPS", "Certificates", std::ref(id)))
         {
             objectPath =
-                std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
+                sdbusplus::message::object_path(certs::httpsObjectPath) / id;
             name = "HTTPS certificate";
             service = certs::httpsServiceName;
         }
-        else if (certURI.starts_with(
-                     "/redfish/v1/AccountService/LDAP/Certificates/"))
+        else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
+                                                "AccountService", "LDAP",
+                                                "Certificates", std::ref(id)))
         {
             objectPath =
-                std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
+                sdbusplus::message::object_path(certs::ldapObjectPath) / id;
             name = "LDAP certificate";
             service = certs::ldapServiceName;
         }
-        else if (certURI.starts_with(
-                     "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
+        else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
+                                                "Managers", "bmc", "Truststore",
+                                                "Certificates", std::ref(id)))
         {
-            objectPath = std::string(certs::authorityObjectPath) + "/" +
-                         std::to_string(id);
+            objectPath =
+                sdbusplus::message::object_path(certs::authorityObjectPath) /
+                id;
             name = "TrustStore certificate";
             service = certs::authorityServiceName;
         }
@@ -838,15 +819,14 @@
                 if (ec.value() ==
                     boost::system::linux_error::bad_request_descriptor)
                 {
-                    messages::resourceNotFound(asyncResp->res, name,
-                                               std::to_string(id));
+                    messages::resourceNotFound(asyncResp->res, name, id);
                     return;
                 }
                 messages::internalError(asyncResp->res);
                 return;
             }
-            getCertificateProperties(asyncResp, objectPath, service,
-                                     std::to_string(id), certURI, name);
+            getCertificateProperties(asyncResp, objectPath, service, id,
+                                     certURI, name);
             BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
                              << certFile->getCertFilePath();
             },