summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/citra/config.cpp2
-rw-r--r--src/citra/default_ini.h2
-rw-r--r--src/citra_qt/CMakeLists.txt5
-rw-r--r--src/citra_qt/configuration/config.cpp6
-rw-r--r--src/citra_qt/configuration/configure_web.cpp58
-rw-r--r--src/citra_qt/configuration/configure_web.h12
-rw-r--r--src/citra_qt/configuration/configure_web.ui75
-rw-r--r--src/core/settings.h1
-rw-r--r--src/core/telemetry_session.cpp12
-rw-r--r--src/core/telemetry_session.h10
-rw-r--r--src/web_service/CMakeLists.txt2
-rw-r--r--src/web_service/verify_login.cpp28
-rw-r--r--src/web_service/verify_login.h24
-rw-r--r--src/web_service/web_backend.cpp101
-rw-r--r--src/web_service/web_backend.h16
15 files changed, 316 insertions, 38 deletions
diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index a48ef08c7..45c28ad09 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -162,6 +162,8 @@ void Config::ReadValues() {
162 sdl2_config->GetBoolean("WebService", "enable_telemetry", true); 162 sdl2_config->GetBoolean("WebService", "enable_telemetry", true);
163 Settings::values.telemetry_endpoint_url = sdl2_config->Get( 163 Settings::values.telemetry_endpoint_url = sdl2_config->Get(
164 "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry"); 164 "WebService", "telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry");
165 Settings::values.verify_endpoint_url = sdl2_config->Get(
166 "WebService", "verify_endpoint_url", "https://services.citra-emu.org/api/profile");
165 Settings::values.citra_username = sdl2_config->Get("WebService", "citra_username", ""); 167 Settings::values.citra_username = sdl2_config->Get("WebService", "citra_username", "");
166 Settings::values.citra_token = sdl2_config->Get("WebService", "citra_token", ""); 168 Settings::values.citra_token = sdl2_config->Get("WebService", "citra_token", "");
167} 169}
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 4b13a2e1b..59faf773f 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -185,6 +185,8 @@ gdbstub_port=24689
185enable_telemetry = 185enable_telemetry =
186# Endpoint URL for submitting telemetry data 186# Endpoint URL for submitting telemetry data
187telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry 187telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry
188# Endpoint URL to verify the username and token
189verify_endpoint_url = https://services.citra-emu.org/api/profile
188# Username and token for Citra Web Service 190# Username and token for Citra Web Service
189# See https://services.citra-emu.org/ for more info 191# See https://services.citra-emu.org/ for more info
190citra_username = 192citra_username =
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index e0a19fd9e..add7566c2 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -79,6 +79,7 @@ set(UIS
79 main.ui 79 main.ui
80 ) 80 )
81 81
82file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*)
82file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*) 83file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
83 84
84create_directory_groups(${SRCS} ${HEADERS} ${UIS}) 85create_directory_groups(${SRCS} ${HEADERS} ${UIS})
@@ -92,10 +93,10 @@ endif()
92if (APPLE) 93if (APPLE)
93 set(MACOSX_ICON "../../dist/citra.icns") 94 set(MACOSX_ICON "../../dist/citra.icns")
94 set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) 95 set_source_files_properties(${MACOSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
95 add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES} ${MACOSX_ICON}) 96 add_executable(citra-qt MACOSX_BUNDLE ${SRCS} ${HEADERS} ${UI_HDRS} ${ICONS} ${THEMES} ${MACOSX_ICON})
96 set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) 97 set_target_properties(citra-qt PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist)
97else() 98else()
98 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${THEMES}) 99 add_executable(citra-qt ${SRCS} ${HEADERS} ${UI_HDRS} ${ICONS} ${THEMES})
99endif() 100endif()
100target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core) 101target_link_libraries(citra-qt PRIVATE audio_core common core input_common network video_core)
101target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets) 102target_link_libraries(citra-qt PRIVATE Boost::boost glad nihstro-headers Qt5::OpenGL Qt5::Widgets)
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index ef114aad3..5261f4c4c 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -146,6 +146,10 @@ void Config::ReadValues() {
146 qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry") 146 qt_config->value("telemetry_endpoint_url", "https://services.citra-emu.org/api/telemetry")
147 .toString() 147 .toString()
148 .toStdString(); 148 .toStdString();
149 Settings::values.verify_endpoint_url =
150 qt_config->value("verify_endpoint_url", "https://services.citra-emu.org/api/profile")
151 .toString()
152 .toStdString();
149 Settings::values.citra_username = qt_config->value("citra_username").toString().toStdString(); 153 Settings::values.citra_username = qt_config->value("citra_username").toString().toStdString();
150 Settings::values.citra_token = qt_config->value("citra_token").toString().toStdString(); 154 Settings::values.citra_token = qt_config->value("citra_token").toString().toStdString();
151 qt_config->endGroup(); 155 qt_config->endGroup();
@@ -293,6 +297,8 @@ void Config::SaveValues() {
293 qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry); 297 qt_config->setValue("enable_telemetry", Settings::values.enable_telemetry);
294 qt_config->setValue("telemetry_endpoint_url", 298 qt_config->setValue("telemetry_endpoint_url",
295 QString::fromStdString(Settings::values.telemetry_endpoint_url)); 299 QString::fromStdString(Settings::values.telemetry_endpoint_url));
300 qt_config->setValue("verify_endpoint_url",
301 QString::fromStdString(Settings::values.verify_endpoint_url));
296 qt_config->setValue("citra_username", QString::fromStdString(Settings::values.citra_username)); 302 qt_config->setValue("citra_username", QString::fromStdString(Settings::values.citra_username));
297 qt_config->setValue("citra_token", QString::fromStdString(Settings::values.citra_token)); 303 qt_config->setValue("citra_token", QString::fromStdString(Settings::values.citra_token));
298 qt_config->endGroup(); 304 qt_config->endGroup();
diff --git a/src/citra_qt/configuration/configure_web.cpp b/src/citra_qt/configuration/configure_web.cpp
index 8715fb018..38ce19c0f 100644
--- a/src/citra_qt/configuration/configure_web.cpp
+++ b/src/citra_qt/configuration/configure_web.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <QMessageBox>
5#include "citra_qt/configuration/configure_web.h" 6#include "citra_qt/configuration/configure_web.h"
6#include "core/settings.h" 7#include "core/settings.h"
7#include "core/telemetry_session.h" 8#include "core/telemetry_session.h"
@@ -11,7 +12,9 @@ ConfigureWeb::ConfigureWeb(QWidget* parent)
11 : QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) { 12 : QWidget(parent), ui(std::make_unique<Ui::ConfigureWeb>()) {
12 ui->setupUi(this); 13 ui->setupUi(this);
13 connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this, 14 connect(ui->button_regenerate_telemetry_id, &QPushButton::clicked, this,
14 &ConfigureWeb::refreshTelemetryID); 15 &ConfigureWeb::RefreshTelemetryID);
16 connect(ui->button_verify_login, &QPushButton::clicked, this, &ConfigureWeb::VerifyLogin);
17 connect(this, &ConfigureWeb::LoginVerified, this, &ConfigureWeb::OnLoginVerified);
15 18
16 this->setConfiguration(); 19 this->setConfiguration();
17} 20}
@@ -34,19 +37,66 @@ void ConfigureWeb::setConfiguration() {
34 ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry); 37 ui->toggle_telemetry->setChecked(Settings::values.enable_telemetry);
35 ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username)); 38 ui->edit_username->setText(QString::fromStdString(Settings::values.citra_username));
36 ui->edit_token->setText(QString::fromStdString(Settings::values.citra_token)); 39 ui->edit_token->setText(QString::fromStdString(Settings::values.citra_token));
40 // Connect after setting the values, to avoid calling OnLoginChanged now
41 connect(ui->edit_token, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
42 connect(ui->edit_username, &QLineEdit::textChanged, this, &ConfigureWeb::OnLoginChanged);
37 ui->label_telemetry_id->setText("Telemetry ID: 0x" + 43 ui->label_telemetry_id->setText("Telemetry ID: 0x" +
38 QString::number(Core::GetTelemetryId(), 16).toUpper()); 44 QString::number(Core::GetTelemetryId(), 16).toUpper());
45 user_verified = true;
39} 46}
40 47
41void ConfigureWeb::applyConfiguration() { 48void ConfigureWeb::applyConfiguration() {
42 Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked(); 49 Settings::values.enable_telemetry = ui->toggle_telemetry->isChecked();
43 Settings::values.citra_username = ui->edit_username->text().toStdString(); 50 if (user_verified) {
44 Settings::values.citra_token = ui->edit_token->text().toStdString(); 51 Settings::values.citra_username = ui->edit_username->text().toStdString();
52 Settings::values.citra_token = ui->edit_token->text().toStdString();
53 } else {
54 QMessageBox::warning(this, tr("Username and token not verfied"),
55 tr("Username and token were not verified. The changes to your "
56 "username and/or token have not been saved."));
57 }
45 Settings::Apply(); 58 Settings::Apply();
46} 59}
47 60
48void ConfigureWeb::refreshTelemetryID() { 61void ConfigureWeb::RefreshTelemetryID() {
49 const u64 new_telemetry_id{Core::RegenerateTelemetryId()}; 62 const u64 new_telemetry_id{Core::RegenerateTelemetryId()};
50 ui->label_telemetry_id->setText("Telemetry ID: 0x" + 63 ui->label_telemetry_id->setText("Telemetry ID: 0x" +
51 QString::number(new_telemetry_id, 16).toUpper()); 64 QString::number(new_telemetry_id, 16).toUpper());
52} 65}
66
67void ConfigureWeb::OnLoginChanged() {
68 if (ui->edit_username->text().isEmpty() && ui->edit_token->text().isEmpty()) {
69 user_verified = true;
70 ui->label_username_verified->setPixmap(QPixmap(":/icons/checked.png"));
71 ui->label_token_verified->setPixmap(QPixmap(":/icons/checked.png"));
72 } else {
73 user_verified = false;
74 ui->label_username_verified->setPixmap(QPixmap(":/icons/failed.png"));
75 ui->label_token_verified->setPixmap(QPixmap(":/icons/failed.png"));
76 }
77}
78
79void ConfigureWeb::VerifyLogin() {
80 verified =
81 Core::VerifyLogin(ui->edit_username->text().toStdString(),
82 ui->edit_token->text().toStdString(), [&]() { emit LoginVerified(); });
83 ui->button_verify_login->setDisabled(true);
84 ui->button_verify_login->setText(tr("Verifying"));
85}
86
87void ConfigureWeb::OnLoginVerified() {
88 ui->button_verify_login->setEnabled(true);
89 ui->button_verify_login->setText(tr("Verify"));
90 if (verified.get()) {
91 user_verified = true;
92 ui->label_username_verified->setPixmap(QPixmap(":/icons/checked.png"));
93 ui->label_token_verified->setPixmap(QPixmap(":/icons/checked.png"));
94 } else {
95 ui->label_username_verified->setPixmap(QPixmap(":/icons/failed.png"));
96 ui->label_token_verified->setPixmap(QPixmap(":/icons/failed.png"));
97 QMessageBox::critical(
98 this, tr("Verification failed"),
99 tr("Verification failed. Check that you have entered your username and token "
100 "correctly, and that your internet connection is working."));
101 }
102}
diff --git a/src/citra_qt/configuration/configure_web.h b/src/citra_qt/configuration/configure_web.h
index 20bc254b9..ad2d58f6e 100644
--- a/src/citra_qt/configuration/configure_web.h
+++ b/src/citra_qt/configuration/configure_web.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <future>
7#include <memory> 8#include <memory>
8#include <QWidget> 9#include <QWidget>
9 10
@@ -21,10 +22,19 @@ public:
21 void applyConfiguration(); 22 void applyConfiguration();
22 23
23public slots: 24public slots:
24 void refreshTelemetryID(); 25 void RefreshTelemetryID();
26 void OnLoginChanged();
27 void VerifyLogin();
28 void OnLoginVerified();
29
30signals:
31 void LoginVerified();
25 32
26private: 33private:
27 void setConfiguration(); 34 void setConfiguration();
28 35
36 bool user_verified = true;
37 std::future<bool> verified;
38
29 std::unique_ptr<Ui::ConfigureWeb> ui; 39 std::unique_ptr<Ui::ConfigureWeb> ui;
30}; 40};
diff --git a/src/citra_qt/configuration/configure_web.ui b/src/citra_qt/configuration/configure_web.ui
index d8d283fad..dd996ab62 100644
--- a/src/citra_qt/configuration/configure_web.ui
+++ b/src/citra_qt/configuration/configure_web.ui
@@ -6,8 +6,8 @@
6 <rect> 6 <rect>
7 <x>0</x> 7 <x>0</x>
8 <y>0</y> 8 <y>0</y>
9 <width>400</width> 9 <width>926</width>
10 <height>300</height> 10 <height>561</height>
11 </rect> 11 </rect>
12 </property> 12 </property>
13 <property name="windowTitle"> 13 <property name="windowTitle">
@@ -31,14 +31,30 @@
31 </item> 31 </item>
32 <item> 32 <item>
33 <layout class="QGridLayout" name="gridLayoutCitraUsername"> 33 <layout class="QGridLayout" name="gridLayoutCitraUsername">
34 <item row="0" column="0"> 34 <item row="2" column="3">
35 <widget class="QLabel" name="label_username"> 35 <widget class="QPushButton" name="button_verify_login">
36 <property name="sizePolicy">
37 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
38 <horstretch>0</horstretch>
39 <verstretch>0</verstretch>
40 </sizepolicy>
41 </property>
42 <property name="layoutDirection">
43 <enum>Qt::RightToLeft</enum>
44 </property>
36 <property name="text"> 45 <property name="text">
37 <string>Username: </string> 46 <string>Verify</string>
38 </property> 47 </property>
39 </widget> 48 </widget>
40 </item> 49 </item>
41 <item row="0" column="1"> 50 <item row="2" column="0">
51 <widget class="QLabel" name="web_signup_link">
52 <property name="text">
53 <string>Sign up</string>
54 </property>
55 </widget>
56 </item>
57 <item row="0" column="1" colspan="3">
42 <widget class="QLineEdit" name="edit_username"> 58 <widget class="QLineEdit" name="edit_username">
43 <property name="maxLength"> 59 <property name="maxLength">
44 <number>36</number> 60 <number>36</number>
@@ -52,7 +68,22 @@
52 </property> 68 </property>
53 </widget> 69 </widget>
54 </item> 70 </item>
55 <item row="1" column="1"> 71 <item row="1" column="4">
72 <widget class="QLabel" name="label_token_verified">
73 </widget>
74 </item>
75 <item row="0" column="0">
76 <widget class="QLabel" name="label_username">
77 <property name="text">
78 <string>Username: </string>
79 </property>
80 </widget>
81 </item>
82 <item row="0" column="4">
83 <widget class="QLabel" name="label_username_verified">
84 </widget>
85 </item>
86 <item row="1" column="1" colspan="3">
56 <widget class="QLineEdit" name="edit_token"> 87 <widget class="QLineEdit" name="edit_token">
57 <property name="maxLength"> 88 <property name="maxLength">
58 <number>36</number> 89 <number>36</number>
@@ -62,13 +93,6 @@
62 </property> 93 </property>
63 </widget> 94 </widget>
64 </item> 95 </item>
65 <item row="2" column="0">
66 <widget class="QLabel" name="web_signup_link">
67 <property name="text">
68 <string>Sign up</string>
69 </property>
70 </widget>
71 </item>
72 <item row="2" column="1"> 96 <item row="2" column="1">
73 <widget class="QLabel" name="web_token_info_link"> 97 <widget class="QLabel" name="web_token_info_link">
74 <property name="text"> 98 <property name="text">
@@ -76,6 +100,19 @@
76 </property> 100 </property>
77 </widget> 101 </widget>
78 </item> 102 </item>
103 <item row="2" column="2">
104 <spacer name="horizontalSpacer">
105 <property name="orientation">
106 <enum>Qt::Horizontal</enum>
107 </property>
108 <property name="sizeHint" stdset="0">
109 <size>
110 <width>40</width>
111 <height>20</height>
112 </size>
113 </property>
114 </spacer>
115 </item>
79 </layout> 116 </layout>
80 </item> 117 </item>
81 </layout> 118 </layout>
@@ -105,17 +142,17 @@
105 <layout class="QGridLayout" name="gridLayoutTelemetryId"> 142 <layout class="QGridLayout" name="gridLayoutTelemetryId">
106 <item row="0" column="0"> 143 <item row="0" column="0">
107 <widget class="QLabel" name="label_telemetry_id"> 144 <widget class="QLabel" name="label_telemetry_id">
108 <property name="text"> 145 <property name="text">
109 <string>Telemetry ID:</string> 146 <string>Telemetry ID:</string>
110 </property> 147 </property>
111 </widget> 148 </widget>
112 </item> 149 </item>
113 <item row="0" column="1"> 150 <item row="0" column="1">
114 <widget class="QPushButton" name="button_regenerate_telemetry_id"> 151 <widget class="QPushButton" name="button_regenerate_telemetry_id">
115 <property name="sizePolicy"> 152 <property name="sizePolicy">
116 <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> 153 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
117 <horstretch>0</horstretch> 154 <horstretch>0</horstretch>
118 <verstretch>0</verstretch> 155 <verstretch>0</verstretch>
119 </sizepolicy> 156 </sizepolicy>
120 </property> 157 </property>
121 <property name="layoutDirection"> 158 <property name="layoutDirection">
diff --git a/src/core/settings.h b/src/core/settings.h
index 024f14666..8d78cb424 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -133,6 +133,7 @@ struct Values {
133 // WebService 133 // WebService
134 bool enable_telemetry; 134 bool enable_telemetry;
135 std::string telemetry_endpoint_url; 135 std::string telemetry_endpoint_url;
136 std::string verify_endpoint_url;
136 std::string citra_username; 137 std::string citra_username;
137 std::string citra_token; 138 std::string citra_token;
138} extern values; 139} extern values;
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index 104a16cc9..ca517ff44 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -15,6 +15,7 @@
15 15
16#ifdef ENABLE_WEB_SERVICE 16#ifdef ENABLE_WEB_SERVICE
17#include "web_service/telemetry_json.h" 17#include "web_service/telemetry_json.h"
18#include "web_service/verify_login.h"
18#endif 19#endif
19 20
20namespace Core { 21namespace Core {
@@ -75,6 +76,17 @@ u64 RegenerateTelemetryId() {
75 return new_telemetry_id; 76 return new_telemetry_id;
76} 77}
77 78
79std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func) {
80#ifdef ENABLE_WEB_SERVICE
81 return WebService::VerifyLogin(username, token, Settings::values.verify_endpoint_url, func);
82#else
83 return std::async(std::launch::async, [func{std::move(func)}]() {
84 func();
85 return false;
86 });
87#endif
88}
89
78TelemetrySession::TelemetrySession() { 90TelemetrySession::TelemetrySession() {
79#ifdef ENABLE_WEB_SERVICE 91#ifdef ENABLE_WEB_SERVICE
80 if (Settings::values.enable_telemetry) { 92 if (Settings::values.enable_telemetry) {
diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h
index 65613daae..550c6ea2d 100644
--- a/src/core/telemetry_session.h
+++ b/src/core/telemetry_session.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <future>
7#include <memory> 8#include <memory>
8#include "common/telemetry.h" 9#include "common/telemetry.h"
9 10
@@ -47,4 +48,13 @@ u64 GetTelemetryId();
47 */ 48 */
48u64 RegenerateTelemetryId(); 49u64 RegenerateTelemetryId();
49 50
51/**
52 * Verifies the username and token.
53 * @param username Citra username to use for authentication.
54 * @param token Citra token to use for authentication.
55 * @param func A function that gets exectued when the verification is finished
56 * @returns Future with bool indicating whether the verification succeeded
57 */
58std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func);
59
50} // namespace Core 60} // namespace Core
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 334d82a8a..c93811892 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -1,10 +1,12 @@
1set(SRCS 1set(SRCS
2 telemetry_json.cpp 2 telemetry_json.cpp
3 verify_login.cpp
3 web_backend.cpp 4 web_backend.cpp
4 ) 5 )
5 6
6set(HEADERS 7set(HEADERS
7 telemetry_json.h 8 telemetry_json.h
9 verify_login.h
8 web_backend.h 10 web_backend.h
9 ) 11 )
10 12
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
new file mode 100644
index 000000000..1bc3b5afe
--- /dev/null
+++ b/src/web_service/verify_login.cpp
@@ -0,0 +1,28 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <json.hpp>
6#include "web_service/verify_login.h"
7#include "web_service/web_backend.h"
8
9namespace WebService {
10
11std::future<bool> VerifyLogin(std::string& username, std::string& token,
12 const std::string& endpoint_url, std::function<void()> func) {
13 auto get_func = [func, username](const std::string& reply) -> bool {
14 func();
15 if (reply.empty())
16 return false;
17 nlohmann::json json = nlohmann::json::parse(reply);
18 std::string result;
19 try {
20 result = json["username"];
21 } catch (const nlohmann::detail::out_of_range&) {
22 }
23 return result == username;
24 };
25 return GetJson<bool>(get_func, endpoint_url, false, username, token);
26}
27
28} // namespace WebService
diff --git a/src/web_service/verify_login.h b/src/web_service/verify_login.h
new file mode 100644
index 000000000..303f5dbbc
--- /dev/null
+++ b/src/web_service/verify_login.h
@@ -0,0 +1,24 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8#include <future>
9#include <string>
10
11namespace WebService {
12
13/**
14 * Checks if username and token is valid
15 * @param username Citra username to use for authentication.
16 * @param token Citra token to use for authentication.
17 * @param endpoint_url URL of the services.citra-emu.org endpoint.
18 * @param func A function that gets exectued when the verification is finished
19 * @returns Future with bool indicating whether the verification succeeded
20 */
21std::future<bool> VerifyLogin(std::string& username, std::string& token,
22 const std::string& endpoint_url, std::function<void()> func);
23
24} // namespace WebService
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index d28a3f757..b17d82f9c 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -18,6 +18,19 @@ static constexpr char API_VERSION[]{"1"};
18 18
19static std::unique_ptr<cpr::Session> g_session; 19static std::unique_ptr<cpr::Session> g_session;
20 20
21void Win32WSAStartup() {
22#ifdef _WIN32
23 // On Windows, CPR/libcurl does not properly initialize Winsock. The below code is used to
24 // initialize Winsock globally, which fixes this problem. Without this, only the first CPR
25 // session will properly be created, and subsequent ones will fail.
26 WSADATA wsa_data;
27 const int wsa_result{WSAStartup(MAKEWORD(2, 2), &wsa_data)};
28 if (wsa_result) {
29 LOG_CRITICAL(WebService, "WSAStartup failed: %d", wsa_result);
30 }
31#endif
32}
33
21void PostJson(const std::string& url, const std::string& data, bool allow_anonymous, 34void PostJson(const std::string& url, const std::string& data, bool allow_anonymous,
22 const std::string& username, const std::string& token) { 35 const std::string& username, const std::string& token) {
23 if (url.empty()) { 36 if (url.empty()) {
@@ -31,16 +44,7 @@ void PostJson(const std::string& url, const std::string& data, bool allow_anonym
31 return; 44 return;
32 } 45 }
33 46
34#ifdef _WIN32 47 Win32WSAStartup();
35 // On Windows, CPR/libcurl does not properly initialize Winsock. The below code is used to
36 // initialize Winsock globally, which fixes this problem. Without this, only the first CPR
37 // session will properly be created, and subsequent ones will fail.
38 WSADATA wsa_data;
39 const int wsa_result{WSAStartup(MAKEWORD(2, 2), &wsa_data)};
40 if (wsa_result) {
41 LOG_CRITICAL(WebService, "WSAStartup failed: %d", wsa_result);
42 }
43#endif
44 48
45 // Built request header 49 // Built request header
46 cpr::Header header; 50 cpr::Header header;
@@ -56,8 +60,81 @@ void PostJson(const std::string& url, const std::string& data, bool allow_anonym
56 } 60 }
57 61
58 // Post JSON asynchronously 62 // Post JSON asynchronously
59 static cpr::AsyncResponse future; 63 static std::future<void> future;
60 future = cpr::PostAsync(cpr::Url{url.c_str()}, cpr::Body{data.c_str()}, header); 64 future = cpr::PostCallback(
65 [](cpr::Response r) {
66 if (r.error) {
67 LOG_ERROR(WebService, "POST returned cpr error: %u:%s",
68 static_cast<u32>(r.error.code), r.error.message.c_str());
69 return;
70 }
71 if (r.status_code >= 400) {
72 LOG_ERROR(WebService, "POST returned error status code: %u", r.status_code);
73 return;
74 }
75 if (r.header["content-type"].find("application/json") == std::string::npos) {
76 LOG_ERROR(WebService, "POST returned wrong content: %s",
77 r.header["content-type"].c_str());
78 return;
79 }
80 },
81 cpr::Url{url}, cpr::Body{data}, header);
82}
83
84template <typename T>
85std::future<T> GetJson(std::function<T(const std::string&)> func, const std::string& url,
86 bool allow_anonymous, const std::string& username,
87 const std::string& token) {
88 if (url.empty()) {
89 LOG_ERROR(WebService, "URL is invalid");
90 return std::async(std::launch::async, [func{std::move(func)}]() { return func(""); });
91 }
92
93 const bool are_credentials_provided{!token.empty() && !username.empty()};
94 if (!allow_anonymous && !are_credentials_provided) {
95 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
96 return std::async(std::launch::async, [func{std::move(func)}]() { return func(""); });
97 }
98
99 Win32WSAStartup();
100
101 // Built request header
102 cpr::Header header;
103 if (are_credentials_provided) {
104 // Authenticated request if credentials are provided
105 header = {{"Content-Type", "application/json"},
106 {"x-username", username.c_str()},
107 {"x-token", token.c_str()},
108 {"api-version", API_VERSION}};
109 } else {
110 // Otherwise, anonymous request
111 header = cpr::Header{{"Content-Type", "application/json"}, {"api-version", API_VERSION}};
112 }
113
114 // Get JSON asynchronously
115 return cpr::GetCallback(
116 [func{std::move(func)}](cpr::Response r) {
117 if (r.error) {
118 LOG_ERROR(WebService, "GET returned cpr error: %u:%s",
119 static_cast<u32>(r.error.code), r.error.message.c_str());
120 return func("");
121 }
122 if (r.status_code >= 400) {
123 LOG_ERROR(WebService, "GET returned error code: %u", r.status_code);
124 return func("");
125 }
126 if (r.header["content-type"].find("application/json") == std::string::npos) {
127 LOG_ERROR(WebService, "GET returned wrong content: %s",
128 r.header["content-type"].c_str());
129 return func("");
130 }
131 return func(r.text);
132 },
133 cpr::Url{url}, header);
61} 134}
62 135
136template std::future<bool> GetJson(std::function<bool(const std::string&)> func,
137 const std::string& url, bool allow_anonymous,
138 const std::string& username, const std::string& token);
139
63} // namespace WebService 140} // namespace WebService
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index d17100398..a63c75d13 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional>
8#include <future>
7#include <string> 9#include <string>
8#include "common/common_types.h" 10#include "common/common_types.h"
9 11
@@ -20,4 +22,18 @@ namespace WebService {
20void PostJson(const std::string& url, const std::string& data, bool allow_anonymous, 22void PostJson(const std::string& url, const std::string& data, bool allow_anonymous,
21 const std::string& username = {}, const std::string& token = {}); 23 const std::string& username = {}, const std::string& token = {});
22 24
25/**
26 * Gets JSON from services.citra-emu.org.
27 * @param func A function that gets exectued when the json as a string is received
28 * @param url URL of the services.citra-emu.org endpoint to post data to.
29 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
30 * @param username Citra username to use for authentication.
31 * @param token Citra token to use for authentication.
32 * @return future that holds the return value T of the func
33 */
34template <typename T>
35std::future<T> GetJson(std::function<T(const std::string&)> func, const std::string& url,
36 bool allow_anonymous, const std::string& username = {},
37 const std::string& token = {});
38
23} // namespace WebService 39} // namespace WebService