diff options
author | Dan Goodliffe <dan@randomdan.homeip.net> | 2025-09-04 20:13:20 +0100 |
---|---|---|
committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2025-09-04 20:13:20 +0100 |
commit | fdab57f966a3928852701a2e41d75499f8f828ac (patch) | |
tree | d6803666d981e6fc3bd9acc65d6a14e3a55af823 | |
parent | 421e9aa1f92b21eb1fc3a98b77a2a181afd7180e (diff) | |
download | webstat-fdab57f966a3928852701a2e41d75499f8f828ac.tar.bz2 webstat-fdab57f966a3928852701a2e41d75499f8f828ac.tar.xz webstat-fdab57f966a3928852701a2e41d75499f8f828ac.zip |
Add function to grab UA details from useragentstring.com
-rw-r--r-- | src/Jamfile.jam | 2 | ||||
-rw-r--r-- | src/uaLookup.cpp | 50 | ||||
-rw-r--r-- | src/uaLookup.hpp | 16 | ||||
-rw-r--r-- | test/test-ingest.cpp | 14 |
4 files changed, 81 insertions, 1 deletions
diff --git a/src/Jamfile.jam b/src/Jamfile.jam index e1d5ec5..9459dd8 100644 --- a/src/Jamfile.jam +++ b/src/Jamfile.jam @@ -1,4 +1,4 @@ -lib webstat : ingestor.cpp logTypes.cpp sql.cpp : +lib webstat : [ glob *.cpp : *_main.cpp ] : <include>. <library>..//adhocutil <library>..//dbppcore diff --git a/src/uaLookup.cpp b/src/uaLookup.cpp new file mode 100644 index 0000000..d62c313 --- /dev/null +++ b/src/uaLookup.cpp @@ -0,0 +1,50 @@ +#include "uaLookup.hpp" +#include "util.hpp" +#include <memory> +#include <string_view> + +namespace WebStat { + namespace { + size_t + stringAppend(const char * ptr, size_t size, size_t nmemb, std::string * result) + { + result->append(ptr, nmemb * size); + return nmemb * size; + } + + void + addForm(curl_mime * mime, const char * name, const std::string_view data) + { + auto part = curl_mime_addpart(mime); + curl_mime_data(part, data.data(), data.length()); + curl_mime_name(part, name); + }; + } + + std::string + getUserAgentDetail(const std::string_view uas, const char * baseUrl) + { + std::string result; + std::array<char, CURL_ERROR_SIZE> err {}; + std::unique_ptr<CURL, DeleteWith<&curl_easy_cleanup>> hnd {curl_easy_init()}; + curl_easy_setopt(hnd.get(), CURLOPT_URL, baseUrl); + curl_easy_setopt(hnd.get(), CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(hnd.get(), CURLOPT_USERAGENT, "WebStat/0; +https://git.randomdan.homeip.net/repo/webstat/"); + curl_easy_setopt(hnd.get(), CURLOPT_MAXREDIRS, 50L); + curl_easy_setopt(hnd.get(), CURLOPT_TCP_KEEPALIVE, 1L); + curl_easy_setopt(hnd.get(), CURLOPT_FAILONERROR, 1L); + curl_easy_setopt(hnd.get(), CURLOPT_ERRORBUFFER, err.data()); + curl_easy_setopt(hnd.get(), CURLOPT_WRITEDATA, &result); + curl_easy_setopt(hnd.get(), CURLOPT_WRITEFUNCTION, &stringAppend); + std::unique_ptr<curl_mime, DeleteWith<&curl_mime_free>> mime {curl_mime_init(hnd.get())}; + addForm(mime.get(), "uas", uas); + addForm(mime.get(), "getJSON", "all"); + curl_easy_setopt(hnd.get(), CURLOPT_MIMEPOST, mime.get()); + + if (CURLcode ret = curl_easy_perform(hnd.get()); ret != CURLE_OK) { + throw CurlError {ret, err.data()}; + } + + return result; + } +} diff --git a/src/uaLookup.hpp b/src/uaLookup.hpp new file mode 100644 index 0000000..5492f05 --- /dev/null +++ b/src/uaLookup.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include <curl/curl.h> +#include <stdexcept> +#include <string> + +namespace WebStat { + class CurlError : public std::runtime_error { + public: + explicit CurlError(CURLcode code, const char * msg) : std::runtime_error {msg}, code(code) { } + + CURLcode code; + }; + + std::string getUserAgentDetail(std::string_view uas, const char * baseUrl = "https://useragentstring.com"); +} diff --git a/test/test-ingest.cpp b/test/test-ingest.cpp index 5f29806..1a62b70 100644 --- a/test/test-ingest.cpp +++ b/test/test-ingest.cpp @@ -5,6 +5,7 @@ #include "test-util.hpp" #include <ingestor.hpp> +#include <uaLookup.hpp> namespace { using namespace WebStat; @@ -181,3 +182,16 @@ BOOST_DATA_TEST_CASE(StoreLogLine, WebStat::Ingestor {WebStat::getTestUtsName("test-hostname"), std::make_shared<MockDBPool>("webstat")}.ingestLogLine( DB::MockDatabase::openConnectionTo("webstat").get(), line); } + +BOOST_AUTO_TEST_CASE(FetchRealUserAgentDetail, *boost::unit_test::disabled()) +{ + const auto uaDetail = WebStat::getUserAgentDetail( + R"(Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36)"); + + BOOST_TEST_CONTEXT(uaDetail) { + BOOST_CHECK(uaDetail.starts_with("{")); + BOOST_CHECK(uaDetail.ends_with("}")); + BOOST_CHECK(uaDetail.contains(R"("agent_type":)")); + BOOST_CHECK(uaDetail.contains(R"("os_type":)")); + } +} |