1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
4 SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
5 SPDX-FileCopyrightText: 2000-2009 Waldo Bastian <bastian@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "job.h"
11#include "job_p.h"
12
13#include <time.h>
14
15#include <KLocalizedString>
16#include <KStringHandler>
17
18#include "worker_p.h"
19#include <kio/jobuidelegateextension.h>
20
21using namespace KIO;
22
23Job::Job()
24 : KCompositeJob(nullptr)
25 , d_ptr(new JobPrivate)
26{
27 d_ptr->q_ptr = this;
28 setCapabilities(KJob::Killable | KJob::Suspendable);
29}
30
31Job::Job(JobPrivate &dd)
32 : KCompositeJob(nullptr)
33 , d_ptr(&dd)
34{
35 d_ptr->q_ptr = this;
36 setCapabilities(KJob::Killable | KJob::Suspendable);
37}
38
39Job::~Job()
40{
41 delete d_ptr;
42}
43
44JobUiDelegateExtension *Job::uiDelegateExtension() const
45{
46 Q_D(const Job);
47 return d->m_uiDelegateExtension;
48}
49
50void Job::setUiDelegateExtension(JobUiDelegateExtension *extension)
51{
52 Q_D(Job);
53 d->m_uiDelegateExtension = extension;
54}
55
56bool Job::addSubjob(KJob *jobBase)
57{
58 // qDebug() << "addSubjob(" << jobBase << ") this=" << this;
59
60 bool ok = KCompositeJob::addSubjob(job: jobBase);
61 KIO::Job *job = qobject_cast<KIO::Job *>(object: jobBase);
62 if (ok && job) {
63 // Copy metadata into subjob (e.g. window-id, user-timestamp etc.)
64 Q_D(Job);
65 job->mergeMetaData(values: d->m_outgoingMetaData);
66
67 // Forward information from that subjob.
68 connect(sender: job, signal: &KJob::speed, context: this, slot: [this](KJob *job, ulong speed) {
69 Q_UNUSED(job);
70 emitSpeed(speed);
71 });
72 job->setProperty(name: "widget", value: property(name: "widget")); // see KJobWidgets
73 job->setProperty(name: "window", value: property(name: "window")); // see KJobWidgets
74 job->setProperty(name: "userTimestamp", value: property(name: "userTimestamp")); // see KJobWidgets
75 job->setUiDelegateExtension(d->m_uiDelegateExtension);
76 }
77 return ok;
78}
79
80bool Job::removeSubjob(KJob *jobBase)
81{
82 // qDebug() << "removeSubjob(" << jobBase << ") this=" << this << "subjobs=" << subjobs().count();
83 return KCompositeJob::removeSubjob(job: jobBase);
84}
85
86static QString url_description_string(const QUrl &url)
87{
88 return url.scheme() == QLatin1String("data") ? QStringLiteral("data:[...]") : KStringHandler::csqueeze(str: url.toDisplayString(options: QUrl::PreferLocalFile), maxlen: 100);
89}
90
91KIO::JobPrivate::~JobPrivate()
92{
93}
94
95void JobPrivate::emitMoving(KIO::Job *job, const QUrl &src, const QUrl &dest)
96{
97 static const QString s_title = i18nc("@title job", "Moving");
98 static const QString s_source = i18nc("The source of a file operation", "Source");
99 static const QString s_destination = i18nc("The destination of a file operation", "Destination");
100 Q_EMIT job->description(job, title: s_title, field1: qMakePair(value1: s_source, value2: url_description_string(url: src)), field2: qMakePair(value1: s_destination, value2: url_description_string(url: dest)));
101}
102
103void JobPrivate::emitRenaming(KIO::Job *job, const QUrl &src, const QUrl &dest)
104{
105 static const QString s_title = i18nc("@title job", "Renaming");
106 static const QString s_source = i18nc("The source of a file operation", "Source");
107 static const QString s_destination = i18nc("The destination of a file operation", "Destination");
108 Q_EMIT job->description(job, title: s_title, field1: {s_source, url_description_string(url: src)}, field2: {s_destination, url_description_string(url: dest)});
109}
110
111void JobPrivate::emitCopying(KIO::Job *job, const QUrl &src, const QUrl &dest)
112{
113 static const QString s_title = i18nc("@title job", "Copying");
114 static const QString s_source = i18nc("The source of a file operation", "Source");
115 static const QString s_destination = i18nc("The destination of a file operation", "Destination");
116 Q_EMIT job->description(job, title: s_title, field1: qMakePair(value1: s_source, value2: url_description_string(url: src)), field2: qMakePair(value1: s_destination, value2: url_description_string(url: dest)));
117}
118
119void JobPrivate::emitCreatingDir(KIO::Job *job, const QUrl &dir)
120{
121 static const QString s_title = i18nc("@title job", "Creating directory");
122 static const QString s_directory = i18n("Directory");
123 Q_EMIT job->description(job, title: s_title, field1: qMakePair(value1: s_directory, value2: url_description_string(url: dir)));
124}
125
126void JobPrivate::emitDeleting(KIO::Job *job, const QUrl &url)
127{
128 static const QString s_title = i18nc("@title job", "Deleting");
129 static const QString s_file = i18n("File");
130 Q_EMIT job->description(job, title: s_title, field1: qMakePair(value1: s_file, value2: url_description_string(url)));
131}
132
133void JobPrivate::emitStating(KIO::Job *job, const QUrl &url)
134{
135 static const QString s_title = i18nc("@title job", "Examining");
136 static const QString s_file = i18n("File");
137 Q_EMIT job->description(job, title: s_title, field1: qMakePair(value1: s_file, value2: url_description_string(url)));
138}
139
140void JobPrivate::emitTransferring(KIO::Job *job, const QUrl &url)
141{
142 static const QString s_title = i18nc("@title job", "Transferring");
143 static const QString s_source = i18nc("The source of a file operation", "Source");
144 Q_EMIT job->description(job, title: s_title, field1: qMakePair(value1: s_source, value2: url_description_string(url)));
145}
146
147void JobPrivate::emitMounting(KIO::Job *job, const QString &dev, const QString &point)
148{
149 Q_EMIT job->description(job, i18nc("@title job", "Mounting"), field1: qMakePair(i18n("Device"), value2: dev), field2: qMakePair(i18n("Mountpoint"), value2: point));
150}
151
152void JobPrivate::emitUnmounting(KIO::Job *job, const QString &point)
153{
154 Q_EMIT job->description(job, i18nc("@title job", "Unmounting"), field1: qMakePair(i18n("Mountpoint"), value2: point));
155}
156
157bool Job::doKill()
158{
159 // kill all subjobs, without triggering their result slot
160 for (KJob *job : subjobs()) {
161 job->kill(verbosity: KJob::Quietly);
162 }
163 clearSubjobs();
164
165 return true;
166}
167
168bool Job::doSuspend()
169{
170 for (KJob *job : subjobs()) {
171 if (!job->suspend()) {
172 return false;
173 }
174 }
175
176 return true;
177}
178
179bool Job::doResume()
180{
181 for (KJob *job : subjobs()) {
182 if (!job->resume()) {
183 return false;
184 }
185 }
186
187 return true;
188}
189
190// Job::errorString is implemented in job_error.cpp
191
192void Job::setParentJob(Job *job)
193{
194 Q_D(Job);
195 Q_ASSERT(d->m_parentJob == nullptr);
196 Q_ASSERT(job);
197 d->m_parentJob = job;
198}
199
200Job *Job::parentJob() const
201{
202 return d_func()->m_parentJob;
203}
204
205MetaData Job::metaData() const
206{
207 return d_func()->m_incomingMetaData;
208}
209
210QString Job::queryMetaData(const QString &key)
211{
212 return d_func()->m_incomingMetaData.value(key, defaultValue: QString());
213}
214
215void Job::setMetaData(const KIO::MetaData &_metaData)
216{
217 Q_D(Job);
218 d->m_outgoingMetaData = _metaData;
219}
220
221void Job::addMetaData(const QString &key, const QString &value)
222{
223 d_func()->m_outgoingMetaData.insert(key, value);
224}
225
226void Job::addMetaData(const QMap<QString, QString> &values)
227{
228 Q_D(Job);
229 for (auto it = values.cbegin(); it != values.cend(); ++it) {
230 d->m_outgoingMetaData.insert(key: it.key(), value: it.value());
231 }
232}
233
234void Job::mergeMetaData(const QMap<QString, QString> &values)
235{
236 Q_D(Job);
237 for (auto it = values.cbegin(); it != values.cend(); ++it) {
238 // there's probably a faster way
239 if (!d->m_outgoingMetaData.contains(key: it.key())) {
240 d->m_outgoingMetaData.insert(key: it.key(), value: it.value());
241 }
242 }
243}
244
245MetaData Job::outgoingMetaData() const
246{
247 return d_func()->m_outgoingMetaData;
248}
249
250QByteArray JobPrivate::privilegeOperationData()
251{
252 PrivilegeOperationStatus status = OperationNotAllowed;
253
254 if (m_parentJob) {
255 QByteArray jobData = m_parentJob->d_func()->privilegeOperationData();
256 // Copy meta-data from parent job
257 m_incomingMetaData.insert(QStringLiteral("TestData"), value: m_parentJob->queryMetaData(QStringLiteral("TestData")));
258 return jobData;
259 } else {
260 if (m_privilegeExecutionEnabled) {
261 status = OperationAllowed;
262 switch (m_operationType) {
263 case ChangeAttr:
264 m_title = i18n("Change Attribute");
265 m_message = i18n(
266 "Root privileges are required to change file attributes. "
267 "Do you want to continue?");
268 break;
269 case Copy:
270 m_title = i18n("Copy Files");
271 m_message = i18n(
272 "Root privileges are required to complete the copy operation. "
273 "Do you want to continue?");
274 break;
275 case Delete:
276 m_title = i18n("Delete Files");
277 m_message = i18n(
278 "Root privileges are required to complete the delete operation. "
279 "However, doing so may damage your system. Do you want to continue?");
280 break;
281 case MkDir:
282 m_title = i18n("Create Folder");
283 m_message = i18n(
284 "Root privileges are required to create this folder. "
285 "Do you want to continue?");
286 break;
287 case Move:
288 m_title = i18n("Move Items");
289 m_message = i18n(
290 "Root privileges are required to complete the move operation. "
291 "Do you want to continue?");
292 break;
293 case Rename:
294 m_title = i18n("Rename");
295 m_message = i18n(
296 "Root privileges are required to complete renaming. "
297 "Do you want to continue?");
298 break;
299 case Symlink:
300 m_title = i18n("Create Symlink");
301 m_message = i18n(
302 "Root privileges are required to create a symlink. "
303 "Do you want to continue?");
304 break;
305 case Transfer:
306 m_title = i18n("Transfer data");
307 m_message = i18n(
308 "Root privileges are required to complete transferring data. "
309 "Do you want to continue?");
310 Q_FALLTHROUGH();
311 default:
312 break;
313 }
314
315 if (m_outgoingMetaData.value(QStringLiteral("UnitTesting")) == QLatin1String("true")) {
316 // Set meta-data for the top-level job
317 m_incomingMetaData.insert(QStringLiteral("TestData"), QStringLiteral("PrivilegeOperationAllowed"));
318 }
319 }
320 }
321
322 QByteArray parentJobData;
323 QDataStream ds(&parentJobData, QIODevice::WriteOnly);
324 ds << status << m_title << m_message;
325 return parentJobData;
326}
327
328//////////////////////////
329
330class KIO::DirectCopyJobPrivate : public KIO::SimpleJobPrivate
331{
332public:
333 DirectCopyJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs)
334 : SimpleJobPrivate(url, command, packedArgs)
335 {
336 }
337
338 /**
339 * @internal
340 * Called by the scheduler when a @p worker gets to
341 * work on this job.
342 * @param worker the worker that starts working on this job
343 */
344 void start(Worker *worker) override;
345
346 Q_DECLARE_PUBLIC(DirectCopyJob)
347};
348
349DirectCopyJob::DirectCopyJob(const QUrl &url, const QByteArray &packedArgs)
350 : SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs))
351{
352 setUiDelegate(KIO::createDefaultJobUiDelegate());
353}
354
355DirectCopyJob::~DirectCopyJob()
356{
357}
358
359void DirectCopyJobPrivate::start(Worker *worker)
360{
361 Q_Q(DirectCopyJob);
362 q->connect(sender: worker, signal: &WorkerInterface::canResume, context: q, slot: &DirectCopyJob::slotCanResume);
363 SimpleJobPrivate::start(worker);
364}
365
366void DirectCopyJob::slotCanResume(KIO::filesize_t offset)
367{
368 Q_EMIT canResume(job: this, offset);
369}
370
371//////////////////////////
372
373SimpleJob *KIO::file_delete(const QUrl &src, JobFlags flags)
374{
375 KIO_ARGS << src << qint8(true); // isFile
376 SimpleJob *job = SimpleJobPrivate::newJob(url: src, command: CMD_DEL, packedArgs, flags);
377 if (job->uiDelegateExtension()) {
378 job->uiDelegateExtension()->createClipboardUpdater(job, mode: JobUiDelegateExtension::RemoveContent);
379 }
380 return job;
381}
382
383//////////
384////
385
386#include "moc_job_base.cpp"
387#include "moc_job_p.cpp"
388

source code of kio/src/core/job.cpp