diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -85,6 +85,7 @@ kmountpoint.cpp kcoredirlister.cpp faviconscache.cpp + kiofuseinterface.cpp ksslcertificatemanager.cpp ksslerroruidata.cpp @@ -220,6 +221,7 @@ DesktopExecParser FileSystemFreeSpaceJob BatchRenameJob + KIOFuseInterface PREFIX KIO REQUIRED_HEADERS KIO_namespaced_HEADERS 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 "kiofuseinterface.h" #include #include @@ -34,6 +35,8 @@ #include #include #include +#include +#include #include // CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 @@ -307,23 +310,57 @@ // Check if we need kioexec bool useKioexec = false; + org::kde::KIOFuse kiofuse_iface(QStringLiteral("org.kde.KIOFuse"), + QStringLiteral("/org/kde/KIOFuse"), + QDBusConnection::sessionBus()); + struct MountRequest { QDBusPendingReply reply; int urlIndex; }; + QVector requests; if (!mx1.hasUrls) { - Q_FOREACH (const QUrl &url, d->urls) + for(QUrl &url : d->urls) { 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(qMakePair(kiofuse_iface.mountUrl(url.toString()), url)); + } else { + parsedUrls.append(url); } + } } else { // app claims to support %u/%U, check which protocols QStringList appSupportedProtocols = supportedProtocols(d->service); - Q_FOREACH (const QUrl &url, d->urls) { + for (QUrl &url : d->urls) { 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(qMakePair(kiofuse_iface.mountUrl(url.toString()), url)); + } else { + parsedUrls.append(url); } } } + + // 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 reply : requests) { + reply.first.waitForFinished(); + if (reply.first.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; + } else { + reply.second = QUrl::fromLocalFile(reply.first.value()); + } + parsedUrls.append(reply.second); + } + if (useKioexec) { // We need to run the app through kioexec result << kioexecPath(); @@ -337,6 +374,8 @@ result << exec; result += QUrl::toStringList(d->urls); return result; + } else { + d->urls = parsedUrls; } 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/widgets/krun.cpp b/src/widgets/krun.cpp --- a/src/widgets/krun.cpp +++ b/src/widgets/krun.cpp @@ -60,6 +60,7 @@ #include "kdesktopfileactions.h" #include "executablefileopendialog_p.h" #include +#include #include #include @@ -589,19 +590,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 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()); } } }