summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Jamfile.jam2
-rw-r--r--src/uaLookup.cpp50
-rw-r--r--src/uaLookup.hpp16
-rw-r--r--test/test-ingest.cpp14
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":)"));
+ }
+}