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 | |
21 | using namespace KIO; |
22 | |
23 | Job::Job() |
24 | : KCompositeJob(nullptr) |
25 | , d_ptr(new JobPrivate) |
26 | { |
27 | d_ptr->q_ptr = this; |
28 | setCapabilities(KJob::Killable | KJob::Suspendable); |
29 | } |
30 | |
31 | Job::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 | |
39 | Job::~Job() |
40 | { |
41 | delete d_ptr; |
42 | } |
43 | |
44 | JobUiDelegateExtension *Job::uiDelegateExtension() const |
45 | { |
46 | Q_D(const Job); |
47 | return d->m_uiDelegateExtension; |
48 | } |
49 | |
50 | void Job::setUiDelegateExtension(JobUiDelegateExtension *extension) |
51 | { |
52 | Q_D(Job); |
53 | d->m_uiDelegateExtension = extension; |
54 | } |
55 | |
56 | bool 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 | |
80 | bool Job::removeSubjob(KJob *jobBase) |
81 | { |
82 | // qDebug() << "removeSubjob(" << jobBase << ") this=" << this << "subjobs=" << subjobs().count(); |
83 | return KCompositeJob::removeSubjob(job: jobBase); |
84 | } |
85 | |
86 | static 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 | |
91 | KIO::JobPrivate::~JobPrivate() |
92 | { |
93 | } |
94 | |
95 | void 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 | |
103 | void 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 | |
111 | void 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 | |
119 | void 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 | |
126 | void 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 | |
133 | void 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 | |
140 | void 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 | |
147 | void 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 | |
152 | void 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 | |
157 | bool 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 | |
168 | bool Job::doSuspend() |
169 | { |
170 | for (KJob *job : subjobs()) { |
171 | if (!job->suspend()) { |
172 | return false; |
173 | } |
174 | } |
175 | |
176 | return true; |
177 | } |
178 | |
179 | bool 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 | |
192 | void 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 | |
200 | Job *Job::parentJob() const |
201 | { |
202 | return d_func()->m_parentJob; |
203 | } |
204 | |
205 | MetaData Job::metaData() const |
206 | { |
207 | return d_func()->m_incomingMetaData; |
208 | } |
209 | |
210 | QString Job::queryMetaData(const QString &key) |
211 | { |
212 | return d_func()->m_incomingMetaData.value(key, defaultValue: QString()); |
213 | } |
214 | |
215 | void Job::setMetaData(const KIO::MetaData &_metaData) |
216 | { |
217 | Q_D(Job); |
218 | d->m_outgoingMetaData = _metaData; |
219 | } |
220 | |
221 | void Job::addMetaData(const QString &key, const QString &value) |
222 | { |
223 | d_func()->m_outgoingMetaData.insert(key, value); |
224 | } |
225 | |
226 | void 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 | |
234 | void 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 | |
245 | MetaData Job::outgoingMetaData() const |
246 | { |
247 | return d_func()->m_outgoingMetaData; |
248 | } |
249 | |
250 | QByteArray 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 | |
330 | class KIO::DirectCopyJobPrivate : public KIO::SimpleJobPrivate |
331 | { |
332 | public: |
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 | |
349 | DirectCopyJob::DirectCopyJob(const QUrl &url, const QByteArray &packedArgs) |
350 | : SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs)) |
351 | { |
352 | setUiDelegate(KIO::createDefaultJobUiDelegate()); |
353 | } |
354 | |
355 | DirectCopyJob::~DirectCopyJob() |
356 | { |
357 | } |
358 | |
359 | void 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 | |
366 | void DirectCopyJob::slotCanResume(KIO::filesize_t offset) |
367 | { |
368 | Q_EMIT canResume(job: this, offset); |
369 | } |
370 | |
371 | ////////////////////////// |
372 | |
373 | SimpleJob *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 | |