WebService: Verify username and token (#2930)
* WebService: Verify username and token; Log errors in PostJson * Fixup: added docstrings to the functions * Webservice: Added Icons to the verification, imrpved error detection in cpr, fixup nits * fixup: fmt warning
This commit is contained in:
parent
255fd8768d
commit
28c726f205
Binary file not shown.
After Width: | Height: | Size: 451 B |
Binary file not shown.
After Width: | Height: | Size: 428 B |
|
@ -0,0 +1,6 @@
|
|||
<RCC>
|
||||
<qresource prefix="icons">
|
||||
<file>checked.png</file>
|
||||
<file>failed.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -162,6 +162,8 @@ void Config::ReadValues() {
|
|||
sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
|
||||
Settings::values.telemetry_endpoint_url = sdl2_config->Get(
|
||||
"WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry");
|
||||
Settings::values.verify_endpoint_url = sdl2_config->Get(
|
||||
"WebService", "verify_endpoint_url", "https://services.citra-emu.org/api/profile");
|
||||
Settings::values.citra_username = sdl2_config->Get("WebService", "citra_username", "");
|
||||
Settings::values.citra_token = sdl2_config->Get("WebService", "citra_token", "");
|
||||
}
|
||||
|
|
|
@ -185,6 +185,8 @@ gdbstub_port=24689
|
|||
enable_telemetry =
|
||||
# Endpoint URL for submitting telemetry data
|
||||
telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry
|
||||
# Endpoint URL to verify the username and token
|
||||
verify_endpoint_url = https://services.citra-emu.org/api/profile
|
||||
# Username and token for Citra Web Service
|
||||
# See https://services.citra-emu.org/ for more info
|
||||
citra_username =
|
||||
|
|
|
@ -79,6 +79,7 @@ set(UIS
|
|||
main.ui
|
||||
)
|
||||
|
||||
file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*)
|
||||
file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
|
||||
|
||||
create_directory_groups(${SRCS} ${HEADERS} ${UIS})
|
||||
|
@ -92,10 +93,10 @@ endif()
|
|||
if (APPLE)
|
||||
set(MACOSX_ICON "../../dist/citra.icns")
|
||||
set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES} ${MACOSX_ICON})
|
||||
add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${ICONS} ${THEMES} ${MACOSX_ICON})
|
||||
set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
|
||||
else()
|
||||
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES})
|
||||
add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${ICONS} ${THEMES})
|
||||
endif()
|
||||
target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
|
||||
target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets)
|
||||
|
|
|
@ -146,6 +146,10 @@ void Config::ReadValues() {
|
|||
qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry")
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.verify_endpoint_url =
|
||||
qt_config->value("verify_endpoint_url", "https://services.citra-emu.org/api/profile")
|
||||
.toString()
|
||||
.toStdString();
|
||||
Settings::values.citra_username = qt_config->value("citra_username").toString().toStdString();
|
||||
Settings::values.citra_token = qt_config->value("citra_token").toString().toStdString();
|
||||
qt_config->endGroup();
|
||||
|
@ -293,6 +297,8 @@ void Config::SaveValues() {
|
|||
qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry);
|
||||
qt_config->setValue("telemetry_endpoint_url",
|
||||
QString::fromStdString(Settings::values.telemetry_endpoint_url));
|
||||
qt_config->setValue("verify_endpoint_url",
|
||||
QString::fromStdString(Settings::values.verify_endpoint_url));
|
||||
qt_config->setValue("citra_username", QString::fromStdString(Settings::values.citra_username));
|
||||
qt_config->setValue("citra_token", QString::fromStdString(Settings::values.citra_token));
|
||||
qt_config->endGroup();
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QMessageBox>
|
||||
#include "citra_qt/configuration/configure_web.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
|
@ -11,7 +12,9 @@ ConfigureWeb::ConfigureWeb(QWidget* parent)
|
|||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) {
|
||||
ui->setupUi(this);
|
||||
connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this,
|
||||
&ConfigureWeb::refreshTelemetryID);
|
||||
&ConfigureWeb::RefreshTelemetryID);
|
||||
connect(ui->button_verify_login, &QPushButton::clicked, this, &ConfigureWeb::VerifyLogin);
|
||||
connect(this, &ConfigureWeb::LoginVerified, this, &ConfigureWeb::OnLoginVerified);
|
||||
|
||||
this->setConfiguration();
|
||||
}
|
||||
|
@ -34,19 +37,66 @@ void ConfigureWeb::setConfiguration() {
|
|||
ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry);
|
||||
ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username));
|
||||
ui->edit_token->setText(QString::fromStdString(Settings::values.citra_token));
|
||||
// Connect after setting the values, to avoid calling OnLoginChanged now
|
||||
connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
|
||||
connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
|
||||
ui->label_telemetry_id->setText("Telemetry ID: 0x" +
|
||||
QString::number(Core::GetTelemetryId(), 16).toUpper());
|
||||
user_verified = true;
|
||||
}
|
||||
|
||||
void ConfigureWeb::applyConfiguration() {
|
||||
Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked();
|
||||
if (user_verified) {
|
||||
Settings::values.citra_username = ui->edit_username->text().toStdString();
|
||||
Settings::values.citra_token = ui->edit_token->text().toStdString();
|
||||
} else {
|
||||
QMessageBox::warning(this, tr("Username and token not verfied"),
|
||||
tr("Username and token were not verified. The changes to your "
|
||||
"username and/or token have not been saved."));
|
||||
}
|
||||
Settings::Apply();
|
||||
}
|
||||
|
||||
void ConfigureWeb::refreshTelemetryID() {
|
||||
void ConfigureWeb::RefreshTelemetryID() {
|
||||
const u64 new_telemetry_id{Core::RegenerateTelemetryId()};
|
||||
ui->label_telemetry_id->setText("Telemetry ID: 0x" +
|
||||
QString::number(new_telemetry_id, 16).toUpper());
|
||||
}
|
||||
|
||||
void ConfigureWeb::OnLoginChanged() {
|
||||
if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) {
|
||||
user_verified = true;
|
||||
ui->label_username_verified->setPixmap(QPixmap(":/icons/checked.png"));
|
||||
ui->label_token_verified->setPixmap(QPixmap(":/icons/checked.png"));
|
||||
} else {
|
||||
user_verified = false;
|
||||
ui->label_username_verified->setPixmap(QPixmap(":/icons/failed.png"));
|
||||
ui->label_token_verified->setPixmap(QPixmap(":/icons/failed.png"));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureWeb::VerifyLogin() {
|
||||
verified =
|
||||
Core::VerifyLogin(ui->edit_username->text().toStdString(),
|
||||
ui->edit_token->text().toStdString(), [&]() { emit LoginVerified(); });
|
||||
ui->button_verify_login->setDisabled(true);
|
||||
ui->button_verify_login->setText(tr("Verifying"));
|
||||
}
|
||||
|
||||
void ConfigureWeb::OnLoginVerified() {
|
||||
ui->button_verify_login->setEnabled(true);
|
||||
ui->button_verify_login->setText(tr("Verify"));
|
||||
if (verified.get()) {
|
||||
user_verified = true;
|
||||
ui->label_username_verified->setPixmap(QPixmap(":/icons/checked.png"));
|
||||
ui->label_token_verified->setPixmap(QPixmap(":/icons/checked.png"));
|
||||
} else {
|
||||
ui->label_username_verified->setPixmap(QPixmap(":/icons/failed.png"));
|
||||
ui->label_token_verified->setPixmap(QPixmap(":/icons/failed.png"));
|
||||
QMessageBox::critical(
|
||||
this, tr("Verification failed"),
|
||||
tr("Verification failed. Check that you have entered your username and token "
|
||||
"correctly, and that your internet connection is working."));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <QWidget>
|
||||
|
||||
|
@ -21,10 +22,19 @@ public:
|
|||
void applyConfiguration();
|
||||
|
||||
public slots:
|
||||
void refreshTelemetryID();
|
||||
void RefreshTelemetryID();
|
||||
void OnLoginChanged();
|
||||
void VerifyLogin();
|
||||
void OnLoginVerified();
|
||||
|
||||
signals:
|
||||
void LoginVerified();
|
||||
|
||||
private:
|
||||
void setConfiguration();
|
||||
|
||||
bool user_verified = true;
|
||||
std::future<bool> verified;
|
||||
|
||||
std::unique_ptr<Ui::ConfigureWeb> ui;
|
||||
};
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
<width>926</width>
|
||||
<height>561</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -31,14 +31,30 @@
|
|||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayoutCitraUsername">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_username">
|
||||
<item row="2" column="3">
|
||||
<widget class="QPushButton" name="button_verify_login">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::RightToLeft</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Username: </string>
|
||||
<string>Verify</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="web_signup_link">
|
||||
<property name="text">
|
||||
<string>Sign up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="edit_username">
|
||||
<property name="maxLength">
|
||||
<number>36</number>
|
||||
|
@ -52,7 +68,22 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="1" column="4">
|
||||
<widget class="QLabel" name="label_token_verified">
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_username">
|
||||
<property name="text">
|
||||
<string>Username: </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QLabel" name="label_username_verified">
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="3">
|
||||
<widget class="QLineEdit" name="edit_token">
|
||||
<property name="maxLength">
|
||||
<number>36</number>
|
||||
|
@ -62,13 +93,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="web_signup_link">
|
||||
<property name="text">
|
||||
<string>Sign up</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="web_token_info_link">
|
||||
<property name="text">
|
||||
|
@ -76,6 +100,19 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
|
|
|
@ -133,6 +133,7 @@ struct Values {
|
|||
// WebService
|
||||
bool enable_telemetry;
|
||||
std::string telemetry_endpoint_url;
|
||||
std::string verify_endpoint_url;
|
||||
std::string citra_username;
|
||||
std::string citra_token;
|
||||
} extern values;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
#include "web_service/telemetry_json.h"
|
||||
#include "web_service/verify_login.h"
|
||||
#endif
|
||||
|
||||
namespace Core {
|
||||
|
@ -75,6 +76,17 @@ u64 RegenerateTelemetryId() {
|
|||
return new_telemetry_id;
|
||||
}
|
||||
|
||||
std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func) {
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
return WebService::VerifyLogin(username, token, Settings::values.verify_endpoint_url, func);
|
||||
#else
|
||||
return std::async(std::launch::async, [func{std::move(func)}]() {
|
||||
func();
|
||||
return false;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
TelemetrySession::TelemetrySession() {
|
||||
#ifdef ENABLE_WEB_SERVICE
|
||||
if (Settings::values.enable_telemetry) {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include "common/telemetry.h"
|
||||
|
||||
|
@ -47,4 +48,13 @@ u64 GetTelemetryId();
|
|||
*/
|
||||
u64 RegenerateTelemetryId();
|
||||
|
||||
/**
|
||||
* Verifies the username and token.
|
||||
* @param username Citra username to use for authentication.
|
||||
* @param token Citra token to use for authentication.
|
||||
* @param func A function that gets exectued when the verification is finished
|
||||
* @returns Future with bool indicating whether the verification succeeded
|
||||
*/
|
||||
std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func);
|
||||
|
||||
} // namespace Core
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
set(SRCS
|
||||
telemetry_json.cpp
|
||||
verify_login.cpp
|
||||
web_backend.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
telemetry_json.h
|
||||
verify_login.h
|
||||
web_backend.h
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <json.hpp>
|
||||
#include "web_service/verify_login.h"
|
||||
#include "web_service/web_backend.h"
|
||||
|
||||
namespace WebService {
|
||||
|
||||
std::future<bool> VerifyLogin(std::string& username, std::string& token,
|
||||
const std::string& endpoint_url, std::function<void()> func) {
|
||||
auto get_func = [func, username](const std::string& reply) -> bool {
|
||||
func();
|
||||
if (reply.empty())
|
||||
return false;
|
||||
nlohmann::json json = nlohmann::json::parse(reply);
|
||||
std::string result;
|
||||
try {
|
||||
result = json["username"];
|
||||
} catch (const nlohmann::detail::out_of_range&) {
|
||||
}
|
||||
return result == username;
|
||||
};
|
||||
return GetJson<bool>(get_func, endpoint_url, false, username, token);
|
||||
}
|
||||
|
||||
} // namespace WebService
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <string>
|
||||
|
||||
namespace WebService {
|
||||
|
||||
/**
|
||||
* Checks if username and token is valid
|
||||
* @param username Citra username to use for authentication.
|
||||
* @param token Citra token to use for authentication.
|
||||
* @param endpoint_url URL of the services.citra-emu.org endpoint.
|
||||
* @param func A function that gets exectued when the verification is finished
|
||||
* @returns Future with bool indicating whether the verification succeeded
|
||||
*/
|
||||
std::future<bool> VerifyLogin(std::string& username, std::string& token,
|
||||
const std::string& endpoint_url, std::function<void()> func);
|
||||
|
||||
} // namespace WebService
|
|
@ -18,6 +18,19 @@ static constexpr char API_VERSION[]{"1"};
|
|||
|
||||
static std::unique_ptr<cpr::Session> g_session;
|
||||
|
||||
void Win32WSAStartup() {
|
||||
#ifdef _WIN32
|
||||
// On Windows, CPR/libcurl does not properly initialize Winsock. The below code is used to
|
||||
// initialize Winsock globally, which fixes this problem. Without this, only the first CPR
|
||||
// session will properly be created, and subsequent ones will fail.
|
||||
WSADATA wsa_data;
|
||||
const int wsa_result{WSAStartup(MAKEWORD(2, 2), &wsa_data)};
|
||||
if (wsa_result) {
|
||||
LOG_CRITICAL(WebService, "WSAStartup failed: %d", wsa_result);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PostJson(const std::string& url, const std::string& data, bool allow_anonymous,
|
||||
const std::string& username, const std::string& token) {
|
||||
if (url.empty()) {
|
||||
|
@ -31,16 +44,7 @@ void PostJson(const std::string& url, const std::string& data, bool allow_anonym
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// On Windows, CPR/libcurl does not properly initialize Winsock. The below code is used to
|
||||
// initialize Winsock globally, which fixes this problem. Without this, only the first CPR
|
||||
// session will properly be created, and subsequent ones will fail.
|
||||
WSADATA wsa_data;
|
||||
const int wsa_result{WSAStartup(MAKEWORD(2, 2), &wsa_data)};
|
||||
if (wsa_result) {
|
||||
LOG_CRITICAL(WebService, "WSAStartup failed: %d", wsa_result);
|
||||
}
|
||||
#endif
|
||||
Win32WSAStartup();
|
||||
|
||||
// Built request header
|
||||
cpr::Header header;
|
||||
|
@ -56,8 +60,81 @@ void PostJson(const std::string& url, const std::string& data, bool allow_anonym
|
|||
}
|
||||
|
||||
// Post JSON asynchronously
|
||||
static cpr::AsyncResponse future;
|
||||
future = cpr::PostAsync(cpr::Url{url.c_str()}, cpr::Body{data.c_str()}, header);
|
||||
static std::future<void> future;
|
||||
future = cpr::PostCallback(
|
||||
[](cpr::Response r) {
|
||||
if (r.error) {
|
||||
LOG_ERROR(WebService, "POST returned cpr error: %u:%s",
|
||||
static_cast<u32>(r.error.code), r.error.message.c_str());
|
||||
return;
|
||||
}
|
||||
if (r.status_code >= 400) {
|
||||
LOG_ERROR(WebService, "POST returned error status code: %u", r.status_code);
|
||||
return;
|
||||
}
|
||||
if (r.header["content-type"].find("application/json") == std::string::npos) {
|
||||
LOG_ERROR(WebService, "POST returned wrong content: %s",
|
||||
r.header["content-type"].c_str());
|
||||
return;
|
||||
}
|
||||
},
|
||||
cpr::Url{url}, cpr::Body{data}, header);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::future<T> GetJson(std::function<T(const std::string&)> func, const std::string& url,
|
||||
bool allow_anonymous, const std::string& username,
|
||||
const std::string& token) {
|
||||
if (url.empty()) {
|
||||
LOG_ERROR(WebService, "URL is invalid");
|
||||
return std::async(std::launch::async, [func{std::move(func)}]() { return func(""); });
|
||||
}
|
||||
|
||||
const bool are_credentials_provided{!token.empty() && !username.empty()};
|
||||
if (!allow_anonymous && !are_credentials_provided) {
|
||||
LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
|
||||
return std::async(std::launch::async, [func{std::move(func)}]() { return func(""); });
|
||||
}
|
||||
|
||||
Win32WSAStartup();
|
||||
|
||||
// Built request header
|
||||
cpr::Header header;
|
||||
if (are_credentials_provided) {
|
||||
// Authenticated request if credentials are provided
|
||||
header = {{"Content-Type", "application/json"},
|
||||
{"x-username", username.c_str()},
|
||||
{"x-token", token.c_str()},
|
||||
{"api-version", API_VERSION}};
|
||||
} else {
|
||||
// Otherwise, anonymous request
|
||||
header = cpr::Header{{"Content-Type", "application/json"}, {"api-version", API_VERSION}};
|
||||
}
|
||||
|
||||
// Get JSON asynchronously
|
||||
return cpr::GetCallback(
|
||||
[func{std::move(func)}](cpr::Response r) {
|
||||
if (r.error) {
|
||||
LOG_ERROR(WebService, "GET returned cpr error: %u:%s",
|
||||
static_cast<u32>(r.error.code), r.error.message.c_str());
|
||||
return func("");
|
||||
}
|
||||
if (r.status_code >= 400) {
|
||||
LOG_ERROR(WebService, "GET returned error code: %u", r.status_code);
|
||||
return func("");
|
||||
}
|
||||
if (r.header["content-type"].find("application/json") == std::string::npos) {
|
||||
LOG_ERROR(WebService, "GET returned wrong content: %s",
|
||||
r.header["content-type"].c_str());
|
||||
return func("");
|
||||
}
|
||||
return func(r.text);
|
||||
},
|
||||
cpr::Url{url}, header);
|
||||
}
|
||||
|
||||
template std::future<bool> GetJson(std::function<bool(const std::string&)> func,
|
||||
const std::string& url, bool allow_anonymous,
|
||||
const std::string& username, const std::string& token);
|
||||
|
||||
} // namespace WebService
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
|
||||
|
@ -20,4 +22,18 @@ namespace WebService {
|
|||
void PostJson(const std::string& url, const std::string& data, bool allow_anonymous,
|
||||
const std::string& username = {}, const std::string& token = {});
|
||||
|
||||
/**
|
||||
* Gets JSON from services.citra-emu.org.
|
||||
* @param func A function that gets exectued when the json as a string is received
|
||||
* @param url URL of the services.citra-emu.org endpoint to post data to.
|
||||
* @param allow_anonymous If true, allow anonymous unauthenticated requests.
|
||||
* @param username Citra username to use for authentication.
|
||||
* @param token Citra token to use for authentication.
|
||||
* @return future that holds the return value T of the func
|
||||
*/
|
||||
template <typename T>
|
||||
std::future<T> GetJson(std::function<T(const std::string&)> func, const std::string& url,
|
||||
bool allow_anonymous, const std::string& username = {},
|
||||
const std::string& token = {});
|
||||
|
||||
} // namespace WebService
|
||||
|
|
Reference in New Issue