Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ build*
cscope.*
tags
t1.cfg
.idea

## Ignore Visual Studio Code config & environment files

Expand Down
4 changes: 4 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ set(client_SRCS
proxyauthdialog.cpp
tooltipupdater.h
tooltipupdater.cpp
urischemehandler.h
urischemehandler.cpp
notificationconfirmjob.h
notificationconfirmjob.cpp
guiutility.h
Expand Down Expand Up @@ -241,6 +243,8 @@ set(client_SRCS
wizard/accountwizardcontroller.cpp
wizard/flow2authwidget.h
wizard/flow2authwidget.cpp
wizard/providersignuppage.h
wizard/providersignuppage.cpp
wizard/linklabel.h
wizard/linklabel.cpp
integration/fileactionsmodel.h
Expand Down
70 changes: 52 additions & 18 deletions src/gui/application.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/*
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2014 ownCloud GmbH
Expand All @@ -15,13 +15,13 @@
#include "configfile.h"
#include "connectionvalidator.h"
#include "creds/abstractcredentials.h"
#include "editlocallymanager.h"
#include "folder.h"
#include "folderman.h"
#include "logger.h"
#include "pushnotifications.h"
#include "socketapi/socketapi.h"
#include "theme.h"
#include "urischemehandler.h"

#if defined(BUILD_UPDATER)
#include "updater/ocupdater.h"
Expand Down Expand Up @@ -50,7 +50,6 @@
#include <QMessageBox>
#include <QDesktopServices>
#include <QGuiApplication>
#include <QUrlQuery>
#include <QVersionNumber>
#include <QRandomGenerator>
#include <QHttp2Configuration>
Expand Down Expand Up @@ -488,7 +487,7 @@

_gui->createTray();

handleEditLocallyFromOptions();
handleUriFromOptions();

#ifdef Q_OS_MACOS
// If any sync folder needs sandbox reapproval after upgrading to v33+,
Expand Down Expand Up @@ -666,7 +665,7 @@
qApp->quit();
}

handleEditLocallyFromOptions();
handleUriFromOptions();

if (AccountSetupCommandLineManager::instance()->isCommandLineParsed()) {
AccountSetupCommandLineManager::instance()->setupAccountFromCommandLine();
Expand Down Expand Up @@ -819,6 +818,12 @@
void Application::slotCheckConnection()
{
if (AccountManager::instance()->accounts().isEmpty()) {
if (_suppressNextEmptyAccountCheck) {
qCInfo(lcApplication) << "Suppressing empty-account check after handling login URI.";
_suppressNextEmptyAccountCheck = false;
return;
}

// let gui open the setup wizard
_gui->slotOpenSettingsDialog();

Expand Down Expand Up @@ -972,14 +977,17 @@
} else if (option.endsWith(QStringLiteral(APPLICATION_DOTVIRTUALFILE_SUFFIX))) {
// virtual file, open it after the Folder were created (if the app is not terminated)
QTimer::singleShot(0, this, [this, option] { openVirtualFile(option); });
} else if (option.startsWith(QStringLiteral(APPLICATION_URI_HANDLER_SCHEME "://open"))) {
// see the section Local file editing of the Architecture page of the user documentation
_editFileLocallyUrl = QUrl::fromUserInput(option);
if (!_editFileLocallyUrl.isValid()) {
_editFileLocallyUrl.clear();
const auto errorParsingLocalFileEditingUrl = QStringLiteral("The supplied url for local file editing '%1' is invalid!").arg(option);
qCInfo(lcApplication) << errorParsingLocalFileEditingUrl;
showHint(errorParsingLocalFileEditingUrl.toStdString());
} else if (option.startsWith(QStringLiteral(APPLICATION_URI_HANDLER_SCHEME "://"))) {
_uriSchemeUrl = QUrl{option};
qCInfo(lcApplication) << "Command line contains custom URI scheme request:"
<< "scheme=" << _uriSchemeUrl.scheme()
<< "host=" << _uriSchemeUrl.host()
<< "path=" << _uriSchemeUrl.path();
if (!_uriSchemeUrl.isValid()) {
_uriSchemeUrl.clear();
const auto errorParsingUri = QStringLiteral("The supplied url '%1' is invalid!").arg(option);
qCInfo(lcApplication) << errorParsingUri;
showHint(errorParsingUri.toStdString());
}
} else if (option == QStringLiteral("--overrideserverurl")) {
if (it.hasNext() && !it.peekNext().startsWith(QLatin1String("--"))) {
Expand Down Expand Up @@ -1122,14 +1130,37 @@
_helpOnly = true;
}

void Application::handleEditLocallyFromOptions()
void Application::handleUriFromOptions()
{
if (!_editFileLocallyUrl.isValid()) {
if (!_uriSchemeUrl.isValid()) {
qCDebug(lcApplication) << "No pending custom URI scheme request from command line options.";
return;
}

EditLocallyManager::instance()->handleRequest(_editFileLocallyUrl);
_editFileLocallyUrl.clear();
handleUriSchemeRequest(_uriSchemeUrl);
_uriSchemeUrl.clear();
}

bool Application::handleUriSchemeRequest(const QUrl &url)
{
const auto parsedUri = UriSchemeHandler::parseUri(url);
const auto suppressEmptyAccountCheck = parsedUri.action == UriSchemeHandler::Action::Login
&& AccountManager::instance()->accounts().isEmpty();
const auto wasCheckConnectionTimerActive = _checkConnectionTimer.isActive();
if (suppressEmptyAccountCheck) {
_suppressNextEmptyAccountCheck = true;
_checkConnectionTimer.stop();
}

const auto handled = UriSchemeHandler::handleUri(url);
if (!handled && suppressEmptyAccountCheck) {
_suppressNextEmptyAccountCheck = false;
if (wasCheckConnectionTimerActive) {
_checkConnectionTimer.start();
}
}

return handled;
}

QString enforcedLanguage()
Expand Down Expand Up @@ -1290,8 +1321,11 @@
} else if (!openEvent->url().isEmpty() && openEvent->url().isValid()) {
// On macOS, Qt does not handle receiving a custom URI as it does on other systems (as an application argument).
// Instead, it sends out a QFileOpenEvent. We therefore need custom handling for our URI handling on macOS.
qCInfo(lcApplication) << "macOS: Opening local file for editing: " << openEvent->url();
EditLocallyManager::instance()->handleRequest(openEvent->url());
qCInfo(lcApplication) << "macOS QFileOpenEvent contains custom URI scheme request:"
<< "scheme=" << openEvent->url().scheme()
<< "host=" << openEvent->url().host()
<< "path=" << openEvent->url().path();
handleUriSchemeRequest(openEvent->url());
} else {
const auto errorParsingLocalFileEditingUrl = QStringLiteral("The supplied url for local file editing '%1' is invalid!").arg(openEvent->url().toString());
qCInfo(lcApplication) << errorParsingLocalFileEditingUrl;
Expand Down
6 changes: 4 additions & 2 deletions src/gui/application.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class Application : public QApplication
bool sendMessage(const QString &message);

void showMainDialog();
bool handleUriSchemeRequest(const QUrl &url);

[[nodiscard]] ownCloudGui *gui() const;

Expand Down Expand Up @@ -120,7 +121,7 @@ protected slots:
private:
void setHelp();

void handleEditLocallyFromOptions();
void handleUriFromOptions();

AccountManager::AccountsRestoreResult restoreLegacyAccount();
void setupConfigFile();
Expand Down Expand Up @@ -163,7 +164,8 @@ protected slots:
bool _userTriggeredConnect = false;
bool _debugMode = false;
bool _backgroundMode = false;
QUrl _editFileLocallyUrl;
bool _suppressNextEmptyAccountCheck = false;
QUrl _uriSchemeUrl;

ClientProxy _proxy;

Expand Down
19 changes: 17 additions & 2 deletions src/gui/cocoainitializer_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
#import <AppKit/NSApplication.h>

#include "application.h"
#include "editlocallymanager.h"

#include <QTimer>

/* In theory, we should be able to just capture QFileOpenEvents
* when we open our custom URLs in our Application class and be
Expand Down Expand Up @@ -52,7 +53,21 @@ - (void)handleURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEv
{
NSURL* url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]];
const auto qtUrl = QUrl::fromNSURL(url);
OCC::EditLocallyManager::instance()->handleRequest(qtUrl);
if (qApp) {
QTimer::singleShot(0, qApp, [qtUrl] {
qCInfo(OCC::lcApplication) << "macOS AppleEvent custom URI scheme request:"
<< "scheme=" << qtUrl.scheme()
<< "host=" << qtUrl.host()
<< "path=" << qtUrl.path();
if (auto application = qobject_cast<OCC::Application *>(qApp)) {
application->handleUriSchemeRequest(qtUrl);
} else {
qCWarning(OCC::lcApplication) << "Could not dispatch macOS AppleEvent custom URI scheme request because qApp is not an Application.";
}
});
} else {
qCWarning(OCC::lcApplication) << "Could not dispatch macOS AppleEvent custom URI scheme request because qApp is not available yet.";
}
}

@end
Expand Down
99 changes: 99 additions & 0 deletions src/gui/owncloudsetupwizard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "owncloudsetupwizard.h"

#include "accountmanager.h"
#include "folderman.h"
#include "systray.h"
#include "theme.h"
Expand Down Expand Up @@ -70,6 +71,37 @@
owncloudSetupWizard.clear();
}

void OwncloudSetupWizard::runWizardForLoginFlow(QObject *obj, const char *amember, const QUrl &serverUrl, QWidget *parent)

Check warning on line 74 in src/gui/owncloudsetupwizard.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "obj" of type "class QObject *" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ84fuP0iXB7nvuYv_ro&open=AZ84fuP0iXB7nvuYv_ro&pullRequest=10311

Check warning on line 74 in src/gui/owncloudsetupwizard.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "parent" of type "class QWidget *" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ84fuP0iXB7nvuYv_rq&open=AZ84fuP0iXB7nvuYv_rq&pullRequest=10311

Check warning on line 74 in src/gui/owncloudsetupwizard.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "amember" of type "const char *" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ84fuP0iXB7nvuYv_rp&open=AZ84fuP0iXB7nvuYv_rp&pullRequest=10311

Check warning on line 74 in src/gui/owncloudsetupwizard.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Make the type of this parameter a pointer-to-const. The current type of "obj" is "class QObject *".

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ84fuP0iXB7nvuYv_rn&open=AZ84fuP0iXB7nvuYv_rn&pullRequest=10311
{
#if defined ENFORCE_SINGLE_ACCOUNT
if (!AccountManager::instance()->accounts().isEmpty()) {
return;
}
#endif

if (!owncloudSetupWizard.isNull()) {
qCInfo(lcWizard) << "Restarting existing setup wizard for URI login flow.";
owncloudSetupWizard->finish(QDialog::Rejected);
}

if (!owncloudSetupWizard.isNull()) {
bringWizardToFrontIfVisible();
return;
}

owncloudSetupWizard = new OwncloudSetupWizard(parent);
connect(owncloudSetupWizard, SIGNAL(ownCloudWizardDone(int)), obj, amember);

FolderMan::instance()->setSyncEnabled(false);
if (owncloudSetupWizard->startQmlWizardForLoginFlow(serverUrl)) {
return;
}

emit owncloudSetupWizard->ownCloudWizardDone(QDialog::Rejected);
owncloudSetupWizard->deleteLater();
owncloudSetupWizard.clear();
}

bool OwncloudSetupWizard::bringWizardToFrontIfVisible()
{
if (owncloudSetupWizard.isNull()
Expand Down Expand Up @@ -151,6 +183,73 @@
return true;
}

bool OwncloudSetupWizard::startQmlWizardForLoginFlow(const QUrl &serverUrl)
{
auto *engine = Systray::instance()->trayEngine();

Check warning on line 188 in src/gui/owncloudsetupwizard.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "engine" of type "class QQmlApplicationEngine *" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ84fuP0iXB7nvuYv_rr&open=AZ84fuP0iXB7nvuYv_rr&pullRequest=10311
if (!engine) {
qCWarning(lcWizard) << "Cannot start QML account wizard without a QML engine.";
return false;
}

_qmlController = new AccountWizardController(this);
if (!_qmlController->setServerUrlForLoginFlow(serverUrl)) {
qCWarning(lcWizard) << "Cannot start QML account wizard for URI login flow with an unconfigured server URL.";
_qmlController->deleteLater();
_qmlController = nullptr;
return false;
}
_qmlController->submitServerUrl();

QQmlComponent component(engine, QStringLiteral("qrc:/qml/src/gui/wizard/qml/AccountWizardWindow.qml"));
QVariantMap initialProperties;
initialProperties.insert(QStringLiteral("controller"), QVariant::fromValue<QObject *>(_qmlController));
auto *createdObject = component.createWithInitialProperties(initialProperties);

Check warning on line 206 in src/gui/owncloudsetupwizard.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "createdObject" of type "class QObject *" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ84fuP0iXB7nvuYv_rs&open=AZ84fuP0iXB7nvuYv_rs&pullRequest=10311

if (component.isError()) {
qCWarning(lcWizard) << "Failed to load QML account wizard:" << component.errors();
}

_qmlWizardWindow = qobject_cast<QQuickWindow *>(createdObject);
if (!_qmlWizardWindow) {
if (createdObject) {
createdObject->deleteLater();
}
_qmlController->deleteLater();
_qmlController = nullptr;
return false;
}

_qmlWizardWindow->setIcon(Theme::instance()->applicationIcon());

#ifdef Q_OS_MACOS
auto *fgbg = new ForegroundBackground(this);
_qmlWizardWindow->installEventFilter(fgbg);
#endif

connect(_qmlController, &AccountWizardController::finished, this, &OwncloudSetupWizard::finish);
connect(_qmlWizardWindow, &QQuickWindow::visibleChanged, this, [this](bool visible) {

Check warning on line 230 in src/gui/owncloudsetupwizard.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unmodified variable "visible" of type "_Bool" should be const-qualified.

See more on https://sonarcloud.io/project/issues?id=nextcloud_desktop&issues=AZ84fuP0iXB7nvuYv_rt&open=AZ84fuP0iXB7nvuYv_rt&pullRequest=10311
if (!visible) {
finish(QDialog::Rejected);
}
});
connect(_qmlWizardWindow, &QObject::destroyed, this, [this] {
finish(QDialog::Rejected);
});

_qmlWizardWindow->show();
_qmlWizardWindow->raise();
_qmlWizardWindow->requestActivate();

#ifdef Q_OS_MACOS
styleNativeTitleBar(_qmlWizardWindow, /*hideTitleText=*/true);
connect(_qmlWizardWindow, &QQuickWindow::colorChanged, this, [this] {
styleNativeTitleBar(_qmlWizardWindow, /*hideTitleText=*/true);
});
#endif

return true;
}

void OwncloudSetupWizard::finish(int result)
{
if (_finished) {
Expand Down
3 changes: 3 additions & 0 deletions src/gui/owncloudsetupwizard.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
#ifndef OWNCLOUDSETUPWIZARD_H
#define OWNCLOUDSETUPWIZARD_H

#include <QObject>

Check failure on line 10 in src/gui/owncloudsetupwizard.h

View workflow job for this annotation

GitHub Actions / build

src/gui/owncloudsetupwizard.h:10:10 [clang-diagnostic-error]

'QObject' file not found
#include <QWidget>
#include <QPointer>
#include <QUrl>

class QQuickWindow;

Expand All @@ -27,6 +28,7 @@
public:
/** Run the wizard */
static void runWizard(QObject *obj, const char *amember, QWidget *parent = nullptr, bool forceRestart = false);
static void runWizardForLoginFlow(QObject *obj, const char *amember, const QUrl &serverUrl, QWidget *parent = nullptr);
static bool bringWizardToFrontIfVisible();

signals:
Expand All @@ -37,6 +39,7 @@
explicit OwncloudSetupWizard(QObject *parent = nullptr);
~OwncloudSetupWizard() override;
bool startQmlWizard();
bool startQmlWizardForLoginFlow(const QUrl &serverUrl);
void finish(int result);

AccountWizardController *_qmlController = nullptr;
Expand Down
Loading
Loading