diff --git a/src/kioexec/CMakeLists.txt b/src/kioexec/CMakeLists.txt --- a/src/kioexec/CMakeLists.txt +++ b/src/kioexec/CMakeLists.txt @@ -1,4 +1,30 @@ -add_executable(kioexec main.cpp) +set(kioexecd_SRCS kioexecd.cpp) + +qt5_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/kioexecd.h org.kde.KIOExecd.xml) +qt5_add_dbus_adaptor(kioexecd_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.KIOExecd.xml kioexecd.h KIOExecd) + + +ecm_qt_declare_logging_category(kioexecd_SRCS + HEADER kioexecdebug.h + IDENTIFIER KIOEXEC + CATEGORY_NAME kf5.kio.execd) + +kcoreaddons_add_plugin(kioexecd + SOURCES ${kioexecd_SRCS} + JSON kioexecd.json + INSTALL_NAMESPACE "kf5/kiod") + +target_link_libraries(kioexecd KF5::I18n KF5::DBusAddons KF5::WidgetsAddons KF5::KIOCore) + +configure_file(org.kde.kioexecd.service.in ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kioexecd.service) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/org.kde.kioexecd.service DESTINATION ${KDE_INSTALL_DBUSSERVICEDIR}) + +# next target + +set(kioexec_SRCS main.cpp) +qt5_add_dbus_interface(kioexec_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.KIOExecd.xml kioexecdinterface) + +add_executable(kioexec ${kioexec_SRCS}) configure_file(config-kioexec.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kioexec.h) diff --git a/src/kioexec/kioexecd.h b/src/kioexec/kioexecd.h new file mode 100644 --- /dev/null +++ b/src/kioexec/kioexecd.h @@ -0,0 +1,53 @@ +/* This file is part of the KDE libraries + Copyright (C) 2017 Elvis Angelaccio + + 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 of the License or ( at + your option ) version 3 or, at the discretion of KDE e.V. ( which shall + act as a proxy as in section 14 of the GPLv3 ), any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef KIOEXECD_H +#define KIOEXECD_H + +#include + +#include +#include + +class KDirWatch; + +class KIOExecd : public KDEDModule +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "org.kde.KIOExecd") + +public: + KIOExecd(QObject *parent, const QList&); + virtual ~KIOExecd(); + +public Q_SLOTS: + void watch(const QString &path, const QString &destUrl); + +private Q_SLOTS: + void slotDirty(const QString &path); + void slotDeleted(const QString &path); + +private: + KDirWatch *m_watcher; + QMap m_watched; +}; + +#endif + diff --git a/src/kioexec/kioexecd.cpp b/src/kioexec/kioexecd.cpp new file mode 100644 --- /dev/null +++ b/src/kioexec/kioexecd.cpp @@ -0,0 +1,111 @@ +/* This file is part of the KDE libraries + Copyright (C) 2017 Elvis Angelaccio + + 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 of the License or ( at + your option ) version 3 or, at the discretion of KDE e.V. ( which shall + act as a proxy as in section 14 of the GPLv3 ), any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "kioexecd.h" +#include "kioexecdadaptor.h" +#include "kioexecdebug.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +K_PLUGIN_FACTORY_WITH_JSON(KIOExecdFactory, + "kioexecd.json", + registerPlugin();) + +KIOExecd::KIOExecd(QObject *parent, const QList &) + : KDEDModule(parent) +{ + qCDebug(KIOEXEC) << "kioexecd started"; + + new KIOExecdAdaptor(this); + m_watcher = new KDirWatch(this); + + connect(m_watcher, &KDirWatch::dirty, this, &KIOExecd::slotDirty); + connect(m_watcher, &KDirWatch::deleted, this, &KIOExecd::slotDeleted); +} + +KIOExecd::~KIOExecd() +{ + for (auto it = m_watched.constBegin(); it != m_watched.constEnd(); ++it) { + QFileInfo info(it.key()); + const auto parentDir = info.path(); + qCDebug(KIOEXEC) << "About to delete" << parentDir << "containing" << info.fileName(); + QFile::remove(it.key()); + QDir().rmdir(parentDir); + } +} + +void KIOExecd::watch(const QString &path, const QString &destUrl) +{ + if (m_watched.contains(path)) { + qCDebug(KIOEXEC) << "Already watching" << path; + return; + } + + qCDebug(KIOEXEC) << "Going to watch" << path << "for changes, remote destination is" << destUrl; + + m_watcher->addFile(path); + m_watched.insert(path, QUrl(destUrl)); +} + +void KIOExecd::slotDirty(const QString &path) +{ + if (!m_watched.contains(path)) { + return; + } + + const auto dest = m_watched.value(path); + + if (KMessageBox::questionYesNo(nullptr, + i18n("The file %1\nhas been modified. Do you want to upload the changes?" , dest.toDisplayString()), + i18n("File Changed"), KGuiItem(i18n("Upload")), KGuiItem(i18n("Do Not Upload"))) != KMessageBox::Yes) { + return; + } + + qCDebug(KIOEXEC) << "Uploading" << path << "to" << dest; + auto job = KIO::copy(QUrl::fromLocalFile(path), dest); + connect(job, &KJob::result, this, [](KJob *job) { + if (job->error()) { + KMessageBox::error(nullptr, job->errorString()); + } + }); +} + +void KIOExecd::slotDeleted(const QString &path) +{ + if (!m_watched.contains(path)) { + return; + } + + qCDebug(KIOEXEC) << "Going to forget" << path; + m_watcher->removeFile(path); + m_watched.remove(path); +} + +#include "kioexecd.moc" + diff --git a/src/kioexec/kioexecd.json b/src/kioexec/kioexecd.json new file mode 100644 --- /dev/null +++ b/src/kioexec/kioexecd.json @@ -0,0 +1,11 @@ +{ + "KPlugin": { + "Description": "Monitors cached remote files for changes", + "Name": "KIOExec Daemon", + "ServiceTypes": [ + "KDEDModule" + ] + }, + "X-KDE-DBus-ModuleName": "kioexecd", + "X-KDE-DBus-ServiceName": "org.kde.kioexecd" +} diff --git a/src/kioexec/main.h b/src/kioexec/main.h --- a/src/kioexec/main.h +++ b/src/kioexec/main.h @@ -57,6 +57,7 @@ protected: bool mExited; bool mTempFiles; + bool mUseDaemon; QString mSuggestedFileName; int counter; int expectedCounter; diff --git a/src/kioexec/main.cpp b/src/kioexec/main.cpp --- a/src/kioexec/main.cpp +++ b/src/kioexec/main.cpp @@ -21,6 +21,7 @@ #include "main.h" #include "kio_version.h" +#include "kioexecdinterface.h" #include #include @@ -58,6 +59,7 @@ KIOExec::KIOExec(const QStringList &args, bool tempFiles, const QString &suggestedFileName) : mExited(false) , mTempFiles(tempFiles) + , mUseDaemon(false) , mSuggestedFileName(suggestedFileName) , expectedCounter(0) , command(args.first()) @@ -116,6 +118,11 @@ KIO::Job *job = KIO::file_copy(url, dest); jobList.append(job); + // Tell kioexecd to watch the file for changes. + OrgKdeKIOExecdInterface kioexecd(QStringLiteral("org.kde.kioexecd"), QStringLiteral("/modules/kioexecd"), QDBusConnection::sessionBus()); + kioexecd.watch(file.path, file.url.toString()); + mUseDaemon = !kioexecd.lastError().isValid(); + connect(job, &KJob::result, this, &KIOExec::slotResult); } } @@ -218,13 +225,14 @@ QString src = it->path; const QUrl dest = it->url; QFileInfo info(src); + const bool uploadChanges = !mUseDaemon && !dest.isLocalFile(); if (info.exists() && (it->time != info.lastModified())) { if (mTempFiles) { if (KMessageBox::questionYesNo(nullptr, i18n("The supposedly temporary file\n%1\nhas been modified.\nDo you still want to delete it?", dest.toDisplayString(QUrl::PreferLocalFile)), i18n("File Changed"), KStandardGuiItem::del(), KGuiItem(i18n("Do Not Delete"))) != KMessageBox::Yes) continue; // don't delete the temp file - } else if (!dest.isLocalFile()) { // no upload when it's already a local file + } else if (uploadChanges) { // no upload when it's already a local file or kioexecd already did it. if (KMessageBox::questionYesNo(nullptr, i18n("The file\n%1\nhas been modified.\nDo you want to upload the changes?" , dest.toDisplayString()), i18n("File Changed"), KGuiItem(i18n("Upload")), KGuiItem(i18n("Do Not Upload"))) == KMessageBox::Yes) { @@ -239,8 +247,7 @@ } } - if ((!dest.isLocalFile() || mTempFiles) && exit_code == 0) { - // Wait for a reasonable time so that even if the application forks on startup (like OOo or amarok) + if ((uploadChanges || mTempFiles) && exit_code == 0) { // Wait for a reasonable time so that even if the application forks on startup (like OOo or amarok) // it will have time to start up and read the file before it gets deleted. #130709. qDebug() << "sleeping..."; QThread::currentThread()->sleep(180); // 3 mn diff --git a/src/kioexec/org.kde.kioexecd.service.in b/src/kioexec/org.kde.kioexecd.service.in new file mode 100644 --- /dev/null +++ b/src/kioexec/org.kde.kioexecd.service.in @@ -0,0 +1,3 @@ +[D-BUS Service] +Name=org.kde.kioexecd +Exec=@KDE_INSTALL_FULL_LIBEXECDIR@/kf5/kiod5