// MassBuilderSaveTool // Copyright (C) 2021-2022 Guillaume Jacquemin // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include #include #include #include "../Logger/Logger.h" #include "SaveTool.h" void SaveTool::updateCheckEvent(SDL_Event& event) { _updateThread.join(); if(event.user.code == CurlInitFailed) { _queue.addToast(Toast::Type::Error, "Couldn't initialise libcurl. Update check aborted."_s); LOG_ERROR("Couldn't initialise libcurl. Update check aborted."); 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}); LOG_ERROR_FORMAT("{}: {}", static_cast(event.user.data1), static_cast(event.user.data2)); return; } else if(event.user.code == CurlTimeout) { _queue.addToast(Toast::Type::Error, "The request timed out."_s); LOG_ERROR("The request timed out."); return; } else if(event.user.code != 200) { _queue.addToast(Toast::Type::Error, Utility::format("The request failed with error code {}.", event.user.code)); LOG_ERROR_FORMAT("The request failed with error code {}.", event.user.code); return; } struct Version { explicit Version(Containers::StringView str) { std::size_t start_point = 0; if(str[0] == 'v') { start_point++; } auto components = Containers::StringView{str.data() + start_point}.split('.'); major = std::strtol(components[0].data(), nullptr, 10); minor = std::strtol(components[1].data(), nullptr, 10); patch = std::strtol(components[2].data(), nullptr, 10); fullVersion = major * 10000 + minor * 100 + patch; if(str.hasSuffix("-pre")) { prerelease = true; } } Int fullVersion; Int major = 0; Int minor = 0; Int patch = 0; bool prerelease = false; bool operator==(const Version& other) const { return fullVersion == other.fullVersion && prerelease == other.prerelease; } bool operator>(const Version& other) const { if((fullVersion > other.fullVersion) || (fullVersion == other.fullVersion && !prerelease && other.prerelease)) { return true; } else { return false; } } explicit operator Containers::String() const { return Utility::format("{}.{}.{}{}", major, minor, patch, prerelease ? "-pre" : ""); } }; static const Version current_ver{SAVETOOL_VERSION}; auto str = static_cast(event.user.data1); Containers::String response{str, strlen(str), nullptr}; auto components = response.splitOnAnyWithoutEmptyParts("\r\n"); Version latest_ver{components.front()}; if(latest_ver > current_ver) { _queue.addToast(Toast::Type::Warning, "Your version is out of date.\nCheck the settings for more information."_s, std::chrono::milliseconds{5000}); _updateAvailable = true; _latestVersion = Containers::String{latest_ver}; _releaseLink = Utility::format("https://williamjcm.ovh/git/williamjcm/MassBuilderSaveTool/releases/tag/v{}", components.front()); _downloadLink = components.back(); } else if(latest_ver == current_ver || (current_ver > latest_ver && current_ver.prerelease)) { _queue.addToast(Toast::Type::Success, "The application is already up to date."_s); } else if(current_ver > latest_ver && !current_ver.prerelease) { _queue.addToast(Toast::Type::Warning, "Your version is more recent than the latest one in the repo. How???"_s); } } 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() { SDL_Event event; SDL_zero(event); event.type = _updateEventId; auto curl = curl_easy_init(); 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, 0L); 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); }