diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -111,6 +111,7 @@ endif() qt5_add_dbus_interface(kiocore_SRCS org.kde.KSlaveLauncher.xml klauncher_interface) +qt5_add_dbus_interface(kiocore_SRCS org.kde.KIOFuse.VFS.xml kiofuse_interface) set_source_files_properties(org.kde.KPasswdServer.xml PROPERTIES INCLUDE authinfo.h diff --git a/src/core/desktopexecparser.cpp b/src/core/desktopexecparser.cpp --- a/src/core/desktopexecparser.cpp +++ b/src/core/desktopexecparser.cpp @@ -20,6 +20,7 @@ */ #include "desktopexecparser.h" +#include "kiofuse_interface.h" #include #include @@ -34,6 +35,8 @@ #include #include #include +#include +#include #include // CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 @@ -307,24 +310,52 @@ // Check if we need kioexec bool useKioexec = false; + org::kde::KIOFuse::VFS kiofuse_iface(QStringLiteral("org.kde.KIOFuse"), + QStringLiteral("/org/kde/KIOFuse"), + QDBusConnection::sessionBus()); + struct MountRequest { QDBusPendingReply reply; int urlIndex; }; + QVector requests; if (!mx1.hasUrls) { - for (const QUrl &url : qAsConst(d->urls)) { + for (int i = 0; i < d->urls.length(); ++i) { + const QUrl url = d->urls[i]; if (!url.isLocalFile() && !hasSchemeHandler(url)) { - useKioexec = true; - //qCDebug(KIO_CORE) << "non-local files, application does not support urls, using kioexec"; - break; + // Lets try a KIOFuse mount instead. + requests.push_back({ kiofuse_iface.mountUrl(url.toString()), i }); } } } else { // app claims to support %u/%U, check which protocols QStringList appSupportedProtocols = supportedProtocols(d->service); - for (const QUrl &url : qAsConst(d->urls)) { + for (int i = 0; i < d->urls.length(); ++i) { + const QUrl url = d->urls[i]; if (!isProtocolInSupportedList(url, appSupportedProtocols) && !hasSchemeHandler(url)) { - useKioexec = true; - //qCDebug(KIO_CORE) << "application does not support url, using kioexec:" << url; - break; + if (url.scheme() == QLatin1String("mtp") + || url.scheme() == QLatin1String("gdrive") + ) { + // KIOFuse doesn't work too well with mtp/gdrive. + // @see https://invent.kde.org/kde/kio-fuse/issues/1 (GDrive) + // @see https://invent.kde.org/kde/kio-fuse/issues/2 (MTP) + // This can be removed once those two issues are fixed. + useKioexec = true; + break; + } + // Lets try a KIOFuse mount instead. + requests.push_back({ kiofuse_iface.mountUrl(url.toString()), i }); } } } + + // Doesn't matter that we've blocked here to process the replies. + // Main thing that we want is to send the mount requests without blocking. + for (auto request : requests) { + request.reply.waitForFinished(); + if (request.reply.isError()) { + useKioexec = true; + // At this point we should just send the original urls to kioexec. + // There's no point sending urls to kioexec that go through kiofuse. + break; + } + } + if (useKioexec) { // We need to run the app through kioexec result << kioexecPath(); @@ -338,6 +369,12 @@ result << exec; result += QUrl::toStringList(d->urls); return result; + } else { + // At this point we know we're not using kioexec, so feel free to replace + // KIO URLs with their KIOFuse local path. + for (auto request : requests) { + d->urls[request.urlIndex] = QUrl::fromLocalFile(request.reply.value()); + } } if (appHasTempFileOption) { diff --git a/src/core/kiofuseinterface.h b/src/core/kiofuseinterface.h new file mode 100644 --- /dev/null +++ b/src/core/kiofuseinterface.h @@ -0,0 +1,56 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp -c KIOFuseInterface -p kiofuseinterface org.kde.kiofuse.xml + * + * qdbusxml2cpp is Copyright (C) 2019 The Qt Company Ltd. + * + * This is an auto-generated file. + * Do not edit! All changes made to it will be lost. + */ + +#ifndef KIOFUSEINTERFACE_H +#define KIOFUSEINTERFACE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kiocore_export.h" + +/* + * Proxy class for interface org.kde.KIOFuse.VFS + */ +class KIOCORE_EXPORT KIOFuseInterface: public QDBusAbstractInterface +{ + Q_OBJECT +public: + static inline const char *staticInterfaceName() + { return "org.kde.KIOFuse.VFS"; } + +public: + KIOFuseInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent = nullptr); + + ~KIOFuseInterface(); + +public Q_SLOTS: // METHODS + inline QDBusPendingReply mountUrl(const QString &remoteUrl) + { + QList argumentList; + argumentList << QVariant::fromValue(remoteUrl); + return asyncCallWithArgumentList(QStringLiteral("mountUrl"), argumentList); + } + +Q_SIGNALS: // SIGNALS +}; + +namespace org { + namespace kde { + typedef ::KIOFuseInterface KIOFuse; + } +} +#endif diff --git a/src/core/kiofuseinterface.cpp b/src/core/kiofuseinterface.cpp new file mode 100644 --- /dev/null +++ b/src/core/kiofuseinterface.cpp @@ -0,0 +1,26 @@ +/* + * This file was generated by qdbusxml2cpp version 0.8 + * Command line was: qdbusxml2cpp -c KIOFuseInterface -p kiofuseinterface org.kde.kiofuse.xml + * + * qdbusxml2cpp is Copyright (C) 2019 The Qt Company Ltd. + * + * This is an auto-generated file. + * This file may have been hand-edited. Look for HAND-EDIT comments + * before re-generating it. + */ + +#include "kiofuseinterface.h" + +/* + * Implementation of interface class KIOFuseInterface + */ + +KIOFuseInterface::KIOFuseInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent) + : QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent) +{ +} + +KIOFuseInterface::~KIOFuseInterface() +{ +} + diff --git a/src/core/org.kde.KIOFuse.VFS.xml b/src/core/org.kde.KIOFuse.VFS.xml new file mode 100644 --- /dev/null +++ b/src/core/org.kde.KIOFuse.VFS.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -77,6 +77,7 @@ qt5_add_dbus_adaptor(kiowidgets_SRCS org.kde.kio.FileUndoManager.xml fileundomanager_p.h KIO::FileUndoManagerPrivate fileundomanager_adaptor KIOFileUndoManagerAdaptor) qt5_add_dbus_interface(kiowidgets_SRCS org.kde.kuiserver.xml kuiserver_interface) +qt5_add_dbus_interface(kiowidgets_SRCS org.kde.KIOFuse.VFS.xml kiofuse_interface) ki18n_wrap_ui(kiowidgets_SRCS checksumswidget.ui diff --git a/src/widgets/krun.cpp b/src/widgets/krun.cpp --- a/src/widgets/krun.cpp +++ b/src/widgets/krun.cpp @@ -59,6 +59,7 @@ #include "krecentdocument.h" #include "kdesktopfileactions.h" #include +#include "kiofuse_interface.h" #include #include @@ -572,19 +573,44 @@ const QStringList appSupportedProtocols = KIO::DesktopExecParser::supportedProtocols(_service); QList urls(_urls); if (!appSupportedProtocols.contains(QLatin1String("KIO"))) { - for (QUrl &url : urls) { - bool supported = KIO::DesktopExecParser::isProtocolInSupportedList(url, appSupportedProtocols); - //qDebug() << "Looking at url=" << url << " supported=" << supported; - if (!supported && KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) { + org::kde::KIOFuse::VFS kiofuse_iface(QStringLiteral("org.kde.KIOFuse"), + QStringLiteral("/org/kde/KIOFuse"), + QDBusConnection::sessionBus()); + struct MountRequest { QDBusPendingReply reply; int urlIndex; }; + QVector requests; + for (int i = 0; i < urls.length(); ++i) { + const QUrl url = urls[i]; + if (KIO::DesktopExecParser::isProtocolInSupportedList(url, appSupportedProtocols)) + continue; + if (KProtocolInfo::protocolClass(url.scheme()) == QLatin1String(":local")) { // Maybe we can resolve to a local URL? KIO::StatJob *job = KIO::mostLocalUrl(url); if (job->exec()) { // ## nasty nested event loop! const QUrl localURL = job->mostLocalUrl(); if (localURL != url) { - url = localURL; + urls[i] = localURL; //qDebug() << "Changed to" << localURL; + // KIOFuse doesn't work too well with mtp/gdrive. + // @see https://invent.kde.org/kde/kio-fuse/issues/1 (GDrive) + // @see https://invent.kde.org/kde/kio-fuse/issues/2 (MTP) + // This can be removed once those two issues are fixed. + } else if (url.scheme() != QLatin1String("mtp") + && url.scheme() != QLatin1String("gdrive")) { + // Can't convert... + // Lets try a KIOFuse mount instead. + requests.push_back({ kiofuse_iface.mountUrl(url.toString()), i }); } } + } else { + requests.push_back({ kiofuse_iface.mountUrl(url.toString()), i }); + } + } + // Doesn't matter that we've blocked here to process the replies. + // Main thing that we want is to send the mount requests without blocking. + for (auto request : requests) { + request.reply.waitForFinished(); + if (!request.reply.isError()) { + urls[request.urlIndex] = QUrl::fromLocalFile(request.reply.value()); } } } diff --git a/src/widgets/org.kde.KIOFuse.VFS.xml b/src/widgets/org.kde.KIOFuse.VFS.xml new file mode 100644 --- /dev/null +++ b/src/widgets/org.kde.KIOFuse.VFS.xml @@ -0,0 +1,9 @@ + + + + + + + + +