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);