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

KIO

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • 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
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal