diff --git a/.github/workflows/update-flake.yml b/.github/workflows/update-flake.yml index 4afe9150e..3293ea15f 100644 --- a/.github/workflows/update-flake.yml +++ b/.github/workflows/update-flake.yml @@ -12,7 +12,7 @@ permissions: jobs: update-flake: - if: github.repository == 'unmojang/Fjord Launcher' + if: github.repository == 'unmojang/FjordLauncher' runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index c521fba4d..b5523f685 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,6 @@ resources/CMakeFiles html/ # Project Files -*.orig *.pro.user CMakeLists.txt.user CMakeLists.txt.user.* diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in index caa759f80..747482b99 100644 --- a/buildconfig/BuildConfig.cpp.in +++ b/buildconfig/BuildConfig.cpp.in @@ -2,7 +2,6 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 Lenny McLennington * * 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 diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index e9890e184..f4f21b031 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -3,7 +3,6 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 Jamie Mansfield * Copyright (C) 2022 Sefa Eyeoglu - * Copyright (C) 2022 Lenny McLennington * Copyright (C) 2023 TheKodeToad * * This program is free software: you can redistribute it and/or modify diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index bd353edca..419f89b63 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -92,8 +92,9 @@ void LaunchController::decideAccount() if (accounts->count() <= 0 || !accounts->anyAccountIsValid()) { // Tell the user they need to log in at least one account in order to play. auto reply = CustomMessageBox::selectable(m_parentWidget, tr("No Accounts"), - tr("In order to play Minecraft, you must pick your username or log in to an account " - "Would you like to open the account manager to do this now?"), + tr("In order to play Minecraft, you must have at least one Microsoft " + "account which owns Minecraft logged in. " + "Would you like to open the account manager to add an account now?"), QMessageBox::Information, QMessageBox::Yes | QMessageBox::No) ->exec(); diff --git a/launcher/java/JavaCheckerJob.h b/launcher/java/JavaCheckerJob.h deleted file mode 100644 index 16b572632..000000000 --- a/launcher/java/JavaCheckerJob.h +++ /dev/null @@ -1,56 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "JavaChecker.h" -#include "tasks/Task.h" - -class JavaCheckerJob; -using JavaCheckerJobPtr = shared_qobject_ptr; - -// FIXME: this just seems horribly redundant -class JavaCheckerJob : public Task { - Q_OBJECT - public: - explicit JavaCheckerJob(QString job_name) : Task(), m_job_name(job_name) {}; - virtual ~JavaCheckerJob() {}; - - bool addJavaCheckerAction(JavaCheckerPtr base) - { - javacheckers.append(base); - // if this is already running, the action needs to be started right away! - if (isRunning()) { - setProgress(num_finished, javacheckers.size()); - connect(base.get(), &JavaChecker::checkFinished, this, &JavaCheckerJob::partFinished); - base->performCheck(); - } - return true; - } - QList getResults() { return javaresults; } - - private slots: - void partFinished(JavaCheckResult result); - - protected: - virtual void executeTask() override; - - private: - QString m_job_name; - QList javacheckers; - QList javaresults; - int num_finished = 0; -}; diff --git a/launcher/minecraft/MinecraftUpdate.h b/launcher/minecraft/MinecraftUpdate.h deleted file mode 100644 index 591c6afcb..000000000 --- a/launcher/minecraft/MinecraftUpdate.h +++ /dev/null @@ -1,57 +0,0 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include -#include "minecraft/VersionFilterData.h" -#include "net/NetJob.h" -#include "tasks/Task.h" - -class MinecraftVersion; -class MinecraftInstance; - -// FIXME: This looks very similar to a SequentialTask. Maybe we can reduce code duplications? :^) - -class MinecraftUpdate : public Task { - Q_OBJECT - public: - explicit MinecraftUpdate(MinecraftInstance* inst, QObject* parent = 0); - virtual ~MinecraftUpdate() {}; - - void executeTask() override; - bool canAbort() const override; - - private slots: - bool abort() override; - void subtaskSucceeded(); - void subtaskFailed(QString error); - - private: - void next(); - - private: - MinecraftInstance* m_inst = nullptr; - QList m_tasks; - QString m_preFailure; - int m_currentTask = -1; - bool m_abort = false; - bool m_failed_out_of_order = false; - QString m_fail_reason; -}; diff --git a/launcher/minecraft/auth/flows/AuthlibInjector.cpp b/launcher/minecraft/auth/flows/AuthlibInjector.cpp deleted file mode 100644 index 0eee1fa01..000000000 --- a/launcher/minecraft/auth/flows/AuthlibInjector.cpp +++ /dev/null @@ -1,23 +0,0 @@ -#include "AuthlibInjector.h" - -#include "minecraft/auth/steps/AuthlibInjectorMetadataStep.h" -#include "minecraft/auth/steps/GetSkinStep.h" -#include "minecraft/auth/steps/MinecraftProfileStepMojang.h" -#include "minecraft/auth/steps/YggdrasilStep.h" - -AuthlibInjectorRefresh::AuthlibInjectorRefresh(AccountData* data, QObject* parent) : AuthFlow(data, parent) -{ - m_steps.append(makeShared(m_data, QString())); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); -} - -AuthlibInjectorLogin::AuthlibInjectorLogin(AccountData* data, QString password, QObject* parent) - : AuthFlow(data, parent), m_password(password) -{ - m_steps.append(makeShared(m_data, m_password)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); -} diff --git a/launcher/minecraft/auth/flows/AuthlibInjector.h b/launcher/minecraft/auth/flows/AuthlibInjector.h deleted file mode 100644 index d2e575ae6..000000000 --- a/launcher/minecraft/auth/flows/AuthlibInjector.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "AuthFlow.h" - -class AuthlibInjectorRefresh : public AuthFlow { - Q_OBJECT - public: - explicit AuthlibInjectorRefresh(AccountData* data, QObject* parent = 0); -}; - -class AuthlibInjectorLogin : public AuthFlow { - Q_OBJECT - public: - explicit AuthlibInjectorLogin(AccountData* data, QString password, QObject* parent = 0); - - private: - QString m_password; -}; diff --git a/launcher/minecraft/auth/flows/Mojang.cpp b/launcher/minecraft/auth/flows/Mojang.cpp deleted file mode 100644 index 7e2db16fa..000000000 --- a/launcher/minecraft/auth/flows/Mojang.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "Mojang.h" - -#include "minecraft/auth/steps/GetSkinStep.h" -#include "minecraft/auth/steps/MigrationEligibilityStep.h" -#include "minecraft/auth/steps/MinecraftProfileStepMojang.h" -#include "minecraft/auth/steps/YggdrasilStep.h" - -MojangRefresh::MojangRefresh(AccountData* data, QObject* parent) : AuthFlow(data, parent) -{ - m_steps.append(makeShared(m_data, QString())); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); -} - -MojangLogin::MojangLogin(AccountData* data, QString password, QObject* parent) : AuthFlow(data, parent), m_password(password) -{ - m_steps.append(makeShared(m_data, m_password)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); - m_steps.append(makeShared(m_data)); -} diff --git a/launcher/minecraft/auth/flows/Mojang.h b/launcher/minecraft/auth/flows/Mojang.h deleted file mode 100644 index 779ca7e34..000000000 --- a/launcher/minecraft/auth/flows/Mojang.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once -#include "AuthFlow.h" - -class MojangRefresh : public AuthFlow { - Q_OBJECT - public: - explicit MojangRefresh(AccountData* data, QObject* parent = 0); -}; - -class MojangLogin : public AuthFlow { - Q_OBJECT - public: - explicit MojangLogin(AccountData* data, QString password, QObject* parent = 0); - - private: - QString m_password; -}; diff --git a/launcher/minecraft/services/CapeChange.cpp b/launcher/minecraft/services/CapeChange.cpp deleted file mode 100644 index 63a1cf05e..000000000 --- a/launcher/minecraft/services/CapeChange.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * 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, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "CapeChange.h" - -#include -#include - -#include "Application.h" - -CapeChange::CapeChange(QObject* parent, MinecraftAccountPtr account, QString cape) : Task(parent), m_capeId(cape), m_account(account) {} - -void CapeChange::setCape([[maybe_unused]] QString& cape) -{ - QNetworkRequest request(QUrl(m_account->servicesServerUrl() + "/minecraft/profile/capes/active")); - QString token = m_account->accessToken(); - auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); - request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); - QNetworkReply* rep = APPLICATION->network()->put(request, requestString.toUtf8()); - - setStatus(tr("Equipping cape")); - - m_reply = shared_qobject_ptr(rep); - connect(rep, &QNetworkReply::uploadProgress, this, &CapeChange::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &CapeChange::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &CapeChange::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &CapeChange::sslErrors); - connect(rep, &QNetworkReply::finished, this, &CapeChange::downloadFinished); -} - -void CapeChange::clearCape() -{ - QNetworkRequest request(QUrl(m_account->servicesServerUrl() + "/minecraft/profile/capes/active")); - QString token = m_account->accessToken(); - auto requestString = QString("{\"capeId\":\"%1\"}").arg(m_capeId); - request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); - QNetworkReply* rep = APPLICATION->network()->deleteResource(request); - - setStatus(tr("Removing cape")); - - m_reply = shared_qobject_ptr(rep); - connect(rep, &QNetworkReply::uploadProgress, this, &CapeChange::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &CapeChange::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &CapeChange::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &CapeChange::sslErrors); - connect(rep, &QNetworkReply::finished, this, &CapeChange::downloadFinished); -} - -void CapeChange::executeTask() -{ - if (m_capeId.isEmpty()) { - clearCape(); - } else { - setCape(m_capeId); - } -} - -void CapeChange::downloadError(QNetworkReply::NetworkError error) -{ - // error happened during download. - qCritical() << "Network error: " << error; - emitFailed(m_reply->errorString()); -} - -void CapeChange::sslErrors(const QList& errors) -{ - int i = 1; - for (auto error : errors) { - qCritical() << "Cape change SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void CapeChange::downloadFinished() -{ - // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) { - emitFailed(QString("Network error: %1").arg(m_reply->errorString())); - m_reply.reset(); - return; - } - emitSucceeded(); -} diff --git a/launcher/minecraft/services/CapeChange.h b/launcher/minecraft/services/CapeChange.h deleted file mode 100644 index 6543e9bf0..000000000 --- a/launcher/minecraft/services/CapeChange.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "QObjectPtr.h" -#include "tasks/Task.h" - -class CapeChange : public Task { - Q_OBJECT - public: - CapeChange(QObject* parent, MinecraftAccountPtr m_account, QString capeId); - virtual ~CapeChange() {} - - private: - void setCape(QString& cape); - void clearCape(); - - private: - QString m_capeId; - MinecraftAccountPtr m_account; - shared_qobject_ptr m_reply; - - protected: - virtual void executeTask(); - - public slots: - void downloadError(QNetworkReply::NetworkError); - void sslErrors(const QList& errors); - void downloadFinished(); -}; diff --git a/launcher/minecraft/services/SkinDelete.cpp b/launcher/minecraft/services/SkinDelete.cpp deleted file mode 100644 index fcaf3703c..000000000 --- a/launcher/minecraft/services/SkinDelete.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * 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, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SkinDelete.h" - -#include -#include - -#include "Application.h" - -SkinDelete::SkinDelete(QObject* parent, MinecraftAccountPtr account) : Task(parent), m_account(account) {} - -void SkinDelete::executeTask() -{ - QNetworkRequest request(QUrl(m_account->servicesServerUrl() + "/minecraft/profile/skins/active")); - QString token = m_account->accessToken(); - request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); - QNetworkReply* rep = APPLICATION->network()->deleteResource(request); - m_reply = shared_qobject_ptr(rep); - - setStatus(tr("Deleting skin")); - connect(rep, &QNetworkReply::uploadProgress, this, &SkinDelete::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &SkinDelete::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &SkinDelete::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &SkinDelete::sslErrors); - connect(rep, &QNetworkReply::finished, this, &SkinDelete::downloadFinished); -} - -void SkinDelete::downloadError(QNetworkReply::NetworkError error) -{ - // error happened during download. - qCritical() << "Network error: " << error; - emitFailed(m_reply->errorString()); -} - -void SkinDelete::sslErrors(const QList& errors) -{ - int i = 1; - for (auto error : errors) { - qCritical() << "Skin Delete SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void SkinDelete::downloadFinished() -{ - // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) { - emitFailed(QString("Network error: %1").arg(m_reply->errorString())); - m_reply.reset(); - return; - } - emitSucceeded(); -} diff --git a/launcher/minecraft/services/SkinDelete.h b/launcher/minecraft/services/SkinDelete.h deleted file mode 100644 index 40664a645..000000000 --- a/launcher/minecraft/services/SkinDelete.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include -#include -#include -#include "tasks/Task.h" - -using SkinDeletePtr = shared_qobject_ptr; - -class SkinDelete : public Task { - Q_OBJECT - public: - SkinDelete(QObject* parent, MinecraftAccountPtr acct); - virtual ~SkinDelete() = default; - - private: - MinecraftAccountPtr m_account; - shared_qobject_ptr m_reply; - - protected: - virtual void executeTask(); - - public slots: - void downloadError(QNetworkReply::NetworkError); - void sslErrors(const QList& errors); - void downloadFinished(); -}; diff --git a/launcher/minecraft/services/SkinUpload.cpp b/launcher/minecraft/services/SkinUpload.cpp deleted file mode 100644 index 7bedfc0c2..000000000 --- a/launcher/minecraft/services/SkinUpload.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * 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, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "SkinUpload.h" - -#include -#include - -#include "Application.h" - -QByteArray getVariant(SkinUpload::Model model) -{ - switch (model) { - default: - qDebug() << "Unknown skin type!"; - case SkinUpload::STEVE: - return "CLASSIC"; - case SkinUpload::ALEX: - return "SLIM"; - } -} - -SkinUpload::SkinUpload(QObject* parent, MinecraftAccountPtr account, QByteArray skin, SkinUpload::Model model) - : Task(parent), m_model(model), m_skin(skin), m_account(account) -{} - -void SkinUpload::executeTask() -{ - QNetworkRequest request(QUrl(m_account->servicesServerUrl() + "/minecraft/profile/skins")); - QString token = m_account->accessToken(); - request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toLocal8Bit()); - QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); - - QHttpPart skin; - skin.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png")); - skin.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"file\"; filename=\"skin.png\"")); - skin.setBody(m_skin); - - QHttpPart model; - model.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"variant\"")); - model.setBody(getVariant(m_model)); - - multiPart->append(skin); - multiPart->append(model); - - QNetworkReply* rep = APPLICATION->network()->post(request, multiPart); - m_reply = shared_qobject_ptr(rep); - - setStatus(tr("Uploading skin")); - connect(rep, &QNetworkReply::uploadProgress, this, &SkinUpload::setProgress); -#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) // QNetworkReply::errorOccurred added in 5.15 - connect(rep, &QNetworkReply::errorOccurred, this, &SkinUpload::downloadError); -#else - connect(rep, QOverload::of(&QNetworkReply::error), this, &SkinUpload::downloadError); -#endif - connect(rep, &QNetworkReply::sslErrors, this, &SkinUpload::sslErrors); - connect(rep, &QNetworkReply::finished, this, &SkinUpload::downloadFinished); -} - -void SkinUpload::downloadError(QNetworkReply::NetworkError error) -{ - // error happened during download. - qCritical() << "Network error: " << error; - emitFailed(m_reply->errorString()); -} - -void SkinUpload::sslErrors(const QList& errors) -{ - int i = 1; - for (auto error : errors) { - qCritical() << "Skin Upload SSL Error #" << i << " : " << error.errorString(); - auto cert = error.certificate(); - qCritical() << "Certificate in question:\n" << cert.toText(); - i++; - } -} - -void SkinUpload::downloadFinished() -{ - // if the download failed - if (m_reply->error() != QNetworkReply::NetworkError::NoError) { - emitFailed(QString("Network error: %1").arg(m_reply->errorString())); - m_reply.reset(); - return; - } - emitSucceeded(); -} diff --git a/launcher/minecraft/services/SkinUpload.h b/launcher/minecraft/services/SkinUpload.h deleted file mode 100644 index 75f1551bb..000000000 --- a/launcher/minecraft/services/SkinUpload.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "tasks/Task.h" - -using SkinUploadPtr = shared_qobject_ptr; - -class SkinUpload : public Task { - Q_OBJECT - public: - enum Model { STEVE, ALEX }; - - // Note this class takes ownership of the file. - SkinUpload(QObject* parent, MinecraftAccountPtr account, QByteArray skin, Model model = STEVE); - virtual ~SkinUpload() {} - - private: - Model m_model; - QByteArray m_skin; - MinecraftAccountPtr m_account; - shared_qobject_ptr m_reply; - - protected: - virtual void executeTask(); - - public slots: - - void downloadError(QNetworkReply::NetworkError); - void sslErrors(const QList& errors); - - void downloadFinished(); -}; diff --git a/launcher/minecraft/skins/CapeChange.cpp b/launcher/minecraft/skins/CapeChange.cpp index abbaa0b67..c8788e3bd 100644 --- a/launcher/minecraft/skins/CapeChange.cpp +++ b/launcher/minecraft/skins/CapeChange.cpp @@ -57,10 +57,11 @@ QNetworkReply* CapeChange::getReply(QNetworkRequest& request) } } -CapeChange::Ptr CapeChange::make(QString token, QString capeId) +CapeChange::Ptr CapeChange::make(MinecraftAccountPtr account, QString capeId) { auto up = makeShared(capeId); - up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/capes/active"); + QString token = account->accessToken(); + up->m_url = QUrl(account->servicesServerUrl() + "/minecraft/profile/capes/active"); up->setObjectName(QString("BYTES:") + up->m_url.toString()); up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); up->addHeaderProxy(new Net::RawHeaderProxy(QList{ diff --git a/launcher/minecraft/skins/CapeChange.h b/launcher/minecraft/skins/CapeChange.h index 2be904f4d..95b6a5572 100644 --- a/launcher/minecraft/skins/CapeChange.h +++ b/launcher/minecraft/skins/CapeChange.h @@ -18,6 +18,7 @@ #pragma once +#include #include "net/NetRequest.h" class CapeChange : public Net::NetRequest { @@ -27,7 +28,7 @@ class CapeChange : public Net::NetRequest { CapeChange(QString capeId); virtual ~CapeChange() = default; - static CapeChange::Ptr make(QString token, QString capeId); + static CapeChange::Ptr make(MinecraftAccountPtr account, QString capeId); protected: virtual QNetworkReply* getReply(QNetworkRequest&) override; diff --git a/launcher/minecraft/skins/SkinDelete.cpp b/launcher/minecraft/skins/SkinDelete.cpp index 94aca62ca..42822a9df 100644 --- a/launcher/minecraft/skins/SkinDelete.cpp +++ b/launcher/minecraft/skins/SkinDelete.cpp @@ -50,10 +50,11 @@ QNetworkReply* SkinDelete::getReply(QNetworkRequest& request) return m_network->deleteResource(request); } -SkinDelete::Ptr SkinDelete::make(QString token) +SkinDelete::Ptr SkinDelete::make(MinecraftAccountPtr account) { auto up = makeShared(); - up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins/active"); + QString token = account->accessToken(); + up->m_url = QUrl(account->servicesServerUrl() + "/minecraft/profile/skins/active"); up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); up->addHeaderProxy(new Net::RawHeaderProxy(QList{ { "Authorization", QString("Bearer %1").arg(token).toLocal8Bit() }, diff --git a/launcher/minecraft/skins/SkinDelete.h b/launcher/minecraft/skins/SkinDelete.h index d6a68d22c..ccbc69d6e 100644 --- a/launcher/minecraft/skins/SkinDelete.h +++ b/launcher/minecraft/skins/SkinDelete.h @@ -18,6 +18,7 @@ #pragma once +#include #include "net/NetRequest.h" class SkinDelete : public Net::NetRequest { @@ -27,7 +28,7 @@ class SkinDelete : public Net::NetRequest { SkinDelete(); virtual ~SkinDelete() = default; - static SkinDelete::Ptr make(QString token); + static SkinDelete::Ptr make(MinecraftAccountPtr account); protected: virtual QNetworkReply* getReply(QNetworkRequest&) override; diff --git a/launcher/minecraft/skins/SkinUpload.cpp b/launcher/minecraft/skins/SkinUpload.cpp index ccc29d281..a0d90c596 100644 --- a/launcher/minecraft/skins/SkinUpload.cpp +++ b/launcher/minecraft/skins/SkinUpload.cpp @@ -67,10 +67,11 @@ QNetworkReply* SkinUpload::getReply(QNetworkRequest& request) return m_network->post(request, multiPart); } -SkinUpload::Ptr SkinUpload::make(QString token, QString path, QString variant) +SkinUpload::Ptr SkinUpload::make(MinecraftAccountPtr account, QString path, QString variant) { auto up = makeShared(path, variant); - up->m_url = QUrl("https://api.minecraftservices.com/minecraft/profile/skins"); + QString token = account->accessToken(); + up->m_url = QUrl(account->servicesServerUrl() + "/minecraft/profile/skins"); up->setObjectName(QString("BYTES:") + up->m_url.toString()); up->m_sink.reset(new Net::ByteArraySink(std::make_shared())); up->addHeaderProxy(new Net::RawHeaderProxy(QList{ diff --git a/launcher/minecraft/skins/SkinUpload.h b/launcher/minecraft/skins/SkinUpload.h index c1a4930b7..333e1f8d1 100644 --- a/launcher/minecraft/skins/SkinUpload.h +++ b/launcher/minecraft/skins/SkinUpload.h @@ -18,6 +18,7 @@ #pragma once +#include #include "net/NetRequest.h" class SkinUpload : public Net::NetRequest { @@ -29,7 +30,7 @@ class SkinUpload : public Net::NetRequest { SkinUpload(QString path, QString variant); virtual ~SkinUpload() = default; - static SkinUpload::Ptr make(QString token, QString path, QString variant); + static SkinUpload::Ptr make(MinecraftAccountPtr account, QString path, QString variant); protected: virtual QNetworkReply* getReply(QNetworkRequest&) override; diff --git a/launcher/qtlogging.ini b/launcher/qtlogging.ini index 5266de59b..c12d1e109 100644 --- a/launcher/qtlogging.ini +++ b/launcher/qtlogging.ini @@ -5,7 +5,6 @@ qt.*.debug=false # don't log credentials by default launcher.auth.credentials.debug=false -katabasis.*.debug=false # remove the debug lines, other log levels still get through launcher.task.net.download.debug=false # enable or disable whole catageries diff --git a/launcher/ui/dialogs/LoginDialog.h b/launcher/ui/dialogs/LoginDialog.h deleted file mode 100644 index e8e449019..000000000 --- a/launcher/ui/dialogs/LoginDialog.h +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2023 Evan Goode - * - * 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, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include - -#include "minecraft/auth/MinecraftAccount.h" -#include "tasks/Task.h" - -namespace Ui { -class LoginDialog; -} - -class LoginDialog : public QDialog { - Q_OBJECT - - public: - ~LoginDialog(); - - static MinecraftAccountPtr newAccount(QWidget* parent, QString message); - - private: - explicit LoginDialog(QWidget* parent = 0); - - void setUserInputsEnabled(bool enable); - - protected slots: - void accept(); - - void onTaskFailed(const QString& reason); - void onTaskSucceeded(); - void onTaskStatus(const QString& status); - void onTaskProgress(qint64 current, qint64 total); - - void on_userTextBox_textEdited(const QString& newText); - void on_passTextBox_textEdited(const QString& newText); - - private: - Ui::LoginDialog* ui; - MinecraftAccountPtr m_account; - Task::Ptr m_loginTask; -}; diff --git a/launcher/ui/dialogs/LoginDialog.ui b/launcher/ui/dialogs/LoginDialog.ui deleted file mode 100644 index 8fa4a45d2..000000000 --- a/launcher/ui/dialogs/LoginDialog.ui +++ /dev/null @@ -1,77 +0,0 @@ - - - LoginDialog - - - - 0 - 0 - 421 - 198 - - - - - 0 - 0 - - - - Add Account - - - - - - Message label placeholder. - - - Qt::RichText - - - Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Email - - - - - - - QLineEdit::Password - - - Password - - - - - - - 24 - - - false - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - diff --git a/launcher/ui/dialogs/SkinUploadDialog.cpp b/launcher/ui/dialogs/SkinUploadDialog.cpp deleted file mode 100644 index 52fc302ac..000000000 --- a/launcher/ui/dialogs/SkinUploadDialog.cpp +++ /dev/null @@ -1,165 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -/* - * Prism Launcher - Minecraft Launcher - * Copyright (C) 2022 Sefa Eyeoglu - * - * 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, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - * Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include - -#include - -#include -#include -#include - -#include "CustomMessageBox.h" -#include "ProgressDialog.h" -#include "SkinUploadDialog.h" -#include "ui_SkinUploadDialog.h" - -void SkinUploadDialog::on_buttonBox_rejected() -{ - close(); -} - -void SkinUploadDialog::on_buttonBox_accepted() -{ - QString fileName; - QString input = ui->skinPathTextBox->text(); - ProgressDialog prog(this); - SequentialTask skinUpload; - - if (!input.isEmpty()) { - QRegularExpression urlPrefixMatcher(QRegularExpression::anchoredPattern("^([a-z]+)://.+$")); - bool isLocalFile = false; - // it has an URL prefix -> it is an URL - if (urlPrefixMatcher.match(input).hasMatch()) { - QUrl fileURL = input; - if (fileURL.isValid()) { - // local? - if (fileURL.isLocalFile()) { - isLocalFile = true; - fileName = fileURL.toLocalFile(); - } else { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Using remote URLs for setting skins is not implemented yet."), - QMessageBox::Warning) - ->exec(); - close(); - return; - } - } else { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("You cannot use an invalid URL for uploading skins."), - QMessageBox::Warning) - ->exec(); - close(); - return; - } - } else { - // just assume it's a path then - isLocalFile = true; - fileName = ui->skinPathTextBox->text(); - } - if (isLocalFile && !QFile::exists(fileName)) { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Skin file does not exist!"), QMessageBox::Warning)->exec(); - close(); - return; - } - SkinUpload::Model model = SkinUpload::STEVE; - if (ui->steveBtn->isChecked()) { - model = SkinUpload::STEVE; - } else if (ui->alexBtn->isChecked()) { - model = SkinUpload::ALEX; - } - skinUpload.addTask(shared_qobject_ptr(new SkinUpload(this, m_account, FS::read(fileName), model))); - } - - auto selectedCape = ui->capeCombo->currentData().toString(); - if (selectedCape != m_account->accountData()->minecraftProfile.currentCape) { - skinUpload.addTask(shared_qobject_ptr(new CapeChange(this, m_account, selectedCape))); - } - if (prog.execWithTask(&skinUpload) != QDialog::Accepted) { - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Failed to upload skin!"), QMessageBox::Warning)->exec(); - close(); - return; - } - CustomMessageBox::selectable(this, tr("Skin Upload"), tr("Success"), QMessageBox::Information)->exec(); - close(); -} - -void SkinUploadDialog::on_skinBrowseBtn_clicked() -{ - auto filter = QMimeDatabase().mimeTypeForName("image/png").filterString(); - QString raw_path = QFileDialog::getOpenFileName(this, tr("Select Skin Texture"), QString(), filter); - if (raw_path.isEmpty() || !QFileInfo::exists(raw_path)) { - return; - } - QString cooked_path = FS::NormalizePath(raw_path); - ui->skinPathTextBox->setText(cooked_path); -} - -SkinUploadDialog::SkinUploadDialog(MinecraftAccountPtr account, QWidget* parent) - : QDialog(parent), m_account(account), ui(new Ui::SkinUploadDialog) -{ - ui->setupUi(this); - - // FIXME: add a model for this, download/refresh the capes on demand - auto& accountData = *account->accountData(); - int index = 0; - ui->capeCombo->addItem(tr("No Cape"), QVariant()); - auto currentCape = accountData.minecraftProfile.currentCape; - if (currentCape.isEmpty()) { - ui->capeCombo->setCurrentIndex(index); - } - - for (auto& cape : accountData.minecraftProfile.capes) { - index++; - if (cape.data.size()) { - QPixmap capeImage; - if (capeImage.loadFromData(cape.data, "PNG")) { - QPixmap preview = QPixmap(10, 16); - QPainter painter(&preview); - painter.drawPixmap(0, 0, capeImage.copy(1, 1, 10, 16)); - ui->capeCombo->addItem(capeImage, cape.alias, cape.id); - if (currentCape == cape.id) { - ui->capeCombo->setCurrentIndex(index); - } - continue; - } - } - ui->capeCombo->addItem(cape.alias, cape.id); - if (currentCape == cape.id) { - ui->capeCombo->setCurrentIndex(index); - } - } -} diff --git a/launcher/ui/dialogs/SkinUploadDialog.h b/launcher/ui/dialogs/SkinUploadDialog.h deleted file mode 100644 index c66296b09..000000000 --- a/launcher/ui/dialogs/SkinUploadDialog.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -namespace Ui { -class SkinUploadDialog; -} - -class SkinUploadDialog : public QDialog { - Q_OBJECT - public: - explicit SkinUploadDialog(MinecraftAccountPtr acct, QWidget* parent = 0); - virtual ~SkinUploadDialog() {}; - - public slots: - void on_buttonBox_accepted(); - - void on_buttonBox_rejected(); - - void on_skinBrowseBtn_clicked(); - - protected: - MinecraftAccountPtr m_account; - - private: - Ui::SkinUploadDialog* ui; -}; diff --git a/launcher/ui/dialogs/skins/SkinManageDialog.cpp b/launcher/ui/dialogs/skins/SkinManageDialog.cpp index 6c85ffa96..f992429fe 100644 --- a/launcher/ui/dialogs/skins/SkinManageDialog.cpp +++ b/launcher/ui/dialogs/skins/SkinManageDialog.cpp @@ -246,11 +246,11 @@ void SkinManageDialog::accept() return; } - skinUpload->addNetAction(SkinUpload::make(m_acct->accessToken(), skin->getPath(), skin->getModelString())); + skinUpload->addNetAction(SkinUpload::make(m_acct, skin->getPath(), skin->getModelString())); auto selectedCape = skin->getCapeId(); if (selectedCape != m_acct->accountData()->minecraftProfile.currentCape) { - skinUpload->addNetAction(CapeChange::make(m_acct->accessToken(), selectedCape)); + skinUpload->addNetAction(CapeChange::make(m_acct, selectedCape)); } skinUpload->addTask(m_acct->refresh().staticCast()); @@ -267,7 +267,7 @@ void SkinManageDialog::on_resetBtn_clicked() { ProgressDialog prog(this); NetJob::Ptr skinReset{ new NetJob(tr("Reset skin"), APPLICATION->network(), 1) }; - skinReset->addNetAction(SkinDelete::make(m_acct->accessToken())); + skinReset->addNetAction(SkinDelete::make(m_acct)); skinReset->addTask(m_acct->refresh().staticCast()); if (prog.execWithTask(skinReset.get()) != QDialog::Accepted) { CustomMessageBox::selectable(this, tr("Skin Delete"), tr("Failed to delete current skin!"), QMessageBox::Warning)->exec(); diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp index 3672934d7..4d489fa98 100644 --- a/launcher/ui/pages/global/AccountListPage.cpp +++ b/launcher/ui/pages/global/AccountListPage.cpp @@ -48,7 +48,6 @@ #include "ui/dialogs/AuthlibInjectorLoginDialog.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/LoginDialog.h" #include "ui/dialogs/MSALoginDialog.h" #include "ui/dialogs/OfflineLoginDialog.h" @@ -59,7 +58,7 @@ AccountListPage::AccountListPage(QWidget* parent) : QMainWindow(parent), ui(new ui->setupUi(this); ui->listView->setEmptyString( tr("Welcome!\n" - "If you're new here, you can select the \"Add Offline\" button to play without an account.")); + "If you're new here, you can select the \"Add Microsoft\" button to link your Microsoft account.")); ui->listView->setEmptyMode(VersionListView::String); ui->listView->setContextMenuPolicy(Qt::CustomContextMenu); @@ -137,7 +136,7 @@ void AccountListPage::on_actionAddAuthlibInjector_triggered() { if (!m_accounts->anyAccountIsValid()) { QMessageBox::warning(this, tr("Error"), - tr("You must add a Microsoft or Mojang account that owns Minecraft before you can add an account on a custom " + tr("You must add a Microsoft account that owns Minecraft before you can add an account on a custom " "authentication server." "

" "If you have lost your account you can contact Microsoft for support.")); diff --git a/libraries/katabasis/src/DeviceFlow.cpp b/libraries/katabasis/src/DeviceFlow.cpp deleted file mode 100644 index c6928b60d..000000000 --- a/libraries/katabasis/src/DeviceFlow.cpp +++ /dev/null @@ -1,471 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "katabasis/DeviceFlow.h" -#include "katabasis/Globals.h" -#include "katabasis/PollServer.h" - -#include "JsonResponse.h" -#include "KatabasisLogging.h" - -namespace { - -// ref: https://tools.ietf.org/html/rfc8628#section-3.2 -// Exception: Google sign-in uses "verification_url" instead of "*_uri" - we'll accept both. -bool hasMandatoryDeviceAuthParams(const QVariantMap& params) -{ - if (!params.contains(Katabasis::OAUTH2_DEVICE_CODE)) - return false; - - if (!params.contains(Katabasis::OAUTH2_USER_CODE)) - return false; - - if (!(params.contains(Katabasis::OAUTH2_VERIFICATION_URI) || params.contains(Katabasis::OAUTH2_VERIFICATION_URL))) - return false; - - if (!params.contains(Katabasis::OAUTH2_EXPIRES_IN)) - return false; - - return true; -} - -QByteArray createQueryParameters(const QList& parameters) -{ - QByteArray ret; - bool first = true; - for (auto& h : parameters) { - if (first) { - first = false; - } else { - ret.append("&"); - } - ret.append(QUrl::toPercentEncoding(h.name) + "=" + QUrl::toPercentEncoding(h.value)); - } - return ret; -} -} // namespace - -namespace Katabasis { - -DeviceFlow::DeviceFlow(Options& opts, Token& token, QObject* parent, QNetworkAccessManager* manager) : QObject(parent), token_(token) -{ - manager_ = manager ? manager : new QNetworkAccessManager(this); - qRegisterMetaType("QNetworkReply::NetworkError"); - options_ = opts; -} - -bool DeviceFlow::linked() -{ - return token_.validity != Validity::None; -} -void DeviceFlow::setLinked(bool v) -{ - qDebug() << "DeviceFlow::setLinked:" << (v ? "true" : "false"); - token_.validity = v ? Validity::Certain : Validity::None; -} - -void DeviceFlow::updateActivity(Activity activity) -{ - if (activity_ == activity) { - return; - } - - activity_ = activity; - switch (activity) { - case Katabasis::Activity::Idle: - case Katabasis::Activity::LoggingIn: - case Katabasis::Activity::LoggingOut: - case Katabasis::Activity::Refreshing: - // non-terminal states... - break; - case Katabasis::Activity::FailedSoft: - // terminal state, tokens did not change - break; - case Katabasis::Activity::FailedHard: - case Katabasis::Activity::FailedGone: - // terminal state, tokens are invalid - token_ = Token(); - break; - case Katabasis::Activity::Succeeded: - setLinked(true); - break; - } - emit activityChanged(activity_); -} - -QString DeviceFlow::token() -{ - return token_.token; -} -void DeviceFlow::setToken(const QString& v) -{ - token_.token = v; -} - -QVariantMap DeviceFlow::extraTokens() -{ - return token_.extra; -} - -void DeviceFlow::setExtraTokens(QVariantMap extraTokens) -{ - token_.extra = extraTokens; -} - -void DeviceFlow::setPollServer(PollServer* server) -{ - if (pollServer_) - pollServer_->deleteLater(); - - pollServer_ = server; -} - -PollServer* DeviceFlow::pollServer() const -{ - return pollServer_; -} - -QVariantMap DeviceFlow::extraRequestParams() -{ - return extraReqParams_; -} - -void DeviceFlow::setExtraRequestParams(const QVariantMap& value) -{ - extraReqParams_ = value; -} - -QString DeviceFlow::grantType() -{ - if (!grantType_.isEmpty()) - return grantType_; - - return OAUTH2_GRANT_TYPE_DEVICE; -} - -void DeviceFlow::setGrantType(const QString& value) -{ - grantType_ = value; -} - -// First get the URL and token to display to the user -void DeviceFlow::login() -{ - qDebug() << "DeviceFlow::link"; - - updateActivity(Activity::LoggingIn); - setLinked(false); - setToken(""); - setExtraTokens(QVariantMap()); - setRefreshToken(QString()); - setExpires(QDateTime()); - - QList parameters; - parameters.append(RequestParameter(OAUTH2_CLIENT_ID, options_.clientIdentifier.toUtf8())); - parameters.append(RequestParameter(OAUTH2_SCOPE, options_.scope.toUtf8())); - parameters.append(RequestParameter(OAUTH2_RESPONSE_TYPE, options_.responseType.toUtf8())); - QByteArray payload = createQueryParameters(parameters); - - QUrl url(options_.authorizationUrl); - QNetworkRequest deviceRequest(url); - deviceRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - QNetworkReply* tokenReply = manager_->post(deviceRequest, payload); - - connect(tokenReply, &QNetworkReply::finished, this, &DeviceFlow::onDeviceAuthReplyFinished, Qt::QueuedConnection); -} - -// Then, once we get them, present them to the user -void DeviceFlow::onDeviceAuthReplyFinished() -{ - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished"; - QNetworkReply* tokenReply = qobject_cast(sender()); - if (!tokenReply) { - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: reply is null"; - return; - } - if (tokenReply->error() == QNetworkReply::NoError) { - QByteArray replyData = tokenReply->readAll(); - - // Dump replyData - // SENSITIVE DATA in RelWithDebInfo or Debug builds - // qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: replyData\n"; - // qDebug() << QString( replyData ); - - QVariantMap params = parseJsonResponse(replyData); - - // Dump tokens - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: Tokens returned:\n"; - foreach (QString key, params.keys()) { - // SENSITIVE DATA in RelWithDebInfo or Debug builds, so it is truncated first - qDebug() << key << ": " << params.value(key).toString(); - } - - // Check for mandatory parameters - if (hasMandatoryDeviceAuthParams(params)) { - qDebug() << "DeviceFlow::onDeviceAuthReplyFinished: Device auth request response"; - - const QString userCode = params.take(OAUTH2_USER_CODE).toString(); - QUrl uri = params.take(OAUTH2_VERIFICATION_URI).toUrl(); - if (uri.isEmpty()) - uri = params.take(OAUTH2_VERIFICATION_URL).toUrl(); - - if (params.contains(OAUTH2_VERIFICATION_URI_COMPLETE)) - emit openBrowser(params.take(OAUTH2_VERIFICATION_URI_COMPLETE).toUrl()); - - bool ok = false; - int expiresIn = params[OAUTH2_EXPIRES_IN].toInt(&ok); - if (!ok) { - qWarning() << "DeviceFlow::startPollServer: No expired_in parameter"; - updateActivity(Activity::FailedHard); - return; - } - - emit showVerificationUriAndCode(uri, userCode, expiresIn); - - startPollServer(params, expiresIn); - } else { - qWarning() << "DeviceFlow::onDeviceAuthReplyFinished: Mandatory parameters missing from response"; - updateActivity(Activity::FailedHard); - } - } - tokenReply->deleteLater(); -} - -// Spin up polling for the user completing the login flow out of band -void DeviceFlow::startPollServer(const QVariantMap& params, int expiresIn) -{ - qDebug() << "DeviceFlow::startPollServer: device_ and user_code expires in" << expiresIn << "seconds"; - - QUrl url(options_.accessTokenUrl); - QNetworkRequest authRequest(url); - authRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); - - const QString deviceCode = params[OAUTH2_DEVICE_CODE].toString(); - const QString grantType = grantType_.isEmpty() ? OAUTH2_GRANT_TYPE_DEVICE : grantType_; - - QList parameters; - parameters.append(RequestParameter(OAUTH2_CLIENT_ID, options_.clientIdentifier.toUtf8())); - if (!options_.clientSecret.isEmpty()) { - parameters.append(RequestParameter(OAUTH2_CLIENT_SECRET, options_.clientSecret.toUtf8())); - } - parameters.append(RequestParameter(OAUTH2_DEVICE_CODE, deviceCode.toUtf8())); - parameters.append(RequestParameter(OAUTH2_GRANT_TYPE, grantType.toUtf8())); - parameters.append(RequestParameter(OAUTH2_RESPONSE_TYPE, options_.responseType.toUtf8())); - - QByteArray payload = createQueryParameters(parameters); - - PollServer* pollServer = new PollServer(manager_, authRequest, payload, expiresIn, this); - if (params.contains(OAUTH2_INTERVAL)) { - bool ok = false; - int interval = params[OAUTH2_INTERVAL].toInt(&ok); - if (ok) { - pollServer->setInterval(interval); - } - } - connect(pollServer, &PollServer::verificationReceived, this, &DeviceFlow::onVerificationReceived); - connect(pollServer, &PollServer::serverClosed, this, &DeviceFlow::serverHasClosed); - setPollServer(pollServer); - pollServer->startPolling(); -} - -// Once the user completes the flow, update the internal state and report it to observers -void DeviceFlow::onVerificationReceived(const QMap response) -{ - qDebug() << "DeviceFlow::onVerificationReceived: Emitting closeBrowser()"; - emit closeBrowser(); - - if (response.contains("error")) { - qWarning() << "DeviceFlow::onVerificationReceived: Verification failed:" << response; - updateActivity(Activity::FailedHard); - return; - } - - // Check for mandatory tokens - if (response.contains(OAUTH2_ACCESS_TOKEN)) { - qDebug() << "DeviceFlow::onVerificationReceived: Access token returned for implicit or device flow"; - setToken(response.value(OAUTH2_ACCESS_TOKEN)); - if (response.contains(OAUTH2_EXPIRES_IN)) { - bool ok = false; - int expiresIn = response.value(OAUTH2_EXPIRES_IN).toInt(&ok); - if (ok) { - qDebug() << "DeviceFlow::onVerificationReceived: Token expires in" << expiresIn << "seconds"; - setExpires(QDateTime::currentDateTimeUtc().addSecs(expiresIn)); - } - } - if (response.contains(OAUTH2_REFRESH_TOKEN)) { - setRefreshToken(response.value(OAUTH2_REFRESH_TOKEN)); - } - updateActivity(Activity::Succeeded); - } else { - qWarning() << "DeviceFlow::onVerificationReceived: Access token missing from response for implicit or device flow"; - updateActivity(Activity::FailedHard); - } -} - -// Or if the flow fails or the polling times out, update the internal state with error and report it to observers -void DeviceFlow::serverHasClosed(bool paramsfound) -{ - if (!paramsfound) { - // server has probably timed out after receiving first response - updateActivity(Activity::FailedHard); - } - // poll server is not re-used for later auth requests - setPollServer(NULL); -} - -void DeviceFlow::logout() -{ - qDebug() << "DeviceFlow::unlink"; - updateActivity(Activity::LoggingOut); - // FIXME: implement logout flows... if they exist - token_ = Token(); - updateActivity(Activity::FailedHard); -} - -QDateTime DeviceFlow::expires() -{ - return token_.notAfter; -} -void DeviceFlow::setExpires(QDateTime v) -{ - token_.notAfter = v; -} - -QString DeviceFlow::refreshToken() -{ - return token_.refresh_token; -} - -void DeviceFlow::setRefreshToken(const QString& v) -{ - qCDebug(katabasisCredentials) << "new refresh token:" << v; - token_.refresh_token = v; -} - -namespace { -QByteArray buildRequestBody(const QMap& parameters) -{ - QByteArray body; - bool first = true; - foreach (QString key, parameters.keys()) { - if (first) { - first = false; - } else { - body.append("&"); - } - QString value = parameters.value(key); - body.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() + QUrl::toPercentEncoding(value)); - } - return body; -} -} // namespace - -bool DeviceFlow::refresh() -{ - qDebug() << "DeviceFlow::refresh: Token: ..." << refreshToken().right(7); - - updateActivity(Activity::Refreshing); - - if (refreshToken().isEmpty()) { - qWarning() << "DeviceFlow::refresh: No refresh token"; - onRefreshError(QNetworkReply::AuthenticationRequiredError, nullptr); - return false; - } - if (options_.accessTokenUrl.isEmpty()) { - qWarning() << "DeviceFlow::refresh: Refresh token URL not set"; - onRefreshError(QNetworkReply::AuthenticationRequiredError, nullptr); - return false; - } - - QNetworkRequest refreshRequest(options_.accessTokenUrl); - refreshRequest.setHeader(QNetworkRequest::ContentTypeHeader, MIME_TYPE_XFORM); - QMap parameters; - parameters.insert(OAUTH2_CLIENT_ID, options_.clientIdentifier); - if (!options_.clientSecret.isEmpty()) { - parameters.insert(OAUTH2_CLIENT_SECRET, options_.clientSecret); - } - parameters.insert(OAUTH2_SCOPE, options_.scope.toUtf8()); - parameters.insert(OAUTH2_REFRESH_TOKEN, refreshToken()); - parameters.insert(OAUTH2_GRANT_TYPE, OAUTH2_REFRESH_TOKEN); - - QByteArray data = buildRequestBody(parameters); - QNetworkReply* refreshReply = manager_->post(refreshRequest, data); - timedReplies_.add(refreshReply); - connect(refreshReply, &QNetworkReply::finished, this, &DeviceFlow::onRefreshFinished, Qt::QueuedConnection); - return true; -} - -void DeviceFlow::onRefreshFinished() -{ - QNetworkReply* refreshReply = qobject_cast(sender()); - - auto networkError = refreshReply->error(); - if (networkError == QNetworkReply::NoError) { - QByteArray reply = refreshReply->readAll(); - QVariantMap tokens = parseJsonResponse(reply); - setToken(tokens.value(OAUTH2_ACCESS_TOKEN).toString()); - setExpires(QDateTime::currentDateTimeUtc().addSecs(tokens.value(OAUTH2_EXPIRES_IN).toInt())); - QString refreshToken = tokens.value(OAUTH2_REFRESH_TOKEN).toString(); - if (!refreshToken.isEmpty()) { - setRefreshToken(refreshToken); - } else { - qDebug() << "No new refresh token. Keep the old one."; - } - timedReplies_.remove(refreshReply); - refreshReply->deleteLater(); - updateActivity(Activity::Succeeded); - qDebug() << "New token expires in" << expires() << "seconds"; - } else { - // FIXME: differentiate the error more here - onRefreshError(networkError, refreshReply); - } -} - -void DeviceFlow::onRefreshError(QNetworkReply::NetworkError error, QNetworkReply* refreshReply) -{ - QString errorString = "No Reply"; - if (refreshReply) { - timedReplies_.remove(refreshReply); - errorString = refreshReply->errorString(); - } - - switch (error) { - // used for invalid credentials and similar errors. Fall through. - case QNetworkReply::AuthenticationRequiredError: - case QNetworkReply::ContentAccessDenied: - case QNetworkReply::ContentOperationNotPermittedError: - case QNetworkReply::ProtocolInvalidOperationError: - updateActivity(Activity::FailedHard); - break; - case QNetworkReply::ContentGoneError: { - updateActivity(Activity::FailedGone); - break; - } - case QNetworkReply::TimeoutError: - case QNetworkReply::OperationCanceledError: - case QNetworkReply::SslHandshakeFailedError: - default: - updateActivity(Activity::FailedSoft); - return; - } - if (refreshReply) { - refreshReply->deleteLater(); - } - qDebug() << "DeviceFlow::onRefreshFinished: Error" << static_cast(error) << " - " << errorString; -} - -} // namespace Katabasis diff --git a/nix/dev.nix b/nix/dev.nix deleted file mode 100644 index 71745a1c4..000000000 --- a/nix/dev.nix +++ /dev/null @@ -1,47 +0,0 @@ -{ - perSystem = - { - config, - lib, - pkgs, - ... - }: - { - pre-commit.settings = { - hooks = { - markdownlint.enable = true; - - alejandra.enable = true; - deadnix.enable = true; - nil.enable = true; - - clang-format = { - enable = true; - types_or = [ - "c" - "c++" - "java" - "json" - "objective-c" - ]; - }; - }; - - tools.clang-tools = lib.mkForce pkgs.clang-tools_18; - }; - - devShells.default = pkgs.mkShell { - shellHook = '' - ${config.pre-commit.installationScript} - ''; - - inputsFrom = [ config.packages.fjordlauncher-unwrapped ]; - buildInputs = with pkgs; [ - ccache - ninja - ]; - }; - - formatter = pkgs.alejandra; - }; -} diff --git a/nix/distribution.nix b/nix/distribution.nix deleted file mode 100644 index a37c354ea..000000000 --- a/nix/distribution.nix +++ /dev/null @@ -1,41 +0,0 @@ -{ - inputs, - self, - ... -}: -{ - perSystem = - { - lib, - pkgs, - ... - }: - { - packages = - let - ourPackages = lib.fix (final: self.overlays.default final pkgs); - in - { - inherit (ourPackages) fjordlauncher-unwrapped fjordlauncher; - default = ourPackages.fjordlauncher; - }; - }; - - flake = { - overlays.default = - final: prev: - let - version = builtins.substring 0 8 self.lastModifiedDate or "dirty"; - in - { - fjordlauncher-unwrapped = prev.callPackage ./pkg { - inherit (inputs) libnbtplusplus; - inherit version; - }; - - fjordlauncher = prev.qt6Packages.callPackage ./pkg/wrapper.nix { - inherit (final) fjordlauncher-unwrapped; - }; - }; - }; -} diff --git a/nix/pkg/default.nix b/nix/pkg/default.nix deleted file mode 100644 index ac4e26e43..000000000 --- a/nix/pkg/default.nix +++ /dev/null @@ -1,107 +0,0 @@ -{ - lib, - stdenv, - cmake, - cmark, - darwin, - extra-cmake-modules, - gamemode, - ghc_filesystem, - jdk17, - kdePackages, - ninja, - stripJavaArchivesHook, - tomlplusplus, - zlib, - msaClientID ? null, - gamemodeSupport ? stdenv.isLinux, - version, - libnbtplusplus, -}: -assert lib.assertMsg ( - gamemodeSupport -> stdenv.isLinux -) "gamemodeSupport is only available on Linux."; -stdenv.mkDerivation { - pname = "fjordlauncher-unwrapped"; - inherit version; - - src = lib.fileset.toSource { - root = ../../.; - fileset = lib.fileset.unions ( - map (fileName: ../../${fileName}) [ - "buildconfig" - "cmake" - "launcher" - "libraries" - "program_info" - "tests" - "COPYING.md" - "CMakeLists.txt" - ] - ); - }; - - postUnpack = '' - rm -rf source/libraries/libnbtplusplus - ln -s ${libnbtplusplus} source/libraries/libnbtplusplus - ''; - - nativeBuildInputs = [ - cmake - ninja - extra-cmake-modules - jdk17 - stripJavaArchivesHook - ]; - - buildInputs = - [ - cmark - ghc_filesystem - kdePackages.qtbase - kdePackages.qtnetworkauth - kdePackages.quazip - tomlplusplus - zlib - ] - ++ lib.optionals stdenv.isDarwin [ darwin.apple_sdk.frameworks.Cocoa ] - ++ lib.optional gamemodeSupport gamemode; - - hardeningEnable = lib.optionals stdenv.isLinux [ "pie" ]; - - cmakeFlags = - [ - (lib.cmakeFeature "Launcher_BUILD_PLATFORM" "nixpkgs") - ] - ++ lib.optionals (msaClientID != null) [ - (lib.cmakeFeature "Launcher_MSA_CLIENT_ID" (toString msaClientID)) - ] - ++ lib.optionals (lib.versionOlder kdePackages.qtbase.version "6") [ - (lib.cmakeFeature "Launcher_QT_VERSION_MAJOR" "5") - ] - ++ lib.optionals stdenv.isDarwin [ - # we wrap our binary manually - (lib.cmakeFeature "INSTALL_BUNDLE" "nodeps") - # disable built-in updater - (lib.cmakeFeature "MACOSX_SPARKLE_UPDATE_FEED_URL" "''") - (lib.cmakeFeature "CMAKE_INSTALL_PREFIX" "${placeholder "out"}/Applications/") - ]; - - dontWrapQtApps = true; - - meta = { - description = "Prism Launcher fork with support for alternative auth servers"; - longDescription = '' - Allows you to have multiple, separate instances of Minecraft (each with - their own mods, texture packs, saves, etc) and helps you manage them and - their associated options with a simple interface. - ''; - homepage = "https://fjordlauncher.org/"; - license = lib.licenses.gpl3Only; - maintainers = with lib.maintainers; [ - evan-goode - ]; - mainProgram = "fjordlauncher"; - platforms = lib.platforms.linux ++ lib.platforms.darwin; - }; -} diff --git a/nix/pkg/wrapper.nix b/nix/pkg/wrapper.nix deleted file mode 100644 index 08ff99ccf..000000000 --- a/nix/pkg/wrapper.nix +++ /dev/null @@ -1,143 +0,0 @@ -{ - lib, - stdenv, - symlinkJoin, - fjordlauncher-unwrapped, - addOpenGLRunpath, - flite, - gamemode, - glfw, - glfw-wayland-minecraft, - glxinfo, - jdk8, - jdk17, - jdk21, - kdePackages, - libGL, - libpulseaudio, - libusb1, - makeWrapper, - openal, - pciutils, - udev, - vulkan-loader, - xorg, - additionalLibs ? [ ], - additionalPrograms ? [ ], - controllerSupport ? stdenv.isLinux, - gamemodeSupport ? stdenv.isLinux, - jdks ? [ - jdk21 - jdk17 - jdk8 - ], - msaClientID ? null, - textToSpeechSupport ? stdenv.isLinux, - # Adds `glfw-wayland-minecraft` to `LD_LIBRARY_PATH` - # when launched on wayland, allowing for the game to be run natively. - # Make sure to enable "Use system installation of GLFW" in instance settings - # for this to take effect - # - # Warning: This build of glfw may be unstable, and the launcher - # itself can take slightly longer to start - withWaylandGLFW ? false, -}: -assert lib.assertMsg ( - controllerSupport -> stdenv.isLinux -) "controllerSupport only has an effect on Linux."; -assert lib.assertMsg ( - textToSpeechSupport -> stdenv.isLinux -) "textToSpeechSupport only has an effect on Linux."; -assert lib.assertMsg ( - withWaylandGLFW -> stdenv.isLinux -) "withWaylandGLFW is only available on Linux."; -let - fjordlauncher' = fjordlauncher-unwrapped.override { inherit msaClientID gamemodeSupport; }; -in -symlinkJoin { - name = "fjordlauncher-${fjordlauncher'.version}"; - - paths = [ fjordlauncher' ]; - - nativeBuildInputs = - [ kdePackages.wrapQtAppsHook ] - # purposefully using a shell wrapper here for variable expansion - # see https://github.com/NixOS/nixpkgs/issues/172583 - ++ lib.optional withWaylandGLFW makeWrapper; - - buildInputs = - [ - kdePackages.qtbase - kdePackages.qtsvg - ] - ++ lib.optional ( - lib.versionAtLeast kdePackages.qtbase.version "6" && stdenv.isLinux - ) kdePackages.qtwayland; - - env = { - waylandPreExec = lib.optionalString withWaylandGLFW '' - if [ -n "$WAYLAND_DISPLAY" ]; then - export LD_LIBRARY_PATH=${lib.getLib glfw-wayland-minecraft}/lib:"$LD_LIBRARY_PATH" - fi - ''; - }; - - postBuild = - lib.optionalString withWaylandGLFW '' - qtWrapperArgs+=(--run "$waylandPreExec") - '' - + '' - wrapQtAppsHook - ''; - - qtWrapperArgs = - let - runtimeLibs = - [ - # lwjgl - glfw - libpulseaudio - libGL - openal - stdenv.cc.cc.lib - - vulkan-loader # VulkanMod's lwjgl - - udev # oshi - - xorg.libX11 - xorg.libXext - xorg.libXcursor - xorg.libXrandr - xorg.libXxf86vm - ] - ++ lib.optional textToSpeechSupport flite - ++ lib.optional gamemodeSupport gamemode.lib - ++ lib.optional controllerSupport libusb1 - ++ additionalLibs; - - runtimePrograms = [ - glxinfo - pciutils # need lspci - xorg.xrandr # needed for LWJGL [2.9.2, 3) https://github.com/LWJGL/lwjgl/issues/128 - ] ++ additionalPrograms; - in - [ "--prefix FJORDLAUNCHER_JAVA_PATHS : ${lib.makeSearchPath "bin/java" jdks}" ] - ++ lib.optionals stdenv.isLinux [ - "--set LD_LIBRARY_PATH ${addOpenGLRunpath.driverLink}/lib:${lib.makeLibraryPath runtimeLibs}" - "--prefix PATH : ${lib.makeBinPath runtimePrograms}" - ]; - - meta = { - inherit (fjordlauncher'.meta) - description - longDescription - homepage - changelog - license - maintainers - mainProgram - platforms - ; - }; -} diff --git a/scripts/getToken.py b/scripts/getToken.py deleted file mode 100644 index 92a2f5260..000000000 --- a/scripts/getToken.py +++ /dev/null @@ -1,14 +0,0 @@ -# Reimplementation of https://git.sakamoto.pl/domi/curseme/-/blob/meow/getToken.sh in python - -import io -import requests -from re import search - -# i have no idea why this is ripping the key from 4chan but what works works i guess -token = requests.get("https://arch.b4k.co/vg/thread/388569358").text -token = search("and put .*? ", token).group(0) -token = search("\$.*$", token).group(0).strip() - -output = io.open("token", "w") -output.write(token) -output.close()