diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(kioexec) add_subdirectory(urifilters) add_subdirectory(kcms) +add_subdirectory(mountserviced) if(NOT WIN32 AND NOT ANDROID) # arpa/nameser.h diff --git a/src/core/kdiskfreespaceinfo.cpp b/src/core/kdiskfreespaceinfo.cpp --- a/src/core/kdiskfreespaceinfo.cpp +++ b/src/core/kdiskfreespaceinfo.cpp @@ -1,4 +1,4 @@ -/* +/* * kdiskfreespaceinfo.h * * Copyright 2008 Sebastian Trug diff --git a/src/ioslaves/file/file.cpp b/src/ioslaves/file/file.cpp --- a/src/ioslaves/file/file.cpp +++ b/src/ioslaves/file/file.cpp @@ -742,7 +742,7 @@ f.close(); - if (f.error() != QFile::NoError) { + if (f.error() != QFile::NoError && f.isOpen()) { qCWarning(KIO_FILE) << "Error when closing file descriptor:" << f.errorString(); error(KIO::ERR_CANNOT_WRITE, dest_orig); return; diff --git a/src/ioslaves/file/file_unix.cpp b/src/ioslaves/file/file_unix.cpp --- a/src/ioslaves/file/file_unix.cpp +++ b/src/ioslaves/file/file_unix.cpp @@ -348,7 +348,7 @@ src_file.close(); dest_file.close(); - if (dest_file.error() != QFile::NoError) { + if (dest_file.error() != QFile::NoError && dest_file.isOpen()) { qCWarning(KIO_FILE) << "Error when closing file descriptor[2]:" << dest_file.errorString(); error(KIO::ERR_CANNOT_WRITE, dest); #if HAVE_POSIX_ACL diff --git a/src/kcms/kio/CMakeLists.txt b/src/kcms/kio/CMakeLists.txt --- a/src/kcms/kio/CMakeLists.txt +++ b/src/kcms/kio/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory( uasproviders ) find_package(KF5TextWidgets ${KF5_DEP_VERSION} REQUIRED) +find_package(KDED CONFIG REQUIRED) ########### next target ############### @@ -18,6 +19,7 @@ useragentselectordlg.cpp netpref.cpp cache.cpp + fusemanager.cpp ksaveioconfig.cpp) ki18n_wrap_ui(kcm_kio_PART_SRCS @@ -27,7 +29,12 @@ kproxydlg.ui kcookiespolicies.ui kcookiesmanagement.ui - kcookiespolicyselectiondlg.ui) + kcookiespolicyselectiondlg.ui + fusemanager.ui + fuseserviceselector.ui) + +qt5_add_dbus_interface(kcm_kio_PART_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../../mountserviced/org.kde.kio.MountServiceManager.xml mountserviced_interface) +qt5_add_dbus_interface(kcm_kio_PART_SRCS ${KDED_DBUS_INTERFACE} kded_interface) add_library(kcm_kio MODULE ${kcm_kio_PART_SRCS}) @@ -51,5 +58,5 @@ ########### install files ############### install( FILES smb.desktop cookies.desktop useragent.desktop cache.desktop - netpref.desktop proxy.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) + netpref.desktop proxy.desktop fusemanager.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) install( FILES uasprovider.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR} ) diff --git a/src/kcms/kio/fusemanager.h b/src/kcms/kio/fusemanager.h new file mode 100644 --- /dev/null +++ b/src/kcms/kio/fusemanager.h @@ -0,0 +1,87 @@ +/*** + Copyright (C) 2019 by Chinmoy Ranjan Pradhan + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +***/ + +#ifndef FUSEMANAGER_H +#define FUSEMANAGER_H + +#include + +#include + +#include "ui_fusemanager.h" +#include "ui_fuseserviceselector.h" + +class QDialogButtonBox; + +class FuseManager : public KCModule +{ + Q_OBJECT + +public: + FuseManager(QWidget *parent, const QVariantList &args); + ~FuseManager(); + + void load() override; + void save() override; + QString quickHelp() const override; + +private Q_SLOTS: + void addPressed(); + void changePressed(); + void deletePressed(); + void deleteAllPressed(); + + void updateButtons(); + void selectionChanged(); + void availableServicesChanged(const QStringList &services); + bool handleDuplicate(const QString &protocol, const QString &handler); + +private: + int mSelectedItemsCount; + QStringList mAvailableServices; + QStringList mProtocolsToRemove; + QMap mManagedProtocols; + Ui::FuseManagerUI mUi; +}; + +class FuseServiceSelectorDlg : public QDialog +{ + Q_OBJECT + +public: + explicit FuseServiceSelectorDlg(QWidget* parent, const QList &fuseHandlers); + ~FuseServiceSelectorDlg(); + + int service() const; + QString protocol() const; + + void setProtocolService(const QString &protocol, int serviceIndex); + +protected Q_SLOTS: + void slotTextChanged (const QString &text); + void slotServiceChanged(int serviceIndex); + +private: + QDialogButtonBox *mButtonBox; + Ui::FuseServiceSelectorDlgUI mUi; +}; + +#endif // FUSEMANAGER_H + diff --git a/src/kcms/kio/fusemanager.cpp b/src/kcms/kio/fusemanager.cpp new file mode 100644 --- /dev/null +++ b/src/kcms/kio/fusemanager.cpp @@ -0,0 +1,256 @@ +/*** + Copyright (C) 2019 by Chinmoy Ranjan Pradhan + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +***/ + + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "fusemanager.h" +#include "mountserviced_interface.h" + +K_PLUGIN_FACTORY_DECLARATION(KioConfigFactory) + +FuseServiceSelectorDlg::FuseServiceSelectorDlg (QWidget* parent, const QList &fuseServices) + : QDialog (parent) + , mButtonBox(nullptr) +{ + QWidget *mainWidget = new QWidget(this); + QVBoxLayout *mainLayout = new QVBoxLayout(this); + mainLayout->addWidget(mainWidget); + + mUi.setupUi(mainWidget); + mUi.cbServices->setMinimumWidth(mUi.cbServices->fontMetrics().maxWidth() * 15); + mUi.cbServices->addItems(fuseServices); + mUi.leProtocol->setFocus(); + + mButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + connect(mButtonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(mButtonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + mainLayout->addWidget(mButtonBox); + + connect(mUi.leProtocol, &QLineEdit::textEdited, this, &FuseServiceSelectorDlg::slotTextChanged); + connect(mUi.cbServices, QOverload::of(&QComboBox::currentIndexChanged), this, &FuseServiceSelectorDlg::slotServiceChanged); +} + +FuseServiceSelectorDlg::~FuseServiceSelectorDlg() = default; + +int FuseServiceSelectorDlg::service() const +{ + return mUi.cbServices->currentIndex(); +} + +QString FuseServiceSelectorDlg::protocol() const +{ + return mUi.leProtocol->text(); +} + +void FuseServiceSelectorDlg::setProtocolService(const QString &protocol, int serviceIndex) +{ + mUi.leProtocol->setText(protocol); + mUi.leProtocol->setReadOnly(true); + mUi.cbServices->setCurrentIndex(serviceIndex); +} + +void FuseServiceSelectorDlg::slotTextChanged(const QString &text) +{ + mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(mUi.cbServices->currentIndex() > 0 && text.length() > 1); +} + +void FuseServiceSelectorDlg::slotServiceChanged(int serviceIndex) +{ + mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(serviceIndex > 0 && mUi.leProtocol->text().length() > 1); +} + +FuseManager::FuseManager(QWidget *parent, const QVariantList &) + : KCModule(parent), mSelectedItemsCount(0) +{ + mUi.setupUi(this); + mAvailableServices.append(i18n("No Service")); + org::kde::kio::MountServiceManager kded(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/mountservicemanager"), QDBusConnection::sessionBus()); + QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/mountservicemanager"), + QStringLiteral("org.kde.kio.MountServiceManager"), QStringLiteral("availableServicesChanged"), this, SLOT(availableServicesChanged)); + mAvailableServices.append(kded.availableServices()); + + + mUi.bAdd->setIcon (QIcon::fromTheme(QStringLiteral("list-add"))); + mUi.bChange->setIcon (QIcon::fromTheme(QStringLiteral("edit-rename"))); + mUi.bDelete->setIcon (QIcon::fromTheme(QStringLiteral("list-remove"))); + mUi.bDeleteAll->setIcon (QIcon::fromTheme(QStringLiteral("edit-delete"))); + + connect(mUi.bAdd, &QAbstractButton::clicked, this, &FuseManager::addPressed); + connect(mUi.bChange, &QAbstractButton::clicked, this, &FuseManager::changePressed); + connect(mUi.bDelete, &QAbstractButton::clicked, this, &FuseManager::deletePressed); + connect(mUi.bDeleteAll, &QAbstractButton::clicked, this, &FuseManager::deleteAllPressed); + connect(mUi.serviceTreeWidget, &QTreeWidget::itemSelectionChanged, this, &FuseManager::selectionChanged); +} + +FuseManager::~FuseManager() = default; + +void FuseManager::load() +{ + KConfigGroup group(KSharedConfig::openConfig(QStringLiteral("fusemanagerrc")), QStringLiteral("Fuse Services")); + const QStringList handledProtocols = group.keyList(); + for (const QString &protocol : handledProtocols) { + const QString service = group.readEntry(protocol); + if (mAvailableServices.contains(service)) { + auto item = new QTreeWidgetItem({protocol, service}); + mUi.serviceTreeWidget->addTopLevelItem(item); + mManagedProtocols.insert(protocol, service); + } else { + mProtocolsToRemove.append(protocol); + } + } + mUi.serviceTreeWidget->sortItems(0, Qt::AscendingOrder); + updateButtons(); +} + +void FuseManager::save() +{ + KConfigGroup group(KSharedConfig::openConfig(QStringLiteral("fusemanagerrc")), QStringLiteral("Fuse Services")); + for (const QString &protocol : mManagedProtocols.keys()) { + group.writeEntry(protocol, mManagedProtocols[protocol]); + } + for (const QString &protocol : mProtocolsToRemove) { + group.deleteEntry(protocol); + } + group.sync(); + emit changed(false); +} + +void FuseManager::addPressed() +{ + FuseServiceSelectorDlg pdlg (this, mAvailableServices); + pdlg.setWindowTitle (i18nc ("@title:window", "Assign Mount Service")); + if (pdlg.exec()) { + const QString protocol = pdlg.protocol(); + const QString service = mAvailableServices.at(pdlg.service()); + if (!handleDuplicate(protocol, service)) { + auto item = new QTreeWidgetItem({protocol, service}); + mUi.serviceTreeWidget->addTopLevelItem(item); + mManagedProtocols.insert(protocol, service); + updateButtons(); + emit changed(true); + } + } +} + +void FuseManager::changePressed() +{ + QTreeWidgetItem *item = mUi.serviceTreeWidget->currentItem(); + Q_ASSERT(item); + FuseServiceSelectorDlg pdlg (this, mAvailableServices); + pdlg.setWindowTitle (i18nc ("@title:window", "Change Mount Service")); + const QString oldProtocol = item->text(0); + const int oldService = mAvailableServices.indexOf(item->text(1)); + pdlg.setProtocolService(oldProtocol, oldService); + if (pdlg.exec() && !pdlg.protocol().isEmpty()) { + item->setText(0, pdlg.protocol()); + item->setText(1, mAvailableServices.at(pdlg.service())); + } +} + +bool FuseManager::handleDuplicate(const QString& protocol, const QString &service) +{ + QTreeWidgetItem* item = mUi.serviceTreeWidget->topLevelItem(0); + while (item != nullptr) { + if (item->text(0) == protocol) { + const int res = KMessageBox::warningContinueCancel (this, + i18n ("A service is already set for" + "
%1
" + "Do you want to update it?
", protocol), + i18nc ("@title:window", "Update Service"), + KGuiItem (i18n ("Replace"))); + if (res == KMessageBox::Continue) { + mManagedProtocols[protocol] = service; + item->setText(0, protocol); + item->setText(1, service); + emit changed(true); + } + return true; + } + item = mUi.serviceTreeWidget->itemBelow(item); + } + return false; +} + +void FuseManager::deletePressed() +{ + QTreeWidgetItem* nextItem = nullptr; + for (QTreeWidgetItem * item : mUi.serviceTreeWidget->selectedItems()) { + nextItem = mUi.serviceTreeWidget->itemBelow(item); + if (!nextItem) + nextItem = mUi.serviceTreeWidget->itemAbove(item); + + mManagedProtocols.remove(item->text(0)); + mProtocolsToRemove.append(item->text(0)); + delete item; + } + if (nextItem) { + nextItem->setSelected(true); + } + updateButtons(); + emit changed(true); +} + +void FuseManager::deleteAllPressed() +{ + mProtocolsToRemove.append(mManagedProtocols.keys()); + mManagedProtocols.clear(); + mUi.serviceTreeWidget->clear(); + updateButtons(); + emit changed(true); +} + +void FuseManager::updateButtons() +{ + bool hasItems = mUi.serviceTreeWidget->topLevelItemCount() > 0; + + mUi.bChange->setEnabled((hasItems && mSelectedItemsCount == 1)); + mUi.bDelete->setEnabled((hasItems && mSelectedItemsCount > 0)); + mUi.bDeleteAll->setEnabled(hasItems); +} + +void FuseManager::selectionChanged() +{ + mSelectedItemsCount = mUi.serviceTreeWidget->selectedItems().count(); + updateButtons(); +} + +void FuseManager::availableServicesChanged(const QStringList &services) +{ + qDebug() << "available service changed"; + mAvailableServices.clear(); + mAvailableServices.append(i18n("No Service")); + mAvailableServices.append(services); +} + +QString FuseManager::quickHelp() const +{ + return i18n( "

Fuse Manager

This module lets you configure a FUSE mount service for KIO Slaves.

" + "

The remote KIO slave can make use of the service to provide a local mount." ); +} diff --git a/src/kcms/kio/fusemanager.desktop b/src/kcms/kio/fusemanager.desktop new file mode 100644 --- /dev/null +++ b/src/kcms/kio/fusemanager.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +Type=Service +X-KDE-ServiceTypes=KCModule +Icon=fuse +Exec=kcmshell5 fusemanager + +X-KDE-Library=kcm_kio +X-KDE-PluginKeyword=fusemanager +X-KDE-ParentApp=kcontrol + +X-KDE-System-Settings-Parent-Category=networksettings +X-KDE-Weight=90 + +Name=Fuse Manager +Comment=Sets fuse mount service for KIO protocols +X-KDE-Keywords=Fuse;FuseManager + diff --git a/src/kcms/kio/fusemanager.ui b/src/kcms/kio/fusemanager.ui new file mode 100644 --- /dev/null +++ b/src/kcms/kio/fusemanager.ui @@ -0,0 +1,103 @@ + + + FuseManagerUI + + + + 0 + 0 + 555 + 352 + + + + + + + + + + Available FUSE services + + + + + + D&elete + + + + + + + &Add... + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 81 + 47 + + + + + + + + Chan&ge... + + + + + + + Delete A&ll + + + + + + + <html><head/><body><p>List of FUSE mount services.</p></body></html> + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + true + + + + Protocol + + + + + Handler + + + + + + + + + + + + diff --git a/src/kcms/kio/fuseserviceselector.ui b/src/kcms/kio/fuseserviceselector.ui new file mode 100644 --- /dev/null +++ b/src/kcms/kio/fuseserviceselector.ui @@ -0,0 +1,64 @@ + + + FuseServiceSelectorDlgUI + + + + 0 + 0 + 287 + 82 + + + + + QFormLayout::ExpandingFieldsGrow + + + + + + + + Protocol: + + + false + + + leProtocol + + + + + + + <html><head/><body><p>Enter the remote KIO protocol, e.g. <span style=" font-weight:600;">smb</span> or <span style=" font-weight:600;">ftp</span>.</p></body></html> + + + + + + + + + + Service: + + + false + + + + + + + <html><head/><body><p>Select the desired FUSE mount service.</p></body></html> + + + + + + + + diff --git a/src/kcms/kio/main.cpp b/src/kcms/kio/main.cpp --- a/src/kcms/kio/main.cpp +++ b/src/kcms/kio/main.cpp @@ -33,14 +33,16 @@ #include "useragentdlg.h" #include "kproxydlg.h" #include "cache.h" +#include "fusemanager.h" K_PLUGIN_FACTORY(KioConfigFactory, registerPlugin(QStringLiteral("useragent")); registerPlugin(QStringLiteral("smb")); registerPlugin(QStringLiteral("netpref")); registerPlugin(QStringLiteral("proxy")); registerPlugin(QStringLiteral("cookie")); registerPlugin(QStringLiteral("cache")); + registerPlugin(QStringLiteral("fusemanager")); ) #include "main.moc" diff --git a/src/mountserviced/CMakeLists.txt b/src/mountserviced/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/src/mountserviced/CMakeLists.txt @@ -0,0 +1,19 @@ +set(mountservicemanager_SRCS mountservicemanager.cpp) + +find_package(KDED CONFIG REQUIRED) + +qt5_add_dbus_adaptor(mountservicemanager_SRCS org.kde.kio.MountServiceManager.xml mountservicemanager.h MountServiceManager) +qt5_add_dbus_interface(mountservicemanager_SRCS ${KDED_DBUS_INTERFACE} kded_interface) + +add_library(mountservicemanager MODULE ${mountservicemanager_SRCS}) + +kcoreaddons_desktop_to_json(mountservicemanager mountservicemanager.desktop) + +target_link_libraries(mountservicemanager + KF5::CoreAddons + KF5::DBusAddons + KF5::KIOCore +) + +install(TARGETS mountservicemanager DESTINATION ${PLUGIN_INSTALL_DIR}/kf5/kded) +install(FILES org.kde.kio.MountServiceManager.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR}) diff --git a/src/mountserviced/mountservicemanager.h b/src/mountserviced/mountservicemanager.h new file mode 100644 --- /dev/null +++ b/src/mountserviced/mountservicemanager.h @@ -0,0 +1,57 @@ +/* This file is part of the KDE libraries + Copyright (C) 2019 by Chinmoy Ranjan Pradhan + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef MOUNTSERVICEMANAGER_H +#define MOUNTSERVICEMANAGER_H + +#include +#include + +class Private; +class KPluginMetaData; + +class MountServiceManager : public KDEDModule +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.kio.MountServiceManager") + + public: + MountServiceManager(QObject* parent, const QList ¶meters); + ~MountServiceManager(); + + public Q_SLOTS: + Q_SCRIPTABLE QStringList availableServices() const; + Q_SCRIPTABLE void mountURL(const QString &remoteURL); + Q_SCRIPTABLE QString localURL(const QString &remoteURL); + + Q_SIGNALS: + Q_SCRIPTABLE void availableServicesChanged(const QStringList &services); + + private Q_SLOTS: + void dirty(const QString &path); + + private: + QString getMountService(const QUrl &url); + void loadModule(const QString &mountService); + + private: + Private *d; +}; + +#endif diff --git a/src/mountserviced/mountservicemanager.cpp b/src/mountserviced/mountservicemanager.cpp new file mode 100644 --- /dev/null +++ b/src/mountserviced/mountservicemanager.cpp @@ -0,0 +1,158 @@ +/* This file is part of the KDE libraries + Copyright (C) 2019 by Chinmoy Ranjan Pradhan + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "mountservicemanager.h" +#include "kded_interface.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +K_PLUGIN_FACTORY_WITH_JSON(MountServiceManagerFactory, + "mountservicemanager.json", + registerPlugin();) + +class Private +{ + public: + Private(MountServiceManager *qq) : q(qq) {} + + void addMountService(const QString &pluginPath) + { + if (QLibrary::isLibrary(pluginPath)) { + KPluginMetaData metadata(pluginPath); + if (metadata.isValid()) { + const QStringList category = metadata.category().split(QLatin1Char('/')); // For e.g, MountService/smb or MountService/all + if (category.first() == QStringLiteral("MountService")) { + mountServices.insert(pluginPath, metadata); + emit q->availableServicesChanged(q->availableServices()); + KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("fusemanagerrc")), QStringLiteral("Fuse Services")); + cfg.writeEntry(category.last(), metadata.pluginId()); + cfg.sync(); + } + } + } + } + + void removeMountService(const QString &pluginPath) + { + if (mountServices.remove(pluginPath) == 1) { + emit q->availableServicesChanged(q->availableServices()); + } + } + + KDirWatch *dirWatch; + MountServiceManager *q; + QMap mountServices; +}; + +MountServiceManager::MountServiceManager(QObject *parent, const QList ¶meters) + : KDEDModule(parent) + , d(new Private(this)) +{ + Q_UNUSED(parameters); + + d->dirWatch = new KDirWatch(this); + QSet dirsToWatch; + const QString subdir = QLatin1String("kf5/kded"); + const QStringList listPaths = QCoreApplication::libraryPaths(); + for (const QString &libDir : listPaths) { + dirsToWatch << libDir + QLatin1Char('/') + subdir; + } + + for (const QString &dir : dirsToWatch) { + d->dirWatch->addDir(dir); + QDirIterator it(dir, QDir::Files); + while (it.hasNext()) { + it.next(); + d->addMountService(it.fileInfo().absoluteFilePath()); + } + } + connect(d->dirWatch, &KDirWatch::dirty, this, &MountServiceManager::dirty); +} + +MountServiceManager::~MountServiceManager() +{ + delete d; +} + +QStringList MountServiceManager::availableServices() const +{ + QStringList mountServices; + for (KPluginMetaData metadata : d->mountServices) { + mountServices << metadata.pluginId(); + } + return mountServices; +} + +void MountServiceManager::mountURL(const QString &remoteURL) +{ + const QUrl url = QUrl::fromUserInput(remoteURL); + const QString mountService = getMountService(url); + loadModule(mountService); + QDBusInterface iface(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/%1").arg(mountService)); + iface.call(QDBus::NoBlock, QStringLiteral("mountUrl"), remoteURL); +} + +QString MountServiceManager::localURL(const QString &remoteURL) +{ + const QUrl url = QUrl::fromUserInput(remoteURL); + const QString mountService = getMountService(url); + QDBusInterface iface(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/%1").arg(mountService)); + QDBusReply reply = iface.call(QStringLiteral("localUrl"), remoteURL); + return reply.value(); +} + +void MountServiceManager::dirty(const QString &path) +{ + if (QFile::exists(path)) { + d->addMountService(path); + } else { + d->removeMountService(path); + } +} + +QString MountServiceManager::getMountService(const QUrl &url) +{ + KConfigGroup cfg = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("fusemanagerrc")), QStringLiteral("Fuse Services")); + QString mountService = cfg.readEntry(url.scheme(), QString()); + if (mountService.isEmpty()) { + mountService = cfg.readEntry(QStringLiteral("all"), QString()); + } + return mountService; +} + +void MountServiceManager::loadModule(const QString &mountService) +{ + org::kde::kded5 kded(QStringLiteral("org.kde.kded5"), QStringLiteral("/kded"), QDBusConnection::sessionBus()); + kded.loadModule(mountService).waitForFinished(); +} + + +#include "mountservicemanager.moc" diff --git a/src/mountserviced/mountservicemanager.desktop b/src/mountserviced/mountservicemanager.desktop new file mode 100644 --- /dev/null +++ b/src/mountserviced/mountservicemanager.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Service +Name=Mount Service Manager +X-KDE-ServiceTypes=KDEDModule +X-KDE-Library=mountservicemanager +X-KDE-FactoryName=mountservicemanager +X-KDE-Kded-load-on-demand=true +X-KDE-Kded-autoload=true +Comment=Manages kded modules that provide mounting capabilities for remote protocols. diff --git a/src/mountserviced/org.kde.kio.MountServiceManager.xml b/src/mountserviced/org.kde.kio.MountServiceManager.xml new file mode 100644 --- /dev/null +++ b/src/mountserviced/org.kde.kio.MountServiceManager.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + 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 ${CMAKE_CURRENT_SOURCE_DIR}/../mountserviced/org.kde.kio.MountServiceManager.xml mountserviced_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 @@ -69,6 +69,8 @@ #include #include +#include "mountserviced_interface.h" + #include #include #include @@ -502,15 +504,22 @@ const QUrl url = *it; bool supported = KIO::DesktopExecParser::isProtocolInSupportedList(url, appSupportedProtocols); //qDebug() << "Looking at url=" << url << " supported=" << supported; - if (!supported && 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) { - *it = localURL; - //qDebug() << "Changed to" << localURL; + if (!supported) { + 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) { + *it = localURL; + //qDebug() << "Changed to" << localURL; + } } + } else { + org::kde::kio::MountServiceManager kded(QStringLiteral("org.kde.kded5"), QStringLiteral("/modules/mountservicemanager"), QDBusConnection::sessionBus()); + auto reply = kded.localURL(url.toString()); + reply.waitForFinished(); + *it = QUrl::fromLocalFile(reply.value()); } } }