--- /dev/null
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=speedtestcpp
+PKG_VERSION:=1.20.2
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=https://codeload.github.com/oskarirauta/speedtestcpp/tar.gz/v$(PKG_VERSION)?
+PKG_HASH:=7d5c85f1d9a46f7d8a3ac4261ef1f92e53c511430bae096f7ec6f12a33d38904
+
+PKG_MAINTAINER:=Oskari Rauta <oskari.rauta@gmail.com>
+PKG_LICENSE:=MIT
+PKG_LICENSE_FILES:=LICENSE
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/libspeedtestcpp
+ SECTION:=libs
+ CATEGORY:=Libraries
+ TITLE:=library for ookla's speedtest
+ DEPENDS:=+libcurl +libstdcpp
+ URL:=https://github.com/oskarirauta/speedtestcpp
+endef
+
+define Package/libspeedtestcpp/description
+ Shared library that provides support for ookla's speedtest
+endef
+
+define Package/speedtestcpp
+ SECTION:=net
+ CATEGORY:=Network
+ TITLE:=SpeedTest++
+ DEPENDS:=+libspeedtestcpp +libstdcpp
+ URL:=https://github.com/oskarirauta/speedtestcpp
+ PROVIDES:=speedtestpp
+endef
+
+define Package/speedtestcpp/description
+ Yet another unofficial speedtest.net client cli interface
+ forked from taganaka's SpeedTest with few improments and
+ lesser depends.
+endef
+
+TARGET_CXXFLAGS += --std=c++23 -fPIC
+
+define Build/Configure
+endef
+
+define Package/libspeedtestcpp/install
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_BUILD_DIR)/libspeedtestcpp.so* $(1)/usr/lib/
+endef
+
+define Package/speedtestcpp/install
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/speedtest $(1)/usr/bin/
+endef
+
+define Build/InstallDev
+ $(INSTALL_DIR) $(1)/usr/lib $(1)/usr/include/speedtest
+ $(CP) $(PKG_BUILD_DIR)/libspeedtestcpp.{so*,a} $(1)/usr/lib/
+ $(CP) $(PKG_BUILD_DIR)/include/speedtest/*.hpp $(1)/usr/include/speedtest/
+endef
+
+$(eval $(call BuildPackage,libspeedtestcpp))
+$(eval $(call BuildPackage,speedtestcpp))
+++ /dev/null
-From b7db370449cfe2b453c22b74a660701f78bd7ce3 Mon Sep 17 00:00:00 2001
-From: Anatolii Lapytskyi <ala@allunite.com>
-Date: Tue, 6 Sep 2022 15:59:27 +0200
-Subject: [PATCH] Remove dependency on libxml2
-
----
- CMakeLists.txt | 5 +-
- README.md | 5 +-
- SpeedTest.cpp | 136 ++++++++++++++-----------------------------------
- SpeedTest.h | 2 -
- 4 files changed, 41 insertions(+), 107 deletions(-)
-
---- a/CMakeLists.txt
-+++ b/CMakeLists.txt
-@@ -44,7 +44,6 @@ add_executable(SpeedTest ${SOURCE_FILES}
-
- INCLUDE (CheckIncludeFiles)
- find_package(CURL REQUIRED)
--find_package(LibXml2 REQUIRED)
-
- if (NOT (APPLE))
- find_package(OpenSSL REQUIRED)
-@@ -52,7 +51,7 @@ else()
- CHECK_INCLUDE_FILES("CommonCrypto/CommonDigest.h" HAVE_COMMON_DIGEST_H)
- endif()
-
--include_directories(${CURL_INCLUDE_DIRS} ${LIBXML2_INCLUDE_DIR})
--target_link_libraries(SpeedTest ${CURL_LIBRARIES} ${LIBXML2_LIBRARIES} -lpthread ${OPENSSL_LIBRARIES})
-+include_directories(${CURL_INCLUDE_DIRS})
-+target_link_libraries(SpeedTest ${CURL_LIBRARIES} -lpthread ${OPENSSL_LIBRARIES})
-
- install(TARGETS SpeedTest RUNTIME DESTINATION bin)
---- a/README.md
-+++ b/README.md
-@@ -26,7 +26,6 @@ It supports the new (undocumented) raw T
- 2. cmake
- 3. libcurl
- 4. libssl
--5. libxml2
-
- ### On Mac OS X
-
-@@ -40,7 +39,7 @@ $ make install
- ### On Ubuntu/Debian
-
- ```
--$ sudo apt-get install build-essential libcurl4-openssl-dev libxml2-dev libssl-dev cmake
-+$ sudo apt-get install build-essential libcurl4-openssl-dev libssl-dev cmake
- $ git clone https://github.com/taganaka/SpeedTest
- $ cd SpeedTest
- $ cmake -DCMAKE_BUILD_TYPE=Release .
-@@ -50,7 +49,7 @@ $ sudo make install
- ### On OpenSuse
-
- ```
--$ sudo zypper install cmake gcc-c++ libcurl-devel libxml2-devel libopenssl-devel git
-+$ sudo zypper install cmake gcc-c++ libcurl-devel libopenssl-devel git
- $ git clone https://github.com/taganaka/SpeedTest
- $ cd SpeedTest
- $ cmake -DCMAKE_BUILD_TYPE=Release .
---- a/SpeedTest.cpp
-+++ b/SpeedTest.cpp
-@@ -353,67 +353,16 @@ std::vector<std::string> SpeedTest::spli
-
- }
-
--ServerInfo SpeedTest::processServerXMLNode(xmlTextReaderPtr reader) {
--
-- auto name = xmlTextReaderConstName(reader);
-- auto nodeName = std::string((char*)name);
--
-- if (!name || nodeName != "server"){
-- return ServerInfo();
-- }
--
-- if (xmlTextReaderAttributeCount(reader) > 0){
-- auto info = ServerInfo();
-- auto server_url = xmlTextReaderGetAttribute(reader, BAD_CAST "url");
-- auto server_lat = xmlTextReaderGetAttribute(reader, BAD_CAST "lat");
-- auto server_lon = xmlTextReaderGetAttribute(reader, BAD_CAST "lon");
-- auto server_name = xmlTextReaderGetAttribute(reader, BAD_CAST "name");
-- auto server_county = xmlTextReaderGetAttribute(reader, BAD_CAST "country");
-- auto server_cc = xmlTextReaderGetAttribute(reader, BAD_CAST "cc");
-- auto server_host = xmlTextReaderGetAttribute(reader, BAD_CAST "host");
-- auto server_id = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
-- auto server_sponsor = xmlTextReaderGetAttribute(reader, BAD_CAST "sponsor");
--
-- if (server_name)
-- info.name.append((char*)server_name);
--
-- if (server_url)
-- info.url.append((char*)server_url);
--
-- if (server_county)
-- info.country.append((char*)server_county);
--
-- if (server_cc)
-- info.country_code.append((char*)server_cc);
--
-- if (server_host)
-- info.host.append((char*)server_host);
--
-- if (server_sponsor)
-- info.sponsor.append((char*)server_sponsor);
--
-- if (server_id)
-- info.id = std::atoi((char*)server_id);
--
-- if (server_lat)
-- info.lat = std::stof((char*)server_lat);
--
-- if (server_lon)
-- info.lon = std::stof((char*)server_lon);
--
-- xmlFree(server_url);
-- xmlFree(server_lat);
-- xmlFree(server_lon);
-- xmlFree(server_name);
-- xmlFree(server_county);
-- xmlFree(server_cc);
-- xmlFree(server_host);
-- xmlFree(server_id);
-- xmlFree(server_sponsor);
-- return info;
-- }
--
-- return ServerInfo();
-+std::string getAttributeValue(const std::string& data, const size_t offset, const size_t max_pos, const std::string& attribute_name) {
-+ size_t pos = data.find(attribute_name + "=\"", offset);
-+ if (pos == std::string::npos)
-+ return "";
-+ if (pos >= max_pos)
-+ return "";
-+ size_t value_pos = pos + attribute_name.length() + 2;
-+ size_t end = data.find("\"", value_pos);
-+ std::string s = data.substr(pos + attribute_name.length() + 2, end - value_pos);
-+ return s;
- }
-
- bool SpeedTest::fetchServers(const std::string& url, std::vector<ServerInfo>& target, int &http_code) {
-@@ -441,53 +390,42 @@ bool SpeedTest::fetchServers(const std::
- http_code = 200;
- }
-
-- size_t len = oss.str().length();
-- auto *xmlbuff = (char*)calloc(len + 1, sizeof(char));
-- if (!xmlbuff){
-- std::cerr << "Unable to calloc" << std::endl;
-+ IPInfo ipInfo;
-+ if (!SpeedTest::ipInfo(ipInfo)){
- curl_easy_cleanup(curl);
-+ std::cerr << "OOPS!" <<std::endl;
- return false;
- }
-- memcpy(xmlbuff, oss.str().c_str(), len);
-- oss.str("");
-
-- xmlTextReaderPtr reader = xmlReaderForMemory(xmlbuff, static_cast<int>(len), nullptr, nullptr, 0);
-+ std::string data = oss.str();
-
-- if (reader != nullptr) {
-- IPInfo ipInfo;
-- if (!SpeedTest::ipInfo(ipInfo)){
-- curl_easy_cleanup(curl);
-- free(xmlbuff);
-- xmlFreeTextReader(reader);
-- std::cerr << "OOPS!" <<std::endl;
-- return false;
-- }
-- auto ret = xmlTextReaderRead(reader);
-- while (ret == 1) {
-- ServerInfo info = processServerXMLNode(reader);
-- if (!info.url.empty()){
-- info.distance = harversine(std::make_pair(ipInfo.lat, ipInfo.lon), std::make_pair(info.lat, info.lon));
-- target.push_back(info);
-- }
-- ret = xmlTextReaderRead(reader);
-- }
-- xmlFreeTextReader(reader);
-- if (ret != 0) {
-- curl_easy_cleanup(curl);
-- free(xmlbuff);
-- std::cerr << "Failed to parse" << std::endl;
-- return false;
-+ const std::string server_tag_start = "<server ";
-+
-+ size_t server_tag_begin = data.find(server_tag_start);
-+ while (server_tag_begin != std::string::npos) {
-+ size_t server_tag_end = data.find("/>", server_tag_begin);
-+
-+ auto info = ServerInfo();
-+ info.name = getAttributeValue(data, server_tag_begin, server_tag_end, "name");
-+ info.url = getAttributeValue(data, server_tag_begin, server_tag_end, "url");
-+ info.country = getAttributeValue(data, server_tag_begin, server_tag_end, "country");
-+ info.country_code = getAttributeValue(data, server_tag_begin, server_tag_end, "cc");
-+ info.host = getAttributeValue(data, server_tag_begin, server_tag_end, "host");
-+ info.sponsor = getAttributeValue(data, server_tag_begin, server_tag_end, "sponsor");
-+ info.id = atoi(getAttributeValue(data, server_tag_begin, server_tag_end, "id").c_str());
-+ info.lat = std::stof(getAttributeValue(data, server_tag_begin, server_tag_end, "lat"));
-+ info.lon = std::stof(getAttributeValue(data, server_tag_begin, server_tag_end, "lon"));
-+
-+ if (!info.url.empty()){
-+ info.distance = harversine(std::make_pair(ipInfo.lat, ipInfo.lon), std::make_pair(info.lat, info.lon));
-+ target.push_back(info);
- }
-- } else {
-- std::cerr << "Unable to initialize xml parser" << std::endl;
-- curl_easy_cleanup(curl);
-- free(xmlbuff);
-- return false;
-+
-+ server_tag_begin = data.find(server_tag_start, server_tag_begin + 1);
- }
-
-+
- curl_easy_cleanup(curl);
-- free(xmlbuff);
-- xmlCleanupParser();
- std::sort(target.begin(), target.end(), [](const ServerInfo &a, const ServerInfo &b) -> bool {
- return a.distance < b.distance;
- });
---- a/SpeedTest.h
-+++ b/SpeedTest.h
-@@ -7,7 +7,6 @@
-
- #include "SpeedTestConfig.h"
- #include "SpeedTestClient.h"
--#include <libxml/xmlreader.h>
- #include <functional>
- #include <cmath>
- #include <curl/curl.h>
-@@ -50,7 +49,6 @@ private:
- const ServerInfo findBestServerWithin(const std::vector<ServerInfo>& serverList, long& latency, int sample_size = 5, std::function<void(bool)> cb = nullptr);
- static CURL* curl_setup(CURL* curl = nullptr);
- static size_t writeFunc(void* buf, size_t size, size_t nmemb, void* userp);
-- static ServerInfo processServerXMLNode(xmlTextReaderPtr reader);
- double execute(const ServerInfo &server, const TestConfig &config, const opFn &fnc, std::function<void(bool)> cb = nullptr);
- template <typename T>
- static T deg2rad(T n);