1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2017 Elvis Angelaccio <elvis.angelaccio@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "kioexecd.h"
9#include "kioexecdadaptor.h"
10#include "kioexecdebug.h"
11
12#include <KDirWatch>
13#include <KIO/CopyJob>
14#include <KLocalizedString>
15#include <KMessageBox>
16#include <KPluginFactory>
17
18#include <QDir>
19#include <QFileInfo>
20#include <QStandardPaths>
21
22static const int predefinedTimeout = 30000; // 30s
23
24K_PLUGIN_CLASS_WITH_JSON(KIOExecd, "kioexecd.json")
25
26KIOExecd::KIOExecd(QObject *parent, const QList<QVariant> &)
27 : KDEDModule(parent)
28{
29 qCDebug(KIOEXEC) << "kioexecd started";
30
31 new KIOExecdAdaptor(this);
32 m_watcher = new KDirWatch(this);
33
34 connect(sender: m_watcher, signal: &KDirWatch::dirty, context: this, slot: &KIOExecd::slotDirty);
35 connect(sender: m_watcher, signal: &KDirWatch::created, context: this, slot: &KIOExecd::slotCreated);
36 connect(sender: m_watcher, signal: &KDirWatch::deleted, context: this, slot: &KIOExecd::slotDeleted);
37 m_timer.setSingleShot(true);
38 m_timer.setInterval(predefinedTimeout);
39 connect(sender: &m_timer, signal: &QTimer::timeout, context: this, slot: &KIOExecd::slotCheckDeletedFiles);
40}
41
42KIOExecd::~KIOExecd()
43{
44 // Remove the remaining temporary files and if possible their parent directories
45 for (auto it = m_watched.constBegin(); it != m_watched.constEnd(); ++it) {
46 QFileInfo info(it.key());
47 const auto parentDir = info.path();
48 qCDebug(KIOEXEC) << "About to delete" << parentDir << "containing" << info.fileName();
49 QFile::remove(fileName: it.key());
50 QDir().rmdir(dirName: parentDir);
51 }
52}
53
54void KIOExecd::watch(const QString &path, const QString &destUrl)
55{
56 if (m_watched.contains(key: path)) {
57 qCDebug(KIOEXEC) << "Already watching" << path;
58 return;
59 }
60
61 qCDebug(KIOEXEC) << "Going to watch" << path << "for changes, remote destination is" << destUrl;
62
63 // Watch the temporary file for modifications, creations or deletions
64 m_watcher->addFile(file: path);
65 m_watched.insert(key: path, value: QUrl(destUrl));
66}
67
68void KIOExecd::slotCreated(const QString &path)
69{
70 m_deleted.remove(key: path);
71
72 // When the file is recreated, it is not signaled as dirty.
73 slotDirty(path);
74}
75
76void KIOExecd::slotDirty(const QString &path)
77{
78 if (!m_watched.contains(key: path)) {
79 return;
80 }
81
82 const auto dest = m_watched.value(key: path);
83
84 const auto result = KMessageBox::questionTwoActions(
85 parent: nullptr,
86 xi18nc("@info", "The file <filename>%1</filename><nl/>has been modified. Do you want to upload the changes?", dest.toDisplayString()),
87 i18n("File Changed"),
88 primaryAction: KGuiItem(i18n("Upload"), QLatin1String("cloud-upload-symbolic")),
89 secondaryAction: KGuiItem(i18n("Do Not Upload"), QLatin1String("dialog-cancel-symbolic")));
90 if (result != KMessageBox::PrimaryAction) {
91 return;
92 }
93
94 qCDebug(KIOEXEC) << "Uploading" << path << "to" << dest;
95 auto job = KIO::copy(src: QUrl::fromLocalFile(localfile: path), dest);
96 connect(sender: job, signal: &KJob::result, context: this, slot: [](KJob *job) {
97 if (job->error()) {
98 KMessageBox::error(parent: nullptr, text: job->errorString());
99 }
100 });
101}
102
103void KIOExecd::slotDeleted(const QString &path)
104{
105 if (!m_watched.contains(key: path)) {
106 return;
107 }
108
109 m_deleted.insert(key: path, value: QDateTime::currentDateTimeUtc());
110 m_timer.start();
111}
112
113void KIOExecd::slotCheckDeletedFiles()
114{
115 const QDateTime currentDateTime = QDateTime::currentDateTimeUtc();
116 // check if the deleted (and not recreated) files where deleted 30s ago or more
117 for (auto it = m_deleted.begin(); it != m_deleted.end();) {
118 if (it.value().msecsTo(currentDateTime) >= predefinedTimeout) {
119 qCDebug(KIOEXEC) << "Going to forget" << it.key();
120 m_watcher->removeFile(file: it.key());
121 m_watched.remove(key: it.key());
122 QFileInfo info(it.key());
123 const auto parentDir = info.path();
124 qCDebug(KIOEXEC) << "About to delete" << parentDir;
125 QDir().rmdir(dirName: parentDir);
126 it = m_deleted.erase(it);
127 } else {
128 ++it;
129 }
130 }
131 if (!m_deleted.isEmpty()) {
132 m_timer.start();
133 }
134}
135
136#include "kioexecd.moc"
137#include "moc_kioexecd.cpp"
138

source code of kio/src/kioexec/kioexecd.cpp