File indexing completed on 2024-10-06 09:39:48

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2017 Elvis Angelaccio <elvis.angelaccio@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "kioexecd.h"
0009 #include "kioexecdadaptor.h"
0010 #include "kioexecdebug.h"
0011 
0012 #include <KDirWatch>
0013 #include <KIO/CopyJob>
0014 #include <KLocalizedString>
0015 #include <KMessageBox>
0016 #include <KPluginFactory>
0017 
0018 #include <QDir>
0019 #include <QFileInfo>
0020 #include <QStandardPaths>
0021 
0022 static const int predefinedTimeout = 30000; // 30s
0023 
0024 K_PLUGIN_CLASS_WITH_JSON(KIOExecd, "kioexecd.json")
0025 
0026 KIOExecd::KIOExecd(QObject *parent, const QList<QVariant> &)
0027     : KDEDModule(parent)
0028 {
0029     qCDebug(KIOEXEC) << "kioexecd started";
0030 
0031     new KIOExecdAdaptor(this);
0032     m_watcher = new KDirWatch(this);
0033 
0034     connect(m_watcher, &KDirWatch::dirty, this, &KIOExecd::slotDirty);
0035     connect(m_watcher, &KDirWatch::created, this, &KIOExecd::slotCreated);
0036     connect(m_watcher, &KDirWatch::deleted, this, &KIOExecd::slotDeleted);
0037     m_timer.setSingleShot(true);
0038     m_timer.setInterval(predefinedTimeout);
0039     connect(&m_timer, &QTimer::timeout, this, &KIOExecd::slotCheckDeletedFiles);
0040 }
0041 
0042 KIOExecd::~KIOExecd()
0043 {
0044     // Remove the remaining temporary files and if possible their parent directories
0045     for (auto it = m_watched.constBegin(); it != m_watched.constEnd(); ++it) {
0046         QFileInfo info(it.key());
0047         const auto parentDir = info.path();
0048         qCDebug(KIOEXEC) << "About to delete" << parentDir << "containing" << info.fileName();
0049         QFile::remove(it.key());
0050         QDir().rmdir(parentDir);
0051     }
0052 }
0053 
0054 void KIOExecd::watch(const QString &path, const QString &destUrl)
0055 {
0056     if (m_watched.contains(path)) {
0057         qCDebug(KIOEXEC) << "Already watching" << path;
0058         return;
0059     }
0060 
0061     qCDebug(KIOEXEC) << "Going to watch" << path << "for changes, remote destination is" << destUrl;
0062 
0063     // Watch the temporary file for modifications, creations or deletions
0064     m_watcher->addFile(path);
0065     m_watched.insert(path, QUrl(destUrl));
0066 }
0067 
0068 void KIOExecd::slotCreated(const QString &path)
0069 {
0070     m_deleted.remove(path);
0071 
0072     // When the file is recreated, it is not signaled as dirty.
0073     slotDirty(path);
0074 }
0075 
0076 void KIOExecd::slotDirty(const QString &path)
0077 {
0078     if (!m_watched.contains(path)) {
0079         return;
0080     }
0081 
0082     const auto dest = m_watched.value(path);
0083 
0084     const auto result = KMessageBox::questionTwoActions(nullptr,
0085                                                         i18n("The file %1\nhas been modified. Do you want to upload the changes?", dest.toDisplayString()),
0086                                                         i18n("File Changed"),
0087                                                         KGuiItem(i18n("Upload")),
0088                                                         KGuiItem(i18n("Do Not Upload")));
0089     if (result != KMessageBox::PrimaryAction) {
0090         return;
0091     }
0092 
0093     qCDebug(KIOEXEC) << "Uploading" << path << "to" << dest;
0094     auto job = KIO::copy(QUrl::fromLocalFile(path), dest);
0095     connect(job, &KJob::result, this, [](KJob *job) {
0096         if (job->error()) {
0097             KMessageBox::error(nullptr, job->errorString());
0098         }
0099     });
0100 }
0101 
0102 void KIOExecd::slotDeleted(const QString &path)
0103 {
0104     if (!m_watched.contains(path)) {
0105         return;
0106     }
0107 
0108     m_deleted.insert(path, QDateTime::currentDateTimeUtc());
0109     m_timer.start();
0110 }
0111 
0112 void KIOExecd::slotCheckDeletedFiles()
0113 {
0114     const QDateTime currentDateTime = QDateTime::currentDateTimeUtc();
0115     // check if the deleted (and not recreated) files where deleted 30s ago or more
0116     for (auto it = m_deleted.begin(); it != m_deleted.end();) {
0117         if (it.value().msecsTo(currentDateTime) >= predefinedTimeout) {
0118             qCDebug(KIOEXEC) << "Going to forget" << it.key();
0119             m_watcher->removeFile(it.key());
0120             m_watched.remove(it.key());
0121             QFileInfo info(it.key());
0122             const auto parentDir = info.path();
0123             qCDebug(KIOEXEC) << "About to delete" << parentDir;
0124             QDir().rmdir(parentDir);
0125             it = m_deleted.erase(it);
0126         } else {
0127             ++it;
0128         }
0129     }
0130     if (!m_deleted.isEmpty()) {
0131         m_timer.start();
0132     }
0133 }
0134 
0135 #include "kioexecd.moc"
0136 #include "moc_kioexecd.cpp"