1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
4 SPDX-FileCopyrightText: 2000-2013 David Faure <faure@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "simplejob.h"
10#include "job_p.h"
11#include "kprotocolinfo.h"
12#include "scheduler.h"
13#include "worker_p.h"
14#include <QDebug>
15#include <QTimer>
16#include <kdirnotify.h>
17
18using namespace KIO;
19
20SimpleJob::SimpleJob(SimpleJobPrivate &dd)
21 : Job(dd)
22{
23 d_func()->simpleJobInit();
24}
25
26void SimpleJobPrivate::simpleJobInit()
27{
28 Q_Q(SimpleJob);
29 if (!m_url.isValid() || m_url.scheme().isEmpty()) {
30 qCWarning(KIO_CORE) << "Invalid URL:" << m_url;
31 q->setError(ERR_MALFORMED_URL);
32 q->setErrorText(m_url.toString());
33 QTimer::singleShot(interval: 0, receiver: q, slot: &SimpleJob::slotFinished);
34 return;
35 }
36
37 Scheduler::doJob(job: q);
38}
39
40bool SimpleJob::doKill()
41{
42 Q_D(SimpleJob);
43 if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) {
44 d->m_extraFlags |= JobPrivate::EF_KillCalled;
45 Scheduler::cancelJob(job: this); // deletes the worker if not 0
46 } else {
47 qCWarning(KIO_CORE) << this << "killed twice, this is overkill";
48 }
49 return Job::doKill();
50}
51
52bool SimpleJob::doSuspend()
53{
54 Q_D(SimpleJob);
55 if (d->m_worker) {
56 d->m_worker->suspend();
57 }
58 return Job::doSuspend();
59}
60
61bool SimpleJob::doResume()
62{
63 Q_D(SimpleJob);
64 if (d->m_worker) {
65 d->m_worker->resume();
66 }
67 return Job::doResume();
68}
69
70const QUrl &SimpleJob::url() const
71{
72 return d_func()->m_url;
73}
74
75void SimpleJob::putOnHold()
76{
77 Q_D(SimpleJob);
78 Q_ASSERT(d->m_worker);
79 if (d->m_worker) {
80 Scheduler::putWorkerOnHold(job: this, url: d->m_url);
81 }
82 // we should now be disassociated from the worker
83 Q_ASSERT(!d->m_worker);
84 kill(verbosity: Quietly);
85}
86
87void SimpleJob::removeOnHold()
88{
89 Scheduler::removeWorkerOnHold();
90}
91
92bool SimpleJob::isRedirectionHandlingEnabled() const
93{
94 return d_func()->m_redirectionHandlingEnabled;
95}
96
97void SimpleJob::setRedirectionHandlingEnabled(bool handle)
98{
99 Q_D(SimpleJob);
100 d->m_redirectionHandlingEnabled = handle;
101}
102
103SimpleJob::~SimpleJob()
104{
105 Q_D(SimpleJob);
106 // last chance to remove this job from the scheduler!
107 if (d->m_schedSerial) {
108 // qDebug() << "Killing job" << this << "in destructor!"/* << qBacktrace()*/;
109 Scheduler::cancelJob(job: this);
110 }
111}
112
113void SimpleJobPrivate::start(Worker *worker)
114{
115 Q_Q(SimpleJob);
116 m_worker = worker;
117
118 // Worker::setJob can send us SSL metadata if there is a persistent connection
119 QObject::connect(sender: worker, signal: &Worker::metaData, context: q, slot: &SimpleJob::slotMetaData);
120
121 worker->setJob(q);
122
123 QObject::connect(sender: worker, signal: &Worker::error, context: q, slot: &SimpleJob::slotError);
124
125 QObject::connect(sender: worker, signal: &Worker::warning, context: q, slot: &SimpleJob::slotWarning);
126
127 QObject::connect(sender: worker, signal: &Worker::finished, context: q, slot: &SimpleJob::slotFinished);
128
129 QObject::connect(sender: worker, signal: &Worker::infoMessage, context: q, slot: [this](const QString &message) {
130 _k_slotWorkerInfoMessage(s: message);
131 });
132
133 QObject::connect(sender: worker, signal: &Worker::connected, context: q, slot: [this]() {
134 slotConnected();
135 });
136
137 QObject::connect(sender: worker, signal: &Worker::privilegeOperationRequested, context: q, slot: [this]() {
138 slotPrivilegeOperationRequested();
139 });
140
141 if ((m_extraFlags & EF_TransferJobDataSent) == 0) { // this is a "get" job
142 QObject::connect(sender: worker, signal: &Worker::totalSize, context: q, slot: [this](KIO::filesize_t size) {
143 slotTotalSize(data_size: size);
144 });
145
146 QObject::connect(sender: worker, signal: &Worker::processedSize, context: q, slot: [this](KIO::filesize_t size) {
147 slotProcessedSize(data_size: size);
148 });
149
150 QObject::connect(sender: worker, signal: &Worker::speed, context: q, slot: [this](ulong speed) {
151 slotSpeed(speed);
152 });
153 }
154
155 const QVariant windowIdProp = q->property(name: "window-id"); // see KJobWidgets::setWindow
156 if (windowIdProp.isValid()) {
157 m_outgoingMetaData.insert(QStringLiteral("window-id"), value: QString::number(windowIdProp.toULongLong()));
158 }
159
160 const QVariant userTimestampProp = q->property(name: "userTimestamp"); // see KJobWidgets::updateUserTimestamp
161 if (userTimestampProp.isValid()) {
162 m_outgoingMetaData.insert(QStringLiteral("user-timestamp"), value: QString::number(userTimestampProp.toULongLong()));
163 }
164
165 if (q->uiDelegate() == nullptr) { // not interactive
166 m_outgoingMetaData.insert(QStringLiteral("no-auth-prompt"), QStringLiteral("true"));
167 }
168
169 if (!m_outgoingMetaData.isEmpty()) {
170 KIO_ARGS << m_outgoingMetaData;
171 worker->send(cmd: CMD_META_DATA, arr: packedArgs);
172 }
173
174 worker->send(cmd: m_command, arr: m_packedArgs);
175 if (q->isSuspended()) {
176 worker->suspend();
177 }
178}
179
180void SimpleJobPrivate::workerDone()
181{
182 Q_Q(SimpleJob);
183 if (m_worker) {
184 if (m_command == CMD_OPEN) {
185 m_worker->send(cmd: CMD_CLOSE);
186 }
187 q->disconnect(receiver: m_worker); // Remove all signals between worker and job
188 }
189 // only finish a job once; Scheduler::jobFinished() resets schedSerial to zero.
190 if (m_schedSerial) {
191 Scheduler::jobFinished(job: q, worker: m_worker);
192 }
193}
194
195void SimpleJob::slotFinished()
196{
197 Q_D(SimpleJob);
198 // Return worker to the scheduler
199 d->workerDone();
200
201 if (!hasSubjobs()) {
202 if (!error() && (d->m_command == CMD_MKDIR || d->m_command == CMD_RENAME)) {
203 if (d->m_command == CMD_MKDIR) {
204 const QUrl urlDir = url().adjusted(options: QUrl::RemoveFilename | QUrl::StripTrailingSlash);
205#ifndef KIO_ANDROID_STUB
206 org::kde::KDirNotify::emitFilesAdded(directory: urlDir);
207#endif
208 } else { /*if ( m_command == CMD_RENAME )*/
209 QUrl src;
210 QUrl dst;
211 QDataStream str(d->m_packedArgs);
212 str >> src >> dst;
213 if (src.adjusted(options: QUrl::RemoveFilename) == dst.adjusted(options: QUrl::RemoveFilename) // For the user, moving isn't
214 // renaming. Only renaming is.
215 ) {
216#ifndef KIO_ANDROID_STUB
217 org::kde::KDirNotify::emitFileRenamed(src, dst);
218#endif
219 }
220
221#ifndef KIO_ANDROID_STUB
222 org::kde::KDirNotify::emitFileMoved(src, dst);
223#endif
224 if (d->m_uiDelegateExtension) {
225 d->m_uiDelegateExtension->updateUrlInClipboard(src, dest: dst);
226 }
227 }
228 }
229 emitResult();
230 }
231}
232
233void SimpleJob::slotError(int err, const QString &errorText)
234{
235 Q_D(SimpleJob);
236 setError(err);
237 setErrorText(errorText);
238 if ((error() == ERR_UNKNOWN_HOST) && d->m_url.host().isEmpty()) {
239 setErrorText(QString());
240 }
241 // error terminates the job
242 slotFinished();
243}
244
245void SimpleJob::slotWarning(const QString &errorText)
246{
247 Q_EMIT warning(job: this, message: errorText);
248}
249
250void SimpleJobPrivate::_k_slotWorkerInfoMessage(const QString &msg)
251{
252 Q_EMIT q_func()->infoMessage(job: q_func(), message: msg);
253}
254
255void SimpleJobPrivate::slotConnected()
256{
257 Q_EMIT q_func()->connected(job: q_func());
258}
259
260void SimpleJobPrivate::slotTotalSize(KIO::filesize_t size)
261{
262 Q_Q(SimpleJob);
263 if (size != q->totalAmount(unit: KJob::Bytes)) {
264 q->setTotalAmount(unit: KJob::Bytes, amount: size);
265 }
266}
267
268void SimpleJobPrivate::slotProcessedSize(KIO::filesize_t size)
269{
270 Q_Q(SimpleJob);
271 // qDebug() << KIO::number(size);
272 q->setProcessedAmount(unit: KJob::Bytes, amount: size);
273}
274
275void SimpleJobPrivate::slotSpeed(unsigned long speed)
276{
277 // qDebug() << speed;
278 q_func()->emitSpeed(speed);
279}
280
281void SimpleJobPrivate::restartAfterRedirection(QUrl *redirectionUrl)
282{
283 Q_Q(SimpleJob);
284 // Return worker to the scheduler while we still have the old URL in place; the scheduler
285 // requires a job URL to stay invariant while the job is running.
286 workerDone();
287
288 m_url = *redirectionUrl;
289 redirectionUrl->clear();
290 if ((m_extraFlags & EF_KillCalled) == 0) {
291 Scheduler::doJob(job: q);
292 }
293}
294
295void SimpleJob::slotMetaData(const KIO::MetaData &_metaData)
296{
297 Q_D(SimpleJob);
298 QMapIterator<QString, QString> it(_metaData);
299 while (it.hasNext()) {
300 it.next();
301 if (it.key().startsWith(s: QLatin1String("{internal~"), cs: Qt::CaseInsensitive)) {
302 d->m_internalMetaData.insert(key: it.key(), value: it.value());
303 } else {
304 d->m_incomingMetaData.insert(key: it.key(), value: it.value());
305 }
306 }
307
308 // Update the internal meta-data values as soon as possible. Waiting until
309 // the KIO worker is finished has unintended consequences if the client starts
310 // a new connection without waiting for the KIO worker to finish.
311 if (!d->m_internalMetaData.isEmpty()) {
312 Scheduler::updateInternalMetaData(job: this);
313 }
314}
315
316void SimpleJobPrivate::slotPrivilegeOperationRequested()
317{
318 m_worker->send(cmd: MSG_PRIVILEGE_EXEC, arr: privilegeOperationData());
319}
320
321//////////
322SimpleJob *KIO::rmdir(const QUrl &url)
323{
324 // qDebug() << "rmdir " << url;
325 KIO_ARGS << url << qint8(false); // isFile is false
326 return SimpleJobPrivate::newJob(url, command: CMD_DEL, packedArgs);
327}
328
329SimpleJob *KIO::chmod(const QUrl &url, int permissions)
330{
331 // qDebug() << "chmod " << url;
332 KIO_ARGS << url << permissions;
333 return SimpleJobPrivate::newJob(url, command: CMD_CHMOD, packedArgs);
334}
335
336SimpleJob *KIO::chown(const QUrl &url, const QString &owner, const QString &group)
337{
338 KIO_ARGS << url << owner << group;
339 return SimpleJobPrivate::newJob(url, command: CMD_CHOWN, packedArgs);
340}
341
342SimpleJob *KIO::setModificationTime(const QUrl &url, const QDateTime &mtime)
343{
344 // qDebug() << "setModificationTime " << url << " " << mtime;
345 KIO_ARGS << url << mtime;
346 return SimpleJobPrivate::newJobNoUi(url, command: CMD_SETMODIFICATIONTIME, packedArgs);
347}
348
349SimpleJob *KIO::rename(const QUrl &src, const QUrl &dest, JobFlags flags)
350{
351 // qDebug() << "rename " << src << " " << dest;
352 KIO_ARGS << src << dest << (qint8)(flags & Overwrite);
353 return SimpleJobPrivate::newJob(url: src, command: CMD_RENAME, packedArgs, flags);
354}
355
356SimpleJob *KIO::symlink(const QString &target, const QUrl &dest, JobFlags flags)
357{
358 // qDebug() << "symlink target=" << target << " " << dest;
359 KIO_ARGS << target << dest << (qint8)(flags & Overwrite);
360 return SimpleJobPrivate::newJob(url: dest, command: CMD_SYMLINK, packedArgs, flags);
361}
362
363SimpleJob *KIO::special(const QUrl &url, const QByteArray &data, JobFlags flags)
364{
365 // qDebug() << "special " << url;
366 return SimpleJobPrivate::newJob(url, command: CMD_SPECIAL, packedArgs: data, flags);
367}
368
369SimpleJob *KIO::mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags)
370{
371 KIO_ARGS << int(1) << qint8(ro ? 1 : 0) << QString::fromLatin1(ba: fstype) << dev << point;
372 SimpleJob *job = special(url: QUrl(QStringLiteral("file:///")), data: packedArgs, flags);
373 if (!(flags & HideProgressInfo)) {
374 KIO::JobPrivate::emitMounting(job, dev, point);
375 }
376 return job;
377}
378
379SimpleJob *KIO::unmount(const QString &point, JobFlags flags)
380{
381 KIO_ARGS << int(2) << point;
382 SimpleJob *job = special(url: QUrl(QStringLiteral("file:///")), data: packedArgs, flags);
383 if (!(flags & HideProgressInfo)) {
384 KIO::JobPrivate::emitUnmounting(job, point);
385 }
386 return job;
387}
388
389//////////
390
391SimpleJob *KIO::http_update_cache(const QUrl &url, bool no_cache, const QDateTime &expireDate)
392{
393 Q_ASSERT(url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https"));
394 // Send http update_cache command (2)
395 KIO_ARGS << (int)2 << url << no_cache << qlonglong(expireDate.toMSecsSinceEpoch() / 1000);
396 return SimpleJobPrivate::newJob(url, command: CMD_SPECIAL, packedArgs);
397}
398
399#include "moc_simplejob.cpp"
400

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