diff --git a/cmake/MacOSXBundleInfo.plist.in b/cmake/MacOSXBundleInfo.plist.in index c439efe25..6d3845dfc 100644 --- a/cmake/MacOSXBundleInfo.plist.in +++ b/cmake/MacOSXBundleInfo.plist.in @@ -79,6 +79,14 @@ curseforge + + CFBundleURLName + Prismlauncher + CFBundleURLSchemes + + prismlauncher + + diff --git a/launcher/Application.cpp b/launcher/Application.cpp index ff23a6ad7..d319a85a7 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -292,12 +292,17 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) QString adjustedBy; QString dataPath; // change folder + QString dataDirEnv; QString dirParam = parser.value("dir"); if (!dirParam.isEmpty()) { // the dir param. it makes multimc data path point to whatever the user specified // on command line adjustedBy = "Command line"; dataPath = dirParam; + } else if (dataDirEnv = QProcessEnvironment::systemEnvironment().value(QString("%1_DATA_DIR").arg(BuildConfig.LAUNCHER_NAME.toUpper())); + !dataDirEnv.isEmpty()) { + adjustedBy = "System environment"; + dataPath = dataDirEnv; } else { QDir foo; if (DesktopServices::isSnap()) { @@ -443,7 +448,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) // search the dataPath() // seach app data standard path - if (!foundLoggingRules && !isPortable() && dirParam.isEmpty()) { + if (!foundLoggingRules && !isPortable() && dirParam.isEmpty() && dataDirEnv.isEmpty()) { logRulesPath = QStandardPaths::locate(QStandardPaths::AppDataLocation, FS::PathCombine("..", logRulesFile)); if (!logRulesPath.isEmpty()) { qDebug() << "Found" << logRulesPath << "..."; @@ -555,6 +560,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("NumberOfConcurrentTasks", 10); m_settings->registerSetting("NumberOfConcurrentDownloads", 6); m_settings->registerSetting("NumberOfManualRetries", 1); + m_settings->registerSetting("RequestTimeout", 60); QString defaultMonospace; int defaultSize = 11; diff --git a/launcher/Application.h b/launcher/Application.h index 7669e08ec..8303c7475 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -48,7 +48,6 @@ #include #include "minecraft/launch/MinecraftServerTarget.h" -#include "ui/themes/CatPack.h" class LaunchController; class LocalPeer; @@ -193,6 +192,8 @@ class Application : public QApplication { void globalSettingsClosed(); int currentCatChanged(int index); + void oauthReplyRecieved(QVariantMap); + #ifdef Q_OS_MACOS void clickedOnDock(); #endif diff --git a/launcher/minecraft/auth/AuthFlow.cpp b/launcher/minecraft/auth/AuthFlow.cpp index 5648fe9f6..45926206c 100644 --- a/launcher/minecraft/auth/AuthFlow.cpp +++ b/launcher/minecraft/auth/AuthFlow.cpp @@ -59,6 +59,9 @@ void AuthFlow::executeTask() void AuthFlow::nextStep() { + if (!Task::isRunning()) { + return; + } if (m_steps.size() == 0) { // we got to the end without an incident... assume this is all. m_currentStep.reset(); @@ -143,4 +146,11 @@ bool AuthFlow::changeState(AccountTaskState newState, QString reason) return false; } } +} +bool AuthFlow::abort() +{ + emitAborted(); + if (m_currentStep) + m_currentStep->abort(); + return true; } \ No newline at end of file diff --git a/launcher/minecraft/auth/AuthFlow.h b/launcher/minecraft/auth/AuthFlow.h index d99deec3c..4d18ac845 100644 --- a/launcher/minecraft/auth/AuthFlow.h +++ b/launcher/minecraft/auth/AuthFlow.h @@ -24,6 +24,9 @@ class AuthFlow : public Task { AccountTaskState taskState() { return m_taskState; } + public slots: + bool abort() override; + signals: void authorizeWithBrowser(const QUrl& url); void authorizeWithBrowserWithExtra(QString url, QString code, int expiresIn); diff --git a/launcher/minecraft/auth/AuthStep.h b/launcher/minecraft/auth/AuthStep.h index cbe157790..aaaec6e7f 100644 --- a/launcher/minecraft/auth/AuthStep.h +++ b/launcher/minecraft/auth/AuthStep.h @@ -34,6 +34,7 @@ class AuthStep : public QObject { public slots: virtual void perform() = 0; + virtual void abort() {} signals: void finished(AccountTaskState resultingState, QString message); diff --git a/launcher/minecraft/auth/steps/GetSkinStep.cpp b/launcher/minecraft/auth/steps/GetSkinStep.cpp index d9785b16a..a54366b50 100644 --- a/launcher/minecraft/auth/steps/GetSkinStep.cpp +++ b/launcher/minecraft/auth/steps/GetSkinStep.cpp @@ -29,5 +29,5 @@ void GetSkinStep::onRequestDone() { if (m_task->error() == QNetworkReply::NoError) m_data->minecraftProfile.skin.data = *m_response; - emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Got skin")); + emit finished(AccountTaskState::STATE_WORKING, tr("Got skin")); } diff --git a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp index 22f5d4069..7df93d04d 100644 --- a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp +++ b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp @@ -46,6 +46,8 @@ MSADeviceCodeStep::MSADeviceCodeStep(AccountData* data) : AuthStep(data) { m_clientId = APPLICATION->getMSAClientID(); + connect(&m_expiration_timer, &QTimer::timeout, this, &MSADeviceCodeStep::abort); + connect(&m_pool_timer, &QTimer::timeout, this, &MSADeviceCodeStep::authenticateUser); } QString MSADeviceCodeStep::describe() @@ -133,9 +135,7 @@ void MSADeviceCodeStep::deviceAutorizationFinished() m_expiration_timer.setTimerType(Qt::VeryCoarseTimer); m_expiration_timer.setInterval(rsp.expires_in * 1000); m_expiration_timer.setSingleShot(true); - connect(&m_expiration_timer, &QTimer::timeout, this, &MSADeviceCodeStep::abort); m_expiration_timer.start(); - m_pool_timer.setTimerType(Qt::VeryCoarseTimer); m_pool_timer.setSingleShot(true); startPoolTimer(); @@ -157,8 +157,12 @@ void MSADeviceCodeStep::startPoolTimer() if (m_is_aborted) { return; } + if (m_expiration_timer.remainingTime() < interval * 1000) { + perform(); + return; + } + m_pool_timer.setInterval(interval * 1000); - connect(&m_pool_timer, &QTimer::timeout, this, &MSADeviceCodeStep::authenticateUser); m_pool_timer.start(); } diff --git a/launcher/minecraft/auth/steps/MSADeviceCodeStep.h b/launcher/minecraft/auth/steps/MSADeviceCodeStep.h index e53eebc62..024927b31 100644 --- a/launcher/minecraft/auth/steps/MSADeviceCodeStep.h +++ b/launcher/minecraft/auth/steps/MSADeviceCodeStep.h @@ -51,7 +51,7 @@ class MSADeviceCodeStep : public AuthStep { QString describe() override; public slots: - void abort(); + void abort() override; signals: void authorizeWithBrowser(QString url, QString code, int expiresIn); diff --git a/launcher/minecraft/auth/steps/MSAStep.cpp b/launcher/minecraft/auth/steps/MSAStep.cpp index 3f31cdc16..3db04bf2f 100644 --- a/launcher/minecraft/auth/steps/MSAStep.cpp +++ b/launcher/minecraft/auth/steps/MSAStep.cpp @@ -35,22 +35,74 @@ #include "MSAStep.h" -#include #include #include +#include +#include #include "Application.h" +#include "BuildConfig.h" +#include "FileSystem.h" + +#include +#include +#include + +bool isSchemeHandlerRegistered() +{ +#ifdef Q_OS_LINUX + QProcess process; + process.start("xdg-mime", { "query", "default", "x-scheme-handler/" + BuildConfig.LAUNCHER_APP_BINARY_NAME }); + process.waitForFinished(); + QString output = process.readAllStandardOutput().trimmed(); + + return output.contains(BuildConfig.LAUNCHER_APP_BINARY_NAME); + +#elif defined(Q_OS_WIN) + QString regPath = QString("HKEY_CURRENT_USER\\Software\\Classes\\%1").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME); + QSettings settings(regPath, QSettings::NativeFormat); + + return settings.contains("shell/open/command/."); +#endif + return true; +} + +class CustomOAuthOobReplyHandler : public QOAuthOobReplyHandler { + Q_OBJECT + + public: + explicit CustomOAuthOobReplyHandler(QObject* parent = nullptr) : QOAuthOobReplyHandler(parent) + { + connect(APPLICATION, &Application::oauthReplyRecieved, this, &QOAuthOobReplyHandler::callbackReceived); + } + ~CustomOAuthOobReplyHandler() override + { + disconnect(APPLICATION, &Application::oauthReplyRecieved, this, &QOAuthOobReplyHandler::callbackReceived); + } + QString callback() const override { return BuildConfig.LAUNCHER_APP_BINARY_NAME + "://oauth/microsoft"; } +}; MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(silent) { m_clientId = APPLICATION->getMSAClientID(); + if (QCoreApplication::applicationFilePath().startsWith("/tmp/.mount_") || + QFile::exists(FS::PathCombine(APPLICATION->root(), "portable.txt")) || !isSchemeHandlerRegistered()) - auto replyHandler = new QOAuthHttpServerReplyHandler(1337, this); - replyHandler->setCallbackText( - "