fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kio / kio / job.cpp
blobb083feac9f4f1150c14c72493122e33391410fd1
1 /* This file is part of the KDE libraries
2 Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
3 David Faure <faure@kde.org>
4 Waldo Bastian <bastian@kde.org>
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
22 #include "job.h"
23 #include "job_p.h"
25 #include <config.h>
27 #include <sys/types.h>
28 #include <sys/wait.h>
29 #include <sys/stat.h>
31 #include <assert.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <time.h>
37 #include <unistd.h>
38 extern "C" {
39 #include <pwd.h>
40 #include <grp.h>
42 #include <QtCore/QTimer>
43 #include <QtCore/QFile>
45 #include <kapplication.h>
46 #include <kauthorized.h>
47 #include <kglobal.h>
48 #include <klocale.h>
49 #include <kconfig.h>
50 #include <kdebug.h>
51 #include <kde_file.h>
53 #include <errno.h>
55 #include "jobuidelegate.h"
56 #include "kmimetype.h"
57 #include "slave.h"
58 #include "scheduler.h"
59 #include "kdirwatch.h"
60 #include "kprotocolinfo.h"
61 #include "kprotocolmanager.h"
62 #include "filejob.h"
64 #include "kssl/ksslcsessioncache.h"
66 #include <kdirnotify.h>
67 #include <ktemporaryfile.h>
69 using namespace KIO;
71 static inline Slave *jobSlave(SimpleJob *job)
73 return SimpleJobPrivate::get(job)->m_slave;
76 //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
77 #define REPORT_TIMEOUT 200
79 #define KIO_ARGS QByteArray packedArgs; QDataStream stream( &packedArgs, QIODevice::WriteOnly ); stream
81 Job::Job() : KCompositeJob(*new JobPrivate, 0)
83 setCapabilities( KJob::Killable | KJob::Suspendable );
86 Job::Job(JobPrivate &dd) : KCompositeJob(dd, 0)
88 setCapabilities( KJob::Killable | KJob::Suspendable );
91 Job::~Job()
95 JobUiDelegate *Job::ui() const
97 return static_cast<JobUiDelegate*>( uiDelegate() );
100 bool Job::addSubjob(KJob *jobBase)
102 //kDebug(7007) << "addSubjob(" << jobBase << ") this=" << this;
104 bool ok = KCompositeJob::addSubjob( jobBase );
105 KIO::Job *job = dynamic_cast<KIO::Job*>( jobBase );
106 if (ok && job) {
107 // Copy metadata into subjob (e.g. window-id, user-timestamp etc.)
108 Q_D(Job);
109 job->mergeMetaData(d->m_outgoingMetaData);
111 // Forward information from that subjob.
112 connect(job, SIGNAL(speed(KJob*,ulong)),
113 SLOT(slotSpeed(KJob*,ulong)));
115 if (ui() && job->ui()) {
116 job->ui()->setWindow( ui()->window() );
117 job->ui()->updateUserTimestamp( ui()->userTimestamp() );
120 return ok;
123 bool Job::removeSubjob( KJob *jobBase )
125 //kDebug(7007) << "removeSubjob(" << jobBase << ") this=" << this << "subjobs=" << subjobs().count();
126 return KCompositeJob::removeSubjob( jobBase );
129 void JobPrivate::emitMoving(KIO::Job * job, const KUrl &src, const KUrl &dest)
131 emit job->description(job, i18nc("@title job","Moving"),
132 qMakePair(i18nc("The source of a file operation", "Source"), src.prettyUrl()),
133 qMakePair(i18nc("The destination of a file operation", "Destination"), dest.prettyUrl()));
136 void JobPrivate::emitCopying(KIO::Job * job, const KUrl &src, const KUrl &dest)
138 emit job->description(job, i18nc("@title job","Copying"),
139 qMakePair(i18nc("The source of a file operation", "Source"), src.prettyUrl()),
140 qMakePair(i18nc("The destination of a file operation", "Destination"), dest.prettyUrl()));
143 void JobPrivate::emitCreatingDir(KIO::Job * job, const KUrl &dir)
145 emit job->description(job, i18nc("@title job","Creating directory"),
146 qMakePair(i18n("Directory"), dir.prettyUrl()));
149 void JobPrivate::emitDeleting(KIO::Job *job, const KUrl &url)
151 emit job->description(job, i18nc("@title job","Deleting"),
152 qMakePair(i18n("File"), url.prettyUrl()));
155 void JobPrivate::emitStating(KIO::Job *job, const KUrl &url)
157 emit job->description(job, i18nc("@title job","Examining"),
158 qMakePair(i18n("File"), url.prettyUrl()));
161 void JobPrivate::emitTransferring(KIO::Job *job, const KUrl &url)
163 emit job->description(job, i18nc("@title job","Transferring"),
164 qMakePair(i18nc("The source of a file operation", "Source"), url.prettyUrl()));
167 void JobPrivate::emitMounting(KIO::Job * job, const QString &dev, const QString &point)
169 emit job->description(job, i18nc("@title job","Mounting"),
170 qMakePair(i18n("Device"), dev),
171 qMakePair(i18n("Mountpoint"), point));
174 void JobPrivate::emitUnmounting(KIO::Job * job, const QString &point)
176 emit job->description(job, i18nc("@title job","Unmounting"),
177 qMakePair(i18n("Mountpoint"), point));
180 bool Job::doKill()
182 // kill all subjobs, without triggering their result slot
183 Q_FOREACH( KJob* it, subjobs()) {
184 it->kill( KJob::Quietly );
186 clearSubjobs();
188 return true;
191 bool Job::doSuspend()
193 Q_FOREACH(KJob* it, subjobs()) {
194 if (!it->suspend())
195 return false;
198 return true;
201 bool Job::doResume()
203 Q_FOREACH ( KJob* it, subjobs() )
205 if (!it->resume())
206 return false;
209 return true;
212 void JobPrivate::slotSpeed( KJob*, unsigned long speed )
214 //kDebug(7007) << speed;
215 q_func()->emitSpeed( speed );
218 //Job::errorString is implemented in global.cpp
220 void Job::showErrorDialog( QWidget *parent )
222 if ( ui() )
224 ui()->setWindow( parent );
225 ui()->showErrorMessage();
227 else
229 kError() << errorString();
233 bool Job::isInteractive() const
235 return uiDelegate() != 0;
238 void Job::setParentJob(Job* job)
240 Q_D(Job);
241 Q_ASSERT(d->m_parentJob == 0L);
242 Q_ASSERT(job);
243 d->m_parentJob = job;
246 Job* Job::parentJob() const
248 return d_func()->m_parentJob;
251 MetaData Job::metaData() const
253 return d_func()->m_incomingMetaData;
256 QString Job::queryMetaData(const QString &key)
258 return d_func()->m_incomingMetaData.value(key, QString());
261 void Job::setMetaData( const KIO::MetaData &_metaData)
263 Q_D(Job);
264 d->m_outgoingMetaData = _metaData;
267 void Job::addMetaData( const QString &key, const QString &value)
269 d_func()->m_outgoingMetaData.insert(key, value);
272 void Job::addMetaData( const QMap<QString,QString> &values)
274 Q_D(Job);
275 QMap<QString,QString>::const_iterator it = values.begin();
276 for(;it != values.end(); ++it)
277 d->m_outgoingMetaData.insert(it.key(), it.value());
280 void Job::mergeMetaData( const QMap<QString,QString> &values)
282 Q_D(Job);
283 QMap<QString,QString>::const_iterator it = values.begin();
284 for(;it != values.end(); ++it)
285 // there's probably a faster way
286 if ( !d->m_outgoingMetaData.contains( it.key() ) )
287 d->m_outgoingMetaData.insert( it.key(), it.value() );
290 MetaData Job::outgoingMetaData() const
292 return d_func()->m_outgoingMetaData;
295 SimpleJob::SimpleJob(SimpleJobPrivate &dd)
296 : Job(dd)
298 d_func()->simpleJobInit();
301 void SimpleJobPrivate::simpleJobInit()
303 Q_Q(SimpleJob);
304 if (!m_url.isValid())
306 q->setError( ERR_MALFORMED_URL );
307 q->setErrorText( m_url.url() );
308 QTimer::singleShot(0, q, SLOT(slotFinished()) );
309 return;
312 Scheduler::doJob(q);
316 bool SimpleJob::doKill()
318 //kDebug() << this;
319 Q_D(SimpleJob);
320 Scheduler::cancelJob( this ); // deletes the slave if not 0
321 d->m_slave = 0; // -> set to 0
322 return Job::doKill();
325 bool SimpleJob::doSuspend()
327 Q_D(SimpleJob);
328 if ( d->m_slave )
329 d->m_slave->suspend();
330 return Job::doSuspend();
333 bool SimpleJob::doResume()
335 Q_D(SimpleJob);
336 if ( d->m_slave )
337 d->m_slave->resume();
338 return Job::doResume();
341 const KUrl& SimpleJob::url() const
343 return d_func()->m_url;
346 void SimpleJob::putOnHold()
348 Q_D(SimpleJob);
349 Q_ASSERT( d->m_slave );
350 if ( d->m_slave )
352 Scheduler::putSlaveOnHold(this, d->m_url);
353 d->m_slave = 0;
355 kill( Quietly );
358 void SimpleJob::removeOnHold()
360 Scheduler::removeSlaveOnHold();
363 SimpleJob::~SimpleJob()
365 Q_D(SimpleJob);
366 if (d->m_slave) // was running
368 kDebug(7007) << "Killing running job in destructor!" << kBacktrace();
369 #if 0
370 d->m_slave->kill();
371 Scheduler::jobFinished( this, d->m_slave ); // deletes the slave
372 #endif
373 d->m_slave = 0; // -> set to 0
375 Scheduler::cancelJob( this );
378 void SimpleJobPrivate::start(Slave *slave)
380 Q_Q(SimpleJob);
381 m_slave = slave;
383 q->connect( slave, SIGNAL(error(int,QString)),
384 SLOT(slotError(int,QString)) );
386 q->connect( slave, SIGNAL(warning(QString)),
387 SLOT(slotWarning(QString)) );
389 q->connect( slave, SIGNAL(infoMessage(QString)),
390 SLOT(_k_slotSlaveInfoMessage(QString)) );
392 q->connect( slave, SIGNAL(connected()),
393 SLOT(slotConnected()));
395 q->connect( slave, SIGNAL(finished()),
396 SLOT(slotFinished()) );
398 if ((m_extraFlags & EF_TransferJobDataSent) == 0) // this is a "get" job
400 q->connect( slave, SIGNAL(totalSize(KIO::filesize_t)),
401 SLOT(slotTotalSize(KIO::filesize_t)) );
403 q->connect( slave, SIGNAL(processedSize(KIO::filesize_t)),
404 SLOT(slotProcessedSize(KIO::filesize_t)) );
406 q->connect( slave, SIGNAL(speed(ulong)),
407 SLOT(slotSpeed(ulong)) );
409 q->connect( slave, SIGNAL(metaData(KIO::MetaData)),
410 SLOT(slotMetaData(KIO::MetaData)) );
412 if (ui() && ui()->window())
414 m_outgoingMetaData.insert("window-id", QString::number((long)ui()->window()->winId()));
417 if (ui() && ui()->userTimestamp())
419 m_outgoingMetaData.insert("user-timestamp", QString::number(ui()->userTimestamp()));
422 QString sslSession = KSSLCSessionCache::getSessionForUrl(m_url);
423 if ( !sslSession.isNull() )
425 m_outgoingMetaData.insert("ssl_session_id", sslSession);
428 if (ui() == 0) // not interactive
430 m_outgoingMetaData.insert("no-auth-prompt", "true");
433 if (!m_outgoingMetaData.isEmpty())
435 KIO_ARGS << m_outgoingMetaData;
436 slave->send( CMD_META_DATA, packedArgs );
439 if (!m_subUrl.isEmpty())
441 KIO_ARGS << m_subUrl;
442 slave->send( CMD_SUBURL, packedArgs );
445 slave->send( m_command, m_packedArgs );
448 void SimpleJobPrivate::slaveDone()
450 Q_Q(SimpleJob);
451 if (!m_slave) return;
452 if (m_command == CMD_OPEN) m_slave->send(CMD_CLOSE);
453 q->disconnect(m_slave); // Remove all signals between slave and job
454 Scheduler::jobFinished( q, m_slave );
455 m_slave = 0;
458 void SimpleJob::slotFinished( )
460 Q_D(SimpleJob);
461 // Return slave to the scheduler
462 d->slaveDone();
464 if (!hasSubjobs())
466 if ( !error() && (d->m_command == CMD_MKDIR || d->m_command == CMD_RENAME ) )
468 if ( d->m_command == CMD_MKDIR )
470 KUrl urlDir( url() );
471 urlDir.setPath( urlDir.directory() );
472 org::kde::KDirNotify::emitFilesAdded( urlDir.url() );
474 else /*if ( m_command == CMD_RENAME )*/
476 KUrl src, dst;
477 QDataStream str( d->m_packedArgs );
478 str >> src >> dst;
479 if( src.directory() == dst.directory() ) // For the user, moving isn't renaming. Only renaming is.
480 org::kde::KDirNotify::emitFileRenamed( src.url(), dst.url() );
482 org::kde::KDirNotify::emitFileMoved( src.url(), dst.url() );
485 emitResult();
489 void SimpleJob::slotError( int err, const QString & errorText )
491 Q_D(SimpleJob);
492 setError( err );
493 setErrorText( errorText );
494 if ((error() == ERR_UNKNOWN_HOST) && d->m_url.host().isEmpty())
495 setErrorText( QString() );
496 // error terminates the job
497 slotFinished();
500 void SimpleJob::slotWarning( const QString & errorText )
502 emit warning( this, errorText );
505 void SimpleJobPrivate::_k_slotSlaveInfoMessage( const QString & msg )
507 emit q_func()->infoMessage( q_func(), msg );
510 void SimpleJobPrivate::slotConnected()
512 emit q_func()->connected( q_func() );
515 void SimpleJobPrivate::slotTotalSize( KIO::filesize_t size )
517 Q_Q(SimpleJob);
518 if (size > q->totalAmount(KJob::Bytes))
520 q->setTotalAmount(KJob::Bytes, size);
524 void SimpleJobPrivate::slotProcessedSize( KIO::filesize_t size )
526 Q_Q(SimpleJob);
527 //kDebug(7007) << KIO::number(size);
528 q->setProcessedAmount(KJob::Bytes, size);
531 void SimpleJobPrivate::slotSpeed( unsigned long speed )
533 //kDebug(7007) << speed;
534 q_func()->emitSpeed( speed );
537 void SimpleJob::slotMetaData( const KIO::MetaData &_metaData )
539 Q_D(SimpleJob);
540 d->m_incomingMetaData += _metaData;
543 void SimpleJob::storeSSLSessionFromJob(const KUrl &redirectionURL)
545 Q_D(SimpleJob);
546 QString sslSession = queryMetaData("ssl_session_id");
548 if ( !sslSession.isNull() ) {
549 const KUrl &queryURL = redirectionURL.isEmpty() ? d->m_url : redirectionURL;
550 KSSLCSessionCache::putSessionForUrl(queryURL, sslSession);
554 //////////
555 class KIO::MkdirJobPrivate: public SimpleJobPrivate
557 public:
558 MkdirJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
559 : SimpleJobPrivate(url, command, packedArgs)
561 KUrl m_redirectionURL;
562 void slotRedirection(const KUrl &url);
565 * @internal
566 * Called by the scheduler when a @p slave gets to
567 * work on this job.
568 * @param slave the slave that starts working on this job
570 virtual void start( Slave *slave );
572 Q_DECLARE_PUBLIC(MkdirJob)
574 static inline MkdirJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs)
576 MkdirJob *job = new MkdirJob(*new MkdirJobPrivate(url, command, packedArgs));
577 job->setUiDelegate(new JobUiDelegate);
578 return job;
582 MkdirJob::MkdirJob(MkdirJobPrivate &dd)
583 : SimpleJob(dd)
587 MkdirJob::~MkdirJob()
591 void MkdirJobPrivate::start(Slave *slave)
593 Q_Q(MkdirJob);
594 q->connect( slave, SIGNAL( redirection(const KUrl &) ),
595 SLOT( slotRedirection(const KUrl &) ) );
597 SimpleJobPrivate::start(slave);
600 // Slave got a redirection request
601 void MkdirJobPrivate::slotRedirection( const KUrl &url)
603 Q_Q(MkdirJob);
604 kDebug(7007) << url;
605 if (!KAuthorized::authorizeUrlAction("redirect", m_url, url))
607 kWarning(7007) << "Redirection from" << m_url << "to" << url << "REJECTED!";
608 q->setError( ERR_ACCESS_DENIED );
609 q->setErrorText( url.prettyUrl() );
610 return;
612 m_redirectionURL = url; // We'll remember that when the job finishes
613 if (m_url.hasUser() && !url.hasUser() && (m_url.host().toLower() == url.host().toLower()))
614 m_redirectionURL.setUser(m_url.user()); // Preserve user
615 // Tell the user that we haven't finished yet
616 emit q->redirection(q, m_redirectionURL);
619 void MkdirJob::slotFinished()
621 Q_D(MkdirJob);
622 if ( d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid())
624 // Return slave to the scheduler
625 SimpleJob::slotFinished();
626 } else {
627 //kDebug(7007) << "MkdirJob: Redirection to " << m_redirectionURL;
628 if (queryMetaData("permanent-redirect")=="true")
629 emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
630 KUrl dummyUrl;
631 int permissions;
632 QDataStream istream( d->m_packedArgs );
633 istream >> dummyUrl >> permissions;
635 d->m_url = d->m_redirectionURL;
636 d->m_redirectionURL = KUrl();
637 d->m_packedArgs.truncate(0);
638 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
639 stream << d->m_url << permissions;
641 // Return slave to the scheduler
642 d->slaveDone();
643 Scheduler::doJob(this);
647 SimpleJob *KIO::mkdir( const KUrl& url, int permissions )
649 //kDebug(7007) << "mkdir " << url;
650 KIO_ARGS << url << permissions;
651 return MkdirJobPrivate::newJob(url, CMD_MKDIR, packedArgs);
654 SimpleJob *KIO::rmdir( const KUrl& url )
656 //kDebug(7007) << "rmdir " << url;
657 KIO_ARGS << url << qint8(false); // isFile is false
658 return SimpleJobPrivate::newJob(url, CMD_DEL, packedArgs);
661 SimpleJob *KIO::chmod( const KUrl& url, int permissions )
663 //kDebug(7007) << "chmod " << url;
664 KIO_ARGS << url << permissions;
665 return SimpleJobPrivate::newJob(url, CMD_CHMOD, packedArgs);
668 SimpleJob *KIO::chown( const KUrl& url, const QString& owner, const QString& group )
670 KIO_ARGS << url << owner << group;
671 return SimpleJobPrivate::newJob(url, CMD_CHOWN, packedArgs);
674 SimpleJob *KIO::setModificationTime( const KUrl& url, const QDateTime& mtime )
676 //kDebug(7007) << "setModificationTime " << url << " " << mtime;
677 KIO_ARGS << url << mtime;
678 return SimpleJobPrivate::newJobNoUi(url, CMD_SETMODIFICATIONTIME, packedArgs);
681 SimpleJob *KIO::rename( const KUrl& src, const KUrl & dest, JobFlags flags )
683 //kDebug(7007) << "rename " << src << " " << dest;
684 KIO_ARGS << src << dest << (qint8) (flags & Overwrite);
685 return SimpleJobPrivate::newJob(src, CMD_RENAME, packedArgs);
688 SimpleJob *KIO::symlink( const QString& target, const KUrl & dest, JobFlags flags )
690 //kDebug(7007) << "symlink target=" << target << " " << dest;
691 KIO_ARGS << target << dest << (qint8) (flags & Overwrite);
692 return SimpleJobPrivate::newJob(dest, CMD_SYMLINK, packedArgs, flags);
695 SimpleJob *KIO::special(const KUrl& url, const QByteArray & data, JobFlags flags)
697 //kDebug(7007) << "special " << url;
698 return SimpleJobPrivate::newJob(url, CMD_SPECIAL, data, flags);
701 SimpleJob *KIO::mount( bool ro, const QByteArray& fstype, const QString& dev, const QString& point, JobFlags flags )
703 KIO_ARGS << int(1) << qint8( ro ? 1 : 0 )
704 << QString::fromLatin1(fstype) << dev << point;
705 SimpleJob *job = special( KUrl("file:/"), packedArgs, flags );
706 if (!(flags & HideProgressInfo)) {
707 KIO::JobPrivate::emitMounting(job, dev, point);
709 return job;
712 SimpleJob *KIO::unmount( const QString& point, JobFlags flags )
714 KIO_ARGS << int(2) << point;
715 SimpleJob *job = special( KUrl("file:/"), packedArgs, flags );
716 if (!(flags & HideProgressInfo)) {
717 KIO::JobPrivate::emitUnmounting(job, point);
719 return job;
724 //////////
726 class KIO::StatJobPrivate: public SimpleJobPrivate
728 public:
729 inline StatJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
730 : SimpleJobPrivate(url, command, packedArgs), m_bSource(true), m_details(2)
733 UDSEntry m_statResult;
734 KUrl m_redirectionURL;
735 bool m_bSource;
736 short int m_details;
737 void slotStatEntry( const KIO::UDSEntry & entry );
738 void slotRedirection( const KUrl &url);
741 * @internal
742 * Called by the scheduler when a @p slave gets to
743 * work on this job.
744 * @param slave the slave that starts working on this job
746 virtual void start( Slave *slave );
748 Q_DECLARE_PUBLIC(StatJob)
750 static inline StatJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs,
751 JobFlags flags )
753 StatJob *job = new StatJob(*new StatJobPrivate(url, command, packedArgs));
754 job->setUiDelegate(new JobUiDelegate);
755 if (!(flags & HideProgressInfo)) {
756 KIO::getJobTracker()->registerJob(job);
757 emitStating(job, url);
759 return job;
763 StatJob::StatJob(StatJobPrivate &dd)
764 : SimpleJob(dd)
768 StatJob::~StatJob()
772 void StatJob::setSide( bool source )
774 d_func()->m_bSource = source;
777 void StatJob::setSide( StatSide side )
779 d_func()->m_bSource = side == SourceSide;
782 void StatJob::setDetails( short int details )
784 d_func()->m_details = details;
787 const UDSEntry & StatJob::statResult() const
789 return d_func()->m_statResult;
792 void StatJobPrivate::start(Slave *slave)
794 Q_Q(StatJob);
795 m_outgoingMetaData.insert( "statSide", m_bSource ? "source" : "dest" );
796 m_outgoingMetaData.insert( "details", QString::number(m_details) );
798 q->connect( slave, SIGNAL( statEntry( const KIO::UDSEntry& ) ),
799 SLOT( slotStatEntry( const KIO::UDSEntry & ) ) );
800 q->connect( slave, SIGNAL( redirection(const KUrl &) ),
801 SLOT( slotRedirection(const KUrl &) ) );
803 SimpleJobPrivate::start(slave);
806 void StatJobPrivate::slotStatEntry( const KIO::UDSEntry & entry )
808 //kDebug(7007);
809 m_statResult = entry;
812 // Slave got a redirection request
813 void StatJobPrivate::slotRedirection( const KUrl &url)
815 Q_Q(StatJob);
816 kDebug(7007) << url;
817 if (!KAuthorized::authorizeUrlAction("redirect", m_url, url))
819 kWarning(7007) << "Redirection from " << m_url << " to " << url << " REJECTED!";
820 q->setError( ERR_ACCESS_DENIED );
821 q->setErrorText( url.prettyUrl() );
822 return;
824 m_redirectionURL = url; // We'll remember that when the job finishes
825 if (m_url.hasUser() && !url.hasUser() && (m_url.host().toLower() == url.host().toLower()))
826 m_redirectionURL.setUser(m_url.user()); // Preserve user
827 // Tell the user that we haven't finished yet
828 emit q->redirection(q, m_redirectionURL);
831 void StatJob::slotFinished()
833 Q_D(StatJob);
834 if ( d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid())
836 // Return slave to the scheduler
837 SimpleJob::slotFinished();
838 } else {
839 //kDebug(7007) << "StatJob: Redirection to " << m_redirectionURL;
840 if (queryMetaData("permanent-redirect")=="true")
841 emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
842 d->m_url = d->m_redirectionURL;
843 d->m_redirectionURL = KUrl();
844 d->m_packedArgs.truncate(0);
845 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
846 stream << d->m_url;
848 // Return slave to the scheduler
849 d->slaveDone();
850 Scheduler::doJob(this);
854 void StatJob::slotMetaData( const KIO::MetaData &_metaData)
856 Q_D(StatJob);
857 SimpleJob::slotMetaData(_metaData);
858 storeSSLSessionFromJob(d->m_redirectionURL);
861 StatJob *KIO::stat(const KUrl& url, JobFlags flags)
863 // Assume sideIsSource. Gets are more common than puts.
864 return stat( url, StatJob::SourceSide, 2, flags );
867 StatJob *KIO::stat(const KUrl& url, bool sideIsSource, short int details, JobFlags flags )
869 //kDebug(7007) << "stat" << url;
870 KIO_ARGS << url;
871 StatJob * job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags);
872 job->setSide( sideIsSource ? StatJob::SourceSide : StatJob::DestinationSide );
873 job->setDetails( details );
874 return job;
877 StatJob *KIO::stat(const KUrl& url, KIO::StatJob::StatSide side, short int details, JobFlags flags )
879 //kDebug(7007) << "stat" << url;
880 KIO_ARGS << url;
881 StatJob * job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags);
882 job->setSide( side );
883 job->setDetails( details );
884 return job;
887 SimpleJob *KIO::http_update_cache( const KUrl& url, bool no_cache, time_t expireDate)
889 assert( (url.protocol() == "http") || (url.protocol() == "https") );
890 // Send http update_cache command (2)
891 KIO_ARGS << (int)2 << url << no_cache << qlonglong(expireDate);
892 SimpleJob * job = SimpleJobPrivate::newJob(url, CMD_SPECIAL, packedArgs);
893 Scheduler::scheduleJob(job);
894 return job;
897 //////////
899 TransferJob::TransferJob(TransferJobPrivate &dd)
900 : SimpleJob(dd)
902 Q_D(TransferJob);
903 if (d->m_command == CMD_PUT) {
904 d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent;
908 TransferJob::~TransferJob()
912 // Slave sends data
913 void TransferJob::slotData( const QByteArray &_data)
915 Q_D(TransferJob);
916 if(d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error())
917 emit data( this, _data);
920 void KIO::TransferJob::setTotalSize(KIO::filesize_t bytes)
922 setTotalAmount(KJob::Bytes, bytes);
925 // Slave got a redirection request
926 void TransferJob::slotRedirection( const KUrl &url)
928 Q_D(TransferJob);
929 kDebug(7007) << url;
930 if (!KAuthorized::authorizeUrlAction("redirect", d->m_url, url))
932 kWarning(7007) << "Redirection from " << d->m_url << " to " << url << " REJECTED!";
933 return;
936 // Some websites keep redirecting to themselves where each redirection
937 // acts as the stage in a state-machine. We define "endless redirections"
938 // as 5 redirections to the same URL.
939 if (d->m_redirectionList.count(url) > 5)
941 kDebug(7007) << "CYCLIC REDIRECTION!";
942 setError( ERR_CYCLIC_LINK );
943 setErrorText( d->m_url.prettyUrl() );
945 else
947 d->m_redirectionURL = url; // We'll remember that when the job finishes
948 if (d->m_url.hasUser() && !url.hasUser() && (d->m_url.host().toLower() == url.host().toLower()))
949 d->m_redirectionURL.setUser(d->m_url.user()); // Preserve user
950 d->m_redirectionList.append(url);
951 d->m_outgoingMetaData["ssl_was_in_use"] = d->m_incomingMetaData["ssl_in_use"];
952 // Tell the user that we haven't finished yet
953 emit redirection(this, d->m_redirectionURL);
957 void TransferJob::slotFinished()
959 Q_D(TransferJob);
960 //kDebug(7007) << this << "," << m_url;
961 if (d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid())
962 SimpleJob::slotFinished();
963 else {
964 //kDebug(7007) << "Redirection to" << m_redirectionURL;
965 if (queryMetaData("permanent-redirect")=="true")
966 emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
967 // Honour the redirection
968 // We take the approach of "redirecting this same job"
969 // Another solution would be to create a subjob, but the same problem
970 // happens (unpacking+repacking)
971 d->staticData.truncate(0);
972 d->m_incomingMetaData.clear();
973 if (queryMetaData("cache") != "reload")
974 addMetaData("cache","refresh");
975 d->m_internalSuspended = false;
976 d->m_url = d->m_redirectionURL;
977 d->m_redirectionURL = KUrl();
978 // The very tricky part is the packed arguments business
979 QString dummyStr;
980 KUrl dummyUrl;
981 QDataStream istream( d->m_packedArgs );
982 switch( d->m_command ) {
983 case CMD_GET: {
984 d->m_packedArgs.truncate(0);
985 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
986 stream << d->m_url;
987 break;
989 case CMD_PUT: {
990 int permissions;
991 qint8 iOverwrite, iResume;
992 istream >> dummyUrl >> iOverwrite >> iResume >> permissions;
993 d->m_packedArgs.truncate(0);
994 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
995 stream << d->m_url << iOverwrite << iResume << permissions;
996 break;
998 case CMD_SPECIAL: {
999 int specialcmd;
1000 istream >> specialcmd;
1001 if (specialcmd == 1) // HTTP POST
1003 addMetaData("cache","reload");
1004 d->m_packedArgs.truncate(0);
1005 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
1006 stream << d->m_url;
1007 d->m_command = CMD_GET;
1009 break;
1013 // Return slave to the scheduler
1014 d->slaveDone();
1015 Scheduler::doJob(this);
1019 void TransferJob::setAsyncDataEnabled(bool enabled)
1021 Q_D(TransferJob);
1022 if (enabled)
1023 d->m_extraFlags |= JobPrivate::EF_TransferJobAsync;
1024 else
1025 d->m_extraFlags &= ~JobPrivate::EF_TransferJobAsync;
1028 void TransferJob::sendAsyncData(const QByteArray &dataForSlave)
1030 Q_D(TransferJob);
1031 if (d->m_extraFlags & JobPrivate::EF_TransferJobNeedData)
1033 d->m_slave->send( MSG_DATA, dataForSlave );
1034 if (d->m_extraFlags & JobPrivate::EF_TransferJobDataSent) // put job -> emit progress
1036 KIO::filesize_t size = processedAmount(KJob::Bytes)+dataForSlave.size();
1037 setProcessedAmount(KJob::Bytes, size);
1041 d->m_extraFlags &= ~JobPrivate::EF_TransferJobNeedData;
1044 void TransferJob::setReportDataSent(bool enabled)
1046 Q_D(TransferJob);
1047 if (enabled)
1048 d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent;
1049 else
1050 d->m_extraFlags &= ~JobPrivate::EF_TransferJobDataSent;
1053 bool TransferJob::reportDataSent() const
1055 return (d_func()->m_extraFlags & JobPrivate::EF_TransferJobDataSent);
1058 QString TransferJob::mimetype() const
1060 return d_func()->m_mimetype;
1064 // Slave requests data
1065 void TransferJob::slotDataReq()
1067 Q_D(TransferJob);
1068 QByteArray dataForSlave;
1070 d->m_extraFlags |= JobPrivate::EF_TransferJobNeedData;
1072 if (!d->staticData.isEmpty())
1074 dataForSlave = d->staticData;
1075 d->staticData.clear();
1077 else
1079 emit dataReq( this, dataForSlave);
1081 if (d->m_extraFlags & JobPrivate::EF_TransferJobAsync)
1082 return;
1085 static const int max_size = 14 * 1024 * 1024;
1086 if (dataForSlave.size() > max_size)
1088 kDebug(7007) << "send " << dataForSlave.size() / 1024 / 1024 << "MB of data in TransferJob::dataReq. This needs to be splitted, which requires a copy. Fix the application.\n";
1089 d->staticData = QByteArray(dataForSlave.data() + max_size , dataForSlave.size() - max_size);
1090 dataForSlave.truncate(max_size);
1093 sendAsyncData(dataForSlave);
1095 if (d->m_subJob)
1097 // Bitburger protocol in action
1098 d->internalSuspend(); // Wait for more data from subJob.
1099 d->m_subJob->d_func()->internalResume(); // Ask for more!
1103 void TransferJob::slotMimetype( const QString& type )
1105 Q_D(TransferJob);
1106 d->m_mimetype = type;
1107 emit mimetype( this, type );
1111 void TransferJobPrivate::internalSuspend()
1113 m_internalSuspended = true;
1114 if (m_slave)
1115 m_slave->suspend();
1118 void TransferJobPrivate::internalResume()
1120 m_internalSuspended = false;
1121 if ( m_slave && !suspended )
1122 m_slave->resume();
1125 bool TransferJob::doResume()
1127 Q_D(TransferJob);
1128 if ( !SimpleJob::doResume() )
1129 return false;
1130 if ( d->m_internalSuspended )
1131 d->internalSuspend();
1132 return true;
1135 bool TransferJob::isErrorPage() const
1137 return d_func()->m_errorPage;
1140 void TransferJobPrivate::start(Slave *slave)
1142 Q_Q(TransferJob);
1143 assert(slave);
1144 JobPrivate::emitTransferring(q, m_url);
1145 q->connect( slave, SIGNAL( data( const QByteArray & ) ),
1146 SLOT( slotData( const QByteArray & ) ) );
1148 q->connect( slave, SIGNAL( dataReq() ),
1149 SLOT( slotDataReq() ) );
1151 q->connect( slave, SIGNAL( redirection(const KUrl &) ),
1152 SLOT( slotRedirection(const KUrl &) ) );
1154 q->connect( slave, SIGNAL(mimeType( const QString& ) ),
1155 SLOT( slotMimetype( const QString& ) ) );
1157 q->connect( slave, SIGNAL(errorPage() ),
1158 SLOT( slotErrorPage() ) );
1160 q->connect( slave, SIGNAL( needSubUrlData() ),
1161 SLOT( slotNeedSubUrlData() ) );
1163 q->connect( slave, SIGNAL(canResume( KIO::filesize_t ) ),
1164 SLOT( slotCanResume( KIO::filesize_t ) ) );
1166 if (slave->suspended())
1168 m_mimetype = "unknown";
1169 // WABA: The slave was put on hold. Resume operation.
1170 slave->resume();
1173 SimpleJobPrivate::start(slave);
1174 if (m_internalSuspended)
1175 slave->suspend();
1178 void TransferJobPrivate::slotNeedSubUrlData()
1180 Q_Q(TransferJob);
1181 // Job needs data from subURL.
1182 m_subJob = KIO::get( m_subUrl, NoReload, HideProgressInfo);
1183 internalSuspend(); // Put job on hold until we have some data.
1184 q->connect(m_subJob, SIGNAL( data(KIO::Job*,const QByteArray &)),
1185 SLOT( slotSubUrlData(KIO::Job*,const QByteArray &)));
1186 q->addSubjob(m_subJob);
1189 void TransferJobPrivate::slotSubUrlData(KIO::Job*, const QByteArray &data)
1191 // The Alternating Bitburg protocol in action again.
1192 staticData = data;
1193 m_subJob->d_func()->internalSuspend(); // Put job on hold until we have delivered the data.
1194 internalResume(); // Activate ourselves again.
1197 void TransferJob::slotMetaData( const KIO::MetaData &_metaData)
1199 Q_D(TransferJob);
1200 SimpleJob::slotMetaData(_metaData);
1201 storeSSLSessionFromJob(d->m_redirectionURL);
1204 void TransferJobPrivate::slotErrorPage()
1206 m_errorPage = true;
1209 void TransferJobPrivate::slotCanResume( KIO::filesize_t offset )
1211 Q_Q(TransferJob);
1212 emit q->canResume(q, offset);
1215 void TransferJob::slotResult( KJob *job)
1217 Q_D(TransferJob);
1218 // This can only be our suburl.
1219 assert(job == d->m_subJob);
1221 SimpleJob::slotResult( job );
1223 if (!error() && job == d->m_subJob)
1225 d->m_subJob = 0; // No action required
1226 d->internalResume(); // Make sure we get the remaining data.
1230 void TransferJob::setModificationTime( const QDateTime& mtime )
1232 addMetaData( "modified", mtime.toString( Qt::ISODate ) );
1235 TransferJob *KIO::get( const KUrl& url, LoadType reload, JobFlags flags )
1237 // Send decoded path and encoded query
1238 KIO_ARGS << url;
1239 TransferJob * job = TransferJobPrivate::newJob(url, CMD_GET, packedArgs,
1240 QByteArray(), flags);
1241 if (reload == Reload)
1242 job->addMetaData("cache", "reload");
1243 return job;
1246 class KIO::StoredTransferJobPrivate: public TransferJobPrivate
1248 public:
1249 StoredTransferJobPrivate(const KUrl& url, int command,
1250 const QByteArray &packedArgs,
1251 const QByteArray &_staticData)
1252 : TransferJobPrivate(url, command, packedArgs, _staticData),
1253 m_uploadOffset( 0 )
1255 QByteArray m_data;
1256 int m_uploadOffset;
1258 void slotStoredData( KIO::Job *job, const QByteArray &data );
1259 void slotStoredDataReq( KIO::Job *job, QByteArray &data );
1261 Q_DECLARE_PUBLIC(StoredTransferJob)
1263 static inline StoredTransferJob *newJob(const KUrl &url, int command,
1264 const QByteArray &packedArgs,
1265 const QByteArray &staticData, JobFlags flags)
1267 StoredTransferJob *job = new StoredTransferJob(
1268 *new StoredTransferJobPrivate(url, command, packedArgs, staticData));
1269 job->setUiDelegate(new JobUiDelegate);
1270 if (!(flags & HideProgressInfo))
1271 KIO::getJobTracker()->registerJob(job);
1272 return job;
1276 namespace KIO {
1277 class PostErrorJob : public StoredTransferJob
1279 public:
1281 PostErrorJob(int _error, const QString& url, const QByteArray &packedArgs, const QByteArray &postData)
1282 : StoredTransferJob(*new StoredTransferJobPrivate(KUrl(), CMD_SPECIAL, packedArgs, postData))
1284 setError( _error );
1285 setErrorText( url );
1291 static KIO::PostErrorJob* precheckHttpPost( const KUrl& url, const QByteArray& postData, JobFlags flags )
1293 int _error = 0;
1295 // filter out some malicious ports
1296 static const int bad_ports[] = {
1297 1, // tcpmux
1298 7, // echo
1299 9, // discard
1300 11, // systat
1301 13, // daytime
1302 15, // netstat
1303 17, // qotd
1304 19, // chargen
1305 20, // ftp-data
1306 21, // ftp-cntl
1307 22, // ssh
1308 23, // telnet
1309 25, // smtp
1310 37, // time
1311 42, // name
1312 43, // nicname
1313 53, // domain
1314 77, // priv-rjs
1315 79, // finger
1316 87, // ttylink
1317 95, // supdup
1318 101, // hostriame
1319 102, // iso-tsap
1320 103, // gppitnp
1321 104, // acr-nema
1322 109, // pop2
1323 110, // pop3
1324 111, // sunrpc
1325 113, // auth
1326 115, // sftp
1327 117, // uucp-path
1328 119, // nntp
1329 123, // NTP
1330 135, // loc-srv / epmap
1331 139, // netbios
1332 143, // imap2
1333 179, // BGP
1334 389, // ldap
1335 512, // print / exec
1336 513, // login
1337 514, // shell
1338 515, // printer
1339 526, // tempo
1340 530, // courier
1341 531, // Chat
1342 532, // netnews
1343 540, // uucp
1344 556, // remotefs
1345 587, // sendmail
1346 601, //
1347 989, // ftps data
1348 990, // ftps
1349 992, // telnets
1350 993, // imap/SSL
1351 995, // pop3/SSL
1352 1080, // SOCKS
1353 2049, // nfs
1354 4045, // lockd
1355 6000, // x11
1356 6667, // irc
1358 for (int cnt=0; bad_ports[cnt]; ++cnt)
1359 if (url.port() == bad_ports[cnt])
1361 _error = KIO::ERR_POST_DENIED;
1362 break;
1365 if ( _error )
1367 static bool override_loaded = false;
1368 static QList< int >* overriden_ports = NULL;
1369 if( !override_loaded ) {
1370 KConfig cfg( "kio_httprc" );
1371 overriden_ports = new QList< int >;
1372 *overriden_ports = cfg.group(QString()).readEntry( "OverriddenPorts", QList<int>() );
1373 override_loaded = true;
1375 for( QList< int >::ConstIterator it = overriden_ports->constBegin();
1376 it != overriden_ports->constEnd();
1377 ++it ) {
1378 if( overriden_ports->contains( url.port())) {
1379 _error = 0;
1384 // filter out non https? protocols
1385 if ((url.protocol() != "http") && (url.protocol() != "https" ))
1386 _error = KIO::ERR_POST_DENIED;
1388 if (!_error && !KAuthorized::authorizeUrlAction("open", KUrl(), url))
1389 _error = KIO::ERR_ACCESS_DENIED;
1391 // if request is not valid, return an invalid transfer job
1392 if (_error)
1394 KIO_ARGS << (int)1 << url;
1395 PostErrorJob * job = new PostErrorJob(_error, url.prettyUrl(), packedArgs, postData);
1396 job->setUiDelegate(new JobUiDelegate());
1397 if (!(flags & HideProgressInfo)) {
1398 KIO::getJobTracker()->registerJob(job);
1400 return job;
1403 // all is ok, return 0
1404 return 0;
1407 TransferJob *KIO::http_post( const KUrl& url, const QByteArray &postData, JobFlags flags )
1409 bool redirection = false;
1410 KUrl _url(url);
1411 if (_url.path().isEmpty())
1413 redirection = true;
1414 _url.setPath("/");
1417 TransferJob* job = precheckHttpPost(_url, postData, flags);
1418 if (job)
1419 return job;
1421 // Send http post command (1), decoded path and encoded query
1422 KIO_ARGS << (int)1 << _url;
1423 job = TransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags);
1425 if (redirection)
1426 QTimer::singleShot(0, job, SLOT(slotPostRedirection()) );
1428 return job;
1431 StoredTransferJob *KIO::storedHttpPost( const QByteArray& postData, const KUrl& url, JobFlags flags )
1433 bool redirection = false;
1434 KUrl _url(url);
1435 if (_url.path().isEmpty())
1437 redirection = true;
1438 _url.setPath("/");
1441 StoredTransferJob* job = precheckHttpPost(_url, postData, flags);
1442 if (job)
1443 return job;
1445 // Send http post command (1), decoded path and encoded query
1446 KIO_ARGS << (int)1 << _url;
1447 job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags );
1448 return job;
1451 // http post got redirected from http://host to http://host/ by TransferJob
1452 // We must do this redirection ourselves because redirections by the
1453 // slave change post jobs into get jobs.
1454 void TransferJobPrivate::slotPostRedirection()
1456 Q_Q(TransferJob);
1457 kDebug(7007) << "TransferJob::slotPostRedirection(" << m_url << ")";
1458 // Tell the user about the new url.
1459 emit q->redirection(q, m_url);
1463 TransferJob *KIO::put( const KUrl& url, int permissions, JobFlags flags )
1465 KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions;
1466 return TransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags);
1469 //////////
1471 StoredTransferJob::StoredTransferJob(StoredTransferJobPrivate &dd)
1472 : TransferJob(dd)
1474 connect( this, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
1475 SLOT( slotStoredData( KIO::Job *, const QByteArray & ) ) );
1476 connect( this, SIGNAL( dataReq( KIO::Job *, QByteArray & ) ),
1477 SLOT( slotStoredDataReq( KIO::Job *, QByteArray & ) ) );
1480 StoredTransferJob::~StoredTransferJob()
1484 void StoredTransferJob::setData( const QByteArray& arr )
1486 Q_D(StoredTransferJob);
1487 Q_ASSERT( d->m_data.isNull() ); // check that we're only called once
1488 Q_ASSERT( d->m_uploadOffset == 0 ); // no upload started yet
1489 d->m_data = arr;
1490 setTotalSize( d->m_data.size() );
1493 QByteArray StoredTransferJob::data() const
1495 return d_func()->m_data;
1498 void StoredTransferJobPrivate::slotStoredData( KIO::Job *, const QByteArray &data )
1500 // check for end-of-data marker:
1501 if ( data.size() == 0 )
1502 return;
1503 unsigned int oldSize = m_data.size();
1504 m_data.resize( oldSize + data.size() );
1505 memcpy( m_data.data() + oldSize, data.data(), data.size() );
1508 void StoredTransferJobPrivate::slotStoredDataReq( KIO::Job *, QByteArray &data )
1510 // Inspired from kmail's KMKernel::byteArrayToRemoteFile
1511 // send the data in 64 KB chunks
1512 const int MAX_CHUNK_SIZE = 64*1024;
1513 int remainingBytes = m_data.size() - m_uploadOffset;
1514 if( remainingBytes > MAX_CHUNK_SIZE ) {
1515 // send MAX_CHUNK_SIZE bytes to the receiver (deep copy)
1516 data = QByteArray( m_data.data() + m_uploadOffset, MAX_CHUNK_SIZE );
1517 m_uploadOffset += MAX_CHUNK_SIZE;
1518 //kDebug() << "Sending " << MAX_CHUNK_SIZE << " bytes ("
1519 // << remainingBytes - MAX_CHUNK_SIZE << " bytes remain)\n";
1520 } else {
1521 // send the remaining bytes to the receiver (deep copy)
1522 data = QByteArray( m_data.data() + m_uploadOffset, remainingBytes );
1523 m_data = QByteArray();
1524 m_uploadOffset = 0;
1525 //kDebug() << "Sending " << remainingBytes << " bytes\n";
1529 StoredTransferJob *KIO::storedGet( const KUrl& url, LoadType reload, JobFlags flags )
1531 // Send decoded path and encoded query
1532 KIO_ARGS << url;
1533 StoredTransferJob * job = StoredTransferJobPrivate::newJob(url, CMD_GET, packedArgs, QByteArray(), flags);
1534 if (reload == Reload)
1535 job->addMetaData("cache", "reload");
1536 return job;
1539 StoredTransferJob *KIO::storedPut( const QByteArray& arr, const KUrl& url, int permissions,
1540 JobFlags flags )
1542 KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions;
1543 StoredTransferJob * job = StoredTransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags );
1544 job->setData( arr );
1545 return job;
1548 //////////
1550 class KIO::MimetypeJobPrivate: public KIO::TransferJobPrivate
1552 public:
1553 MimetypeJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
1554 : TransferJobPrivate(url, command, packedArgs, QByteArray())
1557 Q_DECLARE_PUBLIC(MimetypeJob)
1559 static inline MimetypeJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs,
1560 JobFlags flags)
1562 MimetypeJob *job = new MimetypeJob(*new MimetypeJobPrivate(url, command, packedArgs));
1563 job->setUiDelegate(new JobUiDelegate);
1564 if (!(flags & HideProgressInfo)) {
1565 KIO::getJobTracker()->registerJob(job);
1566 emitStating(job, url);
1568 return job;
1572 MimetypeJob::MimetypeJob(MimetypeJobPrivate &dd)
1573 : TransferJob(dd)
1577 MimetypeJob::~MimetypeJob()
1581 void MimetypeJob::slotFinished( )
1583 Q_D(MimetypeJob);
1584 //kDebug(7007);
1585 if ( error() == KIO::ERR_IS_DIRECTORY )
1587 // It is in fact a directory. This happens when HTTP redirects to FTP.
1588 // Due to the "protocol doesn't support listing" code in KRun, we
1589 // assumed it was a file.
1590 kDebug(7007) << "It is in fact a directory!";
1591 d->m_mimetype = QString::fromLatin1("inode/directory");
1592 emit TransferJob::mimetype( this, d->m_mimetype );
1593 setError( 0 );
1595 if ( d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error() )
1597 // Return slave to the scheduler
1598 TransferJob::slotFinished();
1599 } else {
1600 //kDebug(7007) << "Redirection to " << m_redirectionURL;
1601 if (queryMetaData("permanent-redirect")=="true")
1602 emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
1603 d->staticData.truncate(0);
1604 d->m_internalSuspended = false;
1605 d->m_url = d->m_redirectionURL;
1606 d->m_redirectionURL = KUrl();
1607 d->m_packedArgs.truncate(0);
1608 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
1609 stream << d->m_url;
1611 // Return slave to the scheduler
1612 d->slaveDone();
1613 Scheduler::doJob(this);
1617 MimetypeJob *KIO::mimetype(const KUrl& url, JobFlags flags)
1619 KIO_ARGS << url;
1620 return MimetypeJobPrivate::newJob(url, CMD_MIMETYPE, packedArgs, flags);
1623 //////////////////////////
1625 class KIO::DirectCopyJobPrivate: public KIO::SimpleJobPrivate
1627 public:
1628 DirectCopyJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
1629 : SimpleJobPrivate(url, command, packedArgs)
1633 * @internal
1634 * Called by the scheduler when a @p slave gets to
1635 * work on this job.
1636 * @param slave the slave that starts working on this job
1638 virtual void start(Slave *slave);
1640 Q_DECLARE_PUBLIC(DirectCopyJob)
1643 DirectCopyJob::DirectCopyJob(const KUrl &url, const QByteArray &packedArgs)
1644 : SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs))
1646 setUiDelegate(new JobUiDelegate);
1649 DirectCopyJob::~DirectCopyJob()
1653 void DirectCopyJobPrivate::start( Slave* slave )
1655 Q_Q(DirectCopyJob);
1656 q->connect( slave, SIGNAL(canResume( KIO::filesize_t ) ),
1657 SLOT( slotCanResume( KIO::filesize_t ) ) );
1658 SimpleJobPrivate::start(slave);
1661 void DirectCopyJob::slotCanResume( KIO::filesize_t offset )
1663 emit canResume(this, offset);
1666 //////////////////////////
1668 /** @internal */
1669 class KIO::FileCopyJobPrivate: public KIO::JobPrivate
1671 public:
1672 FileCopyJobPrivate(const KUrl& src, const KUrl& dest, int permissions,
1673 bool move, JobFlags flags)
1674 : m_sourceSize(filesize_t(-1)), m_src(src), m_dest(dest), m_moveJob(0), m_copyJob(0), m_delJob(0),
1675 m_chmodJob(0), m_getJob(0), m_putJob(0), m_permissions(permissions),
1676 m_move(move), m_mustChmod(0), m_flags(flags)
1679 KIO::filesize_t m_sourceSize;
1680 QDateTime m_modificationTime;
1681 KUrl m_src;
1682 KUrl m_dest;
1683 QByteArray m_buffer;
1684 SimpleJob *m_moveJob;
1685 SimpleJob *m_copyJob;
1686 SimpleJob *m_delJob;
1687 SimpleJob *m_chmodJob;
1688 TransferJob *m_getJob;
1689 TransferJob *m_putJob;
1690 int m_permissions;
1691 bool m_move:1;
1692 bool m_canResume:1;
1693 bool m_resumeAnswerSent:1;
1694 bool m_mustChmod:1;
1695 JobFlags m_flags;
1697 void startBestCopyMethod();
1698 void startCopyJob();
1699 void startCopyJob(const KUrl &slave_url);
1700 void startRenameJob(const KUrl &slave_url);
1701 void startDataPump();
1702 void connectSubjob( SimpleJob * job );
1704 void slotStart();
1705 void slotData( KIO::Job *, const QByteArray &data);
1706 void slotDataReq( KIO::Job *, QByteArray &data);
1707 void slotMimetype( KIO::Job*, const QString& type );
1709 * Forward signal from subjob
1710 * @param job the job that emitted this signal
1711 * @param size the processed size in bytes
1713 void slotProcessedSize( KJob *job, qulonglong size );
1715 * Forward signal from subjob
1716 * @param job the job that emitted this signal
1717 * @param size the total size
1719 void slotTotalSize( KJob *job, qulonglong size );
1721 * Forward signal from subjob
1722 * @param job the job that emitted this signal
1723 * @param pct the percentage
1725 void slotPercent( KJob *job, unsigned long pct );
1727 * Forward signal from subjob
1728 * @param job the job that emitted this signal
1729 * @param offset the offset to resume from
1731 void slotCanResume( KIO::Job *job, KIO::filesize_t offset );
1733 Q_DECLARE_PUBLIC(FileCopyJob)
1735 static inline FileCopyJob* newJob(const KUrl& src, const KUrl& dest, int permissions, bool move,
1736 JobFlags flags)
1738 //kDebug(7007) << src << "->" << dest;
1739 FileCopyJob *job = new FileCopyJob(
1740 *new FileCopyJobPrivate(src, dest, permissions, move, flags));
1741 job->setUiDelegate(new JobUiDelegate);
1742 if (!(flags & HideProgressInfo))
1743 KIO::getJobTracker()->registerJob(job);
1744 return job;
1749 * The FileCopyJob works according to the famous Bayern
1750 * 'Alternating Bitburger Protocol': we either drink a beer or we
1751 * we order a beer, but never both at the same time.
1752 * Translated to io-slaves: We alternate between receiving a block of data
1753 * and sending it away.
1755 FileCopyJob::FileCopyJob(FileCopyJobPrivate &dd)
1756 : Job(dd)
1758 //kDebug(7007);
1759 QTimer::singleShot(0, this, SLOT(slotStart()));
1762 void FileCopyJobPrivate::slotStart()
1764 Q_Q(FileCopyJob);
1765 if (!m_move)
1766 JobPrivate::emitCopying( q, m_src, m_dest );
1767 else
1768 JobPrivate::emitMoving( q, m_src, m_dest );
1770 if ( m_move )
1772 // The if() below must be the same as the one in startBestCopyMethod
1773 if ((m_src.protocol() == m_dest.protocol()) &&
1774 (m_src.host() == m_dest.host()) &&
1775 (m_src.port() == m_dest.port()) &&
1776 (m_src.user() == m_dest.user()) &&
1777 (m_src.pass() == m_dest.pass()) &&
1778 !m_src.hasSubUrl() && !m_dest.hasSubUrl())
1780 startRenameJob(m_src);
1781 return;
1783 else if (m_src.isLocalFile() && KProtocolManager::canRenameFromFile(m_dest))
1785 startRenameJob(m_dest);
1786 return;
1788 else if (m_dest.isLocalFile() && KProtocolManager::canRenameToFile(m_src))
1790 startRenameJob(m_src);
1791 return;
1793 // No fast-move available, use copy + del.
1795 startBestCopyMethod();
1798 void FileCopyJobPrivate::startBestCopyMethod()
1800 if ((m_src.protocol() == m_dest.protocol()) &&
1801 (m_src.host() == m_dest.host()) &&
1802 (m_src.port() == m_dest.port()) &&
1803 (m_src.user() == m_dest.user()) &&
1804 (m_src.pass() == m_dest.pass()) &&
1805 !m_src.hasSubUrl() && !m_dest.hasSubUrl())
1807 startCopyJob();
1809 else if (m_src.isLocalFile() && KProtocolManager::canCopyFromFile(m_dest))
1811 startCopyJob(m_dest);
1813 else if (m_dest.isLocalFile() && KProtocolManager::canCopyToFile(m_src))
1815 startCopyJob(m_src);
1817 else
1819 startDataPump();
1823 FileCopyJob::~FileCopyJob()
1827 void FileCopyJob::setSourceSize( KIO::filesize_t size )
1829 Q_D(FileCopyJob);
1830 d->m_sourceSize = size;
1831 if (size != (KIO::filesize_t) -1)
1832 setTotalAmount(KJob::Bytes, size);
1835 void FileCopyJob::setModificationTime( const QDateTime& mtime )
1837 Q_D(FileCopyJob);
1838 d->m_modificationTime = mtime;
1841 KUrl FileCopyJob::srcUrl() const
1843 return d_func()->m_src;
1846 KUrl FileCopyJob::destUrl() const
1848 return d_func()->m_dest;
1851 void FileCopyJobPrivate::startCopyJob()
1853 startCopyJob(m_src);
1856 void FileCopyJobPrivate::startCopyJob(const KUrl &slave_url)
1858 Q_Q(FileCopyJob);
1859 //kDebug(7007);
1860 KIO_ARGS << m_src << m_dest << m_permissions << (qint8) (m_flags & Overwrite);
1861 m_copyJob = new DirectCopyJob(slave_url, packedArgs);
1862 q->addSubjob( m_copyJob );
1863 connectSubjob( m_copyJob );
1864 q->connect( m_copyJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)),
1865 SLOT(slotCanResume(KIO::Job *, KIO::filesize_t)));
1868 void FileCopyJobPrivate::startRenameJob(const KUrl &slave_url)
1870 Q_Q(FileCopyJob);
1871 m_mustChmod = true; // CMD_RENAME by itself doesn't change permissions
1872 KIO_ARGS << m_src << m_dest << (qint8) (m_flags & Overwrite);
1873 m_moveJob = SimpleJobPrivate::newJobNoUi(slave_url, CMD_RENAME, packedArgs);
1874 q->addSubjob( m_moveJob );
1875 connectSubjob( m_moveJob );
1878 void FileCopyJobPrivate::connectSubjob( SimpleJob * job )
1880 Q_Q(FileCopyJob);
1881 q->connect( job, SIGNAL(totalSize( KJob*, qulonglong )),
1882 SLOT( slotTotalSize(KJob*, qulonglong)) );
1884 q->connect( job, SIGNAL(processedSize( KJob*, qulonglong )),
1885 SLOT( slotProcessedSize(KJob*, qulonglong)) );
1887 q->connect( job, SIGNAL(percent( KJob*, unsigned long )),
1888 SLOT( slotPercent(KJob*, unsigned long)) );
1892 bool FileCopyJob::doSuspend()
1894 Q_D(FileCopyJob);
1895 if (d->m_moveJob)
1896 d->m_moveJob->suspend();
1898 if (d->m_copyJob)
1899 d->m_copyJob->suspend();
1901 if (d->m_getJob)
1902 d->m_getJob->suspend();
1904 if (d->m_putJob)
1905 d->m_putJob->suspend();
1907 Job::doSuspend();
1908 return true;
1911 bool FileCopyJob::doResume()
1913 Q_D(FileCopyJob);
1914 if (d->m_moveJob)
1915 d->m_moveJob->resume();
1917 if (d->m_copyJob)
1918 d->m_copyJob->resume();
1920 if (d->m_getJob)
1921 d->m_getJob->resume();
1923 if (d->m_putJob)
1924 d->m_putJob->resume();
1926 Job::doResume();
1927 return true;
1930 void FileCopyJobPrivate::slotProcessedSize( KJob *, qulonglong size )
1932 Q_Q(FileCopyJob);
1933 q->setProcessedAmount(KJob::Bytes, size);
1936 void FileCopyJobPrivate::slotTotalSize( KJob*, qulonglong size )
1938 Q_Q(FileCopyJob);
1939 if (size > q->totalAmount(KJob::Bytes))
1941 q->setTotalAmount(KJob::Bytes, size);
1945 void FileCopyJobPrivate::slotPercent( KJob*, unsigned long pct )
1947 Q_Q(FileCopyJob);
1948 if ( pct > q->percent() ) {
1949 q->setPercent( pct );
1953 void FileCopyJobPrivate::startDataPump()
1955 Q_Q(FileCopyJob);
1956 //kDebug(7007);
1958 m_canResume = false;
1959 m_resumeAnswerSent = false;
1960 m_getJob = 0L; // for now
1961 m_putJob = put( m_dest, m_permissions, (m_flags | HideProgressInfo) /* no GUI */);
1962 //kDebug(7007) << "m_putJob=" << m_putJob << "m_dest=" << m_dest;
1963 if ( m_modificationTime.isValid() ) {
1964 m_putJob->setModificationTime( m_modificationTime );
1967 // The first thing the put job will tell us is whether we can
1968 // resume or not (this is always emitted)
1969 q->connect( m_putJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)),
1970 SLOT( slotCanResume(KIO::Job *, KIO::filesize_t)));
1971 q->connect( m_putJob, SIGNAL(dataReq(KIO::Job *, QByteArray&)),
1972 SLOT( slotDataReq(KIO::Job *, QByteArray&)));
1973 q->addSubjob( m_putJob );
1976 void FileCopyJobPrivate::slotCanResume( KIO::Job* job, KIO::filesize_t offset )
1978 Q_Q(FileCopyJob);
1979 if ( job == m_putJob || job == m_copyJob )
1981 //kDebug(7007) << "'can resume' from PUT job. offset=" << KIO::number(offset);
1982 if (offset)
1984 RenameDialog_Result res = R_RESUME;
1986 if (!KProtocolManager::autoResume() && !(m_flags & Overwrite))
1988 QString newPath;
1989 KIO::Job* job = ( q->parentJob() ) ? q->parentJob() : q;
1990 // Ask confirmation about resuming previous transfer
1991 res = ui()->askFileRename(
1992 job, i18n("File Already Exists"),
1993 m_src.url(),
1994 m_dest.url(),
1995 (RenameDialog_Mode) (M_OVERWRITE | M_RESUME | M_NORENAME), newPath,
1996 m_sourceSize, offset );
1999 if ( res == R_OVERWRITE || (m_flags & Overwrite) )
2000 offset = 0;
2001 else if ( res == R_CANCEL )
2003 if ( job == m_putJob )
2004 m_putJob->kill( FileCopyJob::Quietly );
2005 else
2006 m_copyJob->kill( FileCopyJob::Quietly );
2007 q->setError( ERR_USER_CANCELED );
2008 q->emitResult();
2009 return;
2012 else
2013 m_resumeAnswerSent = true; // No need for an answer
2015 if ( job == m_putJob )
2017 m_getJob = KIO::get( m_src, NoReload, HideProgressInfo /* no GUI */ );
2018 //kDebug(7007) << "m_getJob=" << m_getJob << m_src;
2019 m_getJob->addMetaData( "errorPage", "false" );
2020 m_getJob->addMetaData( "AllowCompressedPage", "false" );
2021 // Set size in subjob. This helps if the slave doesn't emit totalSize.
2022 if ( m_sourceSize != (KIO::filesize_t)-1 )
2023 m_getJob->setTotalAmount(KJob::Bytes, m_sourceSize);
2024 if (offset)
2026 //kDebug(7007) << "Setting metadata for resume to" << (unsigned long) offset;
2027 // TODO KDE4: rename to seek or offset and document it
2028 // This isn't used only for resuming, but potentially also for extracting (#72302).
2029 m_getJob->addMetaData( "resume", KIO::number(offset) );
2031 // Might or might not get emitted
2032 q->connect( m_getJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)),
2033 SLOT( slotCanResume(KIO::Job *, KIO::filesize_t)));
2035 jobSlave(m_putJob)->setOffset( offset );
2037 m_putJob->d_func()->internalSuspend();
2038 q->addSubjob( m_getJob );
2039 connectSubjob( m_getJob ); // Progress info depends on get
2040 m_getJob->d_func()->internalResume(); // Order a beer
2042 q->connect( m_getJob, SIGNAL(data(KIO::Job*,const QByteArray&)),
2043 SLOT( slotData(KIO::Job*,const QByteArray&)) );
2044 q->connect( m_getJob, SIGNAL(mimetype(KIO::Job*,const QString&) ),
2045 SLOT(slotMimetype(KIO::Job*,const QString&)) );
2047 else // copyjob
2049 jobSlave(m_copyJob)->sendResumeAnswer( offset != 0 );
2052 else if ( job == m_getJob )
2054 // Cool, the get job said ok, we can resume
2055 m_canResume = true;
2056 //kDebug(7007) << "'can resume' from the GET job -> we can resume";
2058 jobSlave(m_getJob)->setOffset( jobSlave(m_putJob)->offset() );
2060 else
2061 kWarning(7007) << "unknown job=" << job
2062 << "m_getJob=" << m_getJob << "m_putJob=" << m_putJob;
2065 void FileCopyJobPrivate::slotData( KIO::Job * , const QByteArray &data)
2067 //kDebug(7007) << "data size:" << data.size();
2068 assert(m_putJob);
2069 if (!m_putJob) return; // Don't crash
2070 m_getJob->d_func()->internalSuspend();
2071 m_putJob->d_func()->internalResume(); // Drink the beer
2072 m_buffer += data;
2074 // On the first set of data incoming, we tell the "put" slave about our
2075 // decision about resuming
2076 if (!m_resumeAnswerSent)
2078 m_resumeAnswerSent = true;
2079 //kDebug(7007) << "(first time) -> send resume answer " << m_canResume;
2080 jobSlave(m_putJob)->sendResumeAnswer( m_canResume );
2084 void FileCopyJobPrivate::slotDataReq( KIO::Job * , QByteArray &data)
2086 Q_Q(FileCopyJob);
2087 //kDebug(7007);
2088 if (!m_resumeAnswerSent && !m_getJob) {
2089 // This can't happen
2090 q->setError( ERR_INTERNAL );
2091 q->setErrorText( "'Put' job did not send canResume or 'Get' job did not send data!" );
2092 m_putJob->kill( FileCopyJob::Quietly );
2093 q->emitResult();
2094 return;
2096 if (m_getJob)
2098 m_getJob->d_func()->internalResume(); // Order more beer
2099 m_putJob->d_func()->internalSuspend();
2101 data = m_buffer;
2102 m_buffer = QByteArray();
2105 void FileCopyJobPrivate::slotMimetype( KIO::Job*, const QString& type )
2107 Q_Q(FileCopyJob);
2108 emit q->mimetype( q, type );
2111 void FileCopyJob::slotResult( KJob *job)
2113 Q_D(FileCopyJob);
2114 //kDebug(7007) << "this=" << this << "job=" << job;
2115 removeSubjob(job);
2116 // Did job have an error ?
2117 if ( job->error() )
2119 if ((job == d->m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
2121 d->m_moveJob = 0;
2122 d->startBestCopyMethod();
2123 return;
2125 else if ((job == d->m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
2127 d->m_copyJob = 0;
2128 d->startDataPump();
2129 return;
2131 else if (job == d->m_getJob)
2133 d->m_getJob = 0L;
2134 if (d->m_putJob)
2136 d->m_putJob->kill( Quietly );
2137 removeSubjob( d->m_putJob );
2140 else if (job == d->m_putJob)
2142 d->m_putJob = 0L;
2143 if (d->m_getJob)
2145 d->m_getJob->kill( Quietly );
2146 removeSubjob( d->m_getJob );
2149 setError( job->error() );
2150 setErrorText( job->errorText() );
2151 emitResult();
2152 return;
2155 if (d->m_mustChmod)
2157 // If d->m_permissions == -1, keep the default permissions
2158 if (d->m_permissions != -1)
2160 d->m_chmodJob = chmod(d->m_dest, d->m_permissions);
2162 d->m_mustChmod = false;
2165 if (job == d->m_moveJob)
2167 d->m_moveJob = 0; // Finished
2170 if (job == d->m_copyJob)
2172 d->m_copyJob = 0;
2173 if (d->m_move)
2175 d->m_delJob = file_delete( d->m_src, HideProgressInfo/*no GUI*/ ); // Delete source
2176 addSubjob(d->m_delJob);
2180 if (job == d->m_getJob)
2182 //kDebug(7007) << "m_getJob finished";
2183 d->m_getJob = 0; // No action required
2184 if (d->m_putJob)
2185 d->m_putJob->d_func()->internalResume();
2188 if (job == d->m_putJob)
2190 //kDebug(7007) << "m_putJob finished";
2191 d->m_putJob = 0;
2192 if (d->m_getJob)
2194 // The get job is still running, probably after emitting data(QByteArray())
2195 // and before we receive its finished().
2196 d->m_getJob->d_func()->internalResume();
2198 if (d->m_move)
2200 d->m_delJob = file_delete( d->m_src, HideProgressInfo/*no GUI*/ ); // Delete source
2201 addSubjob(d->m_delJob);
2205 if (job == d->m_delJob)
2207 d->m_delJob = 0; // Finished
2210 if (job == d->m_chmodJob)
2212 d->m_chmodJob = 0; // Finished
2215 if ( !hasSubjobs() )
2216 emitResult();
2219 FileCopyJob *KIO::file_copy( const KUrl& src, const KUrl& dest, int permissions,
2220 JobFlags flags )
2222 return FileCopyJobPrivate::newJob(src, dest, permissions, false, flags);
2225 FileCopyJob *KIO::file_move( const KUrl& src, const KUrl& dest, int permissions,
2226 JobFlags flags )
2228 return FileCopyJobPrivate::newJob(src, dest, permissions, true, flags);
2231 SimpleJob *KIO::file_delete( const KUrl& src, JobFlags flags )
2233 KIO_ARGS << src << qint8(true); // isFile
2234 return SimpleJobPrivate::newJob(src, CMD_DEL, packedArgs, flags);
2237 //////////
2239 class KIO::ListJobPrivate: public KIO::SimpleJobPrivate
2241 public:
2242 ListJobPrivate(const KUrl& url, bool _recursive, const QString &_prefix, bool _includeHidden)
2243 : SimpleJobPrivate(url, CMD_LISTDIR, QByteArray()),
2244 recursive(_recursive), includeHidden(_includeHidden),
2245 prefix(_prefix), m_processedEntries(0)
2247 bool recursive;
2248 bool includeHidden;
2249 QString prefix;
2250 unsigned long m_processedEntries;
2251 KUrl m_redirectionURL;
2254 * @internal
2255 * Called by the scheduler when a @p slave gets to
2256 * work on this job.
2257 * @param slave the slave that starts working on this job
2259 virtual void start( Slave *slave );
2261 void slotListEntries( const KIO::UDSEntryList& list );
2262 void slotRedirection( const KUrl &url );
2263 void gotEntries( KIO::Job * subjob, const KIO::UDSEntryList& list );
2265 Q_DECLARE_PUBLIC(ListJob)
2267 static inline ListJob *newJob(const KUrl& u, bool _recursive, const QString &_prefix,
2268 bool _includeHidden, JobFlags flags = HideProgressInfo)
2270 ListJob *job = new ListJob(*new ListJobPrivate(u, _recursive, _prefix, _includeHidden));
2271 job->setUiDelegate(new JobUiDelegate);
2272 if (!(flags & HideProgressInfo))
2273 KIO::getJobTracker()->registerJob(job);
2274 return job;
2276 static inline ListJob *newJobNoUi(const KUrl& u, bool _recursive, const QString &_prefix,
2277 bool _includeHidden)
2279 return new ListJob(*new ListJobPrivate(u, _recursive, _prefix, _includeHidden));
2283 ListJob::ListJob(ListJobPrivate &dd)
2284 : SimpleJob(dd)
2286 Q_D(ListJob);
2287 // We couldn't set the args when calling the parent constructor,
2288 // so do it now.
2289 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
2290 stream << d->m_url;
2293 ListJob::~ListJob()
2297 void ListJobPrivate::slotListEntries( const KIO::UDSEntryList& list )
2299 Q_Q(ListJob);
2300 // Emit progress info (takes care of emit processedSize and percent)
2301 m_processedEntries += list.count();
2302 slotProcessedSize( m_processedEntries );
2304 if (recursive) {
2305 UDSEntryList::ConstIterator it = list.begin();
2306 const UDSEntryList::ConstIterator end = list.end();
2308 for (; it != end; ++it) {
2310 const UDSEntry& entry = *it;
2312 KUrl itemURL;
2313 // const UDSEntry::ConstIterator end2 = entry.end();
2314 // UDSEntry::ConstIterator it2 = entry.find( KIO::UDSEntry::UDS_URL );
2315 // if ( it2 != end2 )
2316 if (entry.contains(KIO::UDSEntry::UDS_URL))
2317 // itemURL = it2.value().toString();
2318 itemURL = entry.stringValue(KIO::UDSEntry::UDS_URL);
2319 else { // no URL, use the name
2320 itemURL = q->url();
2321 const QString fileName = entry.stringValue(KIO::UDSEntry::UDS_NAME);
2322 Q_ASSERT(!fileName.isEmpty()); // we'll recurse forever otherwise :)
2323 itemURL.addPath(fileName);
2326 if (entry.isDir() && !entry.isLink()) {
2327 const QString filename = itemURL.fileName();
2328 // skip hidden dirs when listing if requested
2329 if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) {
2330 ListJob *job = ListJobPrivate::newJobNoUi(itemURL,
2331 true /*recursive*/,
2332 prefix + filename + '/',
2333 includeHidden);
2334 Scheduler::scheduleJob(job);
2335 q->connect(job, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList& )),
2336 SLOT( gotEntries( KIO::Job*, const KIO::UDSEntryList& )));
2337 q->addSubjob(job);
2343 // Not recursive, or top-level of recursive listing : return now (send . and .. as well)
2344 // exclusion of hidden files also requires the full sweep, but the case for full-listing
2345 // a single dir is probably common enough to justify the shortcut
2346 if (prefix.isNull() && includeHidden) {
2347 emit q->entries(q, list);
2348 } else {
2349 // cull the unwanted hidden dirs and/or parent dir references from the listing, then emit that
2350 UDSEntryList newlist;
2352 UDSEntryList::const_iterator it = list.begin();
2353 const UDSEntryList::const_iterator end = list.end();
2354 for (; it != end; ++it) {
2356 // Modify the name in the UDSEntry
2357 UDSEntry newone = *it;
2358 const QString filename = newone.stringValue( KIO::UDSEntry::UDS_NAME );
2359 // Avoid returning entries like subdir/. and subdir/.., but include . and .. for
2360 // the toplevel dir, and skip hidden files/dirs if that was requested
2361 if ( (prefix.isNull() || (filename != ".." && filename != ".") )
2362 && (includeHidden || (filename[0] != '.') ) )
2364 // ## Didn't find a way to use the iterator instead of re-doing a key lookup
2365 newone.insert( KIO::UDSEntry::UDS_NAME, prefix + filename );
2366 newlist.append(newone);
2370 emit q->entries(q, newlist);
2374 void ListJobPrivate::gotEntries(KIO::Job *, const KIO::UDSEntryList& list )
2376 // Forward entries received by subjob - faking we received them ourselves
2377 Q_Q(ListJob);
2378 emit q->entries(q, list);
2381 void ListJob::slotResult( KJob * job )
2383 // If we can't list a subdir, the result is still ok
2384 // This is why we override Job::slotResult() - to skip error checking
2385 removeSubjob( job );
2386 if ( !hasSubjobs() )
2387 emitResult();
2390 void ListJobPrivate::slotRedirection( const KUrl & url )
2392 Q_Q(ListJob);
2393 if (!KAuthorized::authorizeUrlAction("redirect", m_url, url))
2395 kWarning(7007) << "ListJob: Redirection from " << m_url << " to " << url << " REJECTED!";
2396 return;
2398 m_redirectionURL = url; // We'll remember that when the job finishes
2399 if (m_url.hasUser() && !url.hasUser() && (m_url.host().toLower() == url.host().toLower()))
2400 m_redirectionURL.setUser(m_url.user()); // Preserve user
2401 emit q->redirection( q, m_redirectionURL );
2404 void ListJob::slotFinished()
2406 Q_D(ListJob);
2407 // Support for listing archives as directories
2408 if ( error() == KIO::ERR_IS_FILE && d->m_url.isLocalFile() ) {
2409 KMimeType::Ptr ptr = KMimeType::findByUrl( d->m_url, 0, true, true );
2410 if ( ptr ) {
2411 QString proto = ptr->property("X-KDE-LocalProtocol").toString();
2412 if ( !proto.isEmpty() && KProtocolInfo::isKnownProtocol( proto) ) {
2413 d->m_redirectionURL = d->m_url;
2414 d->m_redirectionURL.setProtocol( proto );
2415 setError( 0 );
2416 emit redirection(this,d->m_redirectionURL);
2420 if ( d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error() ) {
2421 // Return slave to the scheduler
2422 SimpleJob::slotFinished();
2423 } else {
2425 //kDebug(7007) << "Redirection to " << d->m_redirectionURL;
2426 if (queryMetaData("permanent-redirect")=="true")
2427 emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
2428 d->m_url = d->m_redirectionURL;
2429 d->m_redirectionURL = KUrl();
2430 d->m_packedArgs.truncate(0);
2431 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
2432 stream << d->m_url;
2434 // Return slave to the scheduler
2435 d->slaveDone();
2436 Scheduler::doJob(this);
2440 void ListJob::slotMetaData( const KIO::MetaData &_metaData)
2442 Q_D(ListJob);
2443 SimpleJob::slotMetaData(_metaData);
2444 storeSSLSessionFromJob(d->m_redirectionURL);
2447 ListJob *KIO::listDir( const KUrl& url, JobFlags flags, bool includeHidden )
2449 return ListJobPrivate::newJob(url, false, QString(), includeHidden, flags);
2452 ListJob *KIO::listRecursive( const KUrl& url, JobFlags flags, bool includeHidden )
2454 return ListJobPrivate::newJob(url, true, QString(), includeHidden, flags);
2457 void ListJob::setUnrestricted(bool unrestricted)
2459 Q_D(ListJob);
2460 if (unrestricted)
2461 d->m_extraFlags |= JobPrivate::EF_ListJobUnrestricted;
2462 else
2463 d->m_extraFlags &= ~JobPrivate::EF_ListJobUnrestricted;
2466 void ListJobPrivate::start(Slave *slave)
2468 Q_Q(ListJob);
2469 if (!KAuthorized::authorizeUrlAction("list", m_url, m_url) &&
2470 !(m_extraFlags & EF_ListJobUnrestricted))
2472 q->setError( ERR_ACCESS_DENIED );
2473 q->setErrorText( m_url.url() );
2474 QTimer::singleShot(0, q, SLOT(slotFinished()) );
2475 return;
2477 q->connect( slave, SIGNAL( listEntries( const KIO::UDSEntryList& )),
2478 SLOT( slotListEntries( const KIO::UDSEntryList& )));
2479 q->connect( slave, SIGNAL( totalSize( KIO::filesize_t ) ),
2480 SLOT( slotTotalSize( KIO::filesize_t ) ) );
2481 q->connect( slave, SIGNAL( redirection(const KUrl &) ),
2482 SLOT( slotRedirection(const KUrl &) ) );
2484 SimpleJobPrivate::start(slave);
2487 const KUrl& ListJob::redirectionUrl() const
2489 return d_func()->m_redirectionURL;
2492 ////
2494 class KIO::MultiGetJobPrivate: public KIO::TransferJobPrivate
2496 public:
2497 MultiGetJobPrivate(const KUrl& url)
2498 : TransferJobPrivate(url, 0, QByteArray(), QByteArray()),
2499 m_currentEntry( 0, KUrl(), MetaData() )
2501 struct GetRequest {
2502 GetRequest(long _id, const KUrl &_url, const MetaData &_metaData)
2503 : id(_id), url(_url), metaData(_metaData) { }
2504 long id;
2505 KUrl url;
2506 MetaData metaData;
2508 inline bool operator==( const GetRequest& req ) const
2509 { return req.id == id; }
2511 typedef QLinkedList<GetRequest> RequestQueue;
2513 RequestQueue m_waitQueue;
2514 RequestQueue m_activeQueue;
2515 GetRequest m_currentEntry;
2516 bool b_multiGetActive;
2519 * @internal
2520 * Called by the scheduler when a @p slave gets to
2521 * work on this job.
2522 * @param slave the slave that starts working on this job
2524 virtual void start(Slave *slave);
2526 bool findCurrentEntry();
2527 void flushQueue(QLinkedList<GetRequest> &queue);
2529 Q_DECLARE_PUBLIC(MultiGetJob)
2531 static inline MultiGetJob *newJob(const KUrl &url)
2533 MultiGetJob *job = new MultiGetJob(*new MultiGetJobPrivate(url));
2534 job->setUiDelegate(new JobUiDelegate);
2535 return job;
2539 MultiGetJob::MultiGetJob(MultiGetJobPrivate &dd)
2540 : TransferJob(dd)
2544 MultiGetJob::~MultiGetJob()
2548 void MultiGetJob::get(long id, const KUrl &url, const MetaData &metaData)
2550 Q_D(MultiGetJob);
2551 MultiGetJobPrivate::GetRequest entry(id, url, metaData);
2552 entry.metaData["request-id"] = QString::number(id);
2553 d->m_waitQueue.append(entry);
2556 void MultiGetJobPrivate::flushQueue(RequestQueue &queue)
2558 // Use multi-get
2559 // Scan all jobs in m_waitQueue
2560 RequestQueue::iterator wqit = m_waitQueue.begin();
2561 const RequestQueue::iterator wqend = m_waitQueue.end();
2562 while ( wqit != wqend )
2564 const GetRequest& entry = *wqit;
2565 if ((m_url.protocol() == entry.url.protocol()) &&
2566 (m_url.host() == entry.url.host()) &&
2567 (m_url.port() == entry.url.port()) &&
2568 (m_url.user() == entry.url.user()))
2570 queue.append( entry );
2571 wqit = m_waitQueue.erase( wqit );
2573 else
2575 ++wqit;
2578 // Send number of URLs, (URL, metadata)*
2579 KIO_ARGS << (qint32) queue.count();
2580 RequestQueue::const_iterator qit = queue.begin();
2581 const RequestQueue::const_iterator qend = queue.end();
2582 for( ; qit != qend; ++qit )
2584 stream << (*qit).url << (*qit).metaData;
2586 m_packedArgs = packedArgs;
2587 m_command = CMD_MULTI_GET;
2588 m_outgoingMetaData.clear();
2591 void MultiGetJobPrivate::start(Slave *slave)
2593 // Add first job from m_waitQueue and add it to m_activeQueue
2594 GetRequest entry = m_waitQueue.takeFirst();
2595 m_activeQueue.append(entry);
2597 m_url = entry.url;
2599 if (!entry.url.protocol().startsWith("http"))
2601 // Use normal get
2602 KIO_ARGS << entry.url;
2603 m_packedArgs = packedArgs;
2604 m_outgoingMetaData = entry.metaData;
2605 m_command = CMD_GET;
2606 b_multiGetActive = false;
2608 else
2610 flushQueue(m_activeQueue);
2611 b_multiGetActive = true;
2614 TransferJobPrivate::start(slave); // Anything else to do??
2617 bool MultiGetJobPrivate::findCurrentEntry()
2619 if (b_multiGetActive)
2621 long id = m_incomingMetaData["request-id"].toLong();
2622 RequestQueue::const_iterator qit = m_activeQueue.begin();
2623 const RequestQueue::const_iterator qend = m_activeQueue.end();
2624 for( ; qit != qend; ++qit )
2626 if ((*qit).id == id)
2628 m_currentEntry = *qit;
2629 return true;
2632 m_currentEntry.id = 0;
2633 return false;
2635 else
2637 if ( m_activeQueue.isEmpty() )
2638 return false;
2639 m_currentEntry = m_activeQueue.first();
2640 return true;
2644 void MultiGetJob::slotRedirection( const KUrl &url)
2646 Q_D(MultiGetJob);
2647 if (!d->findCurrentEntry()) return; // Error
2648 if (!KAuthorized::authorizeUrlAction("redirect", d->m_url, url))
2650 kWarning(7007) << "MultiGetJob: Redirection from " << d->m_currentEntry.url << " to " << url << " REJECTED!";
2651 return;
2653 d->m_redirectionURL = url;
2654 if (d->m_currentEntry.url.hasUser() && !url.hasUser() && (d->m_currentEntry.url.host().toLower() == url.host().toLower()))
2655 d->m_redirectionURL.setUser(d->m_currentEntry.url.user()); // Preserve user
2656 get(d->m_currentEntry.id, d->m_redirectionURL, d->m_currentEntry.metaData); // Try again
2660 void MultiGetJob::slotFinished()
2662 Q_D(MultiGetJob);
2663 if (!d->findCurrentEntry()) return;
2664 if (d->m_redirectionURL.isEmpty())
2666 // No redirection, tell the world that we are finished.
2667 emit result(d->m_currentEntry.id);
2669 d->m_redirectionURL = KUrl();
2670 setError( 0 );
2671 d->m_incomingMetaData.clear();
2672 d->m_activeQueue.removeAll(d->m_currentEntry);
2673 if (d->m_activeQueue.count() == 0)
2675 if (d->m_waitQueue.count() == 0)
2677 // All done
2678 TransferJob::slotFinished();
2680 else
2682 // return slave to pool
2683 // fetch new slave for first entry in d->m_waitQueue and call start
2684 // again.
2685 d->m_url = d->m_waitQueue.first().url;
2686 d->slaveDone();
2687 Scheduler::doJob(this);
2692 void MultiGetJob::slotData( const QByteArray &_data)
2694 Q_D(MultiGetJob);
2695 if(d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error())
2696 emit data(d->m_currentEntry.id, _data);
2699 void MultiGetJob::slotMimetype( const QString &_mimetype )
2701 Q_D(MultiGetJob);
2702 if (d->b_multiGetActive)
2704 MultiGetJobPrivate::RequestQueue newQueue;
2705 d->flushQueue(newQueue);
2706 if (!newQueue.isEmpty())
2708 d->m_activeQueue += newQueue;
2709 d->m_slave->send( d->m_command, d->m_packedArgs );
2712 if (!d->findCurrentEntry()) return; // Error, unknown request!
2713 emit mimetype(d->m_currentEntry.id, _mimetype);
2716 MultiGetJob *KIO::multi_get(long id, const KUrl &url, const MetaData &metaData)
2718 MultiGetJob * job = MultiGetJobPrivate::newJob(url);
2719 job->get(id, url, metaData);
2720 return job;
2723 class KIO::SpecialJobPrivate: public TransferJobPrivate
2725 SpecialJobPrivate(const KUrl& url, int command,
2726 const QByteArray &packedArgs,
2727 const QByteArray &_staticData)
2728 : TransferJobPrivate(url, command, packedArgs, _staticData)
2732 SpecialJob::SpecialJob(const KUrl &url, const QByteArray &packedArgs)
2733 : TransferJob(*new TransferJobPrivate(url, CMD_SPECIAL, packedArgs, QByteArray()))
2737 SpecialJob::~SpecialJob()
2741 void SpecialJob::setArguments(const QByteArray &data)
2743 Q_D(SpecialJob);
2744 d->m_packedArgs = data;
2747 QByteArray SpecialJob::arguments() const
2749 return d_func()->m_packedArgs;
2752 // Never defined, never used - what's this code about?
2753 #ifdef CACHE_INFO
2754 CacheInfo::CacheInfo(const KUrl &url)
2756 m_url = url;
2759 QString CacheInfo::cachedFileName()
2761 const QChar separator = '_';
2763 QString CEF = m_url.path();
2765 int p = CEF.find('/');
2767 while(p != -1)
2769 CEF[p] = separator;
2770 p = CEF.find('/', p);
2773 QString host = m_url.host().toLower();
2774 CEF = host + CEF + '_';
2776 QString dir = KProtocolManager::cacheDir();
2777 if (dir[dir.length()-1] != '/')
2778 dir += '/';
2780 int l = m_url.host().length();
2781 for(int i = 0; i < l; i++)
2783 if (host[i].isLetter() && (host[i] != 'w'))
2785 dir += host[i];
2786 break;
2789 if (dir[dir.length()-1] == '/')
2790 dir += '0';
2792 unsigned long hash = 0x00000000;
2793 QString u = m_url.url().toLatin1();
2794 for(int i = u.length(); i--;)
2796 hash = (hash * 12211 + u[i]) % 2147483563;
2799 QString hashString;
2800 hashString.sprintf("%08lx", hash);
2802 CEF = CEF + hashString;
2804 CEF = dir + '/' + CEF;
2806 return CEF;
2809 QFile *CacheInfo::cachedFile()
2811 #ifdef Q_WS_WIN
2812 const char *mode = (readWrite ? "rb+" : "rb");
2813 #else
2814 const char *mode = (readWrite ? "r+" : "r");
2815 #endif
2817 FILE *fs = KDE_fopen(QFile::encodeName(CEF), mode); // Open for reading and writing
2818 if (!fs)
2819 return 0;
2821 char buffer[401];
2822 bool ok = true;
2824 // CacheRevision
2825 if (ok && (!fgets(buffer, 400, fs)))
2826 ok = false;
2827 if (ok && (strcmp(buffer, CACHE_REVISION) != 0))
2828 ok = false;
2830 time_t date;
2831 time_t currentDate = time(0);
2833 // URL
2834 if (ok && (!fgets(buffer, 400, fs)))
2835 ok = false;
2836 if (ok)
2838 int l = strlen(buffer);
2839 if (l>0)
2840 buffer[l-1] = 0; // Strip newline
2841 if (m_.url.url() != buffer)
2843 ok = false; // Hash collision
2847 // Creation Date
2848 if (ok && (!fgets(buffer, 400, fs)))
2849 ok = false;
2850 if (ok)
2852 date = (time_t) strtoul(buffer, 0, 10);
2853 if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge))
2855 m_bMustRevalidate = true;
2856 m_expireDate = currentDate;
2860 // Expiration Date
2861 m_cacheExpireDateOffset = KDE_ftell(fs);
2862 if (ok && (!fgets(buffer, 400, fs)))
2863 ok = false;
2864 if (ok)
2866 if (m_request.cache == CC_Verify)
2868 date = (time_t) strtoul(buffer, 0, 10);
2869 // After the expire date we need to revalidate.
2870 if (!date || difftime(currentDate, date) >= 0)
2871 m_bMustRevalidate = true;
2872 m_expireDate = date;
2876 // ETag
2877 if (ok && (!fgets(buffer, 400, fs)))
2878 ok = false;
2879 if (ok)
2881 m_etag = QString(buffer).trimmed();
2884 // Last-Modified
2885 if (ok && (!fgets(buffer, 400, fs)))
2886 ok = false;
2887 if (ok)
2889 m_lastModified = QString(buffer).trimmed();
2892 fclose(fs);
2894 if (ok)
2895 return fs;
2897 unlink( QFile::encodeName(CEF) );
2898 return 0;
2902 void CacheInfo::flush()
2904 cachedFile().remove();
2907 void CacheInfo::touch()
2911 void CacheInfo::setExpireDate(int);
2912 void CacheInfo::setExpireTimeout(int);
2915 int CacheInfo::creationDate();
2916 int CacheInfo::expireDate();
2917 int CacheInfo::expireTimeout();
2918 #endif
2920 #include "jobclasses.moc"
2921 #include "job_p.moc"