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 "slave.h"
14 #include <QDebug>
15 #include <QTimer>
16 #include <kdirnotify.h>
17
18 using namespace KIO;
19
SimpleJob(SimpleJobPrivate & dd)20 SimpleJob::SimpleJob(SimpleJobPrivate &dd)
21 : Job(dd)
22 {
23 d_func()->simpleJobInit();
24 }
25
simpleJobInit()26 void 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(0, q, &SimpleJob::slotFinished);
34 return;
35 }
36
37 Scheduler::doJob(q);
38 }
39
doKill()40 bool 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(this); // deletes the slave if not 0
46 } else {
47 qCWarning(KIO_CORE) << this << "killed twice, this is overkill";
48 }
49 return Job::doKill();
50 }
51
doSuspend()52 bool SimpleJob::doSuspend()
53 {
54 Q_D(SimpleJob);
55 if (d->m_slave) {
56 d->m_slave->suspend();
57 }
58 return Job::doSuspend();
59 }
60
doResume()61 bool SimpleJob::doResume()
62 {
63 Q_D(SimpleJob);
64 if (d->m_slave) {
65 d->m_slave->resume();
66 }
67 return Job::doResume();
68 }
69
url() const70 const QUrl &SimpleJob::url() const
71 {
72 return d_func()->m_url;
73 }
74
putOnHold()75 void SimpleJob::putOnHold()
76 {
77 Q_D(SimpleJob);
78 Q_ASSERT(d->m_slave);
79 if (d->m_slave) {
80 Scheduler::putSlaveOnHold(this, d->m_url);
81 }
82 // we should now be disassociated from the slave
83 Q_ASSERT(!d->m_slave);
84 kill(Quietly);
85 }
86
removeOnHold()87 void SimpleJob::removeOnHold()
88 {
89 Scheduler::removeSlaveOnHold();
90 }
91
isRedirectionHandlingEnabled() const92 bool SimpleJob::isRedirectionHandlingEnabled() const
93 {
94 return d_func()->m_redirectionHandlingEnabled;
95 }
96
setRedirectionHandlingEnabled(bool handle)97 void SimpleJob::setRedirectionHandlingEnabled(bool handle)
98 {
99 Q_D(SimpleJob);
100 d->m_redirectionHandlingEnabled = handle;
101 }
102
~SimpleJob()103 SimpleJob::~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(this);
110 }
111 }
112
start(Slave * slave)113 void SimpleJobPrivate::start(Slave *slave)
114 {
115 Q_Q(SimpleJob);
116 m_slave = slave;
117
118 // Slave::setJob can send us SSL metadata if there is a persistent connection
119 QObject::connect(slave, &Slave::metaData, q, &SimpleJob::slotMetaData);
120
121 slave->setJob(q);
122
123 QObject::connect(slave, &Slave::error, q, &SimpleJob::slotError);
124
125 QObject::connect(slave, &Slave::warning, q, &SimpleJob::slotWarning);
126
127 QObject::connect(slave, &Slave::finished, q, &SimpleJob::slotFinished);
128
129 QObject::connect(slave, &Slave::infoMessage, q, [this](const QString &message) {
130 _k_slotSlaveInfoMessage(message);
131 });
132
133 QObject::connect(slave, &Slave::connected, q, [this]() {
134 slotConnected();
135 });
136
137 QObject::connect(slave, &Slave::privilegeOperationRequested, q, [this]() {
138 slotPrivilegeOperationRequested();
139 });
140
141 if ((m_extraFlags & EF_TransferJobDataSent) == 0) { // this is a "get" job
142 QObject::connect(slave, &Slave::totalSize, q, [this](KIO::filesize_t size) {
143 slotTotalSize(size);
144 });
145
146 QObject::connect(slave, &Slave::processedSize, q, [this](KIO::filesize_t size) {
147 slotProcessedSize(size);
148 });
149
150 QObject::connect(slave, &Slave::speed, q, [this](ulong speed) {
151 slotSpeed(speed);
152 });
153 }
154
155 const QVariant windowIdProp = q->property("window-id"); // see KJobWidgets::setWindow
156 if (windowIdProp.isValid()) {
157 m_outgoingMetaData.insert(QStringLiteral("window-id"), QString::number(windowIdProp.toULongLong()));
158 }
159
160 const QVariant userTimestampProp = q->property("userTimestamp"); // see KJobWidgets::updateUserTimestamp
161 if (userTimestampProp.isValid()) {
162 m_outgoingMetaData.insert(QStringLiteral("user-timestamp"), 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 slave->send(CMD_META_DATA, packedArgs);
172 }
173
174 if (!m_subUrl.isEmpty()) {
175 KIO_ARGS << m_subUrl;
176 slave->send(CMD_SUBURL, packedArgs);
177 }
178
179 slave->send(m_command, m_packedArgs);
180 if (q->isSuspended()) {
181 slave->suspend();
182 }
183 }
184
slaveDone()185 void SimpleJobPrivate::slaveDone()
186 {
187 Q_Q(SimpleJob);
188 if (m_slave) {
189 if (m_command == CMD_OPEN) {
190 m_slave->send(CMD_CLOSE);
191 }
192 q->disconnect(m_slave); // Remove all signals between slave and job
193 }
194 // only finish a job once; Scheduler::jobFinished() resets schedSerial to zero.
195 if (m_schedSerial) {
196 Scheduler::jobFinished(q, m_slave);
197 }
198 }
199
slotFinished()200 void SimpleJob::slotFinished()
201 {
202 Q_D(SimpleJob);
203 // Return slave to the scheduler
204 d->slaveDone();
205
206 if (!hasSubjobs()) {
207 if (!error() && (d->m_command == CMD_MKDIR || d->m_command == CMD_RENAME)) {
208 if (d->m_command == CMD_MKDIR) {
209 const QUrl urlDir = url().adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
210 #ifndef KIO_ANDROID_STUB
211 org::kde::KDirNotify::emitFilesAdded(urlDir);
212 #endif
213 } else { /*if ( m_command == CMD_RENAME )*/
214 QUrl src;
215 QUrl dst;
216 QDataStream str(d->m_packedArgs);
217 str >> src >> dst;
218 if (src.adjusted(QUrl::RemoveFilename) == dst.adjusted(QUrl::RemoveFilename) // For the user, moving isn't renaming. Only renaming is.
219 && !KProtocolInfo::slaveHandlesNotify(dst.scheme()).contains(QLatin1String("Rename"))) {
220 #ifndef KIO_ANDROID_STUB
221 org::kde::KDirNotify::emitFileRenamed(src, dst);
222 #endif
223 }
224
225 #ifndef KIO_ANDROID_STUB
226 org::kde::KDirNotify::emitFileMoved(src, dst);
227 #endif
228 if (d->m_uiDelegateExtension) {
229 d->m_uiDelegateExtension->updateUrlInClipboard(src, dst);
230 }
231 }
232 }
233 emitResult();
234 }
235 }
236
slotError(int err,const QString & errorText)237 void SimpleJob::slotError(int err, const QString &errorText)
238 {
239 Q_D(SimpleJob);
240 setError(err);
241 setErrorText(errorText);
242 if ((error() == ERR_UNKNOWN_HOST) && d->m_url.host().isEmpty()) {
243 setErrorText(QString());
244 }
245 // error terminates the job
246 slotFinished();
247 }
248
slotWarning(const QString & errorText)249 void SimpleJob::slotWarning(const QString &errorText)
250 {
251 Q_EMIT warning(this, errorText);
252 }
253
_k_slotSlaveInfoMessage(const QString & msg)254 void SimpleJobPrivate::_k_slotSlaveInfoMessage(const QString &msg)
255 {
256 Q_EMIT q_func()->infoMessage(q_func(), msg);
257 }
258
slotConnected()259 void SimpleJobPrivate::slotConnected()
260 {
261 Q_EMIT q_func()->connected(q_func());
262 }
263
slotTotalSize(KIO::filesize_t size)264 void SimpleJobPrivate::slotTotalSize(KIO::filesize_t size)
265 {
266 Q_Q(SimpleJob);
267 if (size != q->totalAmount(KJob::Bytes)) {
268 q->setTotalAmount(KJob::Bytes, size);
269 }
270 }
271
slotProcessedSize(KIO::filesize_t size)272 void SimpleJobPrivate::slotProcessedSize(KIO::filesize_t size)
273 {
274 Q_Q(SimpleJob);
275 // qDebug() << KIO::number(size);
276 q->setProcessedAmount(KJob::Bytes, size);
277 }
278
slotSpeed(unsigned long speed)279 void SimpleJobPrivate::slotSpeed(unsigned long speed)
280 {
281 // qDebug() << speed;
282 q_func()->emitSpeed(speed);
283 }
284
restartAfterRedirection(QUrl * redirectionUrl)285 void SimpleJobPrivate::restartAfterRedirection(QUrl *redirectionUrl)
286 {
287 Q_Q(SimpleJob);
288 // Return slave to the scheduler while we still have the old URL in place; the scheduler
289 // requires a job URL to stay invariant while the job is running.
290 slaveDone();
291
292 m_url = *redirectionUrl;
293 redirectionUrl->clear();
294 if ((m_extraFlags & EF_KillCalled) == 0) {
295 Scheduler::doJob(q);
296 }
297 }
298
requestMessageBox(int _type,const QString & text,const QString & caption,const QString & buttonYes,const QString & buttonNo,const QString & iconYes,const QString & iconNo,const QString & dontAskAgainName,const KIO::MetaData & sslMetaData)299 int SimpleJobPrivate::requestMessageBox(int _type,
300 const QString &text,
301 const QString &caption,
302 const QString &buttonYes,
303 const QString &buttonNo,
304 const QString &iconYes,
305 const QString &iconNo,
306 const QString &dontAskAgainName,
307 const KIO::MetaData &sslMetaData)
308 {
309 if (m_uiDelegateExtension) {
310 const JobUiDelegateExtension::MessageBoxType type = static_cast<JobUiDelegateExtension::MessageBoxType>(_type);
311 return m_uiDelegateExtension->requestMessageBox(type, text, caption, buttonYes, buttonNo, iconYes, iconNo, dontAskAgainName, sslMetaData);
312 }
313 qCWarning(KIO_CORE) << "JobUiDelegate not set! Returning -1";
314 return -1;
315 }
316
slotMetaData(const KIO::MetaData & _metaData)317 void SimpleJob::slotMetaData(const KIO::MetaData &_metaData)
318 {
319 Q_D(SimpleJob);
320 QMapIterator<QString, QString> it(_metaData);
321 while (it.hasNext()) {
322 it.next();
323 if (it.key().startsWith(QLatin1String("{internal~"), Qt::CaseInsensitive)) {
324 d->m_internalMetaData.insert(it.key(), it.value());
325 } else {
326 d->m_incomingMetaData.insert(it.key(), it.value());
327 }
328 }
329
330 // Update the internal meta-data values as soon as possible. Waiting until
331 // the ioslave is finished has unintended consequences if the client starts
332 // a new connection without waiting for the ioslave to finish.
333 if (!d->m_internalMetaData.isEmpty()) {
334 Scheduler::updateInternalMetaData(this);
335 }
336 }
337
storeSSLSessionFromJob(const QUrl & redirectionURL)338 void SimpleJob::storeSSLSessionFromJob(const QUrl &redirectionURL)
339 {
340 Q_UNUSED(redirectionURL);
341 }
342
slotPrivilegeOperationRequested()343 void SimpleJobPrivate::slotPrivilegeOperationRequested()
344 {
345 m_slave->send(MSG_PRIVILEGE_EXEC, privilegeOperationData());
346 }
347
348 //////////
rmdir(const QUrl & url)349 SimpleJob *KIO::rmdir(const QUrl &url)
350 {
351 // qDebug() << "rmdir " << url;
352 KIO_ARGS << url << qint8(false); // isFile is false
353 return SimpleJobPrivate::newJob(url, CMD_DEL, packedArgs);
354 }
355
chmod(const QUrl & url,int permissions)356 SimpleJob *KIO::chmod(const QUrl &url, int permissions)
357 {
358 // qDebug() << "chmod " << url;
359 KIO_ARGS << url << permissions;
360 return SimpleJobPrivate::newJob(url, CMD_CHMOD, packedArgs);
361 }
362
chown(const QUrl & url,const QString & owner,const QString & group)363 SimpleJob *KIO::chown(const QUrl &url, const QString &owner, const QString &group)
364 {
365 KIO_ARGS << url << owner << group;
366 return SimpleJobPrivate::newJob(url, CMD_CHOWN, packedArgs);
367 }
368
setModificationTime(const QUrl & url,const QDateTime & mtime)369 SimpleJob *KIO::setModificationTime(const QUrl &url, const QDateTime &mtime)
370 {
371 // qDebug() << "setModificationTime " << url << " " << mtime;
372 KIO_ARGS << url << mtime;
373 return SimpleJobPrivate::newJobNoUi(url, CMD_SETMODIFICATIONTIME, packedArgs);
374 }
375
rename(const QUrl & src,const QUrl & dest,JobFlags flags)376 SimpleJob *KIO::rename(const QUrl &src, const QUrl &dest, JobFlags flags)
377 {
378 // qDebug() << "rename " << src << " " << dest;
379 KIO_ARGS << src << dest << (qint8)(flags & Overwrite);
380 return SimpleJobPrivate::newJob(src, CMD_RENAME, packedArgs, flags);
381 }
382
symlink(const QString & target,const QUrl & dest,JobFlags flags)383 SimpleJob *KIO::symlink(const QString &target, const QUrl &dest, JobFlags flags)
384 {
385 // qDebug() << "symlink target=" << target << " " << dest;
386 KIO_ARGS << target << dest << (qint8)(flags & Overwrite);
387 return SimpleJobPrivate::newJob(dest, CMD_SYMLINK, packedArgs, flags);
388 }
389
special(const QUrl & url,const QByteArray & data,JobFlags flags)390 SimpleJob *KIO::special(const QUrl &url, const QByteArray &data, JobFlags flags)
391 {
392 // qDebug() << "special " << url;
393 return SimpleJobPrivate::newJob(url, CMD_SPECIAL, data, flags);
394 }
395
mount(bool ro,const QByteArray & fstype,const QString & dev,const QString & point,JobFlags flags)396 SimpleJob *KIO::mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags)
397 {
398 KIO_ARGS << int(1) << qint8(ro ? 1 : 0) << QString::fromLatin1(fstype) << dev << point;
399 SimpleJob *job = special(QUrl(QStringLiteral("file:///")), packedArgs, flags);
400 if (!(flags & HideProgressInfo)) {
401 KIO::JobPrivate::emitMounting(job, dev, point);
402 }
403 return job;
404 }
405
unmount(const QString & point,JobFlags flags)406 SimpleJob *KIO::unmount(const QString &point, JobFlags flags)
407 {
408 KIO_ARGS << int(2) << point;
409 SimpleJob *job = special(QUrl(QStringLiteral("file:///")), packedArgs, flags);
410 if (!(flags & HideProgressInfo)) {
411 KIO::JobPrivate::emitUnmounting(job, point);
412 }
413 return job;
414 }
415
416 //////////
417
http_update_cache(const QUrl & url,bool no_cache,const QDateTime & expireDate)418 SimpleJob *KIO::http_update_cache(const QUrl &url, bool no_cache, const QDateTime &expireDate)
419 {
420 Q_ASSERT(url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https"));
421 // Send http update_cache command (2)
422 KIO_ARGS << (int)2 << url << no_cache << qlonglong(expireDate.toMSecsSinceEpoch() / 1000);
423 SimpleJob *job = SimpleJobPrivate::newJob(url, CMD_SPECIAL, packedArgs);
424 Scheduler::setJobPriority(job, 1);
425 return job;
426 }
427
428 #include "moc_simplejob.cpp"
429