Add signup to qt launcher, and cleanup

This commit is contained in:
Ryan Huffman 2019-09-30 10:42:00 -07:00
parent 6db88dae3c
commit e81deff4e9
15 changed files with 575 additions and 153 deletions

View file

@ -127,8 +127,14 @@ set(src_files
src/LauncherState.cpp src/LauncherState.cpp
src/LauncherWindow.h src/LauncherWindow.h
src/LauncherWindow.cpp src/LauncherWindow.cpp
src/PathUtils.cpp src/LoginRequest.h
src/LoginRequest.cpp
src/SignupRequest.h
src/SignupRequest.cpp
src/UserSettingsRequest.h
src/UserSettingsRequest.cpp
src/PathUtils.h src/PathUtils.h
src/PathUtils.cpp
src/Unzipper.h src/Unzipper.h
src/Unzipper.cpp src/Unzipper.cpp
src/Helper.h src/Helper.h
@ -159,7 +165,7 @@ set(TARGET_NAME ${PROJECT_NAME})
set_packaging_parameters() set_packaging_parameters()
if (WIN32) if (WIN32)
add_executable(${PROJECT_NAME} ${src_files} build/resources.qrc) add_executable(${PROJECT_NAME} ${src_files} ${CMAKE_CURRENT_BINARY_DIR}/resources.qrc)
elseif (APPLE) elseif (APPLE)
set_target_properties(${this_target} PROPERTIES set_target_properties(${this_target} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in) MACOSX_BUNDLE_INFO_PLIST MacOSXBundleInfo.plist.in)

View file

@ -47,7 +47,7 @@ Item {
font.family: "Graphik" font.family: "Graphik"
font.pixelSize: 14 font.pixelSize: 14
color: "#C4C4C4" color: "#C4C4C4"
text: "Use the email address that you regisetered with." text: "Use the email address that you regisetered with. " + LauncherState.lastSignupError
anchors { anchors {
left: root.left left: root.left
leftMargin: root.marginLeft leftMargin: root.marginLeft
@ -57,7 +57,7 @@ Item {
} }
HFTextField { HFTextField {
id: organization id: email
width: 430 width: 430
height: 50 height: 50
font.family: "Graphik" font.family: "Graphik"
@ -83,7 +83,7 @@ Item {
color: "#7e8c81" color: "#7e8c81"
seperatorColor: Qt.rgba(1, 1, 1, 0.3) seperatorColor: Qt.rgba(1, 1, 1, 0.3)
anchors { anchors {
top: organization.bottom top: email.bottom
left: root.left left: root.left
leftMargin: root.marginLeft leftMargin: root.marginLeft
topMargin: 18 topMargin: 18
@ -157,7 +157,7 @@ Item {
topMargin: 21 topMargin: 21
} }
onClicked: LauncherState.login(username.text, passwordField.text) onClicked: LauncherState.signup(email.text, username.text, passwordField.text, displayName.text)
} }
@ -181,7 +181,7 @@ Item {
onClicked: { onClicked: {
console.log("clicked"); console.log("clicked");
root.parent.source = PathUtils.resourcePath("qml/Login.qml"); LauncherState.gotoLogin();
} }
} }
} }

View file

@ -109,7 +109,6 @@ Item {
placeholderText: "Display name" placeholderText: "Display name"
color: "#7E8C81" color: "#7E8C81"
seperatorColor: Qt.rgba(1, 1, 1, 0.3) seperatorColor: Qt.rgba(1, 1, 1, 0.3)
echoMode: TextInput.Password
anchors { anchors {
top: displayText.bottom top: displayText.bottom
horizontalCenter: instruction.horizontalCenter horizontalCenter: instruction.horizontalCenter
@ -132,7 +131,7 @@ Item {
topMargin: 25 topMargin: 25
} }
onClicked: LauncherState.login(username.text, password.text) onClicked: LauncherState.login(username.text, password.text, displayName.text)
} }
Text { Text {
@ -155,7 +154,7 @@ Item {
onClicked: { onClicked: {
console.log("clicked"); console.log("clicked");
root.parent.source = PathUtils.resourcePath("qml/Login.qml"); LauncherState.gotoSignup();
} }
} }
} }

View file

@ -25,3 +25,47 @@ void swapLaunchers(const QString& oldLauncherPath, const QString& newLauncherPat
exit(0); exit(0);
} }
} }
QString getHTTPUserAgent() {
#if defined(Q_OS_WIN)
return "HQLauncher/fixme (Windows)";
#elif defined(Q_OS_MACOS)
return "HQLauncher/fixme (MacOS)";
#else
#error Unsupported platform
#endif
}
const QString& getInterfaceSharedMemoryName() {
static const QString applicationName = "High Fidelity Interface - " + qgetenv("USERNAME");
return applicationName;
}
//#ifdef Q_OS_WIN
//#include <Windows.h>
//#include <tlhelp32.h>
//#include <wincrypt.h>
//#include <tlhelp32.h>
//#include <strsafe.h>
//#include <winhttp.h>
//
//bool isProcessRunning(const wchar_t *processName, int& processID) {
// bool exists = false;
// PROCESSENTRY32 entry;
// entry.dwSize = sizeof(PROCESSENTRY32);
//
// HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
//
// if (Process32First(snapshot, &entry)) {
// while (Process32Next(snapshot, &entry)) {
// if (!_wcsicmp(entry.szExeFile, processName)) {
// exists = true;
// processID = entry.th32ProcessID;
// break;
// }
// }
// }
// CloseHandle(snapshot);
// return exists;
//}
//#endif

View file

@ -1,6 +1,14 @@
#include <QString> #include <QString>
#include <string> #include <string>
//#define USE_STAGING
#ifdef USE_STAGING
const QString METAVERSE_API_DOMAIN{ "https://staging.highfidelity.com" };
#else
const QString METAVERSE_API_DOMAIN{ "https://metaverse.highfidelity.com" };
#endif
void launchClient(const QString& clientPath, const QString& homePath, const QString& defaultScriptOverride, void launchClient(const QString& clientPath, const QString& homePath, const QString& defaultScriptOverride,
const QString& displayName, const QString& contentCachePath, QString loginResponseToken = QString()); const QString& displayName, const QString& contentCachePath, QString loginResponseToken = QString());
@ -11,3 +19,11 @@ void swapLaunchers(const QString& oldLauncherPath = QString(), const QString& ne
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
bool replaceDirectory(const QString& orginalDirectory, const QString& newDirectory); bool replaceDirectory(const QString& orginalDirectory, const QString& newDirectory);
#endif #endif
QString getHTTPUserAgent();
const QString& getInterfaceSharedMemoryName();
//#ifdef Q_OS_WIN
//bool isProcessRunning(const wchar_t *processName, int& processID) {
//#endif

View file

@ -9,7 +9,7 @@ void launchClient(const QString& clientPath, const QString& homePath, const QStr
// TODO Fix parameters // TODO Fix parameters
QString params = "--url \"" + homePath + "\"" QString params = "--url \"" + homePath + "\""
+ " --setBookmark \"hqhome=" + homePath + "\"" + " --setBookmark \"hqhome=" + homePath + "\""
+ " --defaultScriptsOverride \"" + defaultScriptsPath + "\"" + " --defaultScriptsOverride \"file:///" + defaultScriptsPath + "\""
+ " --displayName \"" + displayName + "\"" + " --displayName \"" + displayName + "\""
+ " --cache \"" + contentCachePath + "\""; + " --cache \"" + contentCachePath + "\"";

View file

@ -30,8 +30,6 @@
#include <qregularexpression.h> #include <qregularexpression.h>
const QString METAVERSE_API_URL{ "https://metaverse.highfidelity.com" };
const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";
QString LauncherState::getContentCachePath() const { QString LauncherState::getContentCachePath() const {
return _launcherDirectory.filePath("cache"); return _launcherDirectory.filePath("cache");
@ -70,7 +68,7 @@ bool LatestBuilds::getBuild(QString tag, Build* outBuild) {
} }
static const std::array<QString, LauncherState::UIState::UI_STATE_NUM> QML_FILE_FOR_UI_STATE = static const std::array<QString, LauncherState::UIState::UI_STATE_NUM> QML_FILE_FOR_UI_STATE =
{ { "SplashScreen.qml", "qml/HFBase/CreateAccountBase.qml", "DisplayName.qml", { { "SplashScreen.qml", "qml/HFBase/CreateAccountBase.qml", "qml/HFBase/LoginBase.qml", "DisplayName.qml",
"qml/Download.qml", "qml/DownloadFinished.qml", "qml/HFBase/Error.qml" } }; "qml/Download.qml", "qml/DownloadFinished.qml", "qml/HFBase/Error.qml" } };
void LauncherState::ASSERT_STATE(LauncherState::ApplicationState state) { void LauncherState::ASSERT_STATE(LauncherState::ApplicationState state) {
@ -82,7 +80,7 @@ void LauncherState::ASSERT_STATE(LauncherState::ApplicationState state) {
} }
} }
void LauncherState::ASSERT_STATE(std::vector<LauncherState::ApplicationState> states) { void LauncherState::ASSERT_STATE(const std::vector<LauncherState::ApplicationState>& states) {
for (auto state : states) { for (auto state : states) {
if (_applicationState == state) { if (_applicationState == state) {
return; return;
@ -120,6 +118,9 @@ LauncherState::UIState LauncherState::getUIState() const {
case ApplicationState::WaitingForLogin: case ApplicationState::WaitingForLogin:
case ApplicationState::RequestingLogin: case ApplicationState::RequestingLogin:
return LOGIN_SCREEN; return LOGIN_SCREEN;
case ApplicationState::WaitingForSignup:
case ApplicationState::RequestingSignup:
return SIGNUP_SCREEN;
case ApplicationState::DownloadingClient: case ApplicationState::DownloadingClient:
case ApplicationState::InstallingClient: case ApplicationState::InstallingClient:
case ApplicationState::DownloadingContentCache: case ApplicationState::DownloadingContentCache:
@ -240,33 +241,11 @@ void LauncherState::getCurrentClientVersion() {
QProcess client; QProcess client;
QEventLoop loop; QEventLoop loop;
//connect(&client, &QProcess::errorOccurred, &loop, &QEventLoop::exit); connect(&client, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), &loop, &QEventLoop::exit, Qt::QueuedConnection);
connect(&client, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), &loop, &QEventLoop::exit); connect(&client, &QProcess::errorOccurred, &loop, &QEventLoop::exit, Qt::QueuedConnection);
/*
connect(&client, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), [&]() {
qDebug() << "Finished";
});
connect(&client, &QProcess::errorOccurred, [&](QProcess::ProcessError err) {
qDebug() << "Error occurred" << err << client.error();
});
connect(&client, &QProcess::started, [&]() {
qDebug() << "Started";
});
connect(&client, &QProcess::stateChanged, [&]() {
qDebug() << "State changed " << client.state();
});
*/
//qDebug() << "Starting client";
client.start(getClientExecutablePath(), { "--version" }); client.start(getClientExecutablePath(), { "--version" });
//qDebug() << "Started" << client.error(); loop.exec();
if (client.state() != QProcess::NotRunning) {
//qDebug() << "Starting loop";
loop.exec();
} else {
qDebug() << "Not waiting for client, there was an error starting it: " << client.error();
}
// TODO Handle errors // TODO Handle errors
auto output = client.readAllStandardOutput(); auto output = client.readAllStandardOutput();
@ -282,132 +261,140 @@ void LauncherState::getCurrentClientVersion() {
} }
qDebug() << "Current client version is: " << _currentClientVersion; qDebug() << "Current client version is: " << _currentClientVersion;
setApplicationState(ApplicationState::WaitingForLogin); setApplicationState(ApplicationState::WaitingForSignup);
} }
QString getUserAgent() {
#if defined(Q_OS_WIN) void LauncherState::gotoSignup() {
return "HQLauncher/fixme (Windows)"; if (_applicationState == ApplicationState::WaitingForLogin) {
#elif defined(Q_OS_MACOS) setApplicationState(ApplicationState::WaitingForSignup);
return "HQLauncher/fixme (MacOS)"; } else {
#else qDebug() << "Error, can't switch to signup page, current state is: " << _applicationState;
#error Unsupported platform }
#endif
} }
void LauncherState::login(QString username, QString password) { void LauncherState::gotoLogin() {
if (_applicationState == ApplicationState::WaitingForSignup) {
setApplicationState(ApplicationState::WaitingForLogin);
} else {
qDebug() << "Error, can't switch to signup page, current state is: " << _applicationState;
}
}
void LauncherState::signup(QString email, QString username, QString password, QString displayName) {
ASSERT_STATE(ApplicationState::WaitingForSignup);
_username = username;
_password = password;
setApplicationState(ApplicationState::RequestingSignup);
auto signupRequest = new SignupRequest();
_displayName = displayName;
{
_lastSignupError = SignupRequest::Error::None;
emit lastSignupErrorChanged();
}
QObject::connect(signupRequest, &SignupRequest::finished, this, [this, signupRequest] {
signupRequest->deleteLater();
_lastSignupError = signupRequest->getError();
emit lastSignupErrorChanged();
if (_lastSignupError != SignupRequest::Error::None) {
setApplicationStateError("Failed to sign up");
return;
}
setApplicationState(ApplicationState::RequestingLoginAfterSignup);
// After successfully signing up, attempt to login
auto loginRequest = new LoginRequest();
connect(loginRequest, &LoginRequest::finished, this, [this, loginRequest]() {
ASSERT_STATE(ApplicationState::RequestingLoginAfterSignup);
loginRequest->deleteLater();
auto err = loginRequest->getError();
if (err != LoginRequest::Error::None) {
setApplicationStateError("Failed to login");
return;
}
_loginResponse = loginRequest->getToken();
_loginTokenResponse = loginRequest->getRawToken();
requestSettings();
});
setApplicationState(ApplicationState::RequestingLoginAfterSignup);
loginRequest->send(_networkAccessManager, _username, _password);
});
signupRequest->send(_networkAccessManager, email, username, password);
}
void LauncherState::login(QString username, QString password, QString displayName) {
ASSERT_STATE(ApplicationState::WaitingForLogin); ASSERT_STATE(ApplicationState::WaitingForLogin);
setApplicationState(ApplicationState::RequestingLogin); setApplicationState(ApplicationState::RequestingLogin);
_displayName = displayName;
qDebug() << "Got login: " << username << password; qDebug() << "Got login: " << username << password;
auto request = new QNetworkRequest(QUrl(METAVERSE_API_URL + "/oauth/token")); auto request = new LoginRequest();
request->setHeader(QNetworkRequest::UserAgentHeader, getUserAgent()); connect(request, &LoginRequest::finished, this, [this, request]() {
request->setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); ASSERT_STATE(ApplicationState::RequestingLogin);
QUrlQuery query;
query.addQueryItem("grant_type", "password");
query.addQueryItem("username", username);
query.addQueryItem("password", password);
query.addQueryItem("scope", "owner");
auto reply = _networkAccessManager.post(*request, query.toString().toUtf8()); request->deleteLater();
QObject::connect(reply, &QNetworkReply::finished, this, &LauncherState::receivedLoginReply);
}
Q_INVOKABLE void LauncherState::receivedLoginReply() { auto err = request->getError();
ASSERT_STATE(ApplicationState::RequestingLogin); if (err != LoginRequest::Error::None) {
setApplicationStateError("Failed to login");
return;
}
// TODO Check for errors _loginResponse = request->getToken();
auto reply = static_cast<QNetworkReply*>(sender()); _loginTokenResponse = request->getRawToken();
if (reply->error()) { requestSettings();
setApplicationState(ApplicationState::UnexpectedError); });
return;
}
auto data = reply->readAll(); request->send(_networkAccessManager, username, password);
QJsonParseError parseError;
auto doc = QJsonDocument::fromJson(data, &parseError);
auto root = doc.object();
if (!root.contains("access_token")
|| !root.contains("token_type")
|| !root.contains("expires_in")
|| !root.contains("refresh_token")
|| !root.contains("scope")
|| !root.contains("created_at")) {
setApplicationState(ApplicationState::UnexpectedError);
return;
}
_loginResponse.accessToken = root["access_token"].toString();
_loginResponse.refreshToken = root["refresh_token"].toString();
_loginResponse.tokenType = root["token_type"].toString();
qDebug() << "Got response for login: " << data;
_loginTokenResponse = data;
requestSettings();
} }
void LauncherState::requestSettings() { void LauncherState::requestSettings() {
// TODO Request settings if already logged in // TODO Request settings if already logged in
qDebug() << "Requesting settings";
QUrl lockerURL = METAVERSE_API_URL; auto request = new UserSettingsRequest();
lockerURL.setPath("/api/v1/user/locker");
auto lockerRequest = new QNetworkRequest(lockerURL); connect(request, &UserSettingsRequest::finished, this, [this, request]() {
lockerRequest->setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); auto userSettings = request->getUserSettings();
lockerRequest->setHeader(QNetworkRequest::UserAgentHeader, getUserAgent()); if (userSettings.homeLocation.isEmpty()) {
lockerRequest->setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, QString("Bearer %1").arg(_loginResponse.accessToken).toUtf8()); _homeLocation = "hifi://hq";
_contentCacheURL = "";
QNetworkReply* lockerReply = _networkAccessManager.get(*lockerRequest); } else {
connect(lockerReply, &QNetworkReply::finished, this, &LauncherState::receivedSettingsReply); _homeLocation = userSettings.homeLocation;
}
void LauncherState::receivedSettingsReply() {
auto reply = static_cast<QNetworkReply*>(sender());
qDebug() << "Got reply: " << reply->error();
if (reply->error()) {
setApplicationState(ApplicationState::UnexpectedError);
return;
}
auto data = reply->readAll();
qDebug() << "Settings: " << data;
QJsonParseError parseError;
auto doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qDebug() << "Error parsing settings";
setApplicationStateError("Error retreiving settings");
return;
}
auto root = doc.object();
if (root["status"] != "success") {
qDebug() << "Status is not \"success\"";
setApplicationStateError("Error retreiving settings");
return;
}
_homeLocation = "hifi://hq";
if (root["data"].toObject().contains("home_location")) {
auto homeLocation = root["data"].toObject()["home_location"];
if (homeLocation.isString()) {
_homeLocation = homeLocation.toString();
auto host = QUrl(_homeLocation).host(); auto host = QUrl(_homeLocation).host();
_contentCacheURL = "http://orgs.highfidelity.com/host-content-cache/" + host + ".zip"; _contentCacheURL = "http://orgs.highfidelity.com/host-content-cache/" + host + ".zip";
qDebug() << "Home location is: " << _homeLocation;
qDebug() << "Content cache url is: " << _contentCacheURL;
} }
}
//qDebug() << "Home:" << _homeLocation << QUrl(_homeLocation).host(); qDebug() << "Home location is: " << _homeLocation;
qDebug() << "Content cache url is: " << _contentCacheURL;
downloadClient(); downloadClient();
});
request->send(_networkAccessManager, _loginResponse);
} }
void LauncherState::downloadClient() { void LauncherState::downloadClient() {
@ -683,10 +670,9 @@ void LauncherState::launchClient() {
defaultScriptsPath = installDirectory.filePath("interface.app/Contents/Resources/scripts/simplifiedUIBootstrapper.js"); defaultScriptsPath = installDirectory.filePath("interface.app/Contents/Resources/scripts/simplifiedUIBootstrapper.js");
#endif #endif
QString displayName = "fixMe";
QString contentCachePath = _launcherDirectory.filePath("cache"); QString contentCachePath = _launcherDirectory.filePath("cache");
::launchClient(clientPath, _homeLocation, QDir::toNativeSeparators(defaultScriptsPath), displayName, contentCachePath, _loginTokenResponse); ::launchClient(clientPath, _homeLocation, defaultScriptsPath, _displayName, contentCachePath, _loginTokenResponse);
} }
void LauncherState::setApplicationStateError(QString errorMessage) { void LauncherState::setApplicationStateError(QString errorMessage) {

View file

@ -7,6 +7,10 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QFile> #include <QFile>
#include "LoginRequest.h"
#include "SignupRequest.h"
#include "UserSettingsRequest.h"
struct Build { struct Build {
QString tag; QString tag;
int latestVersion; int latestVersion;
@ -22,18 +26,13 @@ struct LatestBuilds {
Build launcherBuild; Build launcherBuild;
}; };
struct LoginResponse {
QString accessToken;
QString tokenType;
QString refreshToken;
};
class LauncherState : public QObject { class LauncherState : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(UIState uiState READ getUIState NOTIFY uiStateChanged); Q_PROPERTY(UIState uiState READ getUIState NOTIFY uiStateChanged);
Q_PROPERTY(ApplicationState applicationState READ getApplicationState NOTIFY applicationStateChanged); Q_PROPERTY(ApplicationState applicationState READ getApplicationState NOTIFY applicationStateChanged);
Q_PROPERTY(float downloadProgress READ getDownloadProgress NOTIFY downloadProgressChanged); Q_PROPERTY(float downloadProgress READ getDownloadProgress NOTIFY downloadProgressChanged);
Q_PROPERTY(SignupRequest::Error lastSignupError MEMBER _lastSignupError NOTIFY lastSignupErrorChanged);
public: public:
LauncherState(); LauncherState();
@ -41,6 +40,7 @@ public:
enum UIState { enum UIState {
SPLASH_SCREEN = 0, SPLASH_SCREEN = 0,
SIGNUP_SCREEN,
LOGIN_SCREEN, LOGIN_SCREEN,
DISPLAY_NAME_SCREEN, DISPLAY_NAME_SCREEN,
DOWNLOAD_SCREEN, DOWNLOAD_SCREEN,
@ -60,6 +60,10 @@ public:
WaitingForLogin, WaitingForLogin,
RequestingLogin, RequestingLogin,
WaitingForSignup,
RequestingSignup,
RequestingLoginAfterSignup,
DownloadingClient, DownloadingClient,
DownloadingLauncher, DownloadingLauncher,
DownloadingContentCache, DownloadingContentCache,
@ -85,7 +89,7 @@ public:
Q_INVOKABLE QString getCurrentUISource() const; Q_INVOKABLE QString getCurrentUISource() const;
void ASSERT_STATE(LauncherState::ApplicationState state); void ASSERT_STATE(LauncherState::ApplicationState state);
void ASSERT_STATE(std::vector<LauncherState::ApplicationState> states); void ASSERT_STATE(const std::vector<LauncherState::ApplicationState>& states);
static void declareQML(); static void declareQML();
@ -98,17 +102,21 @@ public:
void setApplicationState(ApplicationState state); void setApplicationState(ApplicationState state);
ApplicationState getApplicationState() const; ApplicationState getApplicationState() const;
Q_INVOKABLE void gotoSignup();
Q_INVOKABLE void gotoLogin();
// Request builds // Request builds
void requestBuilds(); void requestBuilds();
Q_INVOKABLE void receivedBuildsReply(); Q_INVOKABLE void receivedBuildsReply();
// Signup
Q_INVOKABLE void signup(QString email, QString username, QString password, QString displayName);
// Login // Login
Q_INVOKABLE void login(QString username, QString password); Q_INVOKABLE void login(QString username, QString password, QString displayName);
Q_INVOKABLE void receivedLoginReply();
// Request Settings
void requestSettings(); void requestSettings();
Q_INVOKABLE void receivedSettingsReply();
// Launcher // Launcher
void downloadLauncher(); void downloadLauncher();
@ -132,6 +140,7 @@ signals:
void uiStateChanged(); void uiStateChanged();
void applicationStateChanged(); void applicationStateChanged();
void downloadProgressChanged(); void downloadProgressChanged();
void lastSignupErrorChanged();
private slots: private slots:
void clientDownloadComplete(); void clientDownloadComplete();
@ -154,8 +163,10 @@ private:
// Application State // Application State
ApplicationState _applicationState { ApplicationState::Init }; ApplicationState _applicationState { ApplicationState::Init };
LoginResponse _loginResponse; LoginToken _loginResponse;
LastLoginError _lastLoginError { NONE }; LastLoginError _lastLoginError { NONE };
SignupRequest::Error _lastSignupError{ SignupRequest::Error::None };
QString _displayName;
QString _applicationErrorMessage; QString _applicationErrorMessage;
QString _currentClientVersion; QString _currentClientVersion;
QString _buildTag { QString::null }; QString _buildTag { QString::null };
@ -166,5 +177,8 @@ private:
QFile _launcherZipFile; QFile _launcherZipFile;
QFile _contentZipFile; QFile _contentZipFile;
QString _username;
QString _password;
float _downloadProgress { 0 }; float _downloadProgress { 0 };
}; };

View file

@ -0,0 +1,73 @@
#include "LoginRequest.h"
#include "Helper.h"
#include <QUrlQuery>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
void LoginRequest::send(QNetworkAccessManager& nam, QString username, QString password) {
QNetworkRequest request(QUrl(METAVERSE_API_DOMAIN + "/oauth/token"));
request.setHeader(QNetworkRequest::UserAgentHeader, getHTTPUserAgent());
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QUrlQuery query;
query.addQueryItem("grant_type", "password");
query.addQueryItem("username", username);
query.addQueryItem("password", password);
query.addQueryItem("scope", "owner");
auto reply = nam.post(request, query.toString().toUtf8());
QObject::connect(reply, &QNetworkReply::finished, this, &LoginRequest::receivedResponse);
}
void LoginRequest::receivedResponse() {
_state = State::Finished;
auto reply = static_cast<QNetworkReply*>(sender());
if (reply->error()) {
qDebug() << "Error logging in: " << reply->readAll();
_error = Error::Unknown;
emit finished();
//setApplicationState(ApplicationState::UnexpectedError);
return;
}
auto data = reply->readAll();
QJsonParseError parseError;
auto doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qDebug() << "Error parsing response for login" << data;
_error = Error::BadResponse;
emit finished();
//setApplicationStateError("Failed to login");
return;
}
auto root = doc.object();
if (!root.contains("access_token")
|| !root.contains("token_type")
|| !root.contains("expires_in")
|| !root.contains("refresh_token")
|| !root.contains("scope")
|| !root.contains("created_at")) {
_error = Error::BadUsernameOrPassword;
emit finished();
return;
}
_token.accessToken = root["access_token"].toString();
_token.refreshToken = root["refresh_token"].toString();
_token.tokenType = root["token_type"].toString();
qDebug() << "Got response for login: " << data;
_rawLoginToken = data;
emit finished();
}

View file

@ -0,0 +1,49 @@
#pragma once
#include <QObject>
#include <QNetworkAccessManager>
struct LoginToken {
QString accessToken;
QString tokenType;
QString refreshToken;
};
class LoginRequest : public QObject {
Q_OBJECT
public:
enum class State {
Unsent,
Sending,
Finished
};
enum class Error {
None = 0,
Unknown,
BadResponse,
BadUsernameOrPassword
};
Q_ENUM(Error)
void send(QNetworkAccessManager& nam, QString username, QString password);
Error getError() const { return _error; }
// The token is only valid if the request has finished without error
QString getRawToken() const { return _rawLoginToken; }
LoginToken getToken() const { return _token; }
signals:
void finished();
private slots:
void receivedResponse();
private:
State _state { State::Unsent };
Error _error { Error::None };
QString _rawLoginToken;
LoginToken _token;
};

View file

@ -0,0 +1,79 @@
#include "SignupRequest.h"
#include "Helper.h"
#include <QUrlQuery>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
void SignupRequest::send(QNetworkAccessManager& nam, QString email, QString username, QString password) {
if (_state != State::Unsent) {
qDebug() << "Error: Trying to send signuprequest, but not unsent";
return;
}
_state = State::Sending;
QUrl signupURL { METAVERSE_API_DOMAIN };
signupURL.setPath("/api/v1/user/channel_user");
QNetworkRequest request(signupURL);
request.setHeader(QNetworkRequest::UserAgentHeader, getHTTPUserAgent());
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QUrlQuery query;
query.addQueryItem("email", email);
query.addQueryItem("username", username);
query.addQueryItem("password", password);
auto reply = nam.post(request, query.toString().toUtf8());
QObject::connect(reply, &QNetworkReply::finished, this, &SignupRequest::receivedResponse);
}
void SignupRequest::receivedResponse() {
_state = State::Finished;
auto reply = static_cast<QNetworkReply*>(sender());
if (reply->error() && reply->size() == 0) {
qDebug() << "Error signing up: " << reply->error() << reply->readAll();
_error = Error::Unknown;
emit finished();
return;
}
auto data = reply->readAll();
QJsonParseError parseError;
auto doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qDebug() << "Error parsing response for signup " << data;
_error = Error::Unknown;
emit finished();
return;
}
auto root = doc.object();
auto status = root["status"];
if (status.isString() && status.toString() != "success") {
auto error = root["data"].toString();
_error = Error::Unknown;
if (error == "no_such_email") {
_error = Error::NoSuchEmail;
} else if (error == "user_profile_already_completed") {
_error = Error::UserProfileAlreadyCompleted;
} else if (error == "bad_username") {
_error = Error::BadUsername;
} else if (error == "existing_username") {
_error = Error::ExistingUsername;
} else if (error == "bad_password") {
_error = Error::BadPassword;
}
}
emit finished();
}

View file

@ -0,0 +1,38 @@
#pragma once
#include <QObject>
#include <QNetworkAccessManager>
class SignupRequest : public QObject {
Q_OBJECT
public:
enum class State {
Unsent,
Sending,
Finished
};
enum class Error {
None = 0,
Unknown,
NoSuchEmail,
UserProfileAlreadyCompleted,
BadUsername,
ExistingUsername,
BadPassword,
};
Q_ENUM(Error)
void send(QNetworkAccessManager& nam, QString email, QString username, QString password);
Error getError() const { return _error; }
signals:
void finished();
private slots:
void receivedResponse();
private:
State _state { State::Unsent };
Error _error { Error::None };
};

View file

@ -0,0 +1,67 @@
#include "UserSettingsRequest.h"
#include "Helper.h"
#include <QUrlQuery>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
const QByteArray ACCESS_TOKEN_AUTHORIZATION_HEADER = "Authorization";
void UserSettingsRequest::send(QNetworkAccessManager& nam, const LoginToken& token) {
_state = State::Sending;
QUrl lockerURL = METAVERSE_API_DOMAIN;
lockerURL.setPath("/api/v1/user/locker");
QNetworkRequest lockerRequest(lockerURL);
lockerRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
lockerRequest.setHeader(QNetworkRequest::UserAgentHeader, getHTTPUserAgent());
lockerRequest.setRawHeader(ACCESS_TOKEN_AUTHORIZATION_HEADER, QString("Bearer %1").arg(token.accessToken).toUtf8());
QNetworkReply* lockerReply = nam.get(lockerRequest);
connect(lockerReply, &QNetworkReply::finished, this, &UserSettingsRequest::receivedResponse);
}
void UserSettingsRequest::receivedResponse() {
_state = State::Finished;
auto reply = static_cast<QNetworkReply*>(sender());
qDebug() << "Got reply: " << reply->error();
if (reply->error()) {
_error = Error::Unknown;
emit finished();
return;
}
auto data = reply->readAll();
qDebug() << "Settings: " << data;
QJsonParseError parseError;
auto doc = QJsonDocument::fromJson(data, &parseError);
if (parseError.error != QJsonParseError::NoError) {
qDebug() << "Error parsing settings";
_error = Error::Unknown;
emit finished();
return;
}
auto root = doc.object();
if (root["status"] != "success") {
qDebug() << "Status is not \"success\"";
_error = Error::Unknown;
emit finished();
return;
}
if (root["data"].toObject().contains("home_location")) {
auto homeLocation = root["data"].toObject()["home_location"];
if (homeLocation.isString()) {
_userSettings.homeLocation = homeLocation.toString();
}
}
emit finished();
}

View file

@ -0,0 +1,43 @@
#pragma once
#include <QObject>
#include <QNetworkAccessManager>
#include "LoginRequest.h"
struct UserSettings {
QString homeLocation{ QString::null };
};
class UserSettingsRequest : public QObject {
Q_OBJECT
public:
enum class State {
Unsent,
Sending,
Finished
};
enum class Error {
None = 0,
Unknown,
};
Q_ENUM(Error)
void send(QNetworkAccessManager& nam, const LoginToken& token);
Error getError() const { return _error; }
UserSettings getUserSettings() const { return _userSettings; }
signals:
void finished();
private slots:
void receivedResponse();
private:
State _state { State::Unsent };
Error _error { Error::None };
UserSettings _userSettings;
};

View file

@ -1,5 +1,7 @@
#include <QtPlugin> #include <QtPlugin>
#include <qsharedmemory.h>
#include "LauncherWindow.h" #include "LauncherWindow.h"
#include "Launcher.h" #include "Launcher.h"
#include <iostream> #include <iostream>
@ -19,7 +21,7 @@ Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin);
bool hasSuffix(const std::string path, const std::string suffix) { bool hasSuffix(const std::string& path, const std::string& suffix) {
if (path.substr(path.find_last_of(".") + 1) == suffix) { if (path.substr(path.find_last_of(".") + 1) == suffix) {
return true; return true;
} }
@ -61,6 +63,12 @@ int main(int argc, char *argv[]) {
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QCoreApplication::setOrganizationName(name); QCoreApplication::setOrganizationName(name);
#ifdef Q_OS_WIN
//QSharedMemory sharedMemory{ applicationName };
//instanceMightBeRunning = !sharedMemory.create(1, QSharedMemory::ReadOnly);
#endif
Launcher launcher(argc, argv); Launcher launcher(argc, argv);
return launcher.exec(); return launcher.exec();