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 | |
22 | static const int predefinedTimeout = 30000; // 30s |
23 | |
24 | K_PLUGIN_CLASS_WITH_JSON(KIOExecd, "kioexecd.json" ) |
25 | |
26 | KIOExecd::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 | |
42 | KIOExecd::~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 | |
54 | void 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 | |
68 | void 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 | |
76 | void 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 | |
103 | void 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 | |
113 | void 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 | |