vita3k update: move from curl and powershell to https.

- move openssl code inside https directory.
gui: reworks info msg dialog.
This commit is contained in:
Zangetsu38
2023-02-24 13:51:25 +01:00
committed by Zangetsu
parent 8744f03136
commit 6da03dbbbb
10 changed files with 385 additions and 328 deletions
+1
View File
@@ -126,6 +126,7 @@ add_subdirectory(ngs)
add_subdirectory(np)
add_subdirectory(emuenv)
add_subdirectory(http)
add_subdirectory(https)
add_subdirectory(io)
add_subdirectory(kernel)
add_subdirectory(mem)
+2 -2
View File
@@ -7,5 +7,5 @@ add_library(
)
target_include_directories(compat PUBLIC include)
target_link_libraries(compat PUBLIC gui emuenv)
target_link_libraries(compat PRIVATE pugixml::pugixml ssl)
target_link_libraries(compat PUBLIC gui https emuenv)
target_link_libraries(compat PRIVATE pugixml::pugixml)
+1 -1
View File
@@ -36,7 +36,7 @@ enum CompatibilityState {
struct Compatibility {
uint32_t issue_id;
CompatibilityState state = CompatibilityState::Unknown;
CompatibilityState state = Unknown;
time_t updated_at;
};
+15 -190
View File
@@ -15,24 +15,14 @@
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <boost/algorithm/string/trim.hpp>
#include <compat/functions.h>
#include <compat/state.h>
#ifdef WIN32
#include <io.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <sys/socket.h>
#endif
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <emuenv/state.h>
#include <gui/state.h>
#include <https/functions.h>
#include <pugixml.hpp>
enum LabelIdState {
@@ -70,7 +60,7 @@ bool load_compat_app_db(GuiState &gui, EmuEnvState &emuenv) {
const auto version = compatibility.attribute("version").as_uint();
if (db_version != version) {
LOG_WARN("Compatibility database version {} is outdated, download it again.", version);
return false;
return update_compat_app_db(gui, emuenv);
}
// Clear old compat database
@@ -115,183 +105,18 @@ bool load_compat_app_db(GuiState &gui, EmuEnvState &emuenv) {
return !gui.compat.app_compat_db.empty();
}
constexpr int READ_BUFFER_SIZE = 1048;
static std::string get_https_response(const std::string &url) {
#ifdef WIN32
WORD versionWanted = MAKEWORD(2, 2);
WSADATA wsaData;
WSAStartup(versionWanted, &wsaData);
#endif
// Initialize SSL
const auto ctx = SSL_CTX_new(SSLv23_client_method());
if (!ctx) {
LOG_ERROR("Error creating SSL context");
return {};
}
// Create socket
const auto sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
LOG_ERROR("ERROR opening socket: {}", sockfd);
return {};
}
// Parse URL to get host and uri
std::string host, uri;
size_t start = url.find("://");
if (start != std::string::npos) {
start += 3; // skip "://"
size_t end = url.find("/", start);
if (end != std::string::npos) {
host = url.substr(start, end - start);
uri = url.substr(end);
} else {
host = url.substr(start);
uri = "/";
}
}
// Set address info option
const addrinfo hints = {
AI_PASSIVE, /* For wildcard IP address */
AF_UNSPEC, /* Allow IPv4 or IPv6 */
SOCK_DGRAM, /* Datagram socket */
0, /* Any protocol */
};
addrinfo *result = { 0 };
// Get address info with using host and port
auto ret = getaddrinfo(host.c_str(), "https", &hints, &result);
if (ret < 0) {
LOG_ERROR("getaddrinfo = {}", ret);
return {};
}
// Connect to host
ret = connect(sockfd, result->ai_addr, static_cast<uint32_t>(result->ai_addrlen));
if (ret < 0) {
LOG_ERROR("connect({},...) = {}, errno={}({})", sockfd, ret, errno, strerror(errno));
return {};
}
// Create and connect SSL
SSL *ssl;
ssl = SSL_new(ctx);
SSL_set_fd(ssl, static_cast<uint32_t>(sockfd));
if (SSL_connect(ssl) <= 0) {
char err_buf[256];
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
LOG_ERROR("Error establishing SSL connection: {}", err_buf);
return {};
}
// Send HTTP GET request to extracted URI
std::string request = "GET " + uri + " HTTP/1.1\r\n";
request += "Host: " + host + "\r\n";
request += "User-Agent: OpenSSL/1.1.1\r\n";
request += "Connection: close\r\n\r\n";
if (SSL_write(ssl, request.c_str(), static_cast<uint32_t>(request.length())) <= 0) {
char err_buf[256];
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
LOG_ERROR("Error sending request: {}", err_buf);
return {};
}
std::array<char, READ_BUFFER_SIZE> read_buffer{};
std::string response;
while (auto bytes_read = SSL_read(ssl, read_buffer.data(), static_cast<uint32_t>(read_buffer.size()))) {
response += std::string(read_buffer.data(), bytes_read);
}
SSL_shutdown(ssl);
SSL_free(ssl);
SSL_CTX_free(ctx);
#ifdef WIN32
closesocket(sockfd);
WSACleanup();
#else
close(sockfd);
#endif // WIN32
boost::trim(response);
return response;
}
static std::string get_updated_at() {
const std::string latest_link = "https://api.github.com/repos/Vita3K/compatibility/releases/latest";
std::string updated_at;
// Get the response of last release from the GitHub API
const auto response = get_https_response(latest_link);
// Check if the response is not empty
if (!response.empty()) {
// Extract the content of the response
const std::string content = response.substr(response.find("\r\n\r\n") + 4);
// Get the Updated at of content
updated_at = content.substr(content.find("Updated at:") + 12, content.find_last_of("\r\n}") - content.find("Updated at:") - 13);
}
return updated_at;
}
static bool download_app_compat_db(const std::string &output_file_path) {
const std::string app_compat_db_link = "https://github.com/Vita3K/compatibility/releases/download/compat_db/app_compat_db.xml";
// Get the response of the app compat db
auto response = get_https_response(app_compat_db_link);
// Check if the response is not empty
if (!response.empty()) {
// Check if the response is a redirection
if (response.find("HTTP/1.1 302 Found") != std::string::npos) {
// Extract the redirection URL from the response
std::string location_header = "Location: ";
size_t location_index = response.find(location_header);
size_t end_of_location_index = response.find("\r\n", location_index + location_header.length());
const auto redirected_url = response.substr(location_index + location_header.length(), end_of_location_index - location_index - location_header.length());
// Get response of redirect url
response = get_https_response(redirected_url);
if (response.empty()) {
LOG_ERROR("Failed to get response on redirected url: {}", redirected_url);
return false;
}
}
// Get the XML content from the response
std::string xml = response.substr(response.find("\r\n\r\n") + 4);
// Create the xml output file
std::ofstream output_file(output_file_path, std::ios::binary);
if (!output_file.is_open()) {
LOG_ERROR("Failed to open output file: {}", output_file_path);
return false;
}
// Write the XML content to the output file
output_file.write(xml.c_str(), xml.length());
output_file.close();
return fs::exists(output_file_path);
}
return false;
}
static const std::string latest_link = "https://api.github.com/repos/Vita3K/compatibility/releases/latest";
static const std::string app_compat_db_link = "https://github.com/Vita3K/compatibility/releases/download/compat_db/app_compat_db.xml";
bool update_compat_app_db(GuiState &gui, EmuEnvState &emuenv) {
const auto compat_db_path = fs::path(emuenv.base_path) / "cache/app_compat_db.xml";
const auto app_compat_db_path = fs::path(emuenv.base_path) / "cache/app_compat_db.xml";
gui.info_message.function = SPDLOG_FUNCTION;
// Get current date of last issue updated
const auto updated_at = get_updated_at();
// Get current date of last compat database updated at
const auto updated_at = https::get_web_regex_result(latest_link, std::regex("Updated at: (\\d{2}-\\d{2}-\\d{4} \\d{2}:\\d{2}:\\d{2})"));
if (updated_at.empty()) {
gui.info_message.level = spdlog::level::err;
gui.info_message.msg = "Failed to get current compatibility database version, check firewall/internet access, try again later.";
gui.info_message.msg = "Failed to get current compatibility database updated at, check firewall/internet access, try again later.";
return false;
}
@@ -301,11 +126,11 @@ bool update_compat_app_db(GuiState &gui, EmuEnvState &emuenv) {
return false;
}
const auto compat_db_exist = fs::exists(compat_db_path);
const auto compat_db_exist = fs::exists(app_compat_db_path);
LOG_INFO("Applications compatibility database is {}, attempting to download latest updated at: {}", compat_db_exist ? "outdated" : "missing", updated_at);
if (!download_app_compat_db(compat_db_path.string())) {
if (!https::download_file(app_compat_db_link, app_compat_db_path.string())) {
gui.info_message.level = spdlog::level::err;
gui.info_message.msg = fmt::format("Failed to download Applications compatibility database updated at: {}", updated_at);
return false;
@@ -325,10 +150,10 @@ bool update_compat_app_db(GuiState &gui, EmuEnvState &emuenv) {
if (compat_db_exist) {
const auto dif = static_cast<int32_t>(gui.compat.app_compat_db.size() - old_compat_count);
if (dif > 0)
gui.info_message.msg = fmt::format("The compatibility database was successfully updated from {} to {}.\n\n{} new application(s) are listed!", old_db_updated_at, db_updated_at, dif);
if (!old_db_updated_at.empty() && dif > 0)
gui.info_message.msg = fmt::format("The compatibility database was successfully updated from:\n{} to {}.\n\n{} new application(s) are listed!", old_db_updated_at, db_updated_at, dif);
else
gui.info_message.msg = fmt::format("The compatibility database was successfully updated from {} to {}.\n\n{} applications are listed!", old_db_updated_at, db_updated_at, gui.compat.app_compat_db.size());
gui.info_message.msg = fmt::format("The compatibility database was successfully updated from:\n{} to {}.\n\n{} applications are listed!", old_db_updated_at, db_updated_at, gui.compat.app_compat_db.size());
} else
gui.info_message.msg = fmt::format("The compatibility database updated at {} has been successfully downloaded and loaded.\n\n{} applications are listed!", db_updated_at, gui.compat.app_compat_db.size());
+1 -1
View File
@@ -50,6 +50,6 @@ add_library(
)
target_include_directories(gui PUBLIC include ${CMAKE_SOURCE_DIR}/vita3k)
target_link_libraries(gui PUBLIC app compat config dialog emuenv ime imgui glutil lang np)
target_link_libraries(gui PUBLIC app compat config dialog emuenv https ime imgui glutil lang np)
target_link_libraries(gui PRIVATE audio ctrl kernel miniz psvpfsparser pugixml::pugixml stb renderer packages sdl2 vkutil host::dialog)
target_link_libraries(gui PUBLIC tracy)
+15 -8
View File
@@ -49,28 +49,35 @@ namespace gui {
void draw_info_message(GuiState &gui, EmuEnvState &emuenv) {
if (emuenv.cfg.display_info_message) {
const ImVec2 display_size(emuenv.viewport_size.x, emuenv.viewport_size.y);
const ImVec2 RES_SCALE(display_size.x / emuenv.res_width_dpi_scale, display_size.y / emuenv.res_height_dpi_scale);
const ImVec2 SCALE(RES_SCALE.x * emuenv.dpi_scale, RES_SCALE.y * emuenv.dpi_scale);
const ImVec2 WINDOW_SIZE(680.0f * SCALE.x, 320.0f * SCALE.y);
const ImVec2 BUTTON_SIZE(160.f * SCALE.x, 46.f * SCALE.y);
ImGui::SetNextWindowPos(ImVec2(emuenv.viewport_pos.x, emuenv.viewport_pos.x), ImGuiCond_Always);
ImGui::SetNextWindowSize(display_size, ImGuiCond_Always);
ImGui::Begin("##information", nullptr, ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDecoration);
ImGui::SetNextWindowPos(ImVec2(display_size.x / 2, display_size.y / 2), ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.f * emuenv.dpi_scale);
ImGui::BeginChild("##info", ImVec2(display_size.x / 1.4f, display_size.y / 2.6f), true, ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDecoration);
ImGui::SetNextWindowPos(ImVec2(emuenv.viewport_pos.x + (display_size.x / 2) - (WINDOW_SIZE.x / 2.f), emuenv.viewport_pos.y + (display_size.y / 2.f) - (WINDOW_SIZE.y / 2.f)), ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.f * SCALE.x);
ImGui::BeginChild("##info", WINDOW_SIZE, true, ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDecoration);
const auto title = fmt::format("{}", spdlog::level::to_string_view(gui.info_message.level));
ImGui::SetWindowFontScale(RES_SCALE.x);
ImGui::SetCursorPosX((ImGui::GetWindowWidth() - ImGui::CalcTextSize(title.c_str()).x) / 2);
ImGui::TextColored(GUI_COLOR_TEXT_TITLE, "%s", title.c_str());
ImGui::Spacing();
ImGui::Separator();
ImGui::Spacing();
const auto text_size = ImGui::CalcTextSize(gui.info_message.msg.c_str(), 0 , false, WINDOW_SIZE.x - (24.f * SCALE.x));
const auto text_pos = ImVec2((WINDOW_SIZE.x / 2.f) - (text_size.x / 2.f), (WINDOW_SIZE.y / 2.f) - (text_size.y / 2.f) - (24 * SCALE.y));
ImGui::SetCursorPos(text_pos);
ImGui::TextWrapped("%s", gui.info_message.msg.c_str());
ImGui::Spacing();
ImGui::SetCursorPosY(WINDOW_SIZE.y - BUTTON_SIZE.y - (42.0f * SCALE.y));
ImGui::Separator();
ImGui::Spacing();
const auto BUTTON_SIZE = ImVec2(160.f * emuenv.dpi_scale, 46.f * emuenv.dpi_scale);
ImGui::SetCursorPosX((ImGui::GetWindowWidth() / 2.f) - (BUTTON_SIZE.x / 2.f));
ImGui::SetCursorPos(ImVec2((ImGui::GetWindowWidth() / 2.f) - (BUTTON_SIZE.x / 2.f), WINDOW_SIZE.y - BUTTON_SIZE.y - (24.0f * SCALE.y)));
if (ImGui::Button("Ok", BUTTON_SIZE))
gui.info_message = {};
ImGui::EndChild();
ImGui::PopStyleVar();
ImGui::End();
} else {
+76 -126
View File
@@ -24,10 +24,9 @@
#include <SDL.h>
#include <boost/algorithm/string.hpp>
#ifdef __APPLE__
#include <pwd.h>
#include <unistd.h>
#endif
#include <https/functions.h>
#include <regex>
namespace gui {
@@ -46,42 +45,14 @@ static VitaAreaState vita_area_state;
static int git_version;
static std::vector<std::pair<std::string, std::string>> git_commit_desc_list;
bool init_vita3k_update(GuiState &gui) {
#ifndef __APPLE__
const std::string path = "vita3k-latest.zip";
#else
struct passwd *pwent = getpwuid(getuid());
const std::string path = fmt::format("{}/vita3k-latest.dmg", pwent->pw_dir);
#endif
if (fs::exists(path))
fs::remove(path);
state = NO_UPDATE;
git_commit_desc_list.clear();
git_version = 0;
vita_area_state = {};
const auto latest_link = "https://api.github.com/repos/Vita3K/Vita3K/releases/latest";
// Get Build number of latest release
const auto latest_link = "https://api.github.com/repos/Vita3K/Vita3K/releases/latest";
#ifdef WIN32
std::string power_shell_version;
std::getline(std::ifstream(_popen("powershell (Get-Host).Version.major", "r")), power_shell_version);
if (power_shell_version.empty() || !std::isdigit(power_shell_version[0]) || (std::stoi(power_shell_version) < 3)) {
LOG_WARN("You powershell version {} is outdated and incompatible with Vita3K Update, consider to update it", power_shell_version);
return false;
}
const auto github_version_cmd = fmt::format(R"(powershell ((Invoke-RestMethod {} -timeout 2).body.split([Environment]::NewLine) ^| Select-String -Pattern \"Vita3K Build: \") -replace \"Vita3K Build: \")", latest_link);
#else
const auto github_version_cmd = fmt::format(R"(curl -m 2 -sL {} | grep "Corresponding commit:" | cut -d " " -f 8 | grep -o '[[:digit:]]*')", latest_link);
#endif
char tmpver[L_tmpnam + 1];
std::tmpnam(tmpver);
std::string ver_cmd = github_version_cmd + " >> " + tmpver;
std::system(ver_cmd.c_str());
std::ifstream ver(tmpver, std::ios::in | std::ios::binary);
std::string version;
std::getline(ver, version);
ver.close();
std::remove(tmpver);
const auto version = https::get_web_regex_result(latest_link, std::regex("Vita3K Build: (\\d+)"));
if (!version.empty() && std::isdigit(version[0]))
git_version = std::stoi(version);
else {
@@ -102,66 +73,47 @@ bool init_vita3k_update(GuiState &gui) {
page_count.push_back({ page, per_page });
}
uint32_t commit_pos = 0;
// Browse all page
for (const auto &page : page_count) {
const auto continuous_link = fmt::format(R"(https://api.github.com/repos/Vita3K/Vita3K/commits?sha=continuous&page={}&per_page={})", page.first, dif_from_current < 100 ? dif_from_current : 100);
#ifdef WIN32
const auto github_commit_sha_cmd = fmt::format(R"(powershell ((Invoke-RestMethod \"{}\" -Timeout 2).sha ^| Select-Object -first {}))", continuous_link, page.second);
const auto github_commit_msg_cmd = fmt::format(R"(powershell ((Invoke-RestMethod \"{}\" -Timeout 2).commit.message ^| Select-Object -first {}) -replace(\"\r\n\", \"`n\"))", continuous_link, page.second);
#else
const auto sha_filter = R"(grep '"sha":' | sed 's/"/ /g' | awk -v n=3 'NR%n==1' | awk '{print $3}')";
const auto github_commit_sha_cmd = fmt::format(R"(curl -m 2 -sL "{}" | {} | head -n {})", continuous_link, sha_filter, page.second);
const auto github_commit_msg_cmd = fmt::format(R"(curl -m 2 -sL "{}" | grep '"message":' | cut -d '"' -f4 | sed 's/",/ /g' | head -n {})", continuous_link, page.second);
#endif
std::string line;
// Get Commits SHA
char tmpsha[L_tmpnam + 1];
std::tmpnam(tmpsha);
std::string sha_cmd = github_commit_sha_cmd + " >> " + tmpsha;
std::system(sha_cmd.c_str());
std::ifstream sha_list(tmpsha, std::ios::in | std::ios::binary);
while (std::getline(sha_list, line)) {
git_commit_desc_list.push_back({ line, {} });
}
sha_list.close();
std::remove(tmpsha);
// Get response from github api
auto response = https::get_web_response(continuous_link);
// Get Commits Message
char tmpmsg[L_tmpnam + 1];
std::tmpnam(tmpmsg);
std::string msg_cmd = github_commit_msg_cmd + " >> " + tmpmsg;
std::system(msg_cmd.c_str());
std::ifstream msg_list(tmpmsg, std::ios::in | std::ios::binary);
const auto push_commit = [&](const std::string commit) {
if (git_commit_desc_list.size() < commit_pos) {
LOG_WARN("Error of get commit description, abort");
return false;
// Check if response is not empty
if (!response.empty()) {
// Get commits from response with remove HTTP header
std::string commits = response.substr(response.find("\r\n\r\n") + 4);
std::string msg, sha;
std::smatch match;
// Replace \" to &quot; for help regex search message
boost::replace_all(commits, "\\\"", "&quot;");
// Using regex to get sha from commits
const std::regex commit_regex("\"sha\":\"([a-f0-9]{40})\".*?\"message\":\"([^\"]+)\"");
while (std::regex_search(commits, match, commit_regex)) {
// Get sha and message from regex match result
sha = match[1];
msg = match[2];
if (!sha.empty() && !msg.empty()) {
// Replace &quot; to \" for get back original message
boost::replace_all(msg, "&quot;", "\"");
// Replace \r and \n to new line
boost::replace_all(msg, "\\r\\n", "\n");
boost::replace_all(msg, "\\n", "\n");
// Add commit to list
git_commit_desc_list.push_back({ sha, msg });
}
// Remove current commit for next search
const std::regex end_commit(R"(\s*"parents":\[\{"sha":"[a-f0-9]{40}","url":"[^"]+","html_url":"[^"]+"\}\]\})");
if (std::regex_search(commits, match, end_commit))
commits = match.suffix();
}
git_commit_desc_list[commit_pos].second = commit;
++commit_pos;
return true;
};
std::string commit_msg;
while (std::getline(msg_list, line)) {
#ifdef WIN32
if (line.find(static_cast<char>('\r\n')) != std::string::npos) {
commit_msg += line;
if (!push_commit(commit_msg))
break;
commit_msg.clear();
} else
commit_msg += line + "\n";
#else
// Contrary to windows, each line is a commit
boost::replace_all(line, "\\n", "\n");
if (!push_commit(line))
break;
#endif
}
msg_list.close();
remove(tmpmsg);
}
});
get_commit_desc.detach();
@@ -180,48 +132,46 @@ bool init_vita3k_update(GuiState &gui) {
return has_update;
}
static void download_update() {
#ifndef __APPLE__
const std::string path = "vita3k-latest.zip";
#else
struct passwd *pwent = getpwuid(getuid());
const std::string path = fmt::format("{}/vita3k-latest.dmg", pwent->pw_dir);
#endif
if (!fs::exists(path)) {
std::thread download([path]() {
static void download_update(const std::string base_path) {
std::thread download([base_path]() {
std::string download_continuous_link = "https://github.com/Vita3K/Vita3K/releases/download/continuous";
#ifdef WIN32
const auto download_command = "powershell Invoke-WebRequest https://github.com/Vita3K/Vita3K/releases/download/continuous/windows-latest.zip -OutFile vita3k-latest.zip";
download_continuous_link += "/windows-latest.zip";
#elif defined(__APPLE__)
const auto download_command = "curl -L https://github.com/Vita3K/Vita3K/releases/download/continuous/macos-latest.dmg -o ~/vita3k-latest.dmg";
download_continuous_link += "/macos-latest.dmg";
#else
const auto download_command = "curl -L https://github.com/Vita3K/Vita3K/releases/download/continuous/ubuntu-latest.zip -o ./vita3k-latest.zip";
#endif
LOG_INFO("Attempting to download and extract the latest Vita3K version {} in progress...", git_version);
system(download_command);
if (fs::exists(path)) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
#ifdef WIN32
const auto vita3K_batch = "update-vita3k.bat";
#elif defined(__APPLE__)
char *base_path = SDL_GetBasePath();
std::string batch = fmt::format("sh {}/update-vita3k.sh {}/../../..", base_path, base_path);
const auto vita3K_batch = batch.c_str();
SDL_free(base_path);
#else
const auto vita3K_batch = "chmod +x ./update-vita3k.sh && ./update-vita3k.sh";
download_continuous_link += "/ubuntu-latest.zip";
#endif
std::system(vita3K_batch);
} else {
state = NOT_COMPLETE_UPDATE;
LOG_WARN("Download failed, please try again later.");
}
});
download.detach();
}
#ifdef __APPLE__
const std::string archive_ext = ".dmg";
#else
const std::string archive_ext = ".zip";
#endif
const auto vita3k_latest_path = base_path + "vita3k-latest" + archive_ext;
LOG_INFO("Attempting to download and extract the latest Vita3K version {} in progress...", git_version);
if (https::download_file(download_continuous_link, vita3k_latest_path)) {
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
#ifdef WIN32
const auto vita3K_batch = fmt::format("{}/update-vita3k.bat", base_path);
#elif defined(__APPLE__)
const auto vita3K_batch = fmt::format("sh {}/update-vita3k.sh", base_path);
#else
const auto vita3K_batch = fmt::format("chmod +x {}/update-vita3k.sh && {}/update-vita3k.sh", base_path, base_path);
#endif
std::system(vita3K_batch.c_str());
} else {
state = NOT_COMPLETE_UPDATE;
LOG_WARN("Download failed, please try again later.");
}
});
download.detach();
}
void draw_vita3k_update(GuiState &gui, EmuEnvState &emuenv) {
@@ -346,7 +296,7 @@ void draw_vita3k_update(GuiState &gui, EmuEnvState &emuenv) {
if (ImGui::Button(state < UPDATE_VITA3K ? lang["next"].c_str() : lang["update"].c_str(), BUTTON_SIZE) || ImGui::IsKeyPressed(emuenv.cfg.keyboard_button_circle)) {
state = (Vita3kUpdate)(state + 1);
if (state == DOWNLOAD)
download_update();
download_update(emuenv.base_path);
}
if (state == DOWNLOAD)
ImGui::EndDisabled();
+10
View File
@@ -0,0 +1,10 @@
add_library(
https
STATIC
include/https/functions.h
src/https.cpp
)
target_include_directories(https PUBLIC include)
target_include_directories(https PRIVATE ${OPENSSL_INCLUDE_DIR})
target_link_libraries(https PUBLIC util)
+29
View File
@@ -0,0 +1,29 @@
// Vita3K emulator project
// Copyright (C) 2023 Vita3K team
//
// 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 2 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, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#pragma once
#include <regex>
#include <string>
namespace https {
std::string get_web_response(const std::string url);
std::string get_web_regex_result(const std::string url, const std::regex regex);
bool download_file(const std::string url, std::string output_file_path);
} // namespace https
+235
View File
@@ -0,0 +1,235 @@
// Vita3K emulator project
// Copyright (C) 2023 Vita3K team
//
// 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 2 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, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#include <boost/algorithm/string/trim.hpp>
#include <https/functions.h>
#ifdef WIN32
#include <io.h>
#include <winsock2.h>
#include <ws2tcpip.h>
typedef SOCKET abs_socket;
#else
#include <netdb.h>
#include <sys/socket.h>
typedef int abs_socket;
#endif
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <util/log.h>
#include <array>
namespace https {
static void close_socket(const abs_socket sockfd) {
#ifdef WIN32
closesocket(sockfd);
WSACleanup();
#else
close(sockfd);
#endif // WIN32
}
static void close_ssl(SSL *ssl) {
SSL_shutdown(ssl);
SSL_free(ssl);
}
constexpr int READ_BUFFER_SIZE = 1048;
std::string get_web_response(const std::string url) {
#ifdef WIN32
WORD versionWanted = MAKEWORD(2, 2);
WSADATA wsaData;
WSAStartup(versionWanted, &wsaData);
#endif
// Initialize SSL
const auto ctx = SSL_CTX_new(SSLv23_client_method());
if (!ctx) {
LOG_ERROR("Error creating SSL context");
return {};
}
// Create socket
const auto sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
LOG_ERROR("ERROR opening socket: {}", sockfd);
SSL_CTX_free(ctx);
return {};
}
// Parse URL to get host and uri
std::string host, uri;
size_t start = url.find("://");
if (start != std::string::npos) {
start += 3; // skip "://"
size_t end = url.find("/", start);
if (end != std::string::npos) {
host = url.substr(start, end - start);
uri = url.substr(end);
} else {
host = url.substr(start);
uri = "/";
}
}
// Set address info option
const addrinfo hints = {
AI_PASSIVE, /* For wildcard IP address */
AF_UNSPEC, /* Allow IPv4 or IPv6 */
SOCK_DGRAM, /* Datagram socket */
0, /* Any protocol */
};
addrinfo *result = { 0 };
// Get address info with using host and port
auto ret = getaddrinfo(host.c_str(), "https", &hints, &result);
if (ret < 0) {
LOG_ERROR("getaddrinfo = {}", ret);
SSL_CTX_free(ctx);
close_socket(sockfd);
return {};
}
// Connect to host
ret = connect(sockfd, result->ai_addr, static_cast<uint32_t>(result->ai_addrlen));
if (ret < 0) {
LOG_ERROR("connect({},...) = {}, errno={}({})", sockfd, ret, errno, strerror(errno));
SSL_CTX_free(ctx);
close_socket(sockfd);
return {};
}
// Create and connect SSL
SSL *ssl;
ssl = SSL_new(ctx);
SSL_set_fd(ssl, static_cast<uint32_t>(sockfd));
if (SSL_connect(ssl) <= 0) {
char err_buf[256];
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
LOG_ERROR("Error establishing SSL connection: {}", err_buf);
SSL_CTX_free(ctx);
close_ssl(ssl);
close_socket(sockfd);
return {};
}
// Send HTTP GET request to extracted URI
std::string request = "GET " + uri + " HTTP/1.1\r\n";
request += "Host: " + host + "\r\n";
request += "User-Agent: OpenSSL/1.1.1\r\n";
request += "Connection: close\r\n\r\n";
if (SSL_write(ssl, request.c_str(), static_cast<uint32_t>(request.length())) <= 0) {
char err_buf[256];
ERR_error_string_n(ERR_get_error(), err_buf, sizeof(err_buf));
SSL_CTX_free(ctx);
close_ssl(ssl);
close_socket(sockfd);
LOG_ERROR("Error sending request: {}", err_buf);
return {};
}
std::array<char, READ_BUFFER_SIZE> read_buffer{};
std::string response;
while (auto bytes_read = SSL_read(ssl, read_buffer.data(), static_cast<uint32_t>(read_buffer.size()))) {
response += std::string(read_buffer.data(), bytes_read);
}
SSL_CTX_free(ctx);
close_ssl(ssl);
close_socket(sockfd);
boost::trim(response);
// Check if the response is resource not found
if (response.find("HTTP/1.1 404 Not Found") != std::string::npos) {
LOG_ERROR("404 Not Found");
return {};
}
return response;
}
std::string get_web_regex_result(const std::string url, const std::regex regex) {
std::string result;
// Get the response of the web
const auto response = https::get_web_response(url);
// Check if the response is not empty
if (!response.empty()) {
// Get the content of the response (without headers)
const std::string content = response.substr(response.find("\r\n\r\n") + 4);
std::smatch match;
// Check if the content matches the regex
if (std::regex_search(response, match, regex)) {
result = match[1];
} else
LOG_ERROR("No success found regex: {}", content);
}
return result;
}
bool download_file(const std::string url, const std::string output_file_path) {
// Get the response of the app compat db
auto response = get_web_response(url);
// Check if the response is not empty
if (!response.empty()) {
// Check if the response is a redirection
if (response.find("HTTP/1.1 302 Found") != std::string::npos) {
// Extract the redirection URL from the response
std::string location_header = "Location: ";
size_t location_index = response.find(location_header);
size_t end_of_location_index = response.find("\r\n", location_index + location_header.length());
const auto redirected_url = response.substr(location_index + location_header.length(), end_of_location_index - location_index - location_header.length());
// Get response of redirect url
response = get_web_response(redirected_url);
if (response.empty()) {
LOG_ERROR("Failed to get response on redirected url: {}", redirected_url);
return false;
}
}
// Get the content of the response
std::string content = response.substr(response.find("\r\n\r\n") + 4);
// Create the output file
std::ofstream output_file(output_file_path, std::ios::binary);
if (!output_file.is_open()) {
LOG_ERROR("Failed to open output file: {}", output_file_path);
return false;
}
// Write the content to the output file
output_file.write(content.c_str(), content.length());
output_file.close();
return fs::exists(output_file_path);
}
return false;
}
} // namespace https