• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.9.5 API Reference
  • KDE Home
  • Contact Us
 

KIO

job.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002     Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
00003                   2000-2009 David Faure <faure@kde.org>
00004                        Waldo Bastian <bastian@kde.org>
00005 
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019     Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "job.h"
00023 #include "job_p.h"
00024 
00025 #include <config.h>
00026 
00027 #include <sys/types.h>
00028 #include <sys/wait.h>
00029 #include <sys/stat.h>
00030 
00031 #include <signal.h>
00032 #include <stdlib.h>
00033 #include <stdio.h>
00034 #include <time.h>
00035 #include <unistd.h>
00036 extern "C" {
00037 #include <pwd.h>
00038 #include <grp.h>
00039 }
00040 #include <QtCore/QTimer>
00041 #include <QtCore/QFile>
00042 
00043 #include <kauthorized.h>
00044 #include <klocale.h>
00045 #include <kconfig.h>
00046 #include <kdebug.h>
00047 #include <kde_file.h>
00048 
00049 #include <errno.h>
00050 
00051 #include "jobuidelegate.h"
00052 #include "kmimetype.h"
00053 #include "slave.h"
00054 #include "scheduler.h"
00055 #include "kdirwatch.h"
00056 #include "kprotocolinfo.h"
00057 #include "kprotocolmanager.h"
00058 #include "filejob.h"
00059 
00060 #include <kdirnotify.h>
00061 #include <ktemporaryfile.h>
00062 
00063 using namespace KIO;
00064 
00065 #define MAX_READ_BUF_SIZE  (64 * 1024)       // 64 KB at a time seems reasonable...
00066 
00067 static inline Slave *jobSlave(SimpleJob *job)
00068 {
00069     return SimpleJobPrivate::get(job)->m_slave;
00070 }
00071 
00072 //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
00073 #define REPORT_TIMEOUT 200
00074 
00075 Job::Job() : KCompositeJob(*new JobPrivate, 0)
00076 {
00077     setCapabilities( KJob::Killable | KJob::Suspendable );
00078 }
00079 
00080 Job::Job(JobPrivate &dd) : KCompositeJob(dd, 0)
00081 {
00082     setCapabilities( KJob::Killable | KJob::Suspendable );
00083 }
00084 
00085 Job::~Job()
00086 {
00087 }
00088 
00089 JobUiDelegate *Job::ui() const
00090 {
00091     return static_cast<JobUiDelegate*>( uiDelegate() );
00092 }
00093 
00094 bool Job::addSubjob(KJob *jobBase)
00095 {
00096     //kDebug(7007) << "addSubjob(" << jobBase << ") this=" << this;
00097 
00098     bool ok = KCompositeJob::addSubjob( jobBase );
00099     KIO::Job *job = dynamic_cast<KIO::Job*>( jobBase );
00100     if (ok && job) {
00101         // Copy metadata into subjob (e.g. window-id, user-timestamp etc.)
00102         Q_D(Job);
00103         job->mergeMetaData(d->m_outgoingMetaData);
00104 
00105         // Forward information from that subjob.
00106         connect(job, SIGNAL(speed(KJob*,ulong)),
00107                 SLOT(slotSpeed(KJob*,ulong)));
00108 
00109         if (ui() && job->ui()) {
00110             job->ui()->setWindow( ui()->window() );
00111             job->ui()->updateUserTimestamp( ui()->userTimestamp() );
00112         }
00113     }
00114     return ok;
00115 }
00116 
00117 bool Job::removeSubjob( KJob *jobBase )
00118 {
00119     //kDebug(7007) << "removeSubjob(" << jobBase << ") this=" << this << "subjobs=" << subjobs().count();
00120     return KCompositeJob::removeSubjob( jobBase );
00121 }
00122 
00123 void JobPrivate::emitMoving(KIO::Job * job, const KUrl &src, const KUrl &dest)
00124 {
00125     emit job->description(job, i18nc("@title job","Moving"),
00126                           qMakePair(i18nc("The source of a file operation", "Source"), src.pathOrUrl()),
00127                           qMakePair(i18nc("The destination of a file operation", "Destination"), dest.pathOrUrl()));
00128 }
00129 
00130 void JobPrivate::emitCopying(KIO::Job * job, const KUrl &src, const KUrl &dest)
00131 {
00132     emit job->description(job, i18nc("@title job","Copying"),
00133                           qMakePair(i18nc("The source of a file operation", "Source"), src.pathOrUrl()),
00134                           qMakePair(i18nc("The destination of a file operation", "Destination"), dest.pathOrUrl()));
00135 }
00136 
00137 void JobPrivate::emitCreatingDir(KIO::Job * job, const KUrl &dir)
00138 {
00139     emit job->description(job, i18nc("@title job","Creating directory"),
00140                           qMakePair(i18n("Directory"), dir.pathOrUrl()));
00141 }
00142 
00143 void JobPrivate::emitDeleting(KIO::Job *job, const KUrl &url)
00144 {
00145     emit job->description(job, i18nc("@title job","Deleting"),
00146                           qMakePair(i18n("File"), url.pathOrUrl()));
00147 }
00148 
00149 void JobPrivate::emitStating(KIO::Job *job, const KUrl &url)
00150 {
00151     emit job->description(job, i18nc("@title job","Examining"),
00152                           qMakePair(i18n("File"), url.pathOrUrl()));
00153 }
00154 
00155 void JobPrivate::emitTransferring(KIO::Job *job, const KUrl &url)
00156 {
00157     emit job->description(job, i18nc("@title job","Transferring"),
00158                           qMakePair(i18nc("The source of a file operation", "Source"), url.pathOrUrl()));
00159 }
00160 
00161 void JobPrivate::emitMounting(KIO::Job * job, const QString &dev, const QString &point)
00162 {
00163     emit job->description(job, i18nc("@title job","Mounting"),
00164                           qMakePair(i18n("Device"), dev),
00165                           qMakePair(i18n("Mountpoint"), point));
00166 }
00167 
00168 void JobPrivate::emitUnmounting(KIO::Job * job, const QString &point)
00169 {
00170     emit job->description(job, i18nc("@title job","Unmounting"),
00171                           qMakePair(i18n("Mountpoint"), point));
00172 }
00173 
00174 bool Job::doKill()
00175 {
00176   // kill all subjobs, without triggering their result slot
00177   Q_FOREACH( KJob* it, subjobs()) {
00178       it->kill( KJob::Quietly );
00179   }
00180   clearSubjobs();
00181 
00182   return true;
00183 }
00184 
00185 bool Job::doSuspend()
00186 {
00187     Q_FOREACH(KJob* it, subjobs()) {
00188         if (!it->suspend())
00189             return false;
00190     }
00191 
00192     return true;
00193 }
00194 
00195 bool Job::doResume()
00196 {
00197     Q_FOREACH ( KJob* it, subjobs() )
00198     {
00199         if (!it->resume())
00200             return false;
00201     }
00202 
00203     return true;
00204 }
00205 
00206 void JobPrivate::slotSpeed( KJob*, unsigned long speed )
00207 {
00208     //kDebug(7007) << speed;
00209     q_func()->emitSpeed( speed );
00210 }
00211 
00212 //Job::errorString is implemented in global.cpp
00213 
00214 #ifndef KDE_NO_DEPRECATED
00215 void Job::showErrorDialog( QWidget *parent )
00216 {
00217     if ( ui() )
00218     {
00219         ui()->setWindow( parent );
00220         ui()->showErrorMessage();
00221     }
00222     else
00223     {
00224         kError() << errorString();
00225     }
00226 }
00227 #endif
00228 
00229 bool Job::isInteractive() const
00230 {
00231   return uiDelegate() != 0;
00232 }
00233 
00234 void Job::setParentJob(Job* job)
00235 {
00236   Q_D(Job);
00237   Q_ASSERT(d->m_parentJob == 0L);
00238   Q_ASSERT(job);
00239   d->m_parentJob = job;
00240 }
00241 
00242 Job* Job::parentJob() const
00243 {
00244   return d_func()->m_parentJob;
00245 }
00246 
00247 MetaData Job::metaData() const
00248 {
00249     return d_func()->m_incomingMetaData;
00250 }
00251 
00252 QString Job::queryMetaData(const QString &key)
00253 {
00254     return d_func()->m_incomingMetaData.value(key, QString());
00255 }
00256 
00257 void Job::setMetaData( const KIO::MetaData &_metaData)
00258 {
00259     Q_D(Job);
00260     d->m_outgoingMetaData = _metaData;
00261 }
00262 
00263 void Job::addMetaData( const QString &key, const QString &value)
00264 {
00265     d_func()->m_outgoingMetaData.insert(key, value);
00266 }
00267 
00268 void Job::addMetaData( const QMap<QString,QString> &values)
00269 {
00270     Q_D(Job);
00271     QMap<QString,QString>::const_iterator it = values.begin();
00272     for(;it != values.end(); ++it)
00273         d->m_outgoingMetaData.insert(it.key(), it.value());
00274 }
00275 
00276 void Job::mergeMetaData( const QMap<QString,QString> &values)
00277 {
00278     Q_D(Job);
00279     QMap<QString,QString>::const_iterator it = values.begin();
00280     for(;it != values.end(); ++it)
00281         // there's probably a faster way
00282         if ( !d->m_outgoingMetaData.contains( it.key() ) )
00283             d->m_outgoingMetaData.insert( it.key(), it.value() );
00284 }
00285 
00286 MetaData Job::outgoingMetaData() const
00287 {
00288     return d_func()->m_outgoingMetaData;
00289 }
00290 
00291 SimpleJob::SimpleJob(SimpleJobPrivate &dd)
00292   : Job(dd)
00293 {
00294     d_func()->simpleJobInit();
00295 }
00296 
00297 void SimpleJobPrivate::simpleJobInit()
00298 {
00299     Q_Q(SimpleJob);
00300     if (!m_url.isValid())
00301     {
00302         q->setError( ERR_MALFORMED_URL );
00303         q->setErrorText( m_url.url() );
00304         QTimer::singleShot(0, q, SLOT(slotFinished()) );
00305         return;
00306     }
00307 
00308     Scheduler::doJob(q);
00309 }
00310 
00311 
00312 bool SimpleJob::doKill()
00313 {
00314     Q_D(SimpleJob);
00315     if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) {
00316         d->m_extraFlags |= JobPrivate::EF_KillCalled;
00317         Scheduler::cancelJob(this); // deletes the slave if not 0
00318     } else {
00319         kWarning(7007) << this << "This is overkill.";
00320     }
00321     return Job::doKill();
00322 }
00323 
00324 bool SimpleJob::doSuspend()
00325 {
00326     Q_D(SimpleJob);
00327     if ( d->m_slave )
00328         d->m_slave->suspend();
00329     return Job::doSuspend();
00330 }
00331 
00332 bool SimpleJob::doResume()
00333 {
00334     Q_D(SimpleJob);
00335     if ( d->m_slave )
00336         d->m_slave->resume();
00337     return Job::doResume();
00338 }
00339 
00340 const KUrl& SimpleJob::url() const
00341 {
00342     return d_func()->m_url;
00343 }
00344 
00345 void SimpleJob::putOnHold()
00346 {
00347     Q_D(SimpleJob);
00348     Q_ASSERT( d->m_slave );
00349     if ( d->m_slave )
00350     {
00351         Scheduler::putSlaveOnHold(this, d->m_url);
00352     }
00353     // we should now be disassociated from the slave
00354     Q_ASSERT(!d->m_slave);
00355     kill( Quietly );
00356 }
00357 
00358 void SimpleJob::removeOnHold()
00359 {
00360     Scheduler::removeSlaveOnHold();
00361 }
00362 
00363 bool SimpleJob::isRedirectionHandlingEnabled() const
00364 {
00365     return d_func()->m_redirectionHandlingEnabled;
00366 }
00367 
00368 void SimpleJob::setRedirectionHandlingEnabled(bool handle)
00369 {
00370     Q_D(SimpleJob);
00371     d->m_redirectionHandlingEnabled = handle;
00372 }
00373 
00374 SimpleJob::~SimpleJob()
00375 {
00376     Q_D(SimpleJob);
00377     // last chance to remove this job from the scheduler!
00378     if (d->m_schedSerial) {
00379         kDebug(7007) << "Killing job" << this << "in destructor!"  << kBacktrace();
00380         Scheduler::cancelJob(this);
00381     }
00382 }
00383 
00384 void SimpleJobPrivate::start(Slave *slave)
00385 {
00386     Q_Q(SimpleJob);
00387     m_slave = slave;
00388 
00389     // Slave::setJob can send us SSL metadata if there is a persistent connection
00390     q->connect( slave, SIGNAL(metaData(KIO::MetaData)),
00391                 SLOT(slotMetaData(KIO::MetaData)) );
00392 
00393     slave->setJob(q);
00394 
00395     q->connect( slave, SIGNAL(error(int,QString)),
00396                 SLOT(slotError(int,QString)) );
00397 
00398     q->connect( slave, SIGNAL(warning(QString)),
00399                 SLOT(slotWarning(QString)) );
00400 
00401     q->connect( slave, SIGNAL(infoMessage(QString)),
00402                 SLOT(_k_slotSlaveInfoMessage(QString)) );
00403 
00404     q->connect( slave, SIGNAL(connected()),
00405                 SLOT(slotConnected()));
00406 
00407     q->connect( slave, SIGNAL(finished()),
00408                 SLOT(slotFinished()) );
00409 
00410     if ((m_extraFlags & EF_TransferJobDataSent) == 0) // this is a "get" job
00411     {
00412         q->connect( slave, SIGNAL(totalSize(KIO::filesize_t)),
00413                     SLOT(slotTotalSize(KIO::filesize_t)) );
00414 
00415         q->connect( slave, SIGNAL(processedSize(KIO::filesize_t)),
00416                     SLOT(slotProcessedSize(KIO::filesize_t)) );
00417 
00418         q->connect( slave, SIGNAL(speed(ulong)),
00419                     SLOT(slotSpeed(ulong)) );
00420     }
00421 
00422     if (ui() && ui()->window())
00423     {
00424         m_outgoingMetaData.insert("window-id", QString::number((qptrdiff)ui()->window()->winId()));
00425     }
00426 
00427     if (ui() && ui()->userTimestamp())
00428     {
00429         m_outgoingMetaData.insert("user-timestamp", QString::number(ui()->userTimestamp()));
00430     }
00431 
00432     if (ui() == 0)              // not interactive
00433     {
00434         m_outgoingMetaData.insert("no-auth-prompt", "true");
00435     }
00436 
00437     if (!m_outgoingMetaData.isEmpty())
00438     {
00439         KIO_ARGS << m_outgoingMetaData;
00440         slave->send( CMD_META_DATA, packedArgs );
00441     }
00442 
00443     if (!m_subUrl.isEmpty())
00444     {
00445        KIO_ARGS << m_subUrl;
00446        slave->send( CMD_SUBURL, packedArgs );
00447     }
00448 
00449     slave->send( m_command, m_packedArgs );
00450 }
00451 
00452 void SimpleJobPrivate::slaveDone()
00453 {
00454     Q_Q(SimpleJob);
00455     if (m_slave) {
00456         if (m_command == CMD_OPEN) {
00457             m_slave->send(CMD_CLOSE);
00458         }
00459         q->disconnect(m_slave); // Remove all signals between slave and job
00460     }
00461     // only finish a job once; Scheduler::jobFinished() resets schedSerial to zero.
00462     if (m_schedSerial) {
00463         Scheduler::jobFinished(q, m_slave);
00464     }
00465 }
00466 
00467 void SimpleJob::slotFinished( )
00468 {
00469     Q_D(SimpleJob);
00470     // Return slave to the scheduler
00471     d->slaveDone();
00472 
00473     if (!hasSubjobs())
00474     {
00475         if ( !error() && (d->m_command == CMD_MKDIR || d->m_command == CMD_RENAME ) )
00476         {
00477             if ( d->m_command == CMD_MKDIR )
00478             {
00479                 KUrl urlDir( url() );
00480                 urlDir.setPath( urlDir.directory() );
00481                 org::kde::KDirNotify::emitFilesAdded( urlDir.url() );
00482             }
00483             else /*if ( m_command == CMD_RENAME )*/
00484             {
00485                 KUrl src, dst;
00486                 QDataStream str( d->m_packedArgs );
00487                 str >> src >> dst;
00488                 if( src.directory() == dst.directory() ) // For the user, moving isn't renaming. Only renaming is.
00489                     org::kde::KDirNotify::emitFileRenamed( src.url(), dst.url() );
00490 
00491                 org::kde::KDirNotify::emitFileMoved( src.url(), dst.url() );
00492             }
00493         }
00494         emitResult();
00495     }
00496 }
00497 
00498 void SimpleJob::slotError( int err, const QString & errorText )
00499 {
00500     Q_D(SimpleJob);
00501     setError( err );
00502     setErrorText( errorText );
00503     if ((error() == ERR_UNKNOWN_HOST) && d->m_url.host().isEmpty())
00504        setErrorText( QString() );
00505     // error terminates the job
00506     slotFinished();
00507 }
00508 
00509 void SimpleJob::slotWarning( const QString & errorText )
00510 {
00511     emit warning( this, errorText );
00512 }
00513 
00514 void SimpleJobPrivate::_k_slotSlaveInfoMessage( const QString & msg )
00515 {
00516     emit q_func()->infoMessage( q_func(), msg );
00517 }
00518 
00519 void SimpleJobPrivate::slotConnected()
00520 {
00521     emit q_func()->connected( q_func() );
00522 }
00523 
00524 void SimpleJobPrivate::slotTotalSize( KIO::filesize_t size )
00525 {
00526     Q_Q(SimpleJob);
00527     if (size != q->totalAmount(KJob::Bytes))
00528     {
00529         q->setTotalAmount(KJob::Bytes, size);
00530     }
00531 }
00532 
00533 void SimpleJobPrivate::slotProcessedSize( KIO::filesize_t size )
00534 {
00535     Q_Q(SimpleJob);
00536     //kDebug(7007) << KIO::number(size);
00537     q->setProcessedAmount(KJob::Bytes, size);
00538 }
00539 
00540 void SimpleJobPrivate::slotSpeed( unsigned long speed )
00541 {
00542     //kDebug(7007) << speed;
00543     q_func()->emitSpeed( speed );
00544 }
00545 
00546 void SimpleJobPrivate::restartAfterRedirection(KUrl *redirectionUrl)
00547 {
00548     Q_Q(SimpleJob);
00549     // Return slave to the scheduler while we still have the old URL in place; the scheduler
00550     // requires a job URL to stay invariant while the job is running.
00551     slaveDone();
00552 
00553     m_url = *redirectionUrl;
00554     redirectionUrl->clear();
00555     if ((m_extraFlags & EF_KillCalled) == 0) {
00556         Scheduler::doJob(q);
00557     }
00558 }
00559 
00560 void SimpleJob::slotMetaData( const KIO::MetaData &_metaData )
00561 {
00562     Q_D(SimpleJob);
00563     QMapIterator<QString,QString> it (_metaData);
00564     while (it.hasNext()) {
00565         it.next();
00566         if (it.key().startsWith(QLatin1String("{internal~"), Qt::CaseInsensitive))
00567             d->m_internalMetaData.insert(it.key(), it.value());
00568         else
00569             d->m_incomingMetaData.insert(it.key(), it.value());
00570     }
00571 
00572     // Update the internal meta-data values as soon as possible. Waiting until
00573     // the ioslave is finished has unintended consequences if the client starts
00574     // a new connection without waiting for the ioslave to finish.
00575     if (!d->m_internalMetaData.isEmpty()) {
00576         Scheduler::updateInternalMetaData(this);
00577     }
00578 }
00579 
00580 void SimpleJob::storeSSLSessionFromJob(const KUrl &redirectionURL)
00581 {
00582     Q_UNUSED(redirectionURL);
00583 }
00584 
00585 
00587 class KIO::MkdirJobPrivate: public SimpleJobPrivate
00588 {
00589 public:
00590     MkdirJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
00591         : SimpleJobPrivate(url, command, packedArgs)
00592         { }
00593     KUrl m_redirectionURL;
00594     void slotRedirection(const KUrl &url);
00595 
00602     virtual void start( Slave *slave );
00603 
00604     Q_DECLARE_PUBLIC(MkdirJob)
00605 
00606     static inline MkdirJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs)
00607     {
00608         MkdirJob *job = new MkdirJob(*new MkdirJobPrivate(url, command, packedArgs));
00609         job->setUiDelegate(new JobUiDelegate);
00610         return job;
00611     }
00612 };
00613 
00614 MkdirJob::MkdirJob(MkdirJobPrivate &dd)
00615     : SimpleJob(dd)
00616 {
00617 }
00618 
00619 MkdirJob::~MkdirJob()
00620 {
00621 }
00622 
00623 void MkdirJobPrivate::start(Slave *slave)
00624 {
00625     Q_Q(MkdirJob);
00626     q->connect( slave, SIGNAL(redirection(KUrl)),
00627                 SLOT(slotRedirection(KUrl)) );
00628 
00629     SimpleJobPrivate::start(slave);
00630 }
00631 
00632 // Slave got a redirection request
00633 void MkdirJobPrivate::slotRedirection( const KUrl &url)
00634 {
00635      Q_Q(MkdirJob);
00636      kDebug(7007) << url;
00637      if (!KAuthorized::authorizeUrlAction("redirect", m_url, url))
00638      {
00639          kWarning(7007) << "Redirection from" << m_url << "to" << url << "REJECTED!";
00640          q->setError( ERR_ACCESS_DENIED );
00641          q->setErrorText( url.pathOrUrl() );
00642          return;
00643      }
00644      m_redirectionURL = url; // We'll remember that when the job finishes
00645      // Tell the user that we haven't finished yet
00646      emit q->redirection(q, m_redirectionURL);
00647 }
00648 
00649 void MkdirJob::slotFinished()
00650 {
00651     Q_D(MkdirJob);
00652 
00653     if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() )
00654     {
00655         //kDebug(7007) << "MkdirJob: Redirection to " << m_redirectionURL;
00656         if (queryMetaData("permanent-redirect")=="true")
00657             emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
00658 
00659         if ( d->m_redirectionHandlingEnabled )
00660         {
00661             KUrl dummyUrl;
00662             int permissions;
00663             QDataStream istream( d->m_packedArgs );
00664             istream >> dummyUrl >> permissions;
00665 
00666             d->m_packedArgs.truncate(0);
00667             QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
00668             stream << d->m_redirectionURL << permissions;
00669 
00670             d->restartAfterRedirection(&d->m_redirectionURL);
00671             return;
00672         }
00673     }
00674 
00675     // Return slave to the scheduler
00676     SimpleJob::slotFinished();
00677 }
00678 
00679 SimpleJob *KIO::mkdir( const KUrl& url, int permissions )
00680 {
00681     //kDebug(7007) << "mkdir " << url;
00682     KIO_ARGS << url << permissions;
00683     return MkdirJobPrivate::newJob(url, CMD_MKDIR, packedArgs);
00684 }
00685 
00686 SimpleJob *KIO::rmdir( const KUrl& url )
00687 {
00688     //kDebug(7007) << "rmdir " << url;
00689     KIO_ARGS << url << qint8(false); // isFile is false
00690     return SimpleJobPrivate::newJob(url, CMD_DEL, packedArgs);
00691 }
00692 
00693 SimpleJob *KIO::chmod( const KUrl& url, int permissions )
00694 {
00695     //kDebug(7007) << "chmod " << url;
00696     KIO_ARGS << url << permissions;
00697     return SimpleJobPrivate::newJob(url, CMD_CHMOD, packedArgs);
00698 }
00699 
00700 SimpleJob *KIO::chown( const KUrl& url, const QString& owner, const QString& group )
00701 {
00702     KIO_ARGS << url << owner << group;
00703     return SimpleJobPrivate::newJob(url, CMD_CHOWN, packedArgs);
00704 }
00705 
00706 SimpleJob *KIO::setModificationTime( const KUrl& url, const QDateTime& mtime )
00707 {
00708     //kDebug(7007) << "setModificationTime " << url << " " << mtime;
00709     KIO_ARGS << url << mtime;
00710     return SimpleJobPrivate::newJobNoUi(url, CMD_SETMODIFICATIONTIME, packedArgs);
00711 }
00712 
00713 SimpleJob *KIO::rename( const KUrl& src, const KUrl & dest, JobFlags flags )
00714 {
00715     //kDebug(7007) << "rename " << src << " " << dest;
00716     KIO_ARGS << src << dest << (qint8) (flags & Overwrite);
00717     return SimpleJobPrivate::newJob(src, CMD_RENAME, packedArgs);
00718 }
00719 
00720 SimpleJob *KIO::symlink( const QString& target, const KUrl & dest, JobFlags flags )
00721 {
00722     //kDebug(7007) << "symlink target=" << target << " " << dest;
00723     KIO_ARGS << target << dest << (qint8) (flags & Overwrite);
00724     return SimpleJobPrivate::newJob(dest, CMD_SYMLINK, packedArgs, flags);
00725 }
00726 
00727 SimpleJob *KIO::special(const KUrl& url, const QByteArray & data, JobFlags flags)
00728 {
00729     //kDebug(7007) << "special " << url;
00730     return SimpleJobPrivate::newJob(url, CMD_SPECIAL, data, flags);
00731 }
00732 
00733 SimpleJob *KIO::mount( bool ro, const QByteArray& fstype, const QString& dev, const QString& point, JobFlags flags )
00734 {
00735     KIO_ARGS << int(1) << qint8( ro ? 1 : 0 )
00736              << QString::fromLatin1(fstype) << dev << point;
00737     SimpleJob *job = special( KUrl("file:/"), packedArgs, flags );
00738     if (!(flags & HideProgressInfo)) {
00739         KIO::JobPrivate::emitMounting(job, dev, point);
00740     }
00741     return job;
00742 }
00743 
00744 SimpleJob *KIO::unmount( const QString& point, JobFlags flags )
00745 {
00746     KIO_ARGS << int(2) << point;
00747     SimpleJob *job = special( KUrl("file:/"), packedArgs, flags );
00748     if (!(flags & HideProgressInfo)) {
00749         KIO::JobPrivate::emitUnmounting(job, point);
00750     }
00751     return job;
00752 }
00753 
00754 
00755 
00757 
00758 class KIO::StatJobPrivate: public SimpleJobPrivate
00759 {
00760 public:
00761     inline StatJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
00762         : SimpleJobPrivate(url, command, packedArgs), m_bSource(true), m_details(2)
00763         {}
00764 
00765     UDSEntry m_statResult;
00766     KUrl m_redirectionURL;
00767     bool m_bSource;
00768     short int m_details;
00769     void slotStatEntry( const KIO::UDSEntry & entry );
00770     void slotRedirection( const KUrl &url);
00771 
00778     virtual void start( Slave *slave );
00779 
00780     Q_DECLARE_PUBLIC(StatJob)
00781 
00782     static inline StatJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs,
00783         JobFlags flags )
00784     {
00785         StatJob *job = new StatJob(*new StatJobPrivate(url, command, packedArgs));
00786         job->setUiDelegate(new JobUiDelegate);
00787         if (!(flags & HideProgressInfo)) {
00788             KIO::getJobTracker()->registerJob(job);
00789             emitStating(job, url);
00790         }
00791         return job;
00792     }
00793 };
00794 
00795 StatJob::StatJob(StatJobPrivate &dd)
00796     : SimpleJob(dd)
00797 {
00798 }
00799 
00800 StatJob::~StatJob()
00801 {
00802 }
00803 
00804 #ifndef KDE_NO_DEPRECATED
00805 void StatJob::setSide( bool source )
00806 {
00807     d_func()->m_bSource = source;
00808 }
00809 #endif
00810 
00811 void StatJob::setSide( StatSide side )
00812 {
00813     d_func()->m_bSource = side == SourceSide;
00814 }
00815 
00816 void StatJob::setDetails( short int details )
00817 {
00818     d_func()->m_details = details;
00819 }
00820 
00821 const UDSEntry & StatJob::statResult() const
00822 {
00823     return d_func()->m_statResult;
00824 }
00825 
00826 KUrl StatJob::mostLocalUrl() const
00827 {
00828     if (!url().isLocalFile()) {
00829         const UDSEntry& udsEntry = d_func()->m_statResult;
00830         const QString path = udsEntry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
00831         if (!path.isEmpty())
00832             return KUrl(path);
00833     }
00834     return url();
00835 }
00836 
00837 void StatJobPrivate::start(Slave *slave)
00838 {
00839     Q_Q(StatJob);
00840     m_outgoingMetaData.insert( "statSide", m_bSource ? "source" : "dest" );
00841     m_outgoingMetaData.insert( "details", QString::number(m_details) );
00842 
00843     q->connect( slave, SIGNAL(statEntry(KIO::UDSEntry)),
00844              SLOT(slotStatEntry(KIO::UDSEntry)) );
00845     q->connect( slave, SIGNAL(redirection(KUrl)),
00846              SLOT(slotRedirection(KUrl)) );
00847 
00848     SimpleJobPrivate::start(slave);
00849 }
00850 
00851 void StatJobPrivate::slotStatEntry( const KIO::UDSEntry & entry )
00852 {
00853     //kDebug(7007);
00854     m_statResult = entry;
00855 }
00856 
00857 // Slave got a redirection request
00858 void StatJobPrivate::slotRedirection( const KUrl &url)
00859 {
00860      Q_Q(StatJob);
00861      kDebug(7007) << m_url << "->" << url;
00862      if (!KAuthorized::authorizeUrlAction("redirect", m_url, url))
00863      {
00864        kWarning(7007) << "Redirection from " << m_url << " to " << url << " REJECTED!";
00865        q->setError( ERR_ACCESS_DENIED );
00866        q->setErrorText( url.pathOrUrl() );
00867        return;
00868      }
00869      m_redirectionURL = url; // We'll remember that when the job finishes
00870      // Tell the user that we haven't finished yet
00871      emit q->redirection(q, m_redirectionURL);
00872 }
00873 
00874 void StatJob::slotFinished()
00875 {
00876     Q_D(StatJob);
00877 
00878     if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() )
00879     {
00880         //kDebug(7007) << "StatJob: Redirection to " << m_redirectionURL;
00881         if (queryMetaData("permanent-redirect")=="true")
00882             emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
00883 
00884         if ( d->m_redirectionHandlingEnabled )
00885         {
00886             d->m_packedArgs.truncate(0);
00887             QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
00888             stream << d->m_redirectionURL;
00889 
00890             d->restartAfterRedirection(&d->m_redirectionURL);
00891             return;
00892         }
00893     }
00894 
00895     // Return slave to the scheduler
00896     SimpleJob::slotFinished();
00897 }
00898 
00899 void StatJob::slotMetaData( const KIO::MetaData &_metaData)
00900 {
00901     Q_D(StatJob);
00902     SimpleJob::slotMetaData(_metaData);
00903     storeSSLSessionFromJob(d->m_redirectionURL);
00904 }
00905 
00906 StatJob *KIO::stat(const KUrl& url, JobFlags flags)
00907 {
00908     // Assume sideIsSource. Gets are more common than puts.
00909     return stat( url, StatJob::SourceSide, 2, flags );
00910 }
00911 
00912 StatJob *KIO::mostLocalUrl(const KUrl& url, JobFlags flags)
00913 {
00914     StatJob* job = stat( url, StatJob::SourceSide, 2, flags );
00915     if (url.isLocalFile()) {
00916         QTimer::singleShot(0, job, SLOT(slotFinished()));
00917         Scheduler::cancelJob(job); // deletes the slave if not 0
00918     }
00919     return job;
00920 }
00921 
00922 #ifndef KDE_NO_DEPRECATED
00923 StatJob *KIO::stat(const KUrl& url, bool sideIsSource, short int details, JobFlags flags )
00924 {
00925     //kDebug(7007) << "stat" << url;
00926     KIO_ARGS << url;
00927     StatJob * job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags);
00928     job->setSide( sideIsSource ? StatJob::SourceSide : StatJob::DestinationSide );
00929     job->setDetails( details );
00930     return job;
00931 }
00932 #endif
00933 
00934 StatJob *KIO::stat(const KUrl& url, KIO::StatJob::StatSide side, short int details, JobFlags flags )
00935 {
00936     //kDebug(7007) << "stat" << url;
00937     KIO_ARGS << url;
00938     StatJob * job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags);
00939     job->setSide( side );
00940     job->setDetails( details );
00941     return job;
00942 }
00943 
00944 SimpleJob *KIO::http_update_cache( const KUrl& url, bool no_cache, time_t expireDate)
00945 {
00946     Q_ASSERT(url.protocol() == "http" || url.protocol() == "https");
00947     // Send http update_cache command (2)
00948     KIO_ARGS << (int)2 << url << no_cache << qlonglong(expireDate);
00949     SimpleJob * job = SimpleJobPrivate::newJob(url, CMD_SPECIAL, packedArgs);
00950     Scheduler::setJobPriority(job, 1);
00951     return job;
00952 }
00953 
00955 
00956 TransferJob::TransferJob(TransferJobPrivate &dd)
00957     : SimpleJob(dd)
00958 {
00959     Q_D(TransferJob);
00960     if (d->m_command == CMD_PUT) {
00961         d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent;
00962     }
00963 }
00964 
00965 TransferJob::~TransferJob()
00966 {
00967 }
00968 
00969 // Slave sends data
00970 void TransferJob::slotData( const QByteArray &_data)
00971 {
00972     Q_D(TransferJob);
00973     if (d->m_command == CMD_GET && !d->m_isMimetypeEmitted) {
00974         kWarning(7007) << "mimeType() not emitted when sending first data!; job URL ="
00975                        << d->m_url << "data size =" << _data.size();
00976     }
00977     // shut up the warning, HACK: downside is that it changes the meaning of the variable
00978     d->m_isMimetypeEmitted = true;
00979 
00980     if (d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error()) {
00981         emit data(this, _data);
00982     }
00983 }
00984 
00985 void KIO::TransferJob::setTotalSize(KIO::filesize_t bytes)
00986 {
00987     setTotalAmount(KJob::Bytes, bytes);
00988 }
00989 
00990 // Slave got a redirection request
00991 void TransferJob::slotRedirection( const KUrl &url)
00992 {
00993     Q_D(TransferJob);
00994     kDebug(7007) << url;
00995     if (!KAuthorized::authorizeUrlAction("redirect", d->m_url, url))
00996     {
00997         kWarning(7007) << "Redirection from " << d->m_url << " to " << url << " REJECTED!";
00998         return;
00999     }
01000 
01001     // Some websites keep redirecting to themselves where each redirection
01002     // acts as the stage in a state-machine. We define "endless redirections"
01003     // as 5 redirections to the same URL.
01004     if (d->m_redirectionList.count(url) > 5)
01005     {
01006        kDebug(7007) << "CYCLIC REDIRECTION!";
01007        setError( ERR_CYCLIC_LINK );
01008        setErrorText( d->m_url.pathOrUrl() );
01009     }
01010     else
01011     {
01012        d->m_redirectionURL = url; // We'll remember that when the job finishes
01013        d->m_redirectionList.append(url);
01014        d->m_outgoingMetaData["ssl_was_in_use"] = d->m_incomingMetaData["ssl_in_use"];
01015        // Tell the user that we haven't finished yet
01016        emit redirection(this, d->m_redirectionURL);
01017     }
01018 }
01019 
01020 void TransferJob::slotFinished()
01021 {
01022     Q_D(TransferJob);
01023 
01024     kDebug(7007) << d->m_url;
01025     if (!d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid()) {
01026 
01027         //kDebug(7007) << "Redirection to" << m_redirectionURL;
01028         if (queryMetaData("permanent-redirect")=="true")
01029             emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
01030 
01031         if (d->m_redirectionHandlingEnabled) {
01032             // Honour the redirection
01033             // We take the approach of "redirecting this same job"
01034             // Another solution would be to create a subjob, but the same problem
01035             // happens (unpacking+repacking)
01036             d->staticData.truncate(0);
01037             d->m_incomingMetaData.clear();
01038             if (queryMetaData("cache") != "reload")
01039                 addMetaData("cache","refresh");
01040             d->m_internalSuspended = false;
01041             // The very tricky part is the packed arguments business
01042             QString dummyStr;
01043             KUrl dummyUrl;
01044             QDataStream istream( d->m_packedArgs );
01045             switch( d->m_command ) {
01046                 case CMD_GET: {
01047                     d->m_packedArgs.truncate(0);
01048                     QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
01049                     stream << d->m_redirectionURL;
01050                     break;
01051                 }
01052                 case CMD_PUT: {
01053                     int permissions;
01054                     qint8 iOverwrite, iResume;
01055                     istream >> dummyUrl >> iOverwrite >> iResume >> permissions;
01056                     d->m_packedArgs.truncate(0);
01057                     QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
01058                     stream << d->m_redirectionURL << iOverwrite << iResume << permissions;
01059                     break;
01060                 }
01061                 case CMD_SPECIAL: {
01062                     int specialcmd;
01063                     istream >> specialcmd;
01064                     if (specialcmd == 1) // HTTP POST
01065                     {
01066                       d->m_outgoingMetaData.remove(QLatin1String("content-type"));
01067                       addMetaData("cache","reload");
01068                       d->m_packedArgs.truncate(0);
01069                       QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
01070                       stream << d->m_redirectionURL;
01071                       d->m_command = CMD_GET;
01072                     }
01073                     break;
01074                 }
01075             }
01076             d->restartAfterRedirection(&d->m_redirectionURL);
01077             return;
01078         }
01079     }
01080 
01081     SimpleJob::slotFinished();
01082 }
01083 
01084 void TransferJob::setAsyncDataEnabled(bool enabled)
01085 {
01086     Q_D(TransferJob);
01087     if (enabled)
01088         d->m_extraFlags |= JobPrivate::EF_TransferJobAsync;
01089     else
01090         d->m_extraFlags &= ~JobPrivate::EF_TransferJobAsync;
01091 }
01092 
01093 void TransferJob::sendAsyncData(const QByteArray &dataForSlave)
01094 {
01095     Q_D(TransferJob);
01096     if (d->m_extraFlags & JobPrivate::EF_TransferJobNeedData)
01097     {
01098        d->m_slave->send( MSG_DATA, dataForSlave );
01099        if (d->m_extraFlags & JobPrivate::EF_TransferJobDataSent) // put job -> emit progress
01100        {
01101            KIO::filesize_t size = processedAmount(KJob::Bytes)+dataForSlave.size();
01102            setProcessedAmount(KJob::Bytes, size);
01103        }
01104     }
01105 
01106     d->m_extraFlags &= ~JobPrivate::EF_TransferJobNeedData;
01107 }
01108 
01109 #ifndef KDE_NO_DEPRECATED
01110 void TransferJob::setReportDataSent(bool enabled)
01111 {
01112     Q_D(TransferJob);
01113     if (enabled)
01114        d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent;
01115     else
01116        d->m_extraFlags &= ~JobPrivate::EF_TransferJobDataSent;
01117 }
01118 #endif
01119 
01120 #ifndef KDE_NO_DEPRECATED
01121 bool TransferJob::reportDataSent() const
01122 {
01123     return (d_func()->m_extraFlags & JobPrivate::EF_TransferJobDataSent);
01124 }
01125 #endif
01126 
01127 QString TransferJob::mimetype() const
01128 {
01129     return d_func()->m_mimetype;
01130 }
01131 
01132 // Slave requests data
01133 void TransferJob::slotDataReq()
01134 {
01135     Q_D(TransferJob);
01136     QByteArray dataForSlave;
01137 
01138     d->m_extraFlags |= JobPrivate::EF_TransferJobNeedData;
01139 
01140     if (!d->staticData.isEmpty())
01141     {
01142        dataForSlave = d->staticData;
01143        d->staticData.clear();
01144     }
01145     else
01146     {
01147        emit dataReq( this, dataForSlave);
01148 
01149        if (d->m_extraFlags & JobPrivate::EF_TransferJobAsync)
01150           return;
01151     }
01152 
01153     static const int max_size = 14 * 1024 * 1024;
01154     if (dataForSlave.size() > max_size)
01155     {
01156        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";
01157        d->staticData = QByteArray(dataForSlave.data() + max_size ,  dataForSlave.size() - max_size);
01158        dataForSlave.truncate(max_size);
01159     }
01160 
01161     sendAsyncData(dataForSlave);
01162 
01163     if (d->m_subJob)
01164     {
01165        // Bitburger protocol in action
01166        d->internalSuspend(); // Wait for more data from subJob.
01167        d->m_subJob->d_func()->internalResume(); // Ask for more!
01168     }
01169 }
01170 
01171 void TransferJob::slotMimetype( const QString& type )
01172 {
01173     Q_D(TransferJob);
01174     d->m_mimetype = type;
01175     if (d->m_command == CMD_GET && d->m_isMimetypeEmitted) {
01176         kWarning(7007) << "mimetype() emitted again, or after sending first data!; job URL ="
01177                        << d->m_url;
01178     }
01179     d->m_isMimetypeEmitted = true;
01180     emit mimetype( this, type );
01181 }
01182 
01183 
01184 void TransferJobPrivate::internalSuspend()
01185 {
01186     m_internalSuspended = true;
01187     if (m_slave)
01188        m_slave->suspend();
01189 }
01190 
01191 void TransferJobPrivate::internalResume()
01192 {
01193     m_internalSuspended = false;
01194     if ( m_slave && !suspended )
01195         m_slave->resume();
01196 }
01197 
01198 bool TransferJob::doResume()
01199 {
01200     Q_D(TransferJob);
01201     if ( !SimpleJob::doResume() )
01202         return false;
01203     if ( d->m_internalSuspended )
01204         d->internalSuspend();
01205     return true;
01206 }
01207 
01208 bool TransferJob::isErrorPage() const
01209 {
01210     return d_func()->m_errorPage;
01211 }
01212 
01213 void TransferJobPrivate::start(Slave *slave)
01214 {
01215     Q_Q(TransferJob);
01216     Q_ASSERT(slave);
01217     JobPrivate::emitTransferring(q, m_url);
01218     q->connect( slave, SIGNAL(data(QByteArray)),
01219              SLOT(slotData(QByteArray)) );
01220 
01221     if (m_outgoingDataSource)
01222         q->connect( slave, SIGNAL(dataReq()),
01223                 SLOT(slotDataReqFromDevice()) );
01224     else
01225         q->connect( slave, SIGNAL(dataReq()),
01226                 SLOT(slotDataReq()) );
01227 
01228     q->connect( slave, SIGNAL(redirection(KUrl)),
01229              SLOT(slotRedirection(KUrl)) );
01230 
01231     q->connect( slave, SIGNAL(mimeType(QString)),
01232              SLOT(slotMimetype(QString)) );
01233 
01234     q->connect( slave, SIGNAL(errorPage()),
01235              SLOT(slotErrorPage()) );
01236 
01237     q->connect( slave, SIGNAL(needSubUrlData()),
01238              SLOT(slotNeedSubUrlData()) );
01239 
01240     q->connect( slave, SIGNAL(canResume(KIO::filesize_t)),
01241              SLOT(slotCanResume(KIO::filesize_t)) );
01242 
01243     if (slave->suspended())
01244     {
01245        m_mimetype = "unknown";
01246        // WABA: The slave was put on hold. Resume operation.
01247        slave->resume();
01248     }
01249 
01250     SimpleJobPrivate::start(slave);
01251     if (m_internalSuspended)
01252        slave->suspend();
01253 }
01254 
01255 void TransferJobPrivate::slotNeedSubUrlData()
01256 {
01257     Q_Q(TransferJob);
01258     // Job needs data from subURL.
01259     m_subJob = KIO::get( m_subUrl, NoReload, HideProgressInfo);
01260     internalSuspend(); // Put job on hold until we have some data.
01261     q->connect(m_subJob, SIGNAL(data(KIO::Job*,QByteArray)),
01262             SLOT(slotSubUrlData(KIO::Job*,QByteArray)));
01263     q->addSubjob(m_subJob);
01264 }
01265 
01266 void TransferJobPrivate::slotSubUrlData(KIO::Job*, const QByteArray &data)
01267 {
01268     // The Alternating Bitburg protocol in action again.
01269     staticData = data;
01270     m_subJob->d_func()->internalSuspend(); // Put job on hold until we have delivered the data.
01271     internalResume(); // Activate ourselves again.
01272 }
01273 
01274 void TransferJob::slotMetaData( const KIO::MetaData &_metaData)
01275 {
01276     Q_D(TransferJob);
01277     SimpleJob::slotMetaData(_metaData);
01278     storeSSLSessionFromJob(d->m_redirectionURL);
01279 }
01280 
01281 void TransferJobPrivate::slotErrorPage()
01282 {
01283     m_errorPage = true;
01284 }
01285 
01286 void TransferJobPrivate::slotCanResume( KIO::filesize_t offset )
01287 {
01288     Q_Q(TransferJob);
01289     emit q->canResume(q, offset);
01290 }
01291 
01292 void TransferJobPrivate::slotDataReqFromDevice()
01293 {
01294     Q_Q(TransferJob);
01295 
01296     QByteArray dataForSlave;
01297 
01298     m_extraFlags |= JobPrivate::EF_TransferJobNeedData;
01299 
01300     if (m_outgoingDataSource)
01301         dataForSlave = m_outgoingDataSource.data()->read(MAX_READ_BUF_SIZE);
01302 
01303     if (dataForSlave.isEmpty())
01304     {
01305         emit q->dataReq(q, dataForSlave);
01306         if (m_extraFlags & JobPrivate::EF_TransferJobAsync)
01307             return;
01308     }
01309 
01310     q->sendAsyncData(dataForSlave);
01311 
01312     if (m_subJob)
01313     {
01314        // Bitburger protocol in action
01315        internalSuspend(); // Wait for more data from subJob.
01316        m_subJob->d_func()->internalResume(); // Ask for more!
01317     }
01318 }
01319 
01320 void TransferJob::slotResult( KJob *job)
01321 {
01322     Q_D(TransferJob);
01323     // This can only be our suburl.
01324     Q_ASSERT(job == d->m_subJob);
01325 
01326    SimpleJob::slotResult( job );
01327 
01328    if (!error() && job == d->m_subJob)
01329    {
01330       d->m_subJob = 0; // No action required
01331       d->internalResume(); // Make sure we get the remaining data.
01332    }
01333 }
01334 
01335 void TransferJob::setModificationTime( const QDateTime& mtime )
01336 {
01337     addMetaData( "modified", mtime.toString( Qt::ISODate ) );
01338 }
01339 
01340 TransferJob *KIO::get( const KUrl& url, LoadType reload, JobFlags flags )
01341 {
01342     // Send decoded path and encoded query
01343     KIO_ARGS << url;
01344     TransferJob * job = TransferJobPrivate::newJob(url, CMD_GET, packedArgs,
01345                                                    QByteArray(), flags);
01346     if (reload == Reload)
01347        job->addMetaData("cache", "reload");
01348     return job;
01349 }
01350 
01351 class KIO::StoredTransferJobPrivate: public TransferJobPrivate
01352 {
01353 public:
01354     StoredTransferJobPrivate(const KUrl& url, int command,
01355                              const QByteArray &packedArgs,
01356                              const QByteArray &_staticData)
01357         : TransferJobPrivate(url, command, packedArgs, _staticData),
01358           m_uploadOffset( 0 )
01359         {}
01360     StoredTransferJobPrivate(const KUrl& url, int command,
01361                              const QByteArray &packedArgs,
01362                              QIODevice* ioDevice)
01363         : TransferJobPrivate(url, command, packedArgs, ioDevice),
01364           m_uploadOffset( 0 )
01365         {}
01366 
01367     QByteArray m_data;
01368     int m_uploadOffset;
01369 
01370     void slotStoredData( KIO::Job *job, const QByteArray &data );
01371     void slotStoredDataReq( KIO::Job *job, QByteArray &data );
01372 
01373     Q_DECLARE_PUBLIC(StoredTransferJob)
01374 
01375     static inline StoredTransferJob *newJob(const KUrl &url, int command,
01376                                             const QByteArray &packedArgs,
01377                                             const QByteArray &staticData, JobFlags flags)
01378     {
01379         StoredTransferJob *job = new StoredTransferJob(
01380             *new StoredTransferJobPrivate(url, command, packedArgs, staticData));
01381         job->setUiDelegate(new JobUiDelegate);
01382         if (!(flags & HideProgressInfo))
01383             KIO::getJobTracker()->registerJob(job);
01384         return job;
01385     }
01386 
01387     static inline StoredTransferJob *newJob(const KUrl &url, int command,
01388                                             const QByteArray &packedArgs,
01389                                             QIODevice* ioDevice, JobFlags flags)
01390     {
01391         StoredTransferJob *job = new StoredTransferJob(
01392             *new StoredTransferJobPrivate(url, command, packedArgs, ioDevice));
01393         job->setUiDelegate(new JobUiDelegate);
01394         if (!(flags & HideProgressInfo))
01395             KIO::getJobTracker()->registerJob(job);
01396         return job;
01397     }
01398 };
01399 
01400 namespace KIO {
01401     class PostErrorJob : public StoredTransferJob
01402     {
01403     public:
01404 
01405         PostErrorJob(int _error, const QString& url, const QByteArray &packedArgs, const QByteArray &postData)
01406             : StoredTransferJob(*new StoredTransferJobPrivate(KUrl(), CMD_SPECIAL, packedArgs, postData))
01407             {
01408                 setError( _error );
01409                 setErrorText( url );
01410             }
01411 
01412         PostErrorJob(int _error, const QString& url, const QByteArray &packedArgs, QIODevice* ioDevice)
01413             : StoredTransferJob(*new StoredTransferJobPrivate(KUrl(), CMD_SPECIAL, packedArgs, ioDevice))
01414             {
01415                 setError( _error );
01416                 setErrorText( url );
01417             }
01418     };
01419 }
01420 
01421 static int isUrlPortBad(const KUrl& url)
01422 {
01423     int _error = 0;
01424 
01425     // filter out some malicious ports
01426     static const int bad_ports[] = {
01427         1,   // tcpmux
01428         7,   // echo
01429         9,   // discard
01430         11,   // systat
01431         13,   // daytime
01432         15,   // netstat
01433         17,   // qotd
01434         19,   // chargen
01435         20,   // ftp-data
01436         21,   // ftp-cntl
01437         22,   // ssh
01438         23,   // telnet
01439         25,   // smtp
01440         37,   // time
01441         42,   // name
01442         43,   // nicname
01443         53,   // domain
01444         77,   // priv-rjs
01445         79,   // finger
01446         87,   // ttylink
01447         95,   // supdup
01448         101,  // hostriame
01449         102,  // iso-tsap
01450         103,  // gppitnp
01451         104,  // acr-nema
01452         109,  // pop2
01453         110,  // pop3
01454         111,  // sunrpc
01455         113,  // auth
01456         115,  // sftp
01457         117,  // uucp-path
01458         119,  // nntp
01459         123,  // NTP
01460         135,  // loc-srv / epmap
01461         139,  // netbios
01462         143,  // imap2
01463         179,  // BGP
01464         389,  // ldap
01465         512,  // print / exec
01466         513,  // login
01467         514,  // shell
01468         515,  // printer
01469         526,  // tempo
01470         530,  // courier
01471         531,  // Chat
01472         532,  // netnews
01473         540,  // uucp
01474         556,  // remotefs
01475         587,  // sendmail
01476         601,  //
01477         989,  // ftps data
01478         990,  // ftps
01479         992,  // telnets
01480         993,  // imap/SSL
01481         995,  // pop3/SSL
01482         1080, // SOCKS
01483         2049, // nfs
01484         4045, // lockd
01485         6000, // x11
01486         6667, // irc
01487         0};
01488     if (url.port() != 80)
01489     {
01490         const int port = url.port();
01491         for (int cnt=0; bad_ports[cnt] && bad_ports[cnt] <= port; ++cnt)
01492             if (port == bad_ports[cnt])
01493             {
01494                 _error = KIO::ERR_POST_DENIED;
01495                 break;
01496             }
01497     }
01498 
01499     if ( _error )
01500     {
01501         static bool override_loaded = false;
01502         static QList< int >* overriden_ports = NULL;
01503         if( !override_loaded ) {
01504             KConfig cfg( "kio_httprc" );
01505             overriden_ports = new QList< int >;
01506             *overriden_ports = cfg.group(QString()).readEntry( "OverriddenPorts", QList<int>() );
01507             override_loaded = true;
01508         }
01509         for( QList< int >::ConstIterator it = overriden_ports->constBegin();
01510                 it != overriden_ports->constEnd();
01511                 ++it ) {
01512             if( overriden_ports->contains( url.port())) {
01513                 _error = 0;
01514             }
01515         }
01516     }
01517 
01518     // filter out non https? protocols
01519     if ((url.protocol() != "http") && (url.protocol() != "https" ))
01520         _error = KIO::ERR_POST_DENIED;
01521 
01522     if (!_error && !KAuthorized::authorizeUrlAction("open", KUrl(), url))
01523         _error = KIO::ERR_ACCESS_DENIED;
01524 
01525     return _error;
01526 }
01527 
01528 static KIO::PostErrorJob* precheckHttpPost( const KUrl& url, QIODevice* ioDevice, JobFlags flags )
01529 {
01530     // if request is not valid, return an invalid transfer job
01531     const int _error = isUrlPortBad(url);
01532 
01533     if (_error)
01534     {
01535         KIO_ARGS << (int)1 << url;
01536         PostErrorJob * job = new PostErrorJob(_error, url.pathOrUrl(), packedArgs, ioDevice);
01537         job->setUiDelegate(new JobUiDelegate());
01538         if (!(flags & HideProgressInfo)) {
01539             KIO::getJobTracker()->registerJob(job);
01540         }
01541         return job;
01542     }
01543 
01544     // all is ok, return 0
01545     return 0;
01546 }
01547 
01548 static KIO::PostErrorJob* precheckHttpPost( const KUrl& url, const QByteArray& postData, JobFlags flags )
01549 {
01550     // if request is not valid, return an invalid transfer job
01551     const int _error = isUrlPortBad(url);
01552 
01553     if (_error)
01554     {
01555         KIO_ARGS << (int)1 << url;
01556         PostErrorJob * job = new PostErrorJob(_error, url.pathOrUrl(), packedArgs, postData);
01557         job->setUiDelegate(new JobUiDelegate());
01558         if (!(flags & HideProgressInfo)) {
01559             KIO::getJobTracker()->registerJob(job);
01560         }
01561         return job;
01562     }
01563 
01564     // all is ok, return 0
01565     return 0;
01566 }
01567 
01568 TransferJob *KIO::http_post( const KUrl& url, const QByteArray &postData, JobFlags flags )
01569 {
01570     bool redirection = false;
01571     KUrl _url(url);
01572     if (_url.path().isEmpty())
01573     {
01574       redirection = true;
01575       _url.setPath("/");
01576     }
01577 
01578     TransferJob* job = precheckHttpPost(_url, postData, flags);
01579     if (job)
01580         return job;
01581 
01582     // Send http post command (1), decoded path and encoded query
01583     KIO_ARGS << (int)1 << _url << static_cast<qint64>(postData.size());
01584     job = TransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags);
01585 
01586     if (redirection)
01587       QTimer::singleShot(0, job, SLOT(slotPostRedirection()) );
01588 
01589     return job;
01590 }
01591 
01592 TransferJob *KIO::http_post( const KUrl& url, QIODevice* ioDevice, qint64 size, JobFlags flags )
01593 {
01594     bool redirection = false;
01595     KUrl _url(url);
01596     if (_url.path().isEmpty())
01597     {
01598       redirection = true;
01599       _url.setPath("/");
01600     }
01601 
01602     TransferJob* job = precheckHttpPost(_url, ioDevice, flags);
01603     if (job)
01604         return job;
01605 
01606     // If no size is specified and the QIODevice is not a sequential one,
01607     // attempt to obtain the size information from it.
01608     Q_ASSERT(ioDevice);
01609     if (size < 0)
01610         size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1);
01611 
01612     // Send http post command (1), decoded path and encoded query
01613     KIO_ARGS << (int)1 << _url << size;
01614     job = TransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags);
01615 
01616     if (redirection)
01617       QTimer::singleShot(0, job, SLOT(slotPostRedirection()) );
01618 
01619     return job;
01620 }
01621 
01622 TransferJob* KIO::http_delete(const KUrl& url, JobFlags flags)
01623 {
01624     // Send decoded path and encoded query
01625     KIO_ARGS << url;
01626     TransferJob * job = TransferJobPrivate::newJob(url, CMD_DEL, packedArgs,
01627                                                    QByteArray(), flags);
01628     return job;
01629 }
01630 
01631 StoredTransferJob *KIO::storedHttpPost( const QByteArray& postData, const KUrl& url, JobFlags flags )
01632 {
01633     KUrl _url(url);
01634     if (_url.path().isEmpty())
01635     {
01636       _url.setPath("/");
01637     }
01638 
01639     StoredTransferJob* job = precheckHttpPost(_url, postData, flags);
01640     if (job)
01641         return job;
01642 
01643     // Send http post command (1), decoded path and encoded query
01644     KIO_ARGS << (int)1 << _url << static_cast<qint64>(postData.size());
01645     job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags );
01646     return job;
01647 }
01648 
01649 StoredTransferJob *KIO::storedHttpPost( QIODevice* ioDevice, const KUrl& url, qint64 size, JobFlags flags )
01650 {
01651     KUrl _url(url);
01652     if (_url.path().isEmpty())
01653     {
01654       _url.setPath("/");
01655     }
01656 
01657     StoredTransferJob* job = precheckHttpPost(_url, ioDevice, flags);
01658     if (job)
01659         return job;
01660 
01661     // If no size is specified and the QIODevice is not a sequential one,
01662     // attempt to obtain the size information from it.
01663     Q_ASSERT(ioDevice);
01664     if (size < 0)
01665         size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1);
01666 
01667     // Send http post command (1), decoded path and encoded query
01668     KIO_ARGS << (int)1 << _url << size;
01669     job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags );
01670     return job;
01671 }
01672 
01673 // http post got redirected from http://host to http://host/ by TransferJob
01674 // We must do this redirection ourselves because redirections by the
01675 // slave change post jobs into get jobs.
01676 void TransferJobPrivate::slotPostRedirection()
01677 {
01678     Q_Q(TransferJob);
01679     kDebug(7007) << "TransferJob::slotPostRedirection(" << m_url << ")";
01680     // Tell the user about the new url.
01681     emit q->redirection(q, m_url);
01682 }
01683 
01684 
01685 TransferJob *KIO::put( const KUrl& url, int permissions, JobFlags flags )
01686 {
01687     KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions;
01688     return TransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags);
01689 }
01690 
01692 
01693 StoredTransferJob::StoredTransferJob(StoredTransferJobPrivate &dd)
01694     : TransferJob(dd)
01695 {
01696     connect( this, SIGNAL(data(KIO::Job*,QByteArray)),
01697              SLOT(slotStoredData(KIO::Job*,QByteArray)) );
01698     connect( this, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
01699              SLOT(slotStoredDataReq(KIO::Job*,QByteArray&)) );
01700 }
01701 
01702 StoredTransferJob::~StoredTransferJob()
01703 {
01704 }
01705 
01706 void StoredTransferJob::setData( const QByteArray& arr )
01707 {
01708     Q_D(StoredTransferJob);
01709     Q_ASSERT( d->m_data.isNull() ); // check that we're only called once
01710     Q_ASSERT( d->m_uploadOffset == 0 ); // no upload started yet
01711     d->m_data = arr;
01712     setTotalSize( d->m_data.size() );
01713 }
01714 
01715 QByteArray StoredTransferJob::data() const
01716 {
01717     return d_func()->m_data;
01718 }
01719 
01720 void StoredTransferJobPrivate::slotStoredData( KIO::Job *, const QByteArray &data )
01721 {
01722   // check for end-of-data marker:
01723   if ( data.size() == 0 )
01724     return;
01725   unsigned int oldSize = m_data.size();
01726   m_data.resize( oldSize + data.size() );
01727   memcpy( m_data.data() + oldSize, data.data(), data.size() );
01728 }
01729 
01730 void StoredTransferJobPrivate::slotStoredDataReq( KIO::Job *, QByteArray &data )
01731 {
01732   // Inspired from kmail's KMKernel::byteArrayToRemoteFile
01733   // send the data in 64 KB chunks
01734   const int MAX_CHUNK_SIZE = 64*1024;
01735   int remainingBytes = m_data.size() - m_uploadOffset;
01736   if( remainingBytes > MAX_CHUNK_SIZE ) {
01737     // send MAX_CHUNK_SIZE bytes to the receiver (deep copy)
01738     data = QByteArray( m_data.data() + m_uploadOffset, MAX_CHUNK_SIZE );
01739     m_uploadOffset += MAX_CHUNK_SIZE;
01740     //kDebug() << "Sending " << MAX_CHUNK_SIZE << " bytes ("
01741     //                << remainingBytes - MAX_CHUNK_SIZE << " bytes remain)\n";
01742   } else {
01743     // send the remaining bytes to the receiver (deep copy)
01744     data = QByteArray( m_data.data() + m_uploadOffset, remainingBytes );
01745     m_data = QByteArray();
01746     m_uploadOffset = 0;
01747     //kDebug() << "Sending " << remainingBytes << " bytes\n";
01748   }
01749 }
01750 
01751 StoredTransferJob *KIO::storedGet( const KUrl& url, LoadType reload, JobFlags flags )
01752 {
01753     // Send decoded path and encoded query
01754     KIO_ARGS << url;
01755     StoredTransferJob * job = StoredTransferJobPrivate::newJob(url, CMD_GET, packedArgs, QByteArray(), flags);
01756     if (reload == Reload)
01757        job->addMetaData("cache", "reload");
01758     return job;
01759 }
01760 
01761 StoredTransferJob *KIO::storedPut( const QByteArray& arr, const KUrl& url, int permissions,
01762                                    JobFlags flags )
01763 {
01764     KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions;
01765     StoredTransferJob * job = StoredTransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags );
01766     job->setData( arr );
01767     return job;
01768 }
01769 
01771 
01772 class KIO::MimetypeJobPrivate: public KIO::TransferJobPrivate
01773 {
01774 public:
01775     MimetypeJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
01776         : TransferJobPrivate(url, command, packedArgs, QByteArray())
01777         {}
01778 
01779     Q_DECLARE_PUBLIC(MimetypeJob)
01780 
01781     static inline MimetypeJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs,
01782                                       JobFlags flags)
01783     {
01784         MimetypeJob *job = new MimetypeJob(*new MimetypeJobPrivate(url, command, packedArgs));
01785         job->setUiDelegate(new JobUiDelegate);
01786         if (!(flags & HideProgressInfo)) {
01787             KIO::getJobTracker()->registerJob(job);
01788             emitStating(job, url);
01789         }
01790         return job;
01791     }
01792 };
01793 
01794 MimetypeJob::MimetypeJob(MimetypeJobPrivate &dd)
01795     : TransferJob(dd)
01796 {
01797 }
01798 
01799 MimetypeJob::~MimetypeJob()
01800 {
01801 }
01802 
01803 void MimetypeJob::slotFinished( )
01804 {
01805     Q_D(MimetypeJob);
01806     //kDebug(7007);
01807     if ( error() == KIO::ERR_IS_DIRECTORY )
01808     {
01809         // It is in fact a directory. This happens when HTTP redirects to FTP.
01810         // Due to the "protocol doesn't support listing" code in KRun, we
01811         // assumed it was a file.
01812         kDebug(7007) << "It is in fact a directory!";
01813         d->m_mimetype = QString::fromLatin1("inode/directory");
01814         emit TransferJob::mimetype( this, d->m_mimetype );
01815         setError( 0 );
01816     }
01817 
01818     if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && !error() )
01819     {
01820         //kDebug(7007) << "Redirection to " << m_redirectionURL;
01821         if (queryMetaData("permanent-redirect")=="true")
01822             emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
01823 
01824         if (d->m_redirectionHandlingEnabled)
01825         {
01826             d->staticData.truncate(0);
01827             d->m_internalSuspended = false;
01828             d->m_packedArgs.truncate(0);
01829             QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
01830             stream << d->m_redirectionURL;
01831 
01832             d->restartAfterRedirection(&d->m_redirectionURL);
01833             return;
01834         }
01835     }
01836 
01837     // Return slave to the scheduler
01838     TransferJob::slotFinished();
01839 }
01840 
01841 MimetypeJob *KIO::mimetype(const KUrl& url, JobFlags flags)
01842 {
01843     KIO_ARGS << url;
01844     return MimetypeJobPrivate::newJob(url, CMD_MIMETYPE, packedArgs, flags);
01845 }
01846 
01848 
01849 class KIO::DirectCopyJobPrivate: public KIO::SimpleJobPrivate
01850 {
01851 public:
01852     DirectCopyJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
01853         : SimpleJobPrivate(url, command, packedArgs)
01854         {}
01855 
01862     virtual void start(Slave *slave);
01863 
01864     Q_DECLARE_PUBLIC(DirectCopyJob)
01865 };
01866 
01867 DirectCopyJob::DirectCopyJob(const KUrl &url, const QByteArray &packedArgs)
01868     : SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs))
01869 {
01870     setUiDelegate(new JobUiDelegate);
01871 }
01872 
01873 DirectCopyJob::~DirectCopyJob()
01874 {
01875 }
01876 
01877 void DirectCopyJobPrivate::start( Slave* slave )
01878 {
01879     Q_Q(DirectCopyJob);
01880     q->connect( slave, SIGNAL(canResume(KIO::filesize_t)),
01881              SLOT(slotCanResume(KIO::filesize_t)) );
01882     SimpleJobPrivate::start(slave);
01883 }
01884 
01885 void DirectCopyJob::slotCanResume( KIO::filesize_t offset )
01886 {
01887     emit canResume(this, offset);
01888 }
01889 
01891 
01893 class KIO::FileCopyJobPrivate: public KIO::JobPrivate
01894 {
01895 public:
01896     FileCopyJobPrivate(const KUrl& src, const KUrl& dest, int permissions,
01897                        bool move, JobFlags flags)
01898         : m_sourceSize(filesize_t(-1)), m_src(src), m_dest(dest), m_moveJob(0), m_copyJob(0), m_delJob(0),
01899           m_chmodJob(0), m_getJob(0), m_putJob(0), m_permissions(permissions),
01900           m_move(move), m_mustChmod(0), m_flags(flags)
01901         {
01902         }
01903     KIO::filesize_t m_sourceSize;
01904     QDateTime m_modificationTime;
01905     KUrl m_src;
01906     KUrl m_dest;
01907     QByteArray m_buffer;
01908     SimpleJob *m_moveJob;
01909     SimpleJob *m_copyJob;
01910     SimpleJob *m_delJob;
01911     SimpleJob *m_chmodJob;
01912     TransferJob *m_getJob;
01913     TransferJob *m_putJob;
01914     int m_permissions;
01915     bool m_move:1;
01916     bool m_canResume:1;
01917     bool m_resumeAnswerSent:1;
01918     bool m_mustChmod:1;
01919     JobFlags m_flags;
01920 
01921     void startBestCopyMethod();
01922     void startCopyJob();
01923     void startCopyJob(const KUrl &slave_url);
01924     void startRenameJob(const KUrl &slave_url);
01925     void startDataPump();
01926     void connectSubjob( SimpleJob * job );
01927 
01928     void slotStart();
01929     void slotData( KIO::Job *, const QByteArray &data);
01930     void slotDataReq( KIO::Job *, QByteArray &data);
01931     void slotMimetype( KIO::Job*, const QString& type );
01937     void slotProcessedSize( KJob *job, qulonglong size );
01943     void slotTotalSize( KJob *job, qulonglong size );
01949     void slotPercent( KJob *job, unsigned long pct );
01955     void slotCanResume( KIO::Job *job, KIO::filesize_t offset );
01956 
01957     Q_DECLARE_PUBLIC(FileCopyJob)
01958 
01959     static inline FileCopyJob* newJob(const KUrl& src, const KUrl& dest, int permissions, bool move,
01960                                       JobFlags flags)
01961     {
01962         //kDebug(7007) << src << "->" << dest;
01963         FileCopyJob *job = new FileCopyJob(
01964             *new FileCopyJobPrivate(src, dest, permissions, move, flags));
01965         job->setProperty("destUrl", dest.url());
01966         job->setUiDelegate(new JobUiDelegate);
01967         if (!(flags & HideProgressInfo))
01968             KIO::getJobTracker()->registerJob(job);
01969         return job;
01970     }
01971 };
01972 
01973 /*
01974  * The FileCopyJob works according to the famous Bavarian
01975  * 'Alternating Bitburger Protocol': we either drink a beer or we
01976  * we order a beer, but never both at the same time.
01977  * Translated to io-slaves: We alternate between receiving a block of data
01978  * and sending it away.
01979  */
01980 FileCopyJob::FileCopyJob(FileCopyJobPrivate &dd)
01981     : Job(dd)
01982 {
01983     //kDebug(7007);
01984     QTimer::singleShot(0, this, SLOT(slotStart()));
01985 }
01986 
01987 void FileCopyJobPrivate::slotStart()
01988 {
01989     Q_Q(FileCopyJob);
01990     if (!m_move)
01991         JobPrivate::emitCopying( q, m_src, m_dest );
01992     else
01993         JobPrivate::emitMoving( q, m_src, m_dest );
01994 
01995    if ( m_move )
01996    {
01997       // The if() below must be the same as the one in startBestCopyMethod
01998       if ((m_src.protocol() == m_dest.protocol()) &&
01999           (m_src.host() == m_dest.host()) &&
02000           (m_src.port() == m_dest.port()) &&
02001           (m_src.user() == m_dest.user()) &&
02002           (m_src.pass() == m_dest.pass()) &&
02003           !m_src.hasSubUrl() && !m_dest.hasSubUrl())
02004       {
02005          startRenameJob(m_src);
02006          return;
02007       }
02008       else if (m_src.isLocalFile() && KProtocolManager::canRenameFromFile(m_dest))
02009       {
02010          startRenameJob(m_dest);
02011          return;
02012       }
02013       else if (m_dest.isLocalFile() && KProtocolManager::canRenameToFile(m_src))
02014       {
02015          startRenameJob(m_src);
02016          return;
02017       }
02018       // No fast-move available, use copy + del.
02019    }
02020    startBestCopyMethod();
02021 }
02022 
02023 void FileCopyJobPrivate::startBestCopyMethod()
02024 {
02025    if ((m_src.protocol() == m_dest.protocol()) &&
02026        (m_src.host() == m_dest.host()) &&
02027        (m_src.port() == m_dest.port()) &&
02028        (m_src.user() == m_dest.user()) &&
02029        (m_src.pass() == m_dest.pass()) &&
02030        !m_src.hasSubUrl() && !m_dest.hasSubUrl())
02031    {
02032       startCopyJob();
02033    }
02034    else if (m_src.isLocalFile() && KProtocolManager::canCopyFromFile(m_dest))
02035    {
02036       startCopyJob(m_dest);
02037    }
02038    else if (m_dest.isLocalFile() && KProtocolManager::canCopyToFile(m_src) &&
02039             !KIO::Scheduler::isSlaveOnHoldFor(m_src))
02040    {
02041       startCopyJob(m_src);
02042    }
02043    else
02044    {
02045       startDataPump();
02046    }
02047 }
02048 
02049 FileCopyJob::~FileCopyJob()
02050 {
02051 }
02052 
02053 void FileCopyJob::setSourceSize( KIO::filesize_t size )
02054 {
02055     Q_D(FileCopyJob);
02056     d->m_sourceSize = size;
02057     if (size != (KIO::filesize_t) -1)
02058         setTotalAmount(KJob::Bytes, size);
02059 }
02060 
02061 void FileCopyJob::setModificationTime( const QDateTime& mtime )
02062 {
02063     Q_D(FileCopyJob);
02064     d->m_modificationTime = mtime;
02065 }
02066 
02067 KUrl FileCopyJob::srcUrl() const
02068 {
02069     return d_func()->m_src;
02070 }
02071 
02072 KUrl FileCopyJob::destUrl() const
02073 {
02074     return d_func()->m_dest;
02075 }
02076 
02077 void FileCopyJobPrivate::startCopyJob()
02078 {
02079     startCopyJob(m_src);
02080 }
02081 
02082 void FileCopyJobPrivate::startCopyJob(const KUrl &slave_url)
02083 {
02084     Q_Q(FileCopyJob);
02085     //kDebug(7007);
02086     KIO_ARGS << m_src << m_dest << m_permissions << (qint8) (m_flags & Overwrite);
02087     m_copyJob = new DirectCopyJob(slave_url, packedArgs);
02088     q->addSubjob( m_copyJob );
02089     connectSubjob( m_copyJob );
02090     q->connect( m_copyJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)),
02091                 SLOT(slotCanResume(KIO::Job*,KIO::filesize_t)));
02092 }
02093 
02094 void FileCopyJobPrivate::startRenameJob(const KUrl &slave_url)
02095 {
02096     Q_Q(FileCopyJob);
02097     m_mustChmod = true;  // CMD_RENAME by itself doesn't change permissions
02098     KIO_ARGS << m_src << m_dest << (qint8) (m_flags & Overwrite);
02099     m_moveJob = SimpleJobPrivate::newJobNoUi(slave_url, CMD_RENAME, packedArgs);
02100     q->addSubjob( m_moveJob );
02101     connectSubjob( m_moveJob );
02102 }
02103 
02104 void FileCopyJobPrivate::connectSubjob( SimpleJob * job )
02105 {
02106     Q_Q(FileCopyJob);
02107     q->connect( job, SIGNAL(totalSize(KJob*,qulonglong)),
02108                 SLOT(slotTotalSize(KJob*,qulonglong)) );
02109 
02110     q->connect( job, SIGNAL(processedSize(KJob*,qulonglong)),
02111                 SLOT(slotProcessedSize(KJob*,qulonglong)) );
02112 
02113     q->connect( job, SIGNAL(percent(KJob*,ulong)),
02114                 SLOT(slotPercent(KJob*,ulong)) );
02115 
02116 }
02117 
02118 bool FileCopyJob::doSuspend()
02119 {
02120     Q_D(FileCopyJob);
02121     if (d->m_moveJob)
02122         d->m_moveJob->suspend();
02123 
02124     if (d->m_copyJob)
02125         d->m_copyJob->suspend();
02126 
02127     if (d->m_getJob)
02128         d->m_getJob->suspend();
02129 
02130     if (d->m_putJob)
02131         d->m_putJob->suspend();
02132 
02133     Job::doSuspend();
02134     return true;
02135 }
02136 
02137 bool FileCopyJob::doResume()
02138 {
02139     Q_D(FileCopyJob);
02140     if (d->m_moveJob)
02141         d->m_moveJob->resume();
02142 
02143     if (d->m_copyJob)
02144         d->m_copyJob->resume();
02145 
02146     if (d->m_getJob)
02147         d->m_getJob->resume();
02148 
02149     if (d->m_putJob)
02150         d->m_putJob->resume();
02151 
02152     Job::doResume();
02153     return true;
02154 }
02155 
02156 void FileCopyJobPrivate::slotProcessedSize( KJob *, qulonglong size )
02157 {
02158     Q_Q(FileCopyJob);
02159     q->setProcessedAmount(KJob::Bytes, size);
02160 }
02161 
02162 void FileCopyJobPrivate::slotTotalSize( KJob*, qulonglong size )
02163 {
02164     Q_Q(FileCopyJob);
02165     if (size != q->totalAmount(KJob::Bytes))
02166     {
02167         q->setTotalAmount(KJob::Bytes, size);
02168     }
02169 }
02170 
02171 void FileCopyJobPrivate::slotPercent( KJob*, unsigned long pct )
02172 {
02173   Q_Q(FileCopyJob);
02174   if ( pct > q->percent() ) {
02175       q->setPercent( pct );
02176   }
02177 }
02178 
02179 void FileCopyJobPrivate::startDataPump()
02180 {
02181     Q_Q(FileCopyJob);
02182     //kDebug(7007);
02183 
02184     m_canResume = false;
02185     m_resumeAnswerSent = false;
02186     m_getJob = 0L; // for now
02187     m_putJob = put( m_dest, m_permissions, (m_flags | HideProgressInfo) /* no GUI */);
02188     //kDebug(7007) << "m_putJob=" << m_putJob << "m_dest=" << m_dest;
02189     if ( m_modificationTime.isValid() ) {
02190         m_putJob->setModificationTime( m_modificationTime );
02191     }
02192 
02193     // The first thing the put job will tell us is whether we can
02194     // resume or not (this is always emitted)
02195     q->connect( m_putJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)),
02196                 SLOT(slotCanResume(KIO::Job*,KIO::filesize_t)));
02197     q->connect( m_putJob, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
02198                 SLOT(slotDataReq(KIO::Job*,QByteArray&)));
02199     q->addSubjob( m_putJob );
02200 }
02201 
02202 void FileCopyJobPrivate::slotCanResume( KIO::Job* job, KIO::filesize_t offset )
02203 {
02204     Q_Q(FileCopyJob);
02205     if ( job == m_putJob || job == m_copyJob )
02206     {
02207         //kDebug(7007) << "'can resume' from PUT job. offset=" << KIO::number(offset);
02208         if (offset)
02209         {
02210             RenameDialog_Result res = R_RESUME;
02211 
02212             if (!KProtocolManager::autoResume() && !(m_flags & Overwrite))
02213             {
02214                 QString newPath;
02215                 KIO::Job* job = ( q->parentJob() ) ? q->parentJob() : q;
02216                 // Ask confirmation about resuming previous transfer
02217                 res = ui()->askFileRename(
02218                       job, i18n("File Already Exists"),
02219                       m_src.url(),
02220                       m_dest.url(),
02221                       (RenameDialog_Mode) (M_OVERWRITE | M_RESUME | M_NORENAME), newPath,
02222                       m_sourceSize, offset );
02223             }
02224 
02225             if ( res == R_OVERWRITE || (m_flags & Overwrite) )
02226               offset = 0;
02227             else if ( res == R_CANCEL )
02228             {
02229                 if ( job == m_putJob ) {
02230                     m_putJob->kill( FileCopyJob::Quietly );
02231                     q->removeSubjob(m_putJob);
02232                     m_putJob = 0;
02233                 } else {
02234                     m_copyJob->kill( FileCopyJob::Quietly );
02235                     q->removeSubjob(m_copyJob);
02236                     m_copyJob = 0;
02237                 }
02238                 q->setError( ERR_USER_CANCELED );
02239                 q->emitResult();
02240                 return;
02241             }
02242         }
02243         else
02244             m_resumeAnswerSent = true; // No need for an answer
02245 
02246         if ( job == m_putJob )
02247         {
02248             m_getJob = KIO::get( m_src, NoReload, HideProgressInfo /* no GUI */ );
02249             //kDebug(7007) << "m_getJob=" << m_getJob << m_src;
02250             m_getJob->addMetaData( "errorPage", "false" );
02251             m_getJob->addMetaData( "AllowCompressedPage", "false" );
02252             // Set size in subjob. This helps if the slave doesn't emit totalSize.
02253             if ( m_sourceSize != (KIO::filesize_t)-1 )
02254                 m_getJob->setTotalAmount(KJob::Bytes, m_sourceSize);
02255             if (offset)
02256             {
02257                 //kDebug(7007) << "Setting metadata for resume to" << (unsigned long) offset;
02258                 // TODO KDE4: rename to seek or offset and document it
02259                 // This isn't used only for resuming, but potentially also for extracting (#72302).
02260                 m_getJob->addMetaData( "resume", KIO::number(offset) );
02261 
02262                 // Might or might not get emitted
02263                 q->connect( m_getJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)),
02264                             SLOT(slotCanResume(KIO::Job*,KIO::filesize_t)));
02265             }
02266             jobSlave(m_putJob)->setOffset( offset );
02267 
02268             m_putJob->d_func()->internalSuspend();
02269             q->addSubjob( m_getJob );
02270             connectSubjob( m_getJob ); // Progress info depends on get
02271             m_getJob->d_func()->internalResume(); // Order a beer
02272 
02273             q->connect( m_getJob, SIGNAL(data(KIO::Job*,QByteArray)),
02274                         SLOT(slotData(KIO::Job*,QByteArray)) );
02275             q->connect( m_getJob, SIGNAL(mimetype(KIO::Job*,QString)),
02276                         SLOT(slotMimetype(KIO::Job*,QString)) );
02277         }
02278         else // copyjob
02279         {
02280             jobSlave(m_copyJob)->sendResumeAnswer( offset != 0 );
02281         }
02282     }
02283     else if ( job == m_getJob )
02284     {
02285         // Cool, the get job said ok, we can resume
02286         m_canResume = true;
02287         //kDebug(7007) << "'can resume' from the GET job -> we can resume";
02288 
02289         jobSlave(m_getJob)->setOffset( jobSlave(m_putJob)->offset() );
02290     }
02291     else
02292         kWarning(7007) << "unknown job=" << job
02293                         << "m_getJob=" << m_getJob << "m_putJob=" << m_putJob;
02294 }
02295 
02296 void FileCopyJobPrivate::slotData( KIO::Job * , const QByteArray &data)
02297 {
02298    //kDebug(7007) << "data size:" << data.size();
02299    Q_ASSERT(m_putJob);
02300    if (!m_putJob) return; // Don't crash
02301    m_getJob->d_func()->internalSuspend();
02302    m_putJob->d_func()->internalResume(); // Drink the beer
02303    m_buffer += data;
02304 
02305    // On the first set of data incoming, we tell the "put" slave about our
02306    // decision about resuming
02307    if (!m_resumeAnswerSent)
02308    {
02309        m_resumeAnswerSent = true;
02310        //kDebug(7007) << "(first time) -> send resume answer " << m_canResume;
02311        jobSlave(m_putJob)->sendResumeAnswer( m_canResume );
02312    }
02313 }
02314 
02315 void FileCopyJobPrivate::slotDataReq( KIO::Job * , QByteArray &data)
02316 {
02317    Q_Q(FileCopyJob);
02318    //kDebug(7007);
02319    if (!m_resumeAnswerSent && !m_getJob) {
02320        // This can't happen
02321        q->setError( ERR_INTERNAL );
02322        q->setErrorText( "'Put' job did not send canResume or 'Get' job did not send data!" );
02323        m_putJob->kill( FileCopyJob::Quietly );
02324        q->removeSubjob(m_putJob);
02325        m_putJob = 0;
02326        q->emitResult();
02327        return;
02328    }
02329    if (m_getJob)
02330    {
02331        m_getJob->d_func()->internalResume(); // Order more beer
02332        m_putJob->d_func()->internalSuspend();
02333    }
02334    data = m_buffer;
02335    m_buffer = QByteArray();
02336 }
02337 
02338 void FileCopyJobPrivate::slotMimetype( KIO::Job*, const QString& type )
02339 {
02340     Q_Q(FileCopyJob);
02341     emit q->mimetype( q, type );
02342 }
02343 
02344 void FileCopyJob::slotResult( KJob *job)
02345 {
02346    Q_D(FileCopyJob);
02347    //kDebug(7007) << "this=" << this << "job=" << job;
02348    removeSubjob(job);
02349    // Did job have an error ?
02350    if ( job->error() )
02351    {
02352       if ((job == d->m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
02353       {
02354          d->m_moveJob = 0;
02355          d->startBestCopyMethod();
02356          return;
02357       }
02358       else if ((job == d->m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
02359       {
02360          d->m_copyJob = 0;
02361          d->startDataPump();
02362          return;
02363       }
02364       else if (job == d->m_getJob)
02365       {
02366         d->m_getJob = 0L;
02367         if (d->m_putJob)
02368         {
02369           d->m_putJob->kill( Quietly );
02370           removeSubjob( d->m_putJob );
02371         }
02372       }
02373       else if (job == d->m_putJob)
02374       {
02375         d->m_putJob = 0L;
02376         if (d->m_getJob)
02377         {
02378           d->m_getJob->kill( Quietly );
02379           removeSubjob( d->m_getJob );
02380         }
02381       }
02382       setError( job->error() );
02383       setErrorText( job->errorText() );
02384       emitResult();
02385       return;
02386    }
02387 
02388    if (d->m_mustChmod)
02389    {
02390        // If d->m_permissions == -1, keep the default permissions
02391        if (d->m_permissions != -1)
02392        {
02393            d->m_chmodJob = chmod(d->m_dest, d->m_permissions);
02394        }
02395        d->m_mustChmod = false;
02396    }
02397 
02398    if (job == d->m_moveJob)
02399    {
02400       d->m_moveJob = 0; // Finished
02401    }
02402 
02403    if (job == d->m_copyJob)
02404    {
02405       d->m_copyJob = 0;
02406       if (d->m_move)
02407       {
02408          d->m_delJob = file_delete( d->m_src, HideProgressInfo/*no GUI*/ ); // Delete source
02409          addSubjob(d->m_delJob);
02410       }
02411    }
02412 
02413    if (job == d->m_getJob)
02414    {
02415        //kDebug(7007) << "m_getJob finished";
02416       d->m_getJob = 0; // No action required
02417       if (d->m_putJob)
02418           d->m_putJob->d_func()->internalResume();
02419    }
02420 
02421    if (job == d->m_putJob)
02422    {
02423        //kDebug(7007) << "m_putJob finished";
02424       d->m_putJob = 0;
02425       if (d->m_getJob)
02426       {
02427           // The get job is still running, probably after emitting data(QByteArray())
02428           // and before we receive its finished().
02429          d->m_getJob->d_func()->internalResume();
02430       }
02431       if (d->m_move)
02432       {
02433          d->m_delJob = file_delete( d->m_src, HideProgressInfo/*no GUI*/ ); // Delete source
02434          addSubjob(d->m_delJob);
02435       }
02436    }
02437 
02438    if (job == d->m_delJob)
02439    {
02440       d->m_delJob = 0; // Finished
02441    }
02442 
02443    if (job == d->m_chmodJob)
02444    {
02445        d->m_chmodJob = 0; // Finished
02446    }
02447 
02448    if ( !hasSubjobs() )
02449        emitResult();
02450 }
02451 
02452 FileCopyJob *KIO::file_copy( const KUrl& src, const KUrl& dest, int permissions,
02453                              JobFlags flags )
02454 {
02455     return FileCopyJobPrivate::newJob(src, dest, permissions, false, flags);
02456 }
02457 
02458 FileCopyJob *KIO::file_move( const KUrl& src, const KUrl& dest, int permissions,
02459                              JobFlags flags )
02460 {
02461     return FileCopyJobPrivate::newJob(src, dest, permissions, true, flags);
02462 }
02463 
02464 SimpleJob *KIO::file_delete( const KUrl& src, JobFlags flags )
02465 {
02466     KIO_ARGS << src << qint8(true); // isFile
02467     return SimpleJobPrivate::newJob(src, CMD_DEL, packedArgs, flags);
02468 }
02469 
02471 
02472 class KIO::ListJobPrivate: public KIO::SimpleJobPrivate
02473 {
02474 public:
02475     ListJobPrivate(const KUrl& url, bool _recursive, const QString &_prefix, bool _includeHidden)
02476         : SimpleJobPrivate(url, CMD_LISTDIR, QByteArray()),
02477           recursive(_recursive), includeHidden(_includeHidden),
02478           prefix(_prefix), m_processedEntries(0)
02479     {}
02480     bool recursive;
02481     bool includeHidden;
02482     QString prefix;
02483     unsigned long m_processedEntries;
02484     KUrl m_redirectionURL;
02485 
02492     virtual void start( Slave *slave );
02493 
02494     void slotListEntries( const KIO::UDSEntryList& list );
02495     void slotRedirection( const KUrl &url );
02496     void gotEntries( KIO::Job * subjob, const KIO::UDSEntryList& list );
02497 
02498     Q_DECLARE_PUBLIC(ListJob)
02499 
02500     static inline ListJob *newJob(const KUrl& u, bool _recursive, const QString &_prefix,
02501                                   bool _includeHidden, JobFlags flags = HideProgressInfo)
02502     {
02503         ListJob *job = new ListJob(*new ListJobPrivate(u, _recursive, _prefix, _includeHidden));
02504         job->setUiDelegate(new JobUiDelegate);
02505         if (!(flags & HideProgressInfo))
02506             KIO::getJobTracker()->registerJob(job);
02507         return job;
02508     }
02509     static inline ListJob *newJobNoUi(const KUrl& u, bool _recursive, const QString &_prefix,
02510                                       bool _includeHidden)
02511     {
02512         return new ListJob(*new ListJobPrivate(u, _recursive, _prefix, _includeHidden));
02513     }
02514 };
02515 
02516 ListJob::ListJob(ListJobPrivate &dd)
02517     : SimpleJob(dd)
02518 {
02519     Q_D(ListJob);
02520     // We couldn't set the args when calling the parent constructor,
02521     // so do it now.
02522     QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
02523     stream << d->m_url;
02524 }
02525 
02526 ListJob::~ListJob()
02527 {
02528 }
02529 
02530 void ListJobPrivate::slotListEntries( const KIO::UDSEntryList& list )
02531 {
02532     Q_Q(ListJob);
02533     // Emit progress info (takes care of emit processedSize and percent)
02534     m_processedEntries += list.count();
02535     slotProcessedSize( m_processedEntries );
02536 
02537     if (recursive) {
02538         UDSEntryList::ConstIterator it = list.begin();
02539         const UDSEntryList::ConstIterator end = list.end();
02540 
02541         for (; it != end; ++it) {
02542 
02543             const UDSEntry& entry = *it;
02544 
02545             KUrl itemURL;
02546             // const UDSEntry::ConstIterator end2 = entry.end();
02547             // UDSEntry::ConstIterator it2 = entry.find( KIO::UDSEntry::UDS_URL );
02548             // if ( it2 != end2 )
02549             if (entry.contains(KIO::UDSEntry::UDS_URL))
02550                 // itemURL = it2.value().toString();
02551                 itemURL = entry.stringValue(KIO::UDSEntry::UDS_URL);
02552             else { // no URL, use the name
02553                 itemURL = q->url();
02554                 const QString fileName = entry.stringValue(KIO::UDSEntry::UDS_NAME);
02555                 Q_ASSERT(!fileName.isEmpty()); // we'll recurse forever otherwise :)
02556                 itemURL.addPath(fileName);
02557             }
02558 
02559             if (entry.isDir() && !entry.isLink()) {
02560                 const QString filename = itemURL.fileName();
02561                 // skip hidden dirs when listing if requested
02562                 if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) {
02563                     ListJob *job = ListJobPrivate::newJobNoUi(itemURL,
02564                                                true /*recursive*/,
02565                                                prefix + filename + '/',
02566                                                includeHidden);
02567                     Scheduler::setJobPriority(job, 1);
02568                     q->connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
02569                                SLOT(gotEntries(KIO::Job*,KIO::UDSEntryList)));
02570                     q->addSubjob(job);
02571                 }
02572             }
02573         }
02574     }
02575 
02576     // Not recursive, or top-level of recursive listing : return now (send . and .. as well)
02577     // exclusion of hidden files also requires the full sweep, but the case for full-listing
02578     // a single dir is probably common enough to justify the shortcut
02579     if (prefix.isNull() && includeHidden) {
02580         emit q->entries(q, list);
02581     } else {
02582         // cull the unwanted hidden dirs and/or parent dir references from the listing, then emit that
02583         UDSEntryList newlist;
02584 
02585         UDSEntryList::const_iterator it = list.begin();
02586         const UDSEntryList::const_iterator end = list.end();
02587         for (; it != end; ++it) {
02588 
02589             // Modify the name in the UDSEntry
02590             UDSEntry newone = *it;
02591             const QString filename = newone.stringValue( KIO::UDSEntry::UDS_NAME );
02592             // Avoid returning entries like subdir/. and subdir/.., but include . and .. for
02593             // the toplevel dir, and skip hidden files/dirs if that was requested
02594             if (  (prefix.isNull() || (filename != ".." && filename != ".") )
02595                   && (includeHidden || (filename[0] != '.') )  )
02596             {
02597                 // ## Didn't find a way to use the iterator instead of re-doing a key lookup
02598                 newone.insert( KIO::UDSEntry::UDS_NAME, prefix + filename );
02599                 newlist.append(newone);
02600             }
02601         }
02602 
02603         emit q->entries(q, newlist);
02604     }
02605 }
02606 
02607 void ListJobPrivate::gotEntries(KIO::Job *, const KIO::UDSEntryList& list )
02608 {
02609     // Forward entries received by subjob - faking we received them ourselves
02610     Q_Q(ListJob);
02611     emit q->entries(q, list);
02612 }
02613 
02614 void ListJob::slotResult( KJob * job )
02615 {
02616     if (job->error()) {
02617     // If we can't list a subdir, the result is still ok
02618     // This is why we override KCompositeJob::slotResult - to not set
02619     // an error on parent job.
02620     // Let's emit a signal about this though
02621     emit subError(this, static_cast<KIO::ListJob*>(job));
02622     }
02623     removeSubjob(job);
02624     if (!hasSubjobs())
02625         emitResult();
02626 }
02627 
02628 void ListJobPrivate::slotRedirection( const KUrl & url )
02629 {
02630     Q_Q(ListJob);
02631     if (!KAuthorized::authorizeUrlAction("redirect", m_url, url))
02632     {
02633         kWarning(7007) << "ListJob: Redirection from " << m_url << " to " << url << " REJECTED!";
02634         return;
02635     }
02636     m_redirectionURL = url; // We'll remember that when the job finishes
02637     emit q->redirection( q, m_redirectionURL );
02638 }
02639 
02640 void ListJob::slotFinished()
02641 {
02642     Q_D(ListJob);
02643 
02644     // Support for listing archives as directories
02645     if ( error() == KIO::ERR_IS_FILE && d->m_url.isLocalFile() ) {
02646         KMimeType::Ptr ptr = KMimeType::findByUrl( d->m_url, 0, true, true );
02647         if ( ptr ) {
02648             QString proto = ptr->property("X-KDE-LocalProtocol").toString();
02649             if ( !proto.isEmpty() && KProtocolInfo::isKnownProtocol( proto) ) {
02650                 d->m_redirectionURL = d->m_url;
02651                 d->m_redirectionURL.setProtocol( proto );
02652                 setError( 0 );
02653                 emit redirection(this,d->m_redirectionURL);
02654             }
02655         }
02656     }
02657 
02658     if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && !error() ) {
02659 
02660         //kDebug(7007) << "Redirection to " << d->m_redirectionURL;
02661         if (queryMetaData("permanent-redirect")=="true")
02662             emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
02663 
02664         if ( d->m_redirectionHandlingEnabled ) {
02665             d->m_packedArgs.truncate(0);
02666             QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
02667             stream << d->m_redirectionURL;
02668 
02669             d->restartAfterRedirection(&d->m_redirectionURL);
02670             return;
02671         }
02672     }
02673 
02674     // Return slave to the scheduler
02675     SimpleJob::slotFinished();
02676 }
02677 
02678 void ListJob::slotMetaData( const KIO::MetaData &_metaData)
02679 {
02680     Q_D(ListJob);
02681     SimpleJob::slotMetaData(_metaData);
02682     storeSSLSessionFromJob(d->m_redirectionURL);
02683 }
02684 
02685 ListJob *KIO::listDir( const KUrl& url, JobFlags flags, bool includeHidden )
02686 {
02687     return ListJobPrivate::newJob(url, false, QString(), includeHidden, flags);
02688 }
02689 
02690 ListJob *KIO::listRecursive( const KUrl& url, JobFlags flags, bool includeHidden )
02691 {
02692     return ListJobPrivate::newJob(url, true, QString(), includeHidden, flags);
02693 }
02694 
02695 void ListJob::setUnrestricted(bool unrestricted)
02696 {
02697     Q_D(ListJob);
02698     if (unrestricted)
02699         d->m_extraFlags |= JobPrivate::EF_ListJobUnrestricted;
02700     else
02701         d->m_extraFlags &= ~JobPrivate::EF_ListJobUnrestricted;
02702 }
02703 
02704 void ListJobPrivate::start(Slave *slave)
02705 {
02706     Q_Q(ListJob);
02707     if (!KAuthorized::authorizeUrlAction("list", m_url, m_url) &&
02708         !(m_extraFlags & EF_ListJobUnrestricted))
02709     {
02710         q->setError( ERR_ACCESS_DENIED );
02711         q->setErrorText( m_url.url() );
02712         QTimer::singleShot(0, q, SLOT(slotFinished()) );
02713         return;
02714     }
02715     q->connect( slave, SIGNAL(listEntries(KIO::UDSEntryList)),
02716              SLOT(slotListEntries(KIO::UDSEntryList)));
02717     q->connect( slave, SIGNAL(totalSize(KIO::filesize_t)),
02718              SLOT(slotTotalSize(KIO::filesize_t)) );
02719     q->connect( slave, SIGNAL(redirection(KUrl)),
02720              SLOT(slotRedirection(KUrl)) );
02721 
02722     SimpleJobPrivate::start(slave);
02723 }
02724 
02725 const KUrl& ListJob::redirectionUrl() const
02726 {
02727     return d_func()->m_redirectionURL;
02728 }
02729 
02731 
02732 class KIO::MultiGetJobPrivate: public KIO::TransferJobPrivate
02733 {
02734 public:
02735     MultiGetJobPrivate(const KUrl& url)
02736         : TransferJobPrivate(url, 0, QByteArray(), QByteArray()),
02737           m_currentEntry( 0, KUrl(), MetaData() )
02738     {}
02739     struct GetRequest {
02740         GetRequest(long _id, const KUrl &_url, const MetaData &_metaData)
02741             : id(_id), url(_url), metaData(_metaData) { }
02742         long id;
02743         KUrl url;
02744         MetaData metaData;
02745 
02746         inline bool operator==( const GetRequest& req ) const
02747             { return req.id == id; }
02748     };
02749     typedef QLinkedList<GetRequest> RequestQueue;
02750 
02751     RequestQueue m_waitQueue;
02752     RequestQueue m_activeQueue;
02753     GetRequest m_currentEntry;
02754     bool b_multiGetActive;
02755 
02762     virtual void start(Slave *slave);
02763 
02764     bool findCurrentEntry();
02765     void flushQueue(QLinkedList<GetRequest> &queue);
02766 
02767     Q_DECLARE_PUBLIC(MultiGetJob)
02768 
02769     static inline MultiGetJob *newJob(const KUrl &url)
02770     {
02771         MultiGetJob *job = new MultiGetJob(*new MultiGetJobPrivate(url));
02772         job->setUiDelegate(new JobUiDelegate);
02773         return job;
02774     }
02775 };
02776 
02777 MultiGetJob::MultiGetJob(MultiGetJobPrivate &dd)
02778     : TransferJob(dd)
02779 {
02780 }
02781 
02782 MultiGetJob::~MultiGetJob()
02783 {
02784 }
02785 
02786 void MultiGetJob::get(long id, const KUrl &url, const MetaData &metaData)
02787 {
02788    Q_D(MultiGetJob);
02789    MultiGetJobPrivate::GetRequest entry(id, url, metaData);
02790    entry.metaData["request-id"] = QString::number(id);
02791    d->m_waitQueue.append(entry);
02792 }
02793 
02794 void MultiGetJobPrivate::flushQueue(RequestQueue &queue)
02795 {
02796    // Use multi-get
02797    // Scan all jobs in m_waitQueue
02798    RequestQueue::iterator wqit = m_waitQueue.begin();
02799    const RequestQueue::iterator wqend = m_waitQueue.end();
02800    while ( wqit != wqend )
02801    {
02802       const GetRequest& entry = *wqit;
02803       if ((m_url.protocol() == entry.url.protocol()) &&
02804           (m_url.host() == entry.url.host()) &&
02805           (m_url.port() == entry.url.port()) &&
02806           (m_url.user() == entry.url.user()))
02807       {
02808          queue.append( entry );
02809          wqit = m_waitQueue.erase( wqit );
02810       }
02811       else
02812       {
02813          ++wqit;
02814       }
02815    }
02816    // Send number of URLs, (URL, metadata)*
02817    KIO_ARGS << (qint32) queue.count();
02818    RequestQueue::const_iterator qit = queue.begin();
02819    const RequestQueue::const_iterator qend = queue.end();
02820    for( ; qit != qend; ++qit )
02821    {
02822       stream << (*qit).url << (*qit).metaData;
02823    }
02824    m_packedArgs = packedArgs;
02825    m_command = CMD_MULTI_GET;
02826    m_outgoingMetaData.clear();
02827 }
02828 
02829 void MultiGetJobPrivate::start(Slave *slave)
02830 {
02831    // Add first job from m_waitQueue and add it to m_activeQueue
02832    GetRequest entry = m_waitQueue.takeFirst();
02833    m_activeQueue.append(entry);
02834 
02835    m_url = entry.url;
02836 
02837    if (!entry.url.protocol().startsWith(QLatin1String("http")))
02838    {
02839       // Use normal get
02840       KIO_ARGS << entry.url;
02841       m_packedArgs = packedArgs;
02842       m_outgoingMetaData = entry.metaData;
02843       m_command = CMD_GET;
02844       b_multiGetActive = false;
02845    }
02846    else
02847    {
02848       flushQueue(m_activeQueue);
02849       b_multiGetActive = true;
02850    }
02851 
02852    TransferJobPrivate::start(slave); // Anything else to do??
02853 }
02854 
02855 bool MultiGetJobPrivate::findCurrentEntry()
02856 {
02857    if (b_multiGetActive)
02858    {
02859       long id = m_incomingMetaData["request-id"].toLong();
02860       RequestQueue::const_iterator qit = m_activeQueue.begin();
02861       const RequestQueue::const_iterator qend = m_activeQueue.end();
02862       for( ; qit != qend; ++qit )
02863       {
02864          if ((*qit).id == id)
02865          {
02866             m_currentEntry = *qit;
02867             return true;
02868          }
02869       }
02870       m_currentEntry.id = 0;
02871       return false;
02872    }
02873    else
02874    {
02875       if ( m_activeQueue.isEmpty() )
02876         return false;
02877       m_currentEntry = m_activeQueue.first();
02878       return true;
02879    }
02880 }
02881 
02882 void MultiGetJob::slotRedirection( const KUrl &url)
02883 {
02884   Q_D(MultiGetJob);
02885   if (!d->findCurrentEntry()) return; // Error
02886   if (!KAuthorized::authorizeUrlAction("redirect", d->m_url, url))
02887   {
02888      kWarning(7007) << "MultiGetJob: Redirection from " << d->m_currentEntry.url << " to " << url << " REJECTED!";
02889      return;
02890   }
02891   d->m_redirectionURL = url;
02892   get(d->m_currentEntry.id, d->m_redirectionURL, d->m_currentEntry.metaData); // Try again
02893 }
02894 
02895 
02896 void MultiGetJob::slotFinished()
02897 {
02898   Q_D(MultiGetJob);
02899   if (!d->findCurrentEntry()) return;
02900   if (d->m_redirectionURL.isEmpty())
02901   {
02902      // No redirection, tell the world that we are finished.
02903      emit result(d->m_currentEntry.id);
02904   }
02905   d->m_redirectionURL = KUrl();
02906   setError( 0 );
02907   d->m_incomingMetaData.clear();
02908   d->m_activeQueue.removeAll(d->m_currentEntry);
02909   if (d->m_activeQueue.count() == 0)
02910   {
02911      if (d->m_waitQueue.count() == 0)
02912      {
02913         // All done
02914         TransferJob::slotFinished();
02915      }
02916      else
02917      {
02918         // return slave to pool
02919         // fetch new slave for first entry in d->m_waitQueue and call start
02920         // again.
02921         d->slaveDone();
02922 
02923         d->m_url = d->m_waitQueue.first().url;
02924         if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) {
02925             Scheduler::doJob(this);
02926         }
02927      }
02928   }
02929 }
02930 
02931 void MultiGetJob::slotData( const QByteArray &_data)
02932 {
02933     Q_D(MultiGetJob);
02934     if(d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error())
02935         emit data(d->m_currentEntry.id, _data);
02936 }
02937 
02938 void MultiGetJob::slotMimetype( const QString &_mimetype )
02939 {
02940   Q_D(MultiGetJob);
02941   if (d->b_multiGetActive)
02942   {
02943      MultiGetJobPrivate::RequestQueue newQueue;
02944      d->flushQueue(newQueue);
02945      if (!newQueue.isEmpty())
02946      {
02947         d->m_activeQueue += newQueue;
02948         d->m_slave->send( d->m_command, d->m_packedArgs );
02949      }
02950   }
02951   if (!d->findCurrentEntry()) return; // Error, unknown request!
02952   emit mimetype(d->m_currentEntry.id, _mimetype);
02953 }
02954 
02955 MultiGetJob *KIO::multi_get(long id, const KUrl &url, const MetaData &metaData)
02956 {
02957     MultiGetJob * job = MultiGetJobPrivate::newJob(url);
02958     job->get(id, url, metaData);
02959     return job;
02960 }
02961 
02962 class KIO::SpecialJobPrivate: public TransferJobPrivate
02963 {
02964     SpecialJobPrivate(const KUrl& url, int command,
02965                              const QByteArray &packedArgs,
02966                              const QByteArray &_staticData)
02967         : TransferJobPrivate(url, command, packedArgs, _staticData)
02968     {}
02969 };
02970 
02971 SpecialJob::SpecialJob(const KUrl &url, const QByteArray &packedArgs)
02972     : TransferJob(*new TransferJobPrivate(url, CMD_SPECIAL, packedArgs, QByteArray()))
02973 {
02974 }
02975 
02976 SpecialJob::~SpecialJob()
02977 {
02978 }
02979 
02980 void SpecialJob::setArguments(const QByteArray &data)
02981 {
02982     Q_D(SpecialJob);
02983     d->m_packedArgs = data;
02984 }
02985 
02986 QByteArray SpecialJob::arguments() const
02987 {
02988     return d_func()->m_packedArgs;
02989 }
02990 
02991 // Never defined, never used - what's this code about?
02992 #ifdef CACHE_INFO
02993 CacheInfo::CacheInfo(const KUrl &url)
02994 {
02995     m_url = url;
02996 }
02997 
02998 QString CacheInfo::cachedFileName()
02999 {
03000    const QChar separator = '_';
03001 
03002    QString CEF = m_url.path();
03003 
03004    int p = CEF.find('/');
03005 
03006    while(p != -1)
03007    {
03008       CEF[p] = separator;
03009       p = CEF.find('/', p);
03010    }
03011 
03012    QString host = m_url.host().toLower();
03013    CEF = host + CEF + '_';
03014 
03015    QString dir = KProtocolManager::cacheDir();
03016    if (dir[dir.length()-1] != '/')
03017       dir += '/';
03018 
03019    int l = m_url.host().length();
03020    for(int i = 0; i < l; i++)
03021    {
03022       if (host[i].isLetter() && (host[i] != 'w'))
03023       {
03024          dir += host[i];
03025          break;
03026       }
03027    }
03028    if (dir[dir.length()-1] == '/')
03029       dir += '0';
03030 
03031    unsigned long hash = 0x00000000;
03032    QString u = m_url.url().toLatin1();
03033    for(int i = u.length(); i--;)
03034    {
03035       hash = (hash * 12211 + u[i]) % 2147483563;
03036    }
03037 
03038    QString hashString;
03039    hashString.sprintf("%08lx", hash);
03040 
03041    CEF = CEF + hashString;
03042 
03043    CEF = dir + '/' + CEF;
03044 
03045    return CEF;
03046 }
03047 
03048 QFile *CacheInfo::cachedFile()
03049 {
03050 #ifdef Q_WS_WIN
03051    const char *mode = (readWrite ? "rb+" : "rb");
03052 #else
03053    const char *mode = (readWrite ? "r+" : "r");
03054 #endif
03055 
03056    FILE *fs = KDE::fopen(CEF, mode); // Open for reading and writing
03057    if (!fs)
03058       return 0;
03059 
03060    char buffer[401];
03061    bool ok = true;
03062 
03063   // CacheRevision
03064   if (ok && (!fgets(buffer, 400, fs)))
03065       ok = false;
03066    if (ok && (strcmp(buffer, CACHE_REVISION) != 0))
03067       ok = false;
03068 
03069    time_t date;
03070    time_t currentDate = time(0);
03071 
03072    // URL
03073    if (ok && (!fgets(buffer, 400, fs)))
03074       ok = false;
03075    if (ok)
03076    {
03077       int l = strlen(buffer);
03078       if (l>0)
03079          buffer[l-1] = 0; // Strip newline
03080       if (m_.url.url() != buffer)
03081       {
03082          ok = false; // Hash collision
03083       }
03084    }
03085 
03086    // Creation Date
03087    if (ok && (!fgets(buffer, 400, fs)))
03088       ok = false;
03089    if (ok)
03090    {
03091       date = (time_t) strtoul(buffer, 0, 10);
03092       if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge))
03093       {
03094          m_bMustRevalidate = true;
03095          m_expireDate = currentDate;
03096       }
03097    }
03098 
03099    // Expiration Date
03100    m_cacheExpireDateOffset = KDE_ftell(fs);
03101    if (ok && (!fgets(buffer, 400, fs)))
03102       ok = false;
03103    if (ok)
03104    {
03105       if (m_request.cache == CC_Verify)
03106       {
03107          date = (time_t) strtoul(buffer, 0, 10);
03108          // After the expire date we need to revalidate.
03109          if (!date || difftime(currentDate, date) >= 0)
03110             m_bMustRevalidate = true;
03111          m_expireDate = date;
03112       }
03113    }
03114 
03115    // ETag
03116    if (ok && (!fgets(buffer, 400, fs)))
03117       ok = false;
03118    if (ok)
03119    {
03120       m_etag = QString(buffer).trimmed();
03121    }
03122 
03123    // Last-Modified
03124    if (ok && (!fgets(buffer, 400, fs)))
03125       ok = false;
03126    if (ok)
03127    {
03128       m_lastModified = QString(buffer).trimmed();
03129    }
03130 
03131    fclose(fs);
03132 
03133    if (ok)
03134       return fs;
03135 
03136    unlink( QFile::encodeName(CEF) );
03137    return 0;
03138 
03139 }
03140 
03141 void CacheInfo::flush()
03142 {
03143     cachedFile().remove();
03144 }
03145 
03146 void CacheInfo::touch()
03147 {
03148 
03149 }
03150 void CacheInfo::setExpireDate(int);
03151 void CacheInfo::setExpireTimeout(int);
03152 
03153 
03154 int CacheInfo::creationDate();
03155 int CacheInfo::expireDate();
03156 int CacheInfo::expireTimeout();
03157 #endif
03158 
03159 #include "jobclasses.moc"
03160 #include "job_p.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2019 The KDE developers.
Generated on Mon Jan 21 2019 12:34:58 by doxygen 1.7.5.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.9.5 API Reference

Skip menu "kdelibs-4.9.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal