diff --git a/.gitmodules b/.gitmodules index fb0120f..e4ea8a8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -26,7 +26,7 @@ path = third-party/efsw url = https://github.com/SpartanJ/efsw branch = master -[submodule "third-party/cpr"] - path = third-party/cpr - url = https://github.com/whoshuu/cpr +[submodule "libcurl"] + path = third-party/curl + url = https://github.com/curl/curl branch = master diff --git a/CMakeLists.txt b/CMakeLists.txt index 550ede5..1a9b76f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,8 +88,14 @@ set(BUILD_TEST_APP OFF CACHE BOOL "" FORCE) set(EFSW_INSTALL OFF CACHE BOOL "" FORCE) add_subdirectory(third-party/efsw EXCLUDE_FROM_ALL) -set(CPR_BUILD_TESTS OFF CACHE BOOL "" FORCE) -set(CMAKE_USE_LIBSSH2 OFF CACHE BOOL "" FORCE) # For some reason, even when HTTP_ONLY is set to ON, libcurl will try to link to libssh2. -add_subdirectory(third-party/cpr EXCLUDE_FROM_ALL) +set(BUILD_CURL_EXE OFF CACHE BOOL "" FORCE) +set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) +set(ENABLE_UNICODE ON CACHE BOOL "" FORCE) +set(ENABLE_DEBUG ON CACHE BOOL "" FORCE) +set(ENABLE_THREADED_RESOLVER OFF CACHE BOOL "" FORCE) +set(HTTP_ONLY ON CACHE BOOL "" FORCE) +set(CURL_USE_SCHANNEL ON CACHE BOOL "" FORCE) +set(CURL_USE_LIBSSH2 OFF CACHE BOOL "" FORCE) # For some reason, even when HTTP_ONLY is set to ON, libcurl will try to link to libssh2. +add_subdirectory(third-party/curl EXCLUDE_FROM_ALL) add_subdirectory(src) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3a0531d..61fd23d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -191,6 +191,6 @@ target_link_libraries(MassBuilderSaveTool PRIVATE UESaveFile efsw zip - cpr::cpr + libcurl imm32 wtsapi32) diff --git a/src/SaveTool/SaveTool.cpp b/src/SaveTool/SaveTool.cpp index 4bff821..dffb8a2 100644 --- a/src/SaveTool/SaveTool.cpp +++ b/src/SaveTool/SaveTool.cpp @@ -33,7 +33,7 @@ #include #include -#include +#include #include #include @@ -151,9 +151,10 @@ SaveTool::SaveTool(const Arguments& arguments): break; } + curl_global_init(CURL_GLOBAL_DEFAULT); if(_checkUpdatesOnStartup) { - _updateThread = std::thread{[this]{ checkForUpdates(); }}; _queue.addToast(Toast::Type::Default, "Checking for updates..."_s); + _updateThread = std::thread{[this]{ checkForUpdates(); }}; } if(GL::Context::current().isExtensionSupported() && @@ -174,6 +175,9 @@ SaveTool::SaveTool(const Arguments& arguments): SaveTool::~SaveTool() { Utility::Debug{} << "===Perfoming cleanup==="; + Utility::Debug{} << "Shutting libcurl down..."; + curl_global_cleanup(); + SDL_RemoveTimer(_gameCheckTimerId); Utility::Debug{} << "Saving the configuration..."; @@ -316,20 +320,22 @@ void SaveTool::initEvent(SDL_Event& event) { void SaveTool::updateCheckEvent(SDL_Event& event) { _updateThread.join(); - cpr::Response r{std::move(*static_cast(event.user.data1))}; - delete static_cast(event.user.data1); - - if(r.elapsed > 10.0) { + if(event.user.code == CurlInitFailed) { + _queue.addToast(Toast::Type::Error, "Couldn't initialise libcurl. Update check aborted."_s); + return; + } + else if(event.user.code == CurlError) { + Containers::String error{static_cast(event.user.data2), CURL_ERROR_SIZE, nullptr}; + _queue.addToast(Toast::Type::Error, error, std::chrono::milliseconds{5000}); + _queue.addToast(Toast::Type::Error, static_cast(event.user.data1), std::chrono::milliseconds{5000}); + return; + } + else if(event.user.code == CurlTimeout) { _queue.addToast(Toast::Type::Error, "The request timed out."_s); return; } - - if(r.status_code == 0) { - _queue.addToast(Toast::Type::Error, "Seems like the connection was blocked.\nPlease check your firewall's settings."); - } - - if(r.status_code != 200) { - _queue.addToast(Toast::Type::Error, Utility::format("The request failed with error code {}:\n{}", r.status_code, r.reason.c_str())); + else if(event.user.code != 200) { + _queue.addToast(Toast::Type::Error, Utility::format("The request failed with error code {}", event.user.code)); return; } @@ -373,13 +379,13 @@ void SaveTool::updateCheckEvent(SDL_Event& event) { } } operator Containers::String() const { - return Utility::format("{}.{}.{}", major, minor, patch); + return Utility::format("{}.{}.{}{}", major, minor, patch, prerelease ? "-pre" : ""); } }; static const Version current_ver{SAVETOOL_VERSION}; - Containers::String response = r.text.c_str(); + Containers::String response{static_cast(event.user.data1), strlen(static_cast(event.user.data1)), nullptr}; auto components = response.split('\n'); Version latest_ver{components.front()}; @@ -890,12 +896,55 @@ void SaveTool::checkGameState() { } } +inline auto writeData(char* ptr, std::size_t size, std::size_t nmemb, Containers::String* buf)-> std::size_t { + if(!ptr || !buf) return 0; + (*buf) = Utility::format("{}{}", *buf, Containers::StringView{ptr, size * nmemb}); + return size * nmemb; +} + void SaveTool::checkForUpdates() { - cpr::Response r = cpr::Get(cpr::Url{"https://williamjcm.ovh/mbst/version"}, cpr::Timeout{10000}); + auto curl = curl_easy_init(); SDL_Event event; SDL_zero(event); event.type = _updateEventId; - event.user.data1 = new cpr::Response{std::move(r)}; + + if(!curl) { + event.user.code = CurlInitFailed; + } + + if(curl) { + Containers::String response_body{Containers::AllocatedInit, ""}; + Containers::String error_buffer{ValueInit, CURL_ERROR_SIZE * 2}; + + curl_easy_setopt(curl, CURLOPT_URL, "https://williamjcm.ovh/mbst/version"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeData); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_body); + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer.data()); + curl_easy_setopt(curl, CURLOPT_TCP_KEEPALIVE, 1L); + curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 10000L); + + auto code = curl_easy_perform(curl); + + if(code == CURLE_OK) { + long status = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); + event.user.code = Int(status); + event.user.data1 = response_body.release(); + } + else if(code == CURLE_OPERATION_TIMEDOUT) { + event.user.code = CurlTimeout; + } + else { + event.user.code = CurlError; + event.user.data1 = const_cast(curl_easy_strerror(code)); + event.user.data2 = Containers::String{error_buffer}.release(); + } + + curl_easy_cleanup(curl); + } + SDL_PushEvent(&event); } diff --git a/src/SaveTool/SaveTool.h b/src/SaveTool/SaveTool.h index 0c2a9c7..d281234 100644 --- a/src/SaveTool/SaveTool.h +++ b/src/SaveTool/SaveTool.h @@ -81,6 +81,12 @@ class SaveTool: public Platform::Sdl2Application, public efsw::FileWatchListener ProfileManagerFailure }; void initEvent(SDL_Event& event); + + enum UpdateCheckStatus : Int { + CurlInitFailed = 0, + CurlError = 1, + CurlTimeout = 2, + }; void updateCheckEvent(SDL_Event& event); enum FileEventType: Int { diff --git a/third-party/cpr b/third-party/cpr deleted file mode 160000 index eccea39..0000000 --- a/third-party/cpr +++ /dev/null @@ -1 +0,0 @@ -Subproject commit eccea39e4b112a088ae665eda5854cc366f86128 diff --git a/third-party/curl b/third-party/curl new file mode 160000 index 0000000..64db5c5 --- /dev/null +++ b/third-party/curl @@ -0,0 +1 @@ +Subproject commit 64db5c575d9c5536bd273a890f50777ad1ca7c13