kio Library API Documentation

job.cpp

00001 /* This file is part of the KDE libraries
00002     Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
00003                        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., 59 Temple Place - Suite 330,
00019     Boston, MA 02111-1307, USA.
00020 */
00021 
00022 #include <config.h>
00023 
00024 #include <sys/types.h>
00025 #include <sys/wait.h>
00026 #include <sys/stat.h>
00027 
00028 #include <assert.h>
00029 
00030 #include <signal.h>
00031 #include <stdlib.h>
00032 #include <stdio.h>
00033 #include <time.h>
00034 #include <unistd.h>
00035 extern "C" {
00036 #include <pwd.h>
00037 #include <grp.h>
00038 }
00039 #include <qtimer.h>
00040 #include <qfile.h>
00041 
00042 #include <kapplication.h>
00043 #include <kglobal.h>
00044 #include <klocale.h>
00045 #include <ksimpleconfig.h>
00046 #include <kdebug.h>
00047 #include <kdialog.h>
00048 #include <kmessagebox.h>
00049 #include <kdatastream.h>
00050 #include <kmainwindow.h>
00051 
00052 #include <errno.h>
00053 
00054 #include "slave.h"
00055 #include "kio/job.h"
00056 #include "scheduler.h"
00057 #include "kdirwatch.h"
00058 #include "kmimemagic.h"
00059 #include "kprotocolinfo.h"
00060 #include "kprotocolmanager.h"
00061 
00062 #include "kio/observer.h"
00063 
00064 #include "kssl/ksslcsessioncache.h"
00065 
00066 #include <kdirnotify_stub.h>
00067 #include <ktempfile.h>
00068 #include <dcopclient.h>
00069 
00070 using namespace KIO;
00071 template class QPtrList<KIO::Job>;
00072 
00073 //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
00074 #define REPORT_TIMEOUT 200
00075 
00076 #define KIO_ARGS QByteArray packedArgs; QDataStream stream( packedArgs, IO_WriteOnly ); stream
00077 
00078 class Job::JobPrivate
00079 {
00080 public:
00081     JobPrivate() : m_autoErrorHandling( false ), m_parentJob( 0L ), m_extraFlags(0),
00082                    m_processedSize(0)
00083                    {}
00084 
00085     bool m_autoErrorHandling;
00086     QGuardedPtr<QWidget> m_errorParentWidget;
00087     // Maybe we could use the QObject parent/child mechanism instead
00088     // (requires a new ctor, and moving the ctor code to some init()).
00089     Job* m_parentJob;
00090     int m_extraFlags;
00091     KIO::filesize_t m_processedSize;
00092 };
00093 
00094 Job::Job(bool showProgressInfo) : QObject(0, "job"), m_error(0), m_percent(0)
00095    , m_progressId(0), m_speedTimer(0), d( new JobPrivate )
00096 {
00097     // All jobs delete themselves after emiting 'result'.
00098 
00099     // Notify the UI Server and get a progress id
00100     if ( showProgressInfo )
00101     {
00102         m_progressId = Observer::self()->newJob( this, true );
00103         //kdDebug(7007) << "Created job " << this << " with progress info -- m_progressId=" << m_progressId << endl;
00104         // Connect global progress info signals
00105         connect( this, SIGNAL( percent( KIO::Job*, unsigned long ) ),
00106                  Observer::self(), SLOT( slotPercent( KIO::Job*, unsigned long ) ) );
00107         connect( this, SIGNAL( infoMessage( KIO::Job*, const QString & ) ),
00108                  Observer::self(), SLOT( slotInfoMessage( KIO::Job*, const QString & ) ) );
00109         connect( this, SIGNAL( totalSize( KIO::Job*, KIO::filesize_t ) ),
00110                  Observer::self(), SLOT( slotTotalSize( KIO::Job*, KIO::filesize_t ) ) );
00111         connect( this, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ),
00112                  Observer::self(), SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) );
00113         connect( this, SIGNAL( speed( KIO::Job*, unsigned long ) ),
00114                  Observer::self(), SLOT( slotSpeed( KIO::Job*, unsigned long ) ) );
00115     }
00116     // Don't exit while this job is running
00117     kapp->ref();
00118 }
00119 
00120 Job::~Job()
00121 {
00122     delete m_speedTimer;
00123     delete d;
00124     kapp->deref();
00125 }
00126 
00127 int& Job::extraFlags()
00128 {
00129     return d->m_extraFlags;
00130 }
00131 
00132 void Job::setProcessedSize(KIO::filesize_t size)
00133 {
00134     d->m_processedSize = size;
00135 }
00136 
00137 KIO::filesize_t Job::getProcessedSize()
00138 {
00139     return d->m_processedSize;
00140 }
00141 
00142 void Job::addSubjob(Job *job, bool inheritMetaData)
00143 {
00144     //kdDebug(7007) << "addSubjob(" << job << ") this = " << this << endl;
00145     subjobs.append(job);
00146 
00147     connect( job, SIGNAL(result(KIO::Job*)),
00148              SLOT(slotResult(KIO::Job*)) );
00149 
00150     // Forward information from that subjob.
00151     connect( job, SIGNAL(speed( KIO::Job*, unsigned long )),
00152              SLOT(slotSpeed(KIO::Job*, unsigned long)) );
00153 
00154     connect( job, SIGNAL(infoMessage( KIO::Job*, const QString & )),
00155              SLOT(slotInfoMessage(KIO::Job*, const QString &)) );
00156 
00157     if (inheritMetaData)
00158        job->mergeMetaData(m_outgoingMetaData);
00159 
00160     job->setWindow( m_window );
00161 }
00162 
00163 void Job::removeSubjob( Job *job )
00164 {
00165     //kdDebug(7007) << "removeSubjob(" << job << ") this = " << this << "  subjobs = " << subjobs.count() << endl;
00166     subjobs.remove(job);
00167     if (subjobs.isEmpty())
00168         emitResult();
00169 }
00170 
00171 void Job::emitPercent( KIO::filesize_t processedSize, KIO::filesize_t totalSize )
00172 {
00173   // calculate percents
00174   unsigned long ipercent = m_percent;
00175 
00176   if ( totalSize == 0 )
00177     m_percent = 100;
00178   else
00179     m_percent = (unsigned long)(( (float)(processedSize) / (float)(totalSize) ) * 100.0);
00180 
00181   if ( m_percent != ipercent || m_percent == 100 /* for those buggy total sizes that grow */ ) {
00182     emit percent( this, m_percent );
00183     //kdDebug(7007) << "Job::emitPercent - percent =  " << (unsigned int) m_percent << endl;
00184   }
00185 }
00186 
00187 void Job::emitSpeed( unsigned long bytes_per_second )
00188 {
00189   //kdDebug(7007) << "Job " << this << " emitSpeed " << bytes_per_second << endl;
00190   if ( !m_speedTimer )
00191   {
00192     m_speedTimer = new QTimer();
00193     connect( m_speedTimer, SIGNAL( timeout() ), SLOT( slotSpeedTimeout() ) );
00194   }
00195   emit speed( this, bytes_per_second );
00196   m_speedTimer->start( 5000 );   // 5 seconds interval should be enough
00197 }
00198 
00199 void Job::emitResult()
00200 {
00201   // If we are displaying a progress dialog, remove it first.
00202   if ( m_progressId ) // Did we get an ID from the observer ?
00203     Observer::self()->jobFinished( m_progressId );
00204   if ( m_error && d->m_autoErrorHandling )
00205     showErrorDialog( d->m_errorParentWidget );
00206   emit result(this);
00207   delete this;
00208 }
00209 
00210 void Job::kill( bool quietly )
00211 {
00212   kdDebug(7007) << "Job::kill this=" << this << " m_progressId=" << m_progressId << " quietly=" << quietly << endl;
00213   // kill all subjobs, without triggering their result slot
00214   QPtrListIterator<Job> it( subjobs );
00215   for ( ; it.current() ; ++it )
00216      (*it)->kill( true );
00217   subjobs.clear();
00218 
00219   if ( ! quietly ) {
00220     m_error = ERR_USER_CANCELED;
00221     emit canceled( this ); // Not very useful (deprecated)
00222     emitResult();
00223   } else
00224   {
00225     if ( m_progressId ) // in both cases we want to hide the progress window
00226       Observer::self()->jobFinished( m_progressId );
00227     delete this;
00228   }
00229 }
00230 
00231 void Job::slotResult( Job *job )
00232 {
00233     // Did job have an error ?
00234     if ( job->error() && !m_error )
00235     {
00236         // Store it in the parent only if first error
00237         m_error = job->error();
00238         m_errorText = job->errorText();
00239     }
00240     removeSubjob(job);
00241 }
00242 
00243 void Job::slotSpeed( KIO::Job*, unsigned long bytes_per_second )
00244 {
00245   //kdDebug(7007) << "Job::slotSpeed " << bytes_per_second << endl;
00246   emitSpeed( bytes_per_second );
00247 }
00248 
00249 void Job::slotInfoMessage( KIO::Job*, const QString & msg )
00250 {
00251   emit infoMessage( this, msg );
00252 }
00253 
00254 void Job::slotSpeedTimeout()
00255 {
00256   //kdDebug(7007) << "slotSpeedTimeout()" << endl;
00257   // send 0 and stop the timer
00258   // timer will be restarted only when we receive another speed event
00259   emit speed( this, 0 );
00260   m_speedTimer->stop();
00261 }
00262 
00263 //Job::errorString is implemented in global.cpp
00264 
00265 void Job::showErrorDialog( QWidget * parent )
00266 {
00267   //kdDebug(7007) << "Job::showErrorDialog parent=" << parent << endl;
00268   kapp->enableStyles();
00269   // Show a message box, except for "user canceled" or "no content"
00270   if ( (m_error != ERR_USER_CANCELED) && (m_error != ERR_NO_CONTENT) ) {
00271     //old plain error message
00272     //kdDebug(7007) << "Default language: " << KGlobal::locale()->defaultLanguage() << endl;
00273     if ( 1 )
00274       KMessageBox::queuedMessageBox( parent, KMessageBox::Error, errorString() );
00275 #if 0
00276     } else {
00277       QStringList errors = detailedErrorStrings();
00278       QString caption, err, detail;
00279       QStringList::iterator it = errors.begin();
00280       if ( it != errors.end() )
00281         caption = *(it++);
00282       if ( it != errors.end() )
00283         err = *(it++);
00284       if ( it != errors.end() )
00285         detail = *it;
00286       KMessageBox::queuedDetailedError( parent, err, detail, caption );
00287     }
00288 #endif
00289   }
00290 }
00291 
00292 void Job::setAutoErrorHandlingEnabled( bool enable, QWidget *parentWidget )
00293 {
00294   d->m_autoErrorHandling = enable;
00295   d->m_errorParentWidget = parentWidget;
00296 }
00297 
00298 bool Job::isAutoErrorHandlingEnabled() const
00299 {
00300   return d->m_autoErrorHandling;
00301 }
00302 
00303 void Job::setWindow(QWidget *window)
00304 {
00305   m_window = window;
00306   KIO::Scheduler::registerWindow(window);
00307 }
00308 
00309 QWidget *Job::window() const
00310 {
00311   return m_window;
00312 }
00313 
00314 void Job::setParentJob(Job* job)
00315 {
00316   Q_ASSERT(d->m_parentJob == 0L);
00317   Q_ASSERT(job);
00318   d->m_parentJob = job;
00319 }
00320 
00321 Job* Job::parentJob() const
00322 {
00323   return d->m_parentJob;
00324 }
00325 
00326 MetaData Job::metaData() const
00327 {
00328     return m_incomingMetaData;
00329 }
00330 
00331 QString Job::queryMetaData(const QString &key)
00332 {
00333     if (!m_incomingMetaData.contains(key))
00334        return QString::null;
00335     return m_incomingMetaData[key];
00336 }
00337 
00338 void Job::setMetaData( const KIO::MetaData &_metaData)
00339 {
00340     m_outgoingMetaData = _metaData;
00341 }
00342 
00343 void Job::addMetaData( const QString &key, const QString &value)
00344 {
00345     m_outgoingMetaData.insert(key, value);
00346 }
00347 
00348 void Job::addMetaData( const QMap<QString,QString> &values)
00349 {
00350     QMapConstIterator<QString,QString> it = values.begin();
00351     for(;it != values.end(); ++it)
00352       m_outgoingMetaData.insert(it.key(), it.data());
00353 }
00354 
00355 void Job::mergeMetaData( const QMap<QString,QString> &values)
00356 {
00357     QMapConstIterator<QString,QString> it = values.begin();
00358     for(;it != values.end(); ++it)
00359       m_outgoingMetaData.insert(it.key(), it.data(), false);
00360 }
00361 
00362 MetaData Job::outgoingMetaData() const
00363 {
00364     return m_outgoingMetaData;
00365 }
00366 
00367 
00368 SimpleJob::SimpleJob(const KURL& url, int command, const QByteArray &packedArgs,
00369                      bool showProgressInfo )
00370   : Job(showProgressInfo), m_slave(0), m_packedArgs(packedArgs),
00371     m_url(url), m_command(command), m_totalSize(0)
00372 {
00373     if (!m_url.isValid())
00374     {
00375         m_error = ERR_MALFORMED_URL;
00376         m_errorText = m_url.url();
00377         QTimer::singleShot(0, this, SLOT(slotFinished()) );
00378         return;
00379     }
00380 
00381 
00382     if (m_url.hasSubURL())
00383     {
00384        KURL::List list = KURL::split(m_url);
00385        KURL::List::Iterator it = list.fromLast();
00386        list.remove(it);
00387        m_subUrl = KURL::join(list);
00388        //kdDebug(7007) << "New URL = "  << m_url.url() << endl;
00389        //kdDebug(7007) << "Sub URL = "  << m_subUrl.url() << endl;
00390     }
00391 
00392     Scheduler::doJob(this);
00393 }
00394 
00395 void SimpleJob::kill( bool quietly )
00396 {
00397     Scheduler::cancelJob( this ); // deletes the slave if not 0
00398     m_slave = 0; // -> set to 0
00399     Job::kill( quietly );
00400 }
00401 
00402 void SimpleJob::putOnHold()
00403 {
00404     Scheduler::putSlaveOnHold(this, m_url);
00405     m_slave = 0;
00406     kill(true);
00407 }
00408 
00409 void SimpleJob::removeOnHold()
00410 {
00411     Scheduler::removeSlaveOnHold();
00412 }
00413 
00414 SimpleJob::~SimpleJob()
00415 {
00416     if (m_slave) // was running
00417     {
00418         kdDebug(7007) << "SimpleJob::~SimpleJob: Killing running job in destructor!"  << endl;
00419 #if 0
00420         m_slave->kill();
00421         Scheduler::jobFinished( this, m_slave ); // deletes the slave
00422 #endif
00423         Scheduler::cancelJob( this );
00424         m_slave = 0; // -> set to 0
00425     }
00426 }
00427 
00428 void SimpleJob::start(Slave *slave)
00429 {
00430     m_slave = slave;
00431 
00432     connect( m_slave, SIGNAL( error( int , const QString & ) ),
00433              SLOT( slotError( int , const QString & ) ) );
00434 
00435     connect( m_slave, SIGNAL( warning( const QString & ) ),
00436              SLOT( slotWarning( const QString & ) ) );
00437 
00438     connect( m_slave, SIGNAL( infoMessage( const QString & ) ),
00439              SLOT( slotInfoMessage( const QString & ) ) );
00440 
00441     connect( m_slave, SIGNAL( connected() ),
00442              SLOT( slotConnected() ) );
00443 
00444     connect( m_slave, SIGNAL( finished() ),
00445              SLOT( slotFinished() ) );
00446 
00447     if ((extraFlags() & EF_TransferJobDataSent) == 0)
00448     {
00449         connect( m_slave, SIGNAL( totalSize( KIO::filesize_t ) ),
00450                  SLOT( slotTotalSize( KIO::filesize_t ) ) );
00451 
00452         connect( m_slave, SIGNAL( processedSize( KIO::filesize_t ) ),
00453                  SLOT( slotProcessedSize( KIO::filesize_t ) ) );
00454 
00455         connect( m_slave, SIGNAL( speed( unsigned long ) ),
00456                  SLOT( slotSpeed( unsigned long ) ) );
00457     }
00458 
00459     connect( slave, SIGNAL( needProgressId() ),
00460              SLOT( slotNeedProgressId() ) );
00461 
00462     connect( slave, SIGNAL(metaData( const KIO::MetaData& ) ),
00463              SLOT( slotMetaData( const KIO::MetaData& ) ) );
00464 
00465     if (m_window)
00466     {
00467        QString id;
00468        addMetaData("window-id", id.setNum(m_window->winId()));
00469     }
00470 
00471     QString sslSession = KSSLCSessionCache::getSessionForURL(m_url);
00472     if (sslSession != QString::null)
00473     addMetaData("ssl_session_id", sslSession);
00474 
00475     if (!m_outgoingMetaData.isEmpty())
00476     {
00477        KIO_ARGS << m_outgoingMetaData;
00478        slave->send( CMD_META_DATA, packedArgs );
00479     }
00480 
00481     if (!m_subUrl.isEmpty())
00482     {
00483        KIO_ARGS << m_subUrl;
00484        m_slave->send( CMD_SUBURL, packedArgs );
00485     }
00486 
00487     m_slave->send( m_command, m_packedArgs );
00488 }
00489 
00490 void SimpleJob::slaveDone()
00491 {
00492    if (!m_slave) return;
00493    disconnect(m_slave); // Remove all signals between slave and job
00494    Scheduler::jobFinished( this, m_slave );
00495    m_slave = 0;
00496 }
00497 
00498 void SimpleJob::slotFinished( )
00499 {
00500     // Return slave to the scheduler
00501     slaveDone();
00502 
00503     if (subjobs.isEmpty())
00504     {
00505         if ( !m_error )
00506         {
00507             KDirNotify_stub allDirNotify( "*", "KDirNotify*" );
00508             if ( m_command == CMD_MKDIR )
00509             {
00510                 KURL urlDir( url() );
00511                 urlDir.setPath( urlDir.directory() );
00512                 allDirNotify.FilesAdded( urlDir );
00513             }
00514             else if ( m_command == CMD_RENAME )
00515             {
00516                 KURL src, dst;
00517                 QDataStream str( m_packedArgs, IO_ReadOnly );
00518                 str >> src >> dst;
00519                 if ( src.directory() == dst.directory() ) // For the user, moving isn't renaming. Only renaming is.
00520                     allDirNotify.FileRenamed( src, dst );
00521             }
00522         }
00523         emitResult();
00524     }
00525 }
00526 
00527 void SimpleJob::slotError( int error, const QString & errorText )
00528 {
00529     m_error = error;
00530     m_errorText = errorText;
00531     if ((m_error == ERR_UNKNOWN_HOST) && m_url.host().isEmpty())
00532        m_errorText = QString::null;
00533     // error terminates the job
00534     slotFinished();
00535 }
00536 
00537 void SimpleJob::slotWarning( const QString & errorText )
00538 {
00539     static uint msgBoxDisplayed = 0;
00540     if ( msgBoxDisplayed == 0 ) // don't bomb the user with message boxes, only one at a time
00541     {
00542         msgBoxDisplayed++;
00543         KMessageBox::information( 0L, errorText );
00544         msgBoxDisplayed--;
00545     }
00546     // otherwise just discard it.
00547 }
00548 
00549 void SimpleJob::slotInfoMessage( const QString & msg )
00550 {
00551     emit infoMessage( this, msg );
00552 }
00553 
00554 void SimpleJob::slotConnected()
00555 {
00556     emit connected( this );
00557 }
00558 
00559 void SimpleJob::slotNeedProgressId()
00560 {
00561     if ( !m_progressId )
00562         m_progressId = Observer::self()->newJob( this, false );
00563     m_slave->setProgressId( m_progressId );
00564 }
00565 
00566 void SimpleJob::slotTotalSize( KIO::filesize_t size )
00567 {
00568     m_totalSize = size;
00569     emit totalSize( this, size );
00570 }
00571 
00572 void SimpleJob::slotProcessedSize( KIO::filesize_t size )
00573 {
00574     //kdDebug(7007) << "SimpleJob::slotProcessedSize " << KIO::number(size) << endl;
00575     setProcessedSize(size);
00576     emit processedSize( this, size );
00577     if ( size > m_totalSize ) {
00578         slotTotalSize(size); // safety
00579     }
00580     emitPercent( size, m_totalSize );
00581 }
00582 
00583 void SimpleJob::slotSpeed( unsigned long bytes_per_second )
00584 {
00585     //kdDebug(7007) << "SimpleJob::slotSpeed( " << bytes_per_second << " )" << endl;
00586     emitSpeed( bytes_per_second );
00587 }
00588 
00589 void SimpleJob::slotMetaData( const KIO::MetaData &_metaData)
00590 {
00591     m_incomingMetaData += _metaData;
00592 }
00593 
00594 void SimpleJob::storeSSLSessionFromJob(const KURL &m_redirectionURL) {
00595     QString sslSession = queryMetaData("ssl_session_id");
00596 
00597     if (sslSession != QString::null) {
00598     const KURL &queryURL = m_redirectionURL.isEmpty()?m_url:m_redirectionURL;
00599     KSSLCSessionCache::putSessionForURL(queryURL, sslSession);
00600     }
00601 }
00602 
00603 SimpleJob *KIO::mkdir( const KURL& url, int permissions )
00604 {
00605     //kdDebug(7007) << "mkdir " << url.prettyURL() << endl;
00606     KIO_ARGS << url << permissions;
00607     return new SimpleJob(url, CMD_MKDIR, packedArgs, false);
00608 }
00609 
00610 SimpleJob *KIO::rmdir( const KURL& url )
00611 {
00612     //kdDebug(7007) << "rmdir " << url.prettyURL() << endl;
00613     KIO_ARGS << url << Q_INT8(false); // isFile is false
00614     return new SimpleJob(url, CMD_DEL, packedArgs, false);
00615 }
00616 
00617 SimpleJob *KIO::chmod( const KURL& url, int permissions )
00618 {
00619     //kdDebug(7007) << "chmod " << url.prettyURL() << endl;
00620     KIO_ARGS << url << permissions;
00621     return new SimpleJob(url, CMD_CHMOD, packedArgs, false);
00622 }
00623 
00624 SimpleJob *KIO::rename( const KURL& src, const KURL & dest, bool overwrite )
00625 {
00626     //kdDebug(7007) << "rename " << src.prettyURL() << " " << dest.prettyURL() << endl;
00627     KIO_ARGS << src << dest << (Q_INT8) overwrite;
00628     return new SimpleJob(src, CMD_RENAME, packedArgs, false);
00629 }
00630 
00631 SimpleJob *KIO::symlink( const QString& target, const KURL & dest, bool overwrite, bool showProgressInfo )
00632 {
00633     //kdDebug(7007) << "symlink target=" << target << " " << dest.prettyURL() << endl;
00634     KIO_ARGS << target << dest << (Q_INT8) overwrite;
00635     return new SimpleJob(dest, CMD_SYMLINK, packedArgs, showProgressInfo);
00636 }
00637 
00638 SimpleJob *KIO::special(const KURL& url, const QByteArray & data, bool showProgressInfo)
00639 {
00640     //kdDebug(7007) << "special " << url.prettyURL() << endl;
00641     return new SimpleJob(url, CMD_SPECIAL, data, showProgressInfo);
00642 }
00643 
00644 SimpleJob *KIO::mount( bool ro, const char *fstype, const QString& dev, const QString& point, bool showProgressInfo )
00645 {
00646     KIO_ARGS << int(1) << Q_INT8( ro ? 1 : 0 )
00647              << QString::fromLatin1(fstype) << dev << point;
00648     SimpleJob *job = special( KURL("file:/"), packedArgs, showProgressInfo );
00649     if ( showProgressInfo )
00650          Observer::self()->mounting( job, dev, point );
00651     return job;
00652 }
00653 
00654 SimpleJob *KIO::unmount( const QString& point, bool showProgressInfo )
00655 {
00656     KIO_ARGS << int(2) << point;
00657     SimpleJob *job = special( KURL("file:/"), packedArgs, showProgressInfo );
00658     if ( showProgressInfo )
00659          Observer::self()->unmounting( job, point );
00660     return job;
00661 }
00662 
00664 
00665 StatJob::StatJob( const KURL& url, int command,
00666                   const QByteArray &packedArgs, bool showProgressInfo )
00667     : SimpleJob(url, command, packedArgs, showProgressInfo),
00668     m_bSource(true), m_details(2)
00669 {
00670 }
00671 
00672 void StatJob::start(Slave *slave)
00673 {
00674     m_outgoingMetaData.replace( "statSide", m_bSource ? "source" : "dest" );
00675     m_outgoingMetaData.replace( "details", QString::number(m_details) );
00676 
00677     SimpleJob::start(slave);
00678 
00679     connect( m_slave, SIGNAL( statEntry( const KIO::UDSEntry& ) ),
00680              SLOT( slotStatEntry( const KIO::UDSEntry & ) ) );
00681     connect( slave, SIGNAL( redirection(const KURL &) ),
00682              SLOT( slotRedirection(const KURL &) ) );
00683 }
00684 
00685 void StatJob::slotStatEntry( const KIO::UDSEntry & entry )
00686 {
00687     //kdDebug(7007) << "StatJob::slotStatEntry" << endl;
00688     m_statResult = entry;
00689 }
00690 
00691 // Slave got a redirection request
00692 void StatJob::slotRedirection( const KURL &url)
00693 {
00694      kdDebug(7007) << "StatJob::slotRedirection(" << url.prettyURL() << ")" << endl;
00695      if (!kapp->authorizeURLAction("redirect", m_url, url))
00696      {
00697        kdWarning(7007) << "StatJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl;
00698        m_error = ERR_ACCESS_DENIED;
00699        m_errorText = url.prettyURL();
00700        return;
00701      }
00702      m_redirectionURL = url; // We'll remember that when the job finishes
00703      if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
00704         m_redirectionURL.setUser(m_url.user()); // Preserve user
00705      // Tell the user that we haven't finished yet
00706      emit redirection(this, m_redirectionURL);
00707 }
00708 
00709 void StatJob::slotFinished()
00710 {
00711     if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid())
00712     {
00713         // Return slave to the scheduler
00714         SimpleJob::slotFinished();
00715     } else {
00716         //kdDebug(7007) << "StatJob: Redirection to " << m_redirectionURL.prettyURL() << endl;
00717         if (queryMetaData("permanent-redirect")=="true")
00718             emit permanentRedirection(this, m_url, m_redirectionURL);
00719         m_url = m_redirectionURL;
00720         m_redirectionURL = KURL();
00721         m_packedArgs.truncate(0);
00722         QDataStream stream( m_packedArgs, IO_WriteOnly );
00723         stream << m_url;
00724 
00725         // Return slave to the scheduler
00726         slaveDone();
00727         Scheduler::doJob(this);
00728     }
00729 }
00730 
00731 void StatJob::slotMetaData( const KIO::MetaData &_metaData) {
00732     SimpleJob::slotMetaData(_metaData);
00733     storeSSLSessionFromJob(m_redirectionURL);
00734 }
00735 
00736 StatJob *KIO::stat(const KURL& url, bool showProgressInfo)
00737 {
00738     // Assume sideIsSource. Gets are more common than puts.
00739     return stat( url, true, 2, showProgressInfo );
00740 }
00741 
00742 StatJob *KIO::stat(const KURL& url, bool sideIsSource, short int details, bool showProgressInfo)
00743 {
00744     kdDebug(7007) << "stat " << url.prettyURL() << endl;
00745     KIO_ARGS << url;
00746     StatJob * job = new StatJob(url, CMD_STAT, packedArgs, showProgressInfo );
00747     job->setSide( sideIsSource );
00748     job->setDetails( details );
00749     if ( showProgressInfo )
00750       Observer::self()->stating( job, url );
00751     return job;
00752 }
00753 
00754 SimpleJob *KIO::http_update_cache( const KURL& url, bool no_cache, time_t expireDate)
00755 {
00756     assert( (url.protocol() == "http") || (url.protocol() == "https") );
00757     // Send http update_cache command (2)
00758     KIO_ARGS << (int)2 << url << no_cache << expireDate;
00759     SimpleJob * job = new SimpleJob( url, CMD_SPECIAL, packedArgs, false );
00760     Scheduler::scheduleJob(job);
00761     return job;
00762 }
00763 
00765 
00766 TransferJob::TransferJob( const KURL& url, int command,
00767                           const QByteArray &packedArgs,
00768                           const QByteArray &_staticData,
00769                           bool showProgressInfo)
00770     : SimpleJob(url, command, packedArgs, showProgressInfo), staticData( _staticData)
00771 {
00772     m_suspended = false;
00773     m_errorPage = false;
00774     m_subJob = 0L;
00775     if ( showProgressInfo )
00776         Observer::self()->slotTransferring( this, url );
00777 }
00778 
00779 // Slave sends data
00780 void TransferJob::slotData( const QByteArray &_data)
00781 {
00782     if(m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error)
00783       emit data( this, _data);
00784 }
00785 
00786 // Slave got a redirection request
00787 void TransferJob::slotRedirection( const KURL &url)
00788 {
00789      kdDebug(7007) << "TransferJob::slotRedirection(" << url.prettyURL() << ")" << endl;
00790      if (!kapp->authorizeURLAction("redirect", m_url, url))
00791      {
00792        kdWarning(7007) << "TransferJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl;
00793        return;
00794      }
00795 
00796     // Some websites keep redirecting to themselves where each redirection
00797     // acts as the stage in a state-machine. We define "endless redirections"
00798     // as 5 redirections to the same URL.
00799     if (m_redirectionList.contains(url) > 5)
00800     {
00801        kdDebug(7007) << "TransferJob::slotRedirection: CYCLIC REDIRECTION!" << endl;
00802        m_error = ERR_CYCLIC_LINK;
00803        m_errorText = m_url.prettyURL();
00804     }
00805     else
00806     {
00807        m_redirectionURL = url; // We'll remember that when the job finishes
00808        if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
00809           m_redirectionURL.setUser(m_url.user()); // Preserve user
00810        m_redirectionList.append(url);
00811        m_outgoingMetaData["ssl_was_in_use"] = m_incomingMetaData["ssl_in_use"];
00812        // Tell the user that we haven't finished yet
00813        emit redirection(this, m_redirectionURL);
00814     }
00815 }
00816 
00817 void TransferJob::slotFinished()
00818 {
00819    //kdDebug(7007) << "TransferJob::slotFinished(" << this << ", " << m_url.prettyURL() << ")" << endl;
00820     if (m_redirectionURL.isEmpty() || !m_redirectionURL.isValid())
00821         SimpleJob::slotFinished();
00822     else {
00823         //kdDebug(7007) << "TransferJob: Redirection to " << m_redirectionURL.prettyURL() << endl;
00824         if (queryMetaData("permanent-redirect")=="true")
00825             emit permanentRedirection(this, m_url, m_redirectionURL);
00826         // Honour the redirection
00827         // We take the approach of "redirecting this same job"
00828         // Another solution would be to create a subjob, but the same problem
00829         // happens (unpacking+repacking)
00830         staticData.truncate(0);
00831         m_incomingMetaData.clear();
00832         if (queryMetaData("cache") != "reload")
00833             addMetaData("cache","refresh");
00834         m_suspended = false;
00835         m_url = m_redirectionURL;
00836         m_redirectionURL = KURL();
00837         // The very tricky part is the packed arguments business
00838         QString dummyStr;
00839         KURL dummyUrl;
00840         QDataStream istream( m_packedArgs, IO_ReadOnly );
00841         switch( m_command ) {
00842             case CMD_GET: {
00843                 m_packedArgs.truncate(0);
00844                 QDataStream stream( m_packedArgs, IO_WriteOnly );
00845                 stream << m_url;
00846                 break;
00847             }
00848             case CMD_PUT: {
00849                 int permissions;
00850                 Q_INT8 iOverwrite, iResume;
00851                 istream >> dummyUrl >> iOverwrite >> iResume >> permissions;
00852                 m_packedArgs.truncate(0);
00853                 QDataStream stream( m_packedArgs, IO_WriteOnly );
00854                 stream << m_url << iOverwrite << iResume << permissions;
00855                 break;
00856             }
00857             case CMD_SPECIAL: {
00858                 int specialcmd;
00859                 istream >> specialcmd;
00860                 if (specialcmd == 1) // HTTP POST
00861                 {
00862                    addMetaData("cache","reload");
00863                    m_packedArgs.truncate(0);
00864                    QDataStream stream( m_packedArgs, IO_WriteOnly );
00865                    stream << m_url;
00866                    m_command = CMD_GET;
00867                 }
00868                 break;
00869             }
00870         }
00871 
00872         // Return slave to the scheduler
00873         slaveDone();
00874         Scheduler::doJob(this);
00875     }
00876 }
00877 
00878 void TransferJob::setAsyncDataEnabled(bool enabled)
00879 {
00880     if (enabled)
00881        extraFlags() |= EF_TransferJobAsync;
00882     else
00883        extraFlags() &= ~EF_TransferJobAsync;
00884 }
00885 
00886 void TransferJob::sendAsyncData(const QByteArray &dataForSlave)
00887 {
00888     if (extraFlags() & EF_TransferJobNeedData)
00889     {
00890        m_slave->send( MSG_DATA, dataForSlave );
00891        if (extraFlags() & EF_TransferJobDataSent)
00892        {
00893            KIO::filesize_t size = getProcessedSize()+dataForSlave.size();
00894            setProcessedSize(size);
00895            emit processedSize( this, size );
00896            if ( size > m_totalSize ) {
00897                slotTotalSize(size); // safety
00898            }
00899            emitPercent( size, m_totalSize );
00900        }
00901     }
00902 
00903     extraFlags() &= ~EF_TransferJobNeedData;
00904 }
00905 
00906 void TransferJob::setReportDataSent(bool enabled)
00907 {
00908     if (enabled)
00909        extraFlags() |= EF_TransferJobDataSent;
00910     else
00911        extraFlags() &= ~EF_TransferJobDataSent;
00912 }
00913 
00914 bool TransferJob::reportDataSent()
00915 {
00916     return (extraFlags() & EF_TransferJobDataSent);
00917 }
00918 
00919 
00920 // Slave requests data
00921 void TransferJob::slotDataReq()
00922 {
00923     QByteArray dataForSlave;
00924 
00925     extraFlags() |= EF_TransferJobNeedData;
00926 
00927     if (!staticData.isEmpty())
00928     {
00929        dataForSlave = staticData;
00930        staticData = QByteArray();
00931     }
00932     else
00933     {
00934        emit dataReq( this, dataForSlave);
00935 
00936        if (extraFlags() & EF_TransferJobAsync)
00937           return;
00938     }
00939 
00940     static const size_t max_size = 14 * 1024 * 1024;
00941     if (dataForSlave.size() > max_size)
00942     {
00943        kdDebug(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";
00944        staticData.duplicate(dataForSlave.data() + max_size ,  dataForSlave.size() - max_size);
00945        dataForSlave.truncate(max_size);
00946     }
00947 
00948     sendAsyncData(dataForSlave);
00949 
00950     if (m_subJob)
00951     {
00952        // Bitburger protocol in action
00953        suspend(); // Wait for more data from subJob.
00954        m_subJob->resume(); // Ask for more!
00955     }
00956 }
00957 
00958 void TransferJob::slotMimetype( const QString& type )
00959 {
00960     m_mimetype = type;
00961     emit mimetype( this, m_mimetype);
00962 }
00963 
00964 
00965 void TransferJob::suspend()
00966 {
00967     m_suspended = true;
00968     if (m_slave)
00969        m_slave->suspend();
00970 }
00971 
00972 void TransferJob::resume()
00973 {
00974     m_suspended = false;
00975     if (m_slave)
00976        m_slave->resume();
00977 }
00978 
00979 void TransferJob::start(Slave *slave)
00980 {
00981     assert(slave);
00982     connect( slave, SIGNAL( data( const QByteArray & ) ),
00983              SLOT( slotData( const QByteArray & ) ) );
00984 
00985     connect( slave, SIGNAL( dataReq() ),
00986              SLOT( slotDataReq() ) );
00987 
00988     connect( slave, SIGNAL( redirection(const KURL &) ),
00989              SLOT( slotRedirection(const KURL &) ) );
00990 
00991     connect( slave, SIGNAL(mimeType( const QString& ) ),
00992              SLOT( slotMimetype( const QString& ) ) );
00993 
00994     connect( slave, SIGNAL(errorPage() ),
00995              SLOT( slotErrorPage() ) );
00996 
00997     connect( slave, SIGNAL( needSubURLData() ),
00998              SLOT( slotNeedSubURLData() ) );
00999 
01000     connect( slave, SIGNAL(canResume( KIO::filesize_t ) ),
01001              SLOT( slotCanResume( KIO::filesize_t ) ) );
01002 
01003     if (slave->suspended())
01004     {
01005        m_mimetype = "unknown";
01006        // WABA: The slave was put on hold. Resume operation.
01007        slave->resume();
01008     }
01009 
01010     SimpleJob::start(slave);
01011     if (m_suspended)
01012        slave->suspend();
01013 }
01014 
01015 void TransferJob::slotNeedSubURLData()
01016 {
01017     // Job needs data from subURL.
01018     m_subJob = KIO::get( m_subUrl, false, false);
01019     suspend(); // Put job on hold until we have some data.
01020     connect(m_subJob, SIGNAL( data(KIO::Job*,const QByteArray &)),
01021             SLOT( slotSubURLData(KIO::Job*,const QByteArray &)));
01022     addSubjob(m_subJob);
01023 }
01024 
01025 void TransferJob::slotSubURLData(KIO::Job*, const QByteArray &data)
01026 {
01027     // The Alternating Bitburg protocol in action again.
01028     staticData = data;
01029     m_subJob->suspend(); // Put job on hold until we have delivered the data.
01030     resume(); // Activate ourselves again.
01031 }
01032 
01033 void TransferJob::slotMetaData( const KIO::MetaData &_metaData) {
01034     SimpleJob::slotMetaData(_metaData);
01035     storeSSLSessionFromJob(m_redirectionURL);
01036 }
01037 
01038 void TransferJob::slotErrorPage()
01039 {
01040     m_errorPage = true;
01041 }
01042 
01043 void TransferJob::slotCanResume( KIO::filesize_t offset )
01044 {
01045     emit canResume(this, offset);
01046 }
01047 
01048 void TransferJob::slotResult( KIO::Job *job)
01049 {
01050    // This can only be our suburl.
01051    assert(job == m_subJob);
01052    // Did job have an error ?
01053    if ( job->error() )
01054    {
01055       m_error = job->error();
01056       m_errorText = job->errorText();
01057 
01058       emitResult();
01059       return;
01060    }
01061 
01062    if (job == m_subJob)
01063    {
01064       m_subJob = 0; // No action required
01065       resume(); // Make sure we get the remaining data.
01066    }
01067    subjobs.remove(job); // Remove job, but don't kill this job.
01068 }
01069 
01070 TransferJob *KIO::get( const KURL& url, bool reload, bool showProgressInfo )
01071 {
01072     // Send decoded path and encoded query
01073     KIO_ARGS << url;
01074     TransferJob * job = new TransferJob( url, CMD_GET, packedArgs, QByteArray(), showProgressInfo );
01075     if (reload)
01076        job->addMetaData("cache", "reload");
01077     return job;
01078 }
01079 
01080 class PostErrorJob : public TransferJob
01081 {
01082 public:
01083 
01084   PostErrorJob(const QString& url, const QByteArray &packedArgs, const QByteArray &postData, bool showProgressInfo)
01085       : TransferJob(KURL(), CMD_SPECIAL, packedArgs, postData, showProgressInfo)
01086   {
01087     m_error = KIO::ERR_POST_DENIED;
01088     m_errorText = url;
01089   }
01090 
01091 };
01092 
01093 TransferJob *KIO::http_post( const KURL& url, const QByteArray &postData, bool showProgressInfo )
01094 {
01095     bool valid = true;
01096 
01097     // filter out non https? protocols
01098     if ((url.protocol() != "http") && (url.protocol() != "https" ))
01099         valid = false;
01100 
01101     // filter out some malicious ports
01102     static const int bad_ports[] = {
01103         1,   // tcpmux
01104         7,   // echo
01105         9,   // discard
01106         11,   // systat
01107         13,   // daytime
01108         15,   // netstat
01109         17,   // qotd
01110         19,   // chargen
01111         20,   // ftp-data
01112         21,   // ftp-cntl
01113         22,   // ssh
01114         23,   // telnet
01115         25,   // smtp
01116         37,   // time
01117         42,   // name
01118         43,   // nicname
01119         53,   // domain
01120         77,   // priv-rjs
01121         79,   // finger
01122         87,   // ttylink
01123         95,   // supdup
01124         101,  // hostriame
01125         102,  // iso-tsap
01126         103,  // gppitnp
01127         104,  // acr-nema
01128         109,  // pop2
01129         110,  // pop3
01130         111,  // sunrpc
01131         113,  // auth
01132         115,  // sftp
01133         117,  // uucp-path
01134         119,  // nntp
01135         123,  // NTP
01136         135,  // loc-srv / epmap
01137         139,  // netbios
01138         143,  // imap2
01139         179,  // BGP
01140         389,  // ldap
01141         512,  // print / exec
01142         513,  // login
01143         514,  // shell
01144         515,  // printer
01145         526,  // tempo
01146         530,  // courier
01147         531,  // Chat
01148         532,  // netnews
01149         540,  // uucp
01150         556,  // remotefs
01151         587,  // sendmail
01152         601,  //
01153         989,  // ftps data
01154         990,  // ftps
01155         992,  // telnets
01156         993,  // imap/SSL
01157         995,  // pop3/SSL
01158         1080, // SOCKS
01159         2049, // nfs
01160         4045, // lockd
01161         6000, // x11
01162         6667, // irc
01163         0};
01164     for (int cnt=0; bad_ports[cnt]; ++cnt)
01165         if (url.port() == bad_ports[cnt])
01166         {
01167             valid = false;
01168             break;
01169         }
01170 
01171     if( !valid )
01172     {
01173     static bool override_loaded = false;
01174     static QValueList< int >* overriden_ports = NULL;
01175     if( !override_loaded )
01176     {
01177         KConfig cfg( "kio_httprc", true );
01178         overriden_ports = new QValueList< int >;
01179         *overriden_ports = cfg.readIntListEntry( "OverriddenPorts" );
01180         override_loaded = true;
01181     }
01182     for( QValueList< int >::ConstIterator it = overriden_ports->begin();
01183          it != overriden_ports->end();
01184          ++it )
01185         if( overriden_ports->contains( url.port()))
01186         valid = true;
01187     }
01188 
01189 
01190     // if request is not valid, return an invalid transfer job
01191     if (!valid)
01192     {
01193         KIO_ARGS << (int)1 << url;
01194         TransferJob * job = new PostErrorJob(url.url(), packedArgs, postData, showProgressInfo);
01195         return job;
01196     }
01197 
01198     bool redirection = false;
01199     KURL _url(url);
01200     if (_url.path().isEmpty())
01201     {
01202       redirection = true;
01203       _url.setPath("/");
01204     }
01205 
01206     // Send http post command (1), decoded path and encoded query
01207     KIO_ARGS << (int)1 << _url;
01208     TransferJob * job = new TransferJob( _url, CMD_SPECIAL,
01209                                          packedArgs, postData, showProgressInfo );
01210 
01211     if (redirection)
01212       QTimer::singleShot(0, job, SLOT(slotPostRedirection()) );
01213 
01214     return job;
01215 }
01216 
01217 // http post got redirected from http://host to http://host/ by TransferJob
01218 // We must do this redirection ourselves because redirections by the
01219 // slave change post jobs into get jobs.
01220 void TransferJob::slotPostRedirection()
01221 {
01222     kdDebug(7007) << "TransferJob::slotPostRedirection(" << m_url.prettyURL() << ")" << endl;
01223     // Tell the user about the new url.
01224     emit redirection(this, m_url);
01225 }
01226 
01227 
01228 TransferJob *KIO::put( const KURL& url, int permissions,
01229                   bool overwrite, bool resume, bool showProgressInfo )
01230 {
01231     KIO_ARGS << url << Q_INT8( overwrite ? 1 : 0 ) << Q_INT8( resume ? 1 : 0 ) << permissions;
01232     TransferJob * job = new TransferJob( url, CMD_PUT, packedArgs, QByteArray(), showProgressInfo );
01233     return job;
01234 }
01235 
01237 
01238 MimetypeJob::MimetypeJob( const KURL& url, int command,
01239                   const QByteArray &packedArgs, bool showProgressInfo )
01240     : TransferJob(url, command, packedArgs, QByteArray(), showProgressInfo)
01241 {
01242 }
01243 
01244 void MimetypeJob::start(Slave *slave)
01245 {
01246     TransferJob::start(slave);
01247 }
01248 
01249 
01250 void MimetypeJob::slotFinished( )
01251 {
01252     //kdDebug(7007) << "MimetypeJob::slotFinished()" << endl;
01253     if ( m_error == KIO::ERR_IS_DIRECTORY )
01254     {
01255         // It is in fact a directory. This happens when HTTP redirects to FTP.
01256         // Due to the "protocol doesn't support listing" code in KRun, we
01257         // assumed it was a file.
01258         kdDebug(7007) << "It is in fact a directory!" << endl;
01259         m_mimetype = QString::fromLatin1("inode/directory");
01260         emit TransferJob::mimetype( this, m_mimetype );
01261         m_error = 0;
01262     }
01263     if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error )
01264     {
01265         // Return slave to the scheduler
01266         TransferJob::slotFinished();
01267     } else {
01268         //kdDebug(7007) << "MimetypeJob: Redirection to " << m_redirectionURL.prettyURL() << endl;
01269         if (queryMetaData("permanent-redirect")=="true")
01270             emit permanentRedirection(this, m_url, m_redirectionURL);
01271         staticData.truncate(0);
01272         m_suspended = false;
01273         m_url = m_redirectionURL;
01274         m_redirectionURL = KURL();
01275         m_packedArgs.truncate(0);
01276         QDataStream stream( m_packedArgs, IO_WriteOnly );
01277         stream << m_url;
01278 
01279         // Return slave to the scheduler
01280         slaveDone();
01281         Scheduler::doJob(this);
01282     }
01283 }
01284 
01285 MimetypeJob *KIO::mimetype(const KURL& url, bool showProgressInfo )
01286 {
01287     KIO_ARGS << url;
01288     MimetypeJob * job = new MimetypeJob(url, CMD_MIMETYPE, packedArgs, showProgressInfo);
01289     if ( showProgressInfo )
01290       Observer::self()->stating( job, url );
01291     return job;
01292 }
01293 
01295 
01296 
01297 class FileCopyJob::FileCopyJobPrivate
01298 {
01299 public:
01300     KIO::filesize_t m_sourceSize;
01301     SimpleJob *m_delJob;
01302 };
01303 
01304 /*
01305  * The FileCopyJob works according to the famous Bayern
01306  * 'Alternating Bitburger Protocol': we either drink a beer or we
01307  * we order a beer, but never both at the same time.
01308  * Tranlated to io-slaves: We alternate between receiving a block of data
01309  * and sending it away.
01310  */
01311 FileCopyJob::FileCopyJob( const KURL& src, const KURL& dest, int permissions,
01312                           bool move, bool overwrite, bool resume, bool showProgressInfo)
01313     : Job(showProgressInfo), m_src(src), m_dest(dest),
01314       m_permissions(permissions), m_move(move), m_overwrite(overwrite), m_resume(resume),
01315       m_totalSize(0)
01316 {
01317    if (showProgressInfo && !move)
01318       Observer::self()->slotCopying( this, src, dest );
01319    else if (showProgressInfo && move)
01320       Observer::self()->slotMoving( this, src, dest );
01321 
01322     //kdDebug(7007) << "FileCopyJob::FileCopyJob()" << endl;
01323     m_moveJob = 0;
01324     m_copyJob = 0;
01325     m_getJob = 0;
01326     m_putJob = 0;
01327     d = new FileCopyJobPrivate;
01328     d->m_delJob = 0;
01329     d->m_sourceSize = (KIO::filesize_t) -1;
01330     QTimer::singleShot(0, this, SLOT(slotStart()));
01331 }
01332 
01333 void FileCopyJob::slotStart()
01334 {
01335     if ((m_src.protocol() == m_dest.protocol()) &&
01336         (m_src.host() == m_dest.host()) &&
01337         (m_src.port() == m_dest.port()) &&
01338         (m_src.user() == m_dest.user()) &&
01339         (m_src.pass() == m_dest.pass()) &&
01340         !m_src.hasSubURL() && !m_dest.hasSubURL())
01341     {
01342        if (m_move)
01343        {
01344           m_moveJob = KIO::rename( m_src, m_dest, m_overwrite );
01345           addSubjob( m_moveJob );
01346           connectSubjob( m_moveJob );
01347        }
01348        else
01349        {
01350           startCopyJob();
01351        }
01352     }
01353     else
01354     {
01355        if (!m_move &&
01356            (m_src.isLocalFile() && KProtocolInfo::canCopyFromFile(m_dest))
01357           )
01358        {
01359           startCopyJob(m_dest);
01360        }
01361        else if (!m_move &&
01362            (m_dest.isLocalFile() && KProtocolInfo::canCopyToFile(m_src))
01363           )
01364        {
01365           startCopyJob(m_src);
01366        }
01367        else
01368        {
01369           startDataPump();
01370        }
01371     }
01372 }
01373 
01374 FileCopyJob::~FileCopyJob()
01375 {
01376     delete d;
01377 }
01378 
01379 void FileCopyJob::setSourceSize( off_t size )
01380 {
01381     d->m_sourceSize = size;
01382     m_totalSize = size;
01383 }
01384 
01385 void FileCopyJob::setSourceSize64( KIO::filesize_t size )
01386 {
01387     d->m_sourceSize = size;
01388     m_totalSize = size;
01389 }
01390 
01391 void FileCopyJob::startCopyJob()
01392 {
01393     startCopyJob(m_src);
01394 }
01395 
01396 void FileCopyJob::startCopyJob(const KURL &slave_url)
01397 {
01398     //kdDebug(7007) << "FileCopyJob::startCopyJob()" << endl;
01399     KIO_ARGS << m_src << m_dest << m_permissions << (Q_INT8) m_overwrite;
01400     m_copyJob = new SimpleJob(slave_url, CMD_COPY, packedArgs, false);
01401     addSubjob( m_copyJob );
01402     connectSubjob( m_copyJob );
01403 }
01404 
01405 void FileCopyJob::connectSubjob( SimpleJob * job )
01406 {
01407     connect( job, SIGNAL(totalSize( KIO::Job*, KIO::filesize_t )),
01408              this, SLOT( slotTotalSize(KIO::Job*, KIO::filesize_t)) );
01409 
01410     connect( job, SIGNAL(processedSize( KIO::Job*, KIO::filesize_t )),
01411              this, SLOT( slotProcessedSize(KIO::Job*, KIO::filesize_t)) );
01412 
01413     connect( job, SIGNAL(percent( KIO::Job*, unsigned long )),
01414              this, SLOT( slotPercent(KIO::Job*, unsigned long)) );
01415 
01416 }
01417 
01418 void FileCopyJob::slotProcessedSize( KIO::Job *, KIO::filesize_t size )
01419 {
01420     setProcessedSize(size);
01421     emit processedSize( this, size );
01422     if ( size > m_totalSize ) {
01423         slotTotalSize( this, size ); // safety
01424     }
01425     emitPercent( size, m_totalSize );
01426 }
01427 
01428 void FileCopyJob::slotTotalSize( KIO::Job*, KIO::filesize_t size )
01429 {
01430     m_totalSize = size;
01431     emit totalSize( this, m_totalSize );
01432 }
01433 
01434 void FileCopyJob::slotPercent( KIO::Job*, unsigned long pct )
01435 {
01436     if ( pct > m_percent )
01437     {
01438         m_percent = pct;
01439         emit percent( this, m_percent );
01440     }
01441 }
01442 
01443 void FileCopyJob::startDataPump()
01444 {
01445     //kdDebug(7007) << "FileCopyJob::startDataPump()" << endl;
01446 
01447     m_canResume = false;
01448     m_resumeAnswerSent = false;
01449     m_getJob = 0L; // for now
01450     m_putJob = put( m_dest, m_permissions, m_overwrite, m_resume, false /* no GUI */);
01451     //kdDebug(7007) << "FileCopyJob: m_putJob = " << m_putJob << " m_dest=" << m_dest.prettyURL() << endl;
01452 
01453     // The first thing the put job will tell us is whether we can
01454     // resume or not (this is always emitted)
01455     connect( m_putJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)),
01456              SLOT( slotCanResume(KIO::Job *, KIO::filesize_t)));
01457     connect( m_putJob, SIGNAL(dataReq(KIO::Job *, QByteArray&)),
01458              SLOT( slotDataReq(KIO::Job *, QByteArray&)));
01459     addSubjob( m_putJob );
01460 }
01461 
01462 void FileCopyJob::slotCanResume( KIO::Job* job, KIO::filesize_t offset )
01463 {
01464     if ( job == m_putJob )
01465     {
01466         //kdDebug(7007) << "FileCopyJob::slotCanResume from PUT job. offset=" << KIO::number(offset) << endl;
01467         if (offset)
01468         {
01469             RenameDlg_Result res = R_RESUME;
01470 
01471             if (!KProtocolManager::autoResume())
01472             {
01473                 QString newPath;
01474                 KIO::Job* job = ( !m_progressId && parentJob() ) ? parentJob() : this;
01475                 // Ask confirmation about resuming previous transfer
01476                 res = Observer::self()->open_RenameDlg(
01477                       job, i18n("File Already Exists"),
01478                       m_src.prettyURL(0, KURL::StripFileProtocol),
01479                       m_dest.prettyURL(0, KURL::StripFileProtocol),
01480                       (RenameDlg_Mode) (M_OVERWRITE | M_RESUME | M_NORENAME), newPath,
01481                       d->m_sourceSize, offset );
01482             }
01483 
01484             if ( res == R_OVERWRITE )
01485               offset = 0;
01486             else if ( res == R_CANCEL )
01487             {
01488                 m_putJob->kill(true);
01489                 m_error = ERR_USER_CANCELED;
01490                 emitResult();
01491                 return;
01492             }
01493         }
01494         else
01495             m_resumeAnswerSent = true; // No need for an answer
01496 
01497         m_getJob = get( m_src, false, false /* no GUI */ );
01498         //kdDebug(7007) << "FileCopyJob: m_getJob = " << m_getJob << endl;
01499         m_getJob->addMetaData( "errorPage", "false" );
01500         m_getJob->addMetaData( "AllowCompressedPage", "false" );
01501         // Set size in subjob. This helps if the slave doesn't emit totalSize.
01502         if ( d->m_sourceSize != (KIO::filesize_t)-1 )
01503             m_getJob->slotTotalSize( d->m_sourceSize );
01504         if (offset)
01505         {
01506             //kdDebug(7007) << "Setting metadata for resume to " << (unsigned long) offset << endl;
01507             m_getJob->addMetaData( "resume", KIO::number(offset) );
01508 
01509             // Might or might not get emitted
01510             connect( m_getJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)),
01511                      SLOT( slotCanResume(KIO::Job *, KIO::filesize_t)));
01512         }
01513         m_putJob->slave()->setOffset( offset );
01514 
01515         m_putJob->suspend();
01516         addSubjob( m_getJob );
01517         connectSubjob( m_getJob ); // Progress info depends on get
01518         m_getJob->resume(); // Order a beer
01519 
01520         connect( m_getJob, SIGNAL(data(KIO::Job *, const QByteArray&)),
01521                  SLOT( slotData(KIO::Job *, const QByteArray&)));
01522     }
01523     else if ( job == m_getJob )
01524     {
01525         // Cool, the get job said ok, we can resume
01526         m_canResume = true;
01527         //kdDebug(7007) << "FileCopyJob::slotCanResume from the GET job -> we can resume" << endl;
01528 
01529         m_getJob->slave()->setOffset( m_putJob->slave()->offset() );
01530     }
01531     else
01532         kdWarning(7007) << "FileCopyJob::slotCanResume from unknown job=" << job
01533                         << " m_getJob=" << m_getJob << " m_putJob=" << m_putJob << endl;
01534 }
01535 
01536 void FileCopyJob::slotData( KIO::Job * , const QByteArray &data)
01537 {
01538    //kdDebug(7007) << "FileCopyJob::slotData" << endl;
01539    //kdDebug(7007) << " data size : " << data.size() << endl;
01540    assert(m_putJob);
01541    m_getJob->suspend();
01542    m_putJob->resume(); // Drink the beer
01543    m_buffer = data;
01544 
01545    // On the first set of data incoming, we tell the "put" slave about our
01546    // decision about resuming
01547    if (!m_resumeAnswerSent)
01548    {
01549        m_resumeAnswerSent = true;
01550        //kdDebug(7007) << "FileCopyJob::slotData (first time) -> send resume answer " << m_canResume << endl;
01551        m_putJob->slave()->sendResumeAnswer( m_canResume );
01552    }
01553 }
01554 
01555 void FileCopyJob::slotDataReq( KIO::Job * , QByteArray &data)
01556 {
01557    //kdDebug(7007) << "FileCopyJob::slotDataReq" << endl;
01558    if (!m_resumeAnswerSent && !m_getJob)
01559    {
01560        // This can't happen (except as a migration bug on 12/10/2000)
01561        m_error = ERR_INTERNAL;
01562        m_errorText = "'Put' job didn't send canResume or 'Get' job didn't send data!";
01563        m_putJob->kill(true);
01564        emitResult();
01565        return;
01566    }
01567    if (m_getJob)
01568    {
01569       m_getJob->resume(); // Order more beer
01570       m_putJob->suspend();
01571    }
01572    data = m_buffer;
01573    m_buffer = QByteArray();
01574 }
01575 
01576 void FileCopyJob::slotResult( KIO::Job *job)
01577 {
01578    //kdDebug(7007) << "FileCopyJob this=" << this << " ::slotResult(" << job << ")" << endl;
01579    // Did job have an error ?
01580    if ( job->error() )
01581    {
01582       if ((job == m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
01583       {
01584          m_moveJob = 0;
01585          startCopyJob();
01586          removeSubjob(job);
01587          return;
01588       }
01589       else if ((job == m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
01590       {
01591          m_copyJob = 0;
01592          startDataPump();
01593          removeSubjob(job);
01594          return;
01595       }
01596       else if (job == m_getJob)
01597       {
01598         m_getJob = 0L;
01599         if (m_putJob)
01600           m_putJob->kill(true);
01601       }
01602       else if (job == m_putJob)
01603       {
01604         m_putJob = 0L;
01605         if (m_getJob)
01606           m_getJob->kill(true);
01607       }
01608       m_error = job->error();
01609       m_errorText = job->errorText();
01610       emitResult();
01611       return;
01612    }
01613 
01614    if (job == m_moveJob)
01615    {
01616       m_moveJob = 0; // Finished
01617    }
01618 
01619    if (job == m_copyJob)
01620    {
01621       m_copyJob = 0;
01622       if (m_move)
01623       {
01624          d->m_delJob = file_delete( m_src, false/*no GUI*/ ); // Delete source
01625          addSubjob(d->m_delJob);
01626       }
01627    }
01628 
01629    if (job == m_getJob)
01630    {
01631       m_getJob = 0; // No action required
01632       if (m_putJob)
01633          m_putJob->resume();
01634    }
01635 
01636    if (job == m_putJob)
01637    {
01638       //kdDebug(7007) << "FileCopyJob: m_putJob finished " << endl;
01639       m_putJob = 0;
01640       if (m_getJob)
01641       {
01642          kdWarning(7007) << "WARNING ! Get still going on..." << endl;
01643          m_getJob->resume();
01644       }
01645       if (m_move)
01646       {
01647          d->m_delJob = file_delete( m_src, false/*no GUI*/ ); // Delete source
01648          addSubjob(d->m_delJob);
01649       }
01650    }
01651 
01652    if (job == d->m_delJob)
01653    {
01654       d->m_delJob = 0; // Finished
01655    }
01656    removeSubjob(job);
01657 }
01658 
01659 FileCopyJob *KIO::file_copy( const KURL& src, const KURL& dest, int permissions,
01660                              bool overwrite, bool resume, bool showProgressInfo)
01661 {
01662    return new FileCopyJob( src, dest, permissions, false, overwrite, resume, showProgressInfo );
01663 }
01664 
01665 FileCopyJob *KIO::file_move( const KURL& src, const KURL& dest, int permissions,
01666                              bool overwrite, bool resume, bool showProgressInfo)
01667 {
01668    return new FileCopyJob( src, dest, permissions, true, overwrite, resume, showProgressInfo );
01669 }
01670 
01671 SimpleJob *KIO::file_delete( const KURL& src, bool showProgressInfo)
01672 {
01673     KIO_ARGS << src << Q_INT8(true); // isFile
01674     return new SimpleJob(src, CMD_DEL, packedArgs, showProgressInfo );
01675 }
01676 
01678 
01679 // KDE 4: Make it const QString & _prefix
01680 ListJob::ListJob(const KURL& u, bool showProgressInfo, bool _recursive, QString _prefix, bool _includeHidden) :
01681     SimpleJob(u, CMD_LISTDIR, QByteArray(), showProgressInfo),
01682     recursive(_recursive), includeHidden(_includeHidden), prefix(_prefix), m_processedEntries(0)
01683 {
01684     // We couldn't set the args when calling the parent constructor,
01685     // so do it now.
01686     QDataStream stream( m_packedArgs, IO_WriteOnly );
01687     stream << u;
01688 }
01689 
01690 void ListJob::slotListEntries( const KIO::UDSEntryList& list )
01691 {
01692     // Emit progress info (takes care of emit processedSize and percent)
01693     m_processedEntries += list.count();
01694     slotProcessedSize( m_processedEntries );
01695 
01696     if (recursive) {
01697         UDSEntryListConstIterator it = list.begin();
01698         UDSEntryListConstIterator end = list.end();
01699 
01700         for (; it != end; ++it) {
01701             bool isDir = false;
01702             bool isLink = false;
01703             QString filename;
01704 
01705             UDSEntry::ConstIterator it2 = (*it).begin();
01706             UDSEntry::ConstIterator end2 = (*it).end();
01707             for( ; it2 != end2; it2++ ) {
01708                 switch( (*it2).m_uds ) {
01709                     case UDS_FILE_TYPE:
01710                         isDir = S_ISDIR((*it2).m_long);
01711                         break;
01712                     case UDS_NAME:
01713                         filename = (*it2).m_str;
01714                         break;
01715                     case UDS_LINK_DEST:
01716                         // This is a link !!! Don't follow !
01717                         isLink = !(*it2).m_str.isEmpty();
01718                         break;
01719                     default:
01720                         break;
01721                 }
01722             }
01723             if (isDir && !isLink) {
01724                 // skip hidden dirs when listing if requested
01725                 if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) {
01726                     KURL newone = url();
01727                     newone.addPath(filename);
01728                     ListJob *job = new ListJob(newone,
01729                                                false /*no progress info!*/,
01730                                                true /*recursive*/,
01731                                                prefix + filename + "/",
01732                                                includeHidden);
01733                     Scheduler::scheduleJob(job);
01734                     connect(job, SIGNAL(entries( KIO::Job *,
01735                                                  const KIO::UDSEntryList& )),
01736                             SLOT( gotEntries( KIO::Job*,
01737                                               const KIO::UDSEntryList& )));
01738                     addSubjob(job);
01739                 }
01740             }
01741         }
01742     }
01743 
01744     // Not recursive, or top-level of recursive listing : return now (send . and .. as well)
01745     // exclusion of hidden files also requires the full sweep, but the case for full-listing
01746     // a single dir is probably common enough to justify the shortcut
01747     if (prefix.isNull() && includeHidden) {
01748         emit entries(this, list);
01749     } else {
01750         // cull the unwanted hidden dirs and/or parent dir references from the listing, then emit that
01751         UDSEntryList newlist;
01752 
01753         UDSEntryListConstIterator it = list.begin();
01754         UDSEntryListConstIterator end = list.end();
01755         for (; it != end; ++it) {
01756 
01757             UDSEntry newone = *it;
01758             UDSEntry::Iterator it2 = newone.begin();
01759             QString filename;
01760             for( ; it2 != newone.end(); it2++ ) {
01761                 if ((*it2).m_uds == UDS_NAME) {
01762                     filename = (*it2).m_str;
01763                     (*it2).m_str = prefix + filename;
01764                 }
01765             }
01766             // Avoid returning entries like subdir/. and subdir/.., but include . and .. for
01767             // the the toplevel dir, and skip hidden files/dirs if that was requested
01768             if (  (prefix.isNull() || (filename != ".." && filename != ".") )
01769                && (includeHidden || (filename[0] != '.') )  )
01770                 newlist.append(newone);
01771         }
01772 
01773         emit entries(this, newlist);
01774     }
01775 }
01776 
01777 void ListJob::gotEntries(KIO::Job *, const KIO::UDSEntryList& list )
01778 {
01779     // Forward entries received by subjob - faking we received them ourselves
01780     emit entries(this, list);
01781 }
01782 
01783 void ListJob::slotResult( KIO::Job * job )
01784 {
01785     // If we can't list a subdir, the result is still ok
01786     // This is why we override Job::slotResult() - to skip error checking
01787     removeSubjob( job );
01788 }
01789 
01790 void ListJob::slotRedirection( const KURL & url )
01791 {
01792      if (!kapp->authorizeURLAction("redirect", m_url, url))
01793      {
01794        kdWarning(7007) << "ListJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl;
01795        return;
01796      }
01797     m_redirectionURL = url; // We'll remember that when the job finishes
01798     if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
01799         m_redirectionURL.setUser(m_url.user()); // Preserve user
01800     emit redirection( this, url );
01801 }
01802 
01803 void ListJob::slotFinished()
01804 {
01805     if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error )
01806     {
01807         // Return slave to the scheduler
01808         SimpleJob::slotFinished();
01809     } else {
01810         //kdDebug(7007) << "ListJob: Redirection to " << m_redirectionURL.prettyURL() << endl;
01811         if (queryMetaData("permanent-redirect")=="true")
01812             emit permanentRedirection(this, m_url, m_redirectionURL);
01813         m_url = m_redirectionURL;
01814         m_redirectionURL = KURL();
01815         m_packedArgs.truncate(0);
01816         QDataStream stream( m_packedArgs, IO_WriteOnly );
01817         stream << m_url;
01818 
01819         // Return slave to the scheduler
01820         slaveDone();
01821         Scheduler::doJob(this);
01822     }
01823 }
01824 
01825 void ListJob::slotMetaData( const KIO::MetaData &_metaData) {
01826     SimpleJob::slotMetaData(_metaData);
01827     storeSSLSessionFromJob(m_redirectionURL);
01828 }
01829 
01830 ListJob *KIO::listDir( const KURL& url, bool showProgressInfo, bool includeHidden )
01831 {
01832     ListJob * job = new ListJob(url, showProgressInfo,false,QString::null,includeHidden);
01833     return job;
01834 }
01835 
01836 ListJob *KIO::listRecursive( const KURL& url, bool showProgressInfo, bool includeHidden )
01837 {
01838     ListJob * job = new ListJob(url, showProgressInfo, true,QString::null,includeHidden);
01839     return job;
01840 }
01841 
01842 void ListJob::setUnrestricted(bool unrestricted)
01843 {
01844     if (unrestricted)
01845        extraFlags() |= EF_ListJobUnrestricted;
01846     else
01847        extraFlags() &= ~EF_ListJobUnrestricted;
01848 }
01849 
01850 void ListJob::start(Slave *slave)
01851 {
01852     if (!kapp->authorizeURLAction("list", m_url, m_url) && !(extraFlags() & EF_ListJobUnrestricted))
01853     {
01854         m_error = ERR_ACCESS_DENIED;
01855         m_errorText = m_url.url();
01856         QTimer::singleShot(0, this, SLOT(slotFinished()) );
01857         return;
01858     }
01859     connect( slave, SIGNAL( listEntries( const KIO::UDSEntryList& )),
01860              SLOT( slotListEntries( const KIO::UDSEntryList& )));
01861     connect( slave, SIGNAL( totalSize( KIO::filesize_t ) ),
01862              SLOT( slotTotalSize( KIO::filesize_t ) ) );
01863     connect( slave, SIGNAL( redirection(const KURL &) ),
01864              SLOT( slotRedirection(const KURL &) ) );
01865 
01866     SimpleJob::start(slave);
01867 }
01868 
01869 
01870 CopyJob::CopyJob( const KURL::List& src, const KURL& dest, CopyMode mode, bool asMethod, bool showProgressInfo )
01871   : Job(showProgressInfo), m_mode(mode), m_asMethod(asMethod),
01872     destinationState(DEST_NOT_STATED), state(STATE_STATING),
01873     m_totalSize(0), m_processedSize(0), m_fileProcessedSize(0),
01874     m_processedFiles(0), m_processedDirs(0),
01875     m_srcList(src), m_currentStatSrc(m_srcList.begin()),
01876     m_bCurrentOperationIsLink(false), m_bSingleFileCopy(false), m_bOnlyRenames(mode==Move),
01877     m_dest(dest), m_bAutoSkip( false ), m_bOverwriteAll( false ),
01878     m_conflictError(0), m_reportTimer(0)
01879 {
01880     if ( showProgressInfo ) {
01881         connect( this, SIGNAL( totalFiles( KIO::Job*, unsigned long ) ),
01882                  Observer::self(), SLOT( slotTotalFiles( KIO::Job*, unsigned long ) ) );
01883 
01884         connect( this, SIGNAL( totalDirs( KIO::Job*, unsigned long ) ),
01885                  Observer::self(), SLOT( slotTotalDirs( KIO::Job*, unsigned long ) ) );
01886     }
01887     QTimer::singleShot(0, this, SLOT(slotStart()));
01888 }
01889 
01890 void CopyJob::slotStart()
01891 {
01897     m_reportTimer = new QTimer(this);
01898 
01899     connect(m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
01900     m_reportTimer->start(REPORT_TIMEOUT,false);
01901 
01902     // Stat the dest
01903     KIO::Job * job = KIO::stat( m_dest, false, 2, false );
01904     //kdDebug(7007) << "CopyJob:stating the dest " << m_dest.prettyURL() << endl;
01905     addSubjob(job);
01906 }
01907 
01908 void CopyJob::slotResultStating( Job *job )
01909 {
01910     //kdDebug(7007) << "CopyJob::slotResultStating" << endl;
01911     // Was there an error while stating the src ?
01912     if (job->error() && destinationState != DEST_NOT_STATED )
01913     {
01914         KURL srcurl = ((SimpleJob*)job)->url();
01915         if ( !srcurl.isLocalFile() )
01916         {
01917             // Probably : src doesn't exist. Well, over some protocols (e.g. FTP)
01918             // this info isn't really reliable (thanks to MS FTP servers).
01919             // We'll assume a file, and try to download anyway.
01920             kdDebug(7007) << "Error while stating source. Activating hack" << endl;
01921             subjobs.remove( job );
01922             assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
01923             struct CopyInfo info;
01924             info.permissions = (mode_t) -1;
01925             info.mtime = (time_t) -1;
01926             info.ctime = (time_t) -1;
01927             info.size = (KIO::filesize_t)-1;
01928             info.uSource = srcurl;
01929             info.uDest = m_dest;
01930             // Append filename or dirname to destination URL, if allowed
01931             if ( destinationState == DEST_IS_DIR && !m_asMethod )
01932                 info.uDest.addPath( srcurl.fileName() );
01933 
01934             files.append( info );
01935             ++m_currentStatSrc;
01936             statNextSrc();
01937             return;
01938         }
01939         // Local file. If stat fails, the file definitely doesn't exist.
01940         Job::slotResult( job ); // will set the error and emit result(this)
01941         return;
01942     }
01943 
01944     // Is it a file or a dir ?
01945     UDSEntry entry = ((StatJob*)job)->statResult();
01946     bool bDir = false;
01947     bool bLink = false;
01948     UDSEntry::ConstIterator it2 = entry.begin();
01949     for( ; it2 != entry.end(); it2++ ) {
01950         if ( ((*it2).m_uds) == UDS_FILE_TYPE )
01951             bDir = S_ISDIR( (mode_t)(*it2).m_long );
01952         else if ( ((*it2).m_uds) == UDS_LINK_DEST )
01953             bLink = !((*it2).m_str.isEmpty());
01954     }
01955 
01956     if ( destinationState == DEST_NOT_STATED )
01957         // we were stating the dest
01958     {
01959         if (job->error())
01960             destinationState = DEST_DOESNT_EXIST;
01961         else {
01962             // Treat symlinks to dirs as dirs here, so no test on bLink
01963             destinationState = bDir ? DEST_IS_DIR : DEST_IS_FILE;
01964             //kdDebug(7007) << "CopyJob::slotResultStating dest is dir:" << bDir << endl;
01965         }
01966         subjobs.remove( job );
01967         assert ( subjobs.isEmpty() );
01968 
01969         // After knowing what the dest is, we can start stat'ing the first src.
01970         statNextSrc();
01971         return;
01972     }
01973     // We were stating the current source URL
01974     m_currentDest = m_dest; // used by slotEntries
01975     // Create a dummy list with it, for slotEntries
01976     UDSEntryList lst;
01977     lst.append(entry);
01978 
01979     // There 6 cases, and all end up calling slotEntries(job, lst) first :
01980     // 1 - src is a dir, destination is a directory,
01981     // slotEntries will append the source-dir-name to the destination
01982     // 2 - src is a dir, destination is a file, ERROR (done later on)
01983     // 3 - src is a dir, destination doesn't exist, then it's the destination dirname,
01984     // so slotEntries will use it as destination.
01985 
01986     // 4 - src is a file, destination is a directory,
01987     // slotEntries will append the filename to the destination.
01988     // 5 - src is a file, destination is a file, m_dest is the exact destination name
01989     // 6 - src is a file, destination doesn't exist, m_dest is the exact destination name
01990     // Tell slotEntries not to alter the src url
01991     m_bCurrentSrcIsDir = false;
01992     slotEntries(job, lst);
01993 
01994     KURL srcurl = ((SimpleJob*)job)->url();
01995 
01996     subjobs.remove( job );
01997     assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
01998 
01999     if ( bDir
02000          && !bLink // treat symlinks as files (no recursion)
02001          && m_mode != Link ) // No recursion in Link mode either.
02002     {
02003         //kdDebug(7007) << " Source is a directory " << endl;
02004 
02005         m_bCurrentSrcIsDir = true; // used by slotEntries
02006         if ( destinationState == DEST_IS_DIR ) // (case 1)
02007         {
02008             if ( !m_asMethod )
02009                 // Use <desturl>/<directory_copied> as destination, from now on
02010                 m_currentDest.addPath( srcurl.fileName() );
02011         }
02012         else if ( destinationState == DEST_IS_FILE ) // (case 2)
02013         {
02014             m_error = ERR_IS_FILE;
02015             m_errorText = m_dest.prettyURL();
02016             emitResult();
02017             return;
02018         }
02019         else // (case 3)
02020         {
02021             // otherwise dest is new name for toplevel dir
02022             // so the destination exists, in fact, from now on.
02023             // (This even works with other src urls in the list, since the
02024             //  dir has effectively been created)
02025             destinationState = DEST_IS_DIR;
02026         }
02027 
02028         startListing( srcurl );
02029     }
02030     else
02031     {
02032         //kdDebug(7007) << " Source is a file (or a symlink), or we are linking -> no recursive listing " << endl;
02033         ++m_currentStatSrc;
02034         statNextSrc();
02035     }
02036 }
02037 
02038 void CopyJob::slotReport()
02039 {
02040     // If showProgressInfo was set, m_progressId is > 0.
02041     Observer * observer = m_progressId ? Observer::self() : 0L;
02042     switch (state) {
02043         case STATE_COPYING_FILES:
02044             emit processedFiles( this, m_processedFiles );
02045             if (observer) observer->slotProcessedFiles(this,m_processedFiles);
02046             if (m_mode==Move)
02047             {
02048                 if (observer) observer->slotMoving( this, m_currentSrcURL,m_currentDestURL);
02049                 emit moving( this, m_currentSrcURL, m_currentDestURL);
02050             }
02051             else if (m_mode==Link)
02052             {
02053                 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL ); // we don't have a slotLinking
02054                 emit linking( this, m_currentSrcURL.path(), m_currentDestURL );
02055             }
02056             else
02057             {
02058                 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL );
02059                 emit copying( this, m_currentSrcURL, m_currentDestURL );
02060             };
02061             break;
02062 
02063         case STATE_CREATING_DIRS:
02064             if (observer) {
02065                 observer->slotProcessedDirs( this, m_processedDirs );
02066                 observer->slotCreatingDir( this,m_currentDestURL);
02067             }
02068             emit processedDirs( this, m_processedDirs );
02069             emit creatingDir( this, m_currentDestURL );
02070             break;
02071 
02072         case STATE_STATING:
02073         case STATE_LISTING:
02074             if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL );
02075             emit totalSize( this, m_totalSize );
02076             emit totalFiles( this, files.count() );
02077             emit totalDirs( this, dirs.count() );
02078             if (!dirs.isEmpty())
02079                emit aboutToCreate( this, dirs );
02080             if (!files.isEmpty())
02081                emit aboutToCreate( this, files );
02082             break;
02083 
02084         default:
02085             break;
02086     }
02087 }
02088 
02089 void CopyJob::slotEntries(KIO::Job* job, const UDSEntryList& list)
02090 {
02091     UDSEntryListConstIterator it = list.begin();
02092     UDSEntryListConstIterator end = list.end();
02093     for (; it != end; ++it) {
02094         UDSEntry::ConstIterator it2 = (*it).begin();
02095         struct CopyInfo info;
02096         info.permissions = -1;
02097         info.mtime = (time_t) -1;
02098         info.ctime = (time_t) -1;
02099         info.size = (KIO::filesize_t)-1;
02100         QString relName;
02101         bool isDir = false;
02102         for( ; it2 != (*it).end(); it2++ ) {
02103             switch ((*it2).m_uds) {
02104                 case UDS_FILE_TYPE:
02105                     //info.type = (mode_t)((*it2).m_long);
02106                     isDir = S_ISDIR( (mode_t)((*it2).m_long) );
02107                     break;
02108                 case UDS_NAME:
02109                     relName = (*it2).m_str;
02110                     break;
02111                 case UDS_LINK_DEST:
02112                     info.linkDest = (*it2).m_str;
02113                     break;
02114                 case UDS_ACCESS:
02115                     info.permissions = ((*it2).m_long);
02116                     break;
02117                 case UDS_SIZE:
02118                     info.size = (KIO::filesize_t)((*it2).m_long);
02119                     m_totalSize += info.size;
02120                     break;
02121                 case UDS_MODIFICATION_TIME:
02122                     info.mtime = (time_t)((*it2).m_long);
02123                     break;
02124                 case UDS_CREATION_TIME:
02125                     info.ctime = (time_t)((*it2).m_long);
02126                 default:
02127                     break;
02128             }
02129         }
02130         if (relName != ".." && relName != ".")
02131         {
02132             //kdDebug(7007) << "CopyJob::slotEntries '" << relName << "'" << endl;
02133             info.uSource = ((SimpleJob *)job)->url();
02134             if ( m_bCurrentSrcIsDir ) // Only if src is a directory. Otherwise uSource is fine as is
02135                 info.uSource.addPath( relName );
02136             info.uDest = m_currentDest;
02137             //kdDebug(7007) << " uSource=" << info.uSource << " uDest(1)=" << info.uDest.prettyURL() << endl;
02138             // Append filename or dirname to destination URL, if allowed
02139             if ( destinationState == DEST_IS_DIR &&
02140                  // "copy/move as <foo>" means 'foo' is the dest for the base srcurl
02141                  // (passed here during stating) but not its children (during listing)
02142                  ( ! ( m_asMethod && state == STATE_STATING ) ) )
02143             {
02144                 // Here we _really_ have to add some filename to the dest.
02145                 // Otherwise, we end up with e.g. dest=..../Desktop/ itself.
02146                 // (This can happen when dropping a link to a webpage with no path)
02147                 if ( relName.isEmpty() )
02148                     info.uDest.addPath( KIO::encodeFileName( info.uSource.prettyURL() ) );
02149                 else
02150                     info.uDest.addPath( relName );
02151             }
02152             //kdDebug(7007) << " uDest(2)=" << info.uDest.prettyURL() << endl;
02153             //kdDebug(7007) << " " << info.uSource << " -> " << info.uDest << endl;
02154             if ( info.linkDest.isEmpty() && (isDir /*S_ISDIR(info.type)*/) && m_mode != Link ) // Dir
02155             {
02156                 dirs.append( info ); // Directories
02157                 if (m_mode == Move)
02158                     dirsToRemove.append( info.uSource );
02159             }
02160             else {
02161                 files.append( info ); // Files and any symlinks
02162             }
02163         }
02164     }
02165 }
02166 
02167 void CopyJob::statNextSrc()
02168 {
02169     if ( m_currentStatSrc != m_srcList.end() )
02170     {
02171         m_currentSrcURL = (*m_currentStatSrc);
02172         if ( m_mode == Link )
02173         {
02174             // Skip the "stating the source" stage, we don't need it for linking
02175             m_currentDest = m_dest;
02176             struct CopyInfo info;
02177             info.permissions = -1;
02178             info.mtime = (time_t) -1;
02179             info.ctime = (time_t) -1;
02180             info.size = (KIO::filesize_t)-1;
02181             info.uSource = m_currentSrcURL;
02182             info.uDest = m_currentDest;
02183             // Append filename or dirname to destination URL, if allowed
02184             if ( destinationState == DEST_IS_DIR && !m_asMethod )
02185             {
02186                 if (
02187                     (m_currentSrcURL.protocol() == info.uDest.protocol()) &&
02188                     (m_currentSrcURL.host() == info.uDest.host()) &&
02189                     (m_currentSrcURL.port() == info.uDest.port()) &&
02190                     (m_currentSrcURL.user() == info.uDest.user()) &&
02191                     (m_currentSrcURL.pass() == info.uDest.pass()) )
02192                 {
02193                     // This is the case of creating a real symlink
02194                     info.uDest.addPath( m_currentSrcURL.fileName() );
02195                 }
02196                 else
02197                 {
02198                     // Different protocols, we'll create a .desktop file
02199                     // We have to change the extension anyway, so while we're at it,
02200                     // name the file like the URL
02201                     info.uDest.addPath( KIO::encodeFileName( m_currentSrcURL.prettyURL() )+".desktop" );
02202                 }
02203             }
02204             files.append( info ); // Files and any symlinks
02205             ++m_currentStatSrc;
02206             statNextSrc(); // we could use a loop instead of a recursive call :)
02207         }
02208         // If moving, before going for the full stat+[list+]copy+del thing, try to rename
02209         else if ( m_mode == Move &&
02210                   (m_currentSrcURL.protocol() == m_dest.protocol()) &&
02211                   (m_currentSrcURL.host() == m_dest.host()) &&
02212                   (m_currentSrcURL.port() == m_dest.port()) &&
02213                   (m_currentSrcURL.user() == m_dest.user()) &&
02214                   (m_currentSrcURL.pass() == m_dest.pass()) )
02215         {
02216             KURL dest = m_dest;
02217             // Append filename or dirname to destination URL, if allowed
02218             if ( destinationState == DEST_IS_DIR && !m_asMethod )
02219                 dest.addPath( m_currentSrcURL.fileName() );
02220             kdDebug(7007) << "This seems to be a suitable case for trying to rename before stat+[list+]copy+del" << endl;
02221             state = STATE_RENAMING;
02222 
02223             struct CopyInfo info;
02224             info.permissions = -1;
02225             info.mtime = (time_t) -1;
02226             info.ctime = (time_t) -1;
02227             info.size = (KIO::filesize_t)-1;
02228             info.uSource = m_currentSrcURL;
02229             info.uDest = dest;
02230             QValueList<CopyInfo> files;
02231             files.append(info);
02232             emit aboutToCreate( this, files );
02233 
02234             SimpleJob * newJob = KIO::rename( m_currentSrcURL, dest, false /*no overwrite */);
02235             Scheduler::scheduleJob(newJob);
02236             addSubjob( newJob );
02237             if ( m_currentSrcURL.directory() != dest.directory() ) // For the user, moving isn't renaming. Only renaming is.
02238                 m_bOnlyRenames = false;
02239         }
02240         else
02241         {
02242             // if the file system doesn't support deleting, we do not even stat
02243             if (m_mode == Move && !KProtocolInfo::supportsDeleting(m_currentSrcURL)) {
02244                 KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentSrcURL.prettyURL()));
02245         ++m_currentStatSrc;
02246                 statNextSrc(); // we could use a loop instead of a recursive call :)
02247                 return;
02248             }
02249             // Stat the next src url
02250             Job * job = KIO::stat( m_currentSrcURL, true, 2, false );
02251             //kdDebug(7007) << "KIO::stat on " << m_currentSrcURL.prettyURL() << endl;
02252             state = STATE_STATING;
02253             addSubjob(job);
02254             m_currentDestURL=m_dest;
02255             m_bOnlyRenames = false;
02256         }
02257     } else
02258     {
02259         // Finished the stat'ing phase
02260         // First make sure that the totals were correctly emitted
02261         state = STATE_STATING;
02262         slotReport();
02263         // Check if we are copying a single file
02264         m_bSingleFileCopy = ( files.count() == 1 && dirs.isEmpty() );
02265         // Then start copying things
02266         state = STATE_CREATING_DIRS;
02267         createNextDir();
02268     }
02269 }
02270 
02271 
02272 void CopyJob::startListing( const KURL & src )
02273 {
02274     state = STATE_LISTING;
02275     ListJob * newjob = listRecursive( src, false );
02276     newjob->setUnrestricted(true);
02277     connect(newjob, SIGNAL(entries( KIO::Job *,
02278                                     const KIO::UDSEntryList& )),
02279             SLOT( slotEntries( KIO::Job*,
02280                                const KIO::UDSEntryList& )));
02281     addSubjob( newjob );
02282 }
02283 
02284 void CopyJob::skip( const KURL & sourceUrl )
02285 {
02286     // Check if this is one if toplevel sources
02287     // IF yes, remove it from m_srcList, for a correct FilesRemoved() signal
02288     //kdDebug(7007) << "CopyJob::skip: looking for " << sourceUrl.prettyURL() << endl;
02289     KURL::List::Iterator sit = m_srcList.find( sourceUrl );
02290     if ( sit != m_srcList.end() )
02291     {
02292         //kdDebug(7007) << "CopyJob::skip: removing " << sourceUrl.prettyURL() << " from list" << endl;
02293         m_srcList.remove( sit );
02294     }
02295     dirsToRemove.remove( sourceUrl );
02296 }
02297 
02298 void CopyJob::slotResultCreatingDirs( Job * job )
02299 {
02300     // The dir we are trying to create:
02301     QValueList<CopyInfo>::Iterator it = dirs.begin();
02302     // Was there an error creating a dir ?
02303     if ( job->error() )
02304     {
02305         m_conflictError = job->error();
02306         if ( (m_conflictError == ERR_DIR_ALREADY_EXIST)
02307              || (m_conflictError == ERR_FILE_ALREADY_EXIST) )
02308         {
02309             KURL oldURL = ((SimpleJob*)job)->url();
02310             // Should we skip automatically ?
02311             if ( m_bAutoSkip ) {
02312                 // We dont want to copy files in this directory, so we put it on the skip list
02313                 m_skipList.append( oldURL.path( 1 ) );
02314                 skip( oldURL );
02315                 dirs.remove( it ); // Move on to next dir
02316             } else if ( m_bOverwriteAll ) { // overwrite all => just skip
02317                 emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ );
02318                 dirs.remove( it ); // Move on to next dir
02319             } else
02320             {
02321                 assert( ((SimpleJob*)job)->url().url() == (*it).uDest.url() );
02322                 subjobs.remove( job );
02323                 assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
02324 
02325                 // We need to stat the existing dir, to get its last-modification time
02326                 KURL existingDest( (*it).uDest );
02327                 SimpleJob * newJob = KIO::stat( existingDest, false, 2, false );
02328                 Scheduler::scheduleJob(newJob);
02329                 kdDebug(7007) << "KIO::stat for resolving conflict on " << existingDest.prettyURL() << endl;
02330                 state = STATE_CONFLICT_CREATING_DIRS;
02331                 addSubjob(newJob);
02332                 return; // Don't move to next dir yet !
02333             }
02334         }
02335         else
02336         {
02337             // Severe error, abort
02338             Job::slotResult( job ); // will set the error and emit result(this)
02339             return;
02340         }
02341     }
02342     else // no error : remove from list, to move on to next dir
02343     {
02344        //this is required for the undo feature
02345         emit copyingDone( this, (*it).uSource, (*it).uDest, true, false );
02346         dirs.remove( it );
02347     }
02348 
02349     m_processedDirs++;
02350     //emit processedDirs( this, m_processedDirs );
02351     subjobs.remove( job );
02352     assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
02353     createNextDir();
02354 }
02355 
02356 void CopyJob::slotResultConflictCreatingDirs( KIO::Job * job )
02357 {
02358     // We come here after a conflict has been detected and we've stated the existing dir
02359 
02360     // The dir we were trying to create:
02361     QValueList<CopyInfo>::Iterator it = dirs.begin();
02362     // Its modification time:
02363     time_t destmtime = (time_t)-1;
02364     time_t destctime = (time_t)-1;
02365     KIO::filesize_t destsize = 0;
02366     UDSEntry entry = ((KIO::StatJob*)job)->statResult();
02367     KIO::UDSEntry::ConstIterator it2 = entry.begin();
02368     for( ; it2 != entry.end(); it2++ ) {
02369         switch ((*it2).m_uds) {
02370             case UDS_MODIFICATION_TIME:
02371                 destmtime = (time_t)((*it2).m_long);
02372                 break;
02373             case UDS_CREATION_TIME:
02374                 destctime = (time_t)((*it2).m_long);
02375                 break;
02376             case UDS_SIZE:
02377                 destsize = (*it2).m_long;
02378                 break;
02379         }
02380     }
02381     subjobs.remove( job );
02382     assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
02383 
02384     // Always multi and skip (since there are files after that)
02385     RenameDlg_Mode mode = (RenameDlg_Mode)( M_MULTI | M_SKIP );
02386     // Overwrite only if the existing thing is a dir (no chance with a file)
02387     if ( m_conflictError == ERR_DIR_ALREADY_EXIST )
02388         mode = (RenameDlg_Mode)( mode | (((*it).uSource == (*it).uDest) ? M_OVERWRITE_ITSELF : M_OVERWRITE ));
02389 
02390     QString existingDest = (*it).uDest.path();
02391     QString newPath;
02392     if (m_reportTimer)
02393         m_reportTimer->stop();
02394     RenameDlg_Result r = Observer::self()->open_RenameDlg( this, i18n("Folder Already Exists"),
02395                                          (*it).uSource.prettyURL(0, KURL::StripFileProtocol),
02396                                          (*it).uDest.prettyURL(0, KURL::StripFileProtocol),
02397                                          mode, newPath,
02398                                          (*it).size, destsize,
02399                                          (*it).ctime, destctime,
02400                                          (*it).mtime, destmtime );
02401     if (m_reportTimer)
02402         m_reportTimer->start(REPORT_TIMEOUT,false);
02403     switch ( r ) {
02404         case R_CANCEL:
02405             m_error = ERR_USER_CANCELED;
02406             emitResult();
02407             return;
02408         case R_RENAME:
02409         {
02410             QString oldPath = (*it).uDest.path( 1 );
02411             KURL newUrl( (*it).uDest );
02412             newUrl.setPath( newPath );
02413             emit renamed( this, (*it).uDest, newUrl ); // for e.g. kpropsdlg
02414 
02415             // Change the current one and strip the trailing '/'
02416             (*it).uDest = newUrl.path( -1 );
02417             newPath = newUrl.path( 1 ); // With trailing slash
02418             QValueList<CopyInfo>::Iterator renamedirit = it;
02419             ++renamedirit;
02420             // Change the name of subdirectories inside the directory
02421             for( ; renamedirit != dirs.end() ; ++renamedirit )
02422             {
02423                 QString path = (*renamedirit).uDest.path();
02424                 if ( path.left(oldPath.length()) == oldPath ) {
02425                     QString n = path;
02426                     n.replace( 0, oldPath.length(), newPath );
02427                     kdDebug(7007) << "dirs list: " << (*renamedirit).uSource.path()
02428                                   << " was going to be " << path
02429                                   << ", changed into " << n << endl;
02430                     (*renamedirit).uDest.setPath( n );
02431                 }
02432             }
02433             // Change filenames inside the directory
02434             QValueList<CopyInfo>::Iterator renamefileit = files.begin();
02435             for( ; renamefileit != files.end() ; ++renamefileit )
02436             {
02437                 QString path = (*renamefileit).uDest.path();
02438                 if ( path.left(oldPath.length()) == oldPath ) {
02439                     QString n = path;
02440                     n.replace( 0, oldPath.length(), newPath );
02441                     kdDebug(7007) << "files list: " << (*renamefileit).uSource.path()
02442                                   << " was going to be " << path
02443                                   << ", changed into " << n << endl;
02444                     (*renamefileit).uDest.setPath( n );
02445                 }
02446             }
02447             if (!dirs.isEmpty())
02448                 emit aboutToCreate( this, dirs );
02449             if (!files.isEmpty())
02450                 emit aboutToCreate( this, files );
02451         }
02452         break;
02453         case R_AUTO_SKIP:
02454             m_bAutoSkip = true;
02455             // fall through
02456         case R_SKIP:
02457             m_skipList.append( existingDest );
02458             skip( (*it).uSource );
02459             // Move on to next dir
02460             dirs.remove( it );
02461             break;
02462         case R_OVERWRITE:
02463             m_overwriteList.append( existingDest );
02464             emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ );
02465             // Move on to next dir
02466             dirs.remove( it );
02467             break;
02468         case R_OVERWRITE_ALL:
02469             m_bOverwriteAll = true;
02470             emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ );
02471             // Move on to next dir
02472             dirs.remove( it );
02473             break;
02474         default:
02475             assert( 0 );
02476     }
02477     state = STATE_CREATING_DIRS;
02478     m_processedDirs++;
02479     //emit processedDirs( this, m_processedDirs );
02480     createNextDir();
02481 }
02482 
02483 void CopyJob::createNextDir()
02484 {
02485     KURL udir;
02486     if ( !dirs.isEmpty() )
02487     {
02488         // Take first dir to create out of list
02489         QValueList<CopyInfo>::Iterator it = dirs.begin();
02490         // Is this URL on the skip list or the overwrite list ?
02491         while( it != dirs.end() && udir.isEmpty() )
02492         {
02493             QString dir = (*it).uDest.path();
02494             bool bCreateDir = true; // we'll create it if it's not in any list
02495 
02496             QStringList::Iterator sit = m_skipList.begin();
02497             for( ; sit != m_skipList.end() && bCreateDir; sit++ )
02498                 // Is dir a subdirectory of *sit ?
02499                 if ( *sit == dir.left( (*sit).length() ) )
02500                     bCreateDir = false; // skip this dir
02501 
02502             if ( !bCreateDir ) {
02503                 dirs.remove( it );
02504                 it = dirs.begin();
02505             } else
02506                 udir = (*it).uDest;
02507         }
02508     }
02509     if ( !udir.isEmpty() ) // any dir to create, finally ?
02510     {
02511         // Create the directory - with default permissions so that we can put files into it
02512         // TODO : change permissions once all is finished
02513         KIO::SimpleJob *newjob = KIO::mkdir( udir, -1 );
02514         Scheduler::scheduleJob(newjob);
02515 
02516         m_currentDestURL = udir;
02517 
02518         addSubjob(newjob);
02519         return;
02520     }
02521     else // we have finished creating dirs
02522     {
02523         state = STATE_COPYING_FILES;
02524         m_processedFiles++; // Ralf wants it to start a 1, not 0
02525         copyNextFile();
02526     }
02527 }
02528 
02529 void CopyJob::slotResultCopyingFiles( Job * job )
02530 {
02531     // The file we were trying to copy:
02532     QValueList<CopyInfo>::Iterator it = files.begin();
02533     if ( job->error() )
02534     {
02535         // Should we skip automatically ?
02536         if ( m_bAutoSkip )
02537         {
02538             skip( (*it).uSource );
02539             files.remove( it ); // Move on to next file
02540         }
02541         else
02542         {
02543             m_conflictError = job->error(); // save for later
02544             // Existing dest ?
02545             if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
02546                  || ( m_conflictError == ERR_DIR_ALREADY_EXIST ) )
02547             {
02548                 subjobs.remove( job );
02549                 assert ( subjobs.isEmpty() );
02550                 // We need to stat the existing file, to get its last-modification time
02551                 KURL existingFile( (*it).uDest );
02552                 SimpleJob * newJob = KIO::stat( existingFile, false, 2, false );
02553                 Scheduler::scheduleJob(newJob);
02554                 kdDebug(7007) << "KIO::stat for resolving conflict on " << existingFile.prettyURL() << endl;
02555                 state = STATE_CONFLICT_COPYING_FILES;
02556                 addSubjob(newJob);
02557                 return; // Don't move to next file yet !
02558             }
02559             else
02560             {
02561                 if ( m_bCurrentOperationIsLink && job->inherits( "KIO::DeleteJob" ) )
02562                 {
02563                     // Very special case, see a few lines below
02564                     // We are deleting the source of a symlink we successfully moved... ignore error
02565                     files.remove( it );
02566                 } else {
02567                     // Go directly to the conflict resolution, there is nothing to stat
02568                     slotResultConflictCopyingFiles( job );
02569                     return;
02570                 }
02571             }
02572         }
02573     } else // no error
02574     {
02575         // Special case for moving links. That operation needs two jobs, unlike others.
02576         if ( m_bCurrentOperationIsLink && m_mode == Move
02577              && !job->inherits( "KIO::DeleteJob" ) // Deleting source not already done
02578              )
02579         {
02580             subjobs.remove( job );
02581             assert ( subjobs.isEmpty() );
02582             // The only problem with this trick is that the error handling for this del operation
02583             // is not going to be right... see 'Very special case' above.
02584             KIO::Job * newjob = KIO::del( (*it).uSource, false /*don't shred*/, false /*no GUI*/ );
02585             addSubjob( newjob );
02586             return; // Don't move to next file yet !
02587         }
02588 
02589         if ( m_bCurrentOperationIsLink )
02590         {
02591             QString target = ( m_mode == Link ? (*it).uSource.path() : (*it).linkDest );
02592             //required for the undo feature
02593             emit copyingLinkDone( this, (*it).uSource, target, (*it).uDest );
02594         }
02595         else
02596             //required for the undo feature
02597             emit copyingDone( this, (*it).uSource, (*it).uDest, false, false );
02598         // remove from list, to move on to next file
02599         files.remove( it );
02600     }
02601     m_processedFiles++;
02602 
02603     // clear processed size for last file and add it to overall processed size
02604     m_processedSize += m_fileProcessedSize;
02605     m_fileProcessedSize = 0;
02606 
02607     //kdDebug(7007) << files.count() << " files remaining" << endl;
02608     subjobs.remove( job );
02609     assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
02610     copyNextFile();
02611 }
02612 
02613 void CopyJob::slotResultConflictCopyingFiles( KIO::Job * job )
02614 {
02615     // We come here after a conflict has been detected and we've stated the existing file
02616     // The file we were trying to create:
02617     QValueList<CopyInfo>::Iterator it = files.begin();
02618 
02619     RenameDlg_Result res;
02620     QString newPath;
02621 
02622     if (m_reportTimer)
02623         m_reportTimer->stop();
02624 
02625     if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
02626       || ( m_conflictError == ERR_DIR_ALREADY_EXIST ) )
02627     {
02628         // Its modification time:
02629         time_t destmtime = (time_t)-1;
02630         time_t destctime = (time_t)-1;
02631         KIO::filesize_t destsize = 0;
02632         UDSEntry entry = ((KIO::StatJob*)job)->statResult();
02633         KIO::UDSEntry::ConstIterator it2 = entry.begin();
02634         for( ; it2 != entry.end(); it2++ ) {
02635             switch ((*it2).m_uds) {
02636                 case UDS_MODIFICATION_TIME:
02637                     destmtime = (time_t)((*it2).m_long);
02638                     break;
02639                 case UDS_CREATION_TIME:
02640                     destctime = (time_t)((*it2).m_long);
02641                     break;
02642                 case UDS_SIZE:
02643                     destsize = (*it2).m_long;
02644                     break;
02645             }
02646         }
02647 
02648         // Offer overwrite only if the existing thing is a file
02649         // If src==dest, use "overwrite-itself"
02650         RenameDlg_Mode mode = (RenameDlg_Mode)
02651             ( ( m_conflictError == ERR_DIR_ALREADY_EXIST ? 0 :
02652              ( (*it).uSource == (*it).uDest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE ) );
02653         if ( files.count() > 1 ) // Not last one
02654             mode = (RenameDlg_Mode) ( mode | M_MULTI | M_SKIP );
02655         else
02656             mode = (RenameDlg_Mode) ( mode | M_SINGLE );
02657         res = Observer::self()->open_RenameDlg( this, m_conflictError == ERR_FILE_ALREADY_EXIST ?
02658                                 i18n("File Already Exists") : i18n("Already Exists as Folder"),
02659                                 (*it).uSource.prettyURL(0, KURL::StripFileProtocol),
02660                                 (*it).uDest.prettyURL(0, KURL::StripFileProtocol),
02661                                 mode, newPath,
02662                               (*it).size, destsize,
02663                               (*it).ctime, destctime,
02664                               (*it).mtime, destmtime );
02665 
02666     }
02667     else
02668     {
02669         if ( job->error() == ERR_USER_CANCELED )
02670             res = R_CANCEL;
02671         else
02672         {
02673             SkipDlg_Result skipResult = Observer::self()->open_SkipDlg( this, files.count() > 1,
02674                                                                         job->errorString() );
02675 
02676             // Convert the return code from SkipDlg into a RenameDlg code
02677             res = ( skipResult == S_SKIP ) ? R_SKIP :
02678                          ( skipResult == S_AUTO_SKIP ) ? R_AUTO_SKIP :
02679                                         R_CANCEL;
02680         }
02681     }
02682 
02683     if (m_reportTimer)
02684         m_reportTimer->start(REPORT_TIMEOUT,false);
02685 
02686     subjobs.remove( job );
02687     assert ( subjobs.isEmpty() );
02688     switch ( res ) {
02689         case R_CANCEL:
02690             m_error = ERR_USER_CANCELED;
02691             emitResult();
02692             return;
02693         case R_RENAME:
02694         {
02695             KURL newUrl( (*it).uDest );
02696             newUrl.setPath( newPath );
02697             emit renamed( this, (*it).uDest, newUrl ); // for e.g. kpropsdlg
02698             (*it).uDest = newUrl;
02699 
02700             QValueList<CopyInfo> files;
02701             files.append(*it);
02702             emit aboutToCreate( this, files );
02703         }
02704         break;
02705         case R_AUTO_SKIP:
02706             m_bAutoSkip = true;
02707             // fall through
02708         case R_SKIP:
02709             // Move on to next file
02710             skip( (*it).uSource );
02711             files.remove( it );
02712             break;
02713        case R_OVERWRITE_ALL:
02714             m_bOverwriteAll = true;
02715             break;
02716         case R_OVERWRITE:
02717             // Add to overwrite list, so that copyNextFile knows to overwrite
02718             m_overwriteList.append( (*it).uDest.path() );
02719             break;
02720         default:
02721             assert( 0 );
02722     }
02723     state = STATE_COPYING_FILES;
02724     m_processedFiles++;
02725     //emit processedFiles( this, m_processedFiles );
02726     copyNextFile();
02727 }
02728 
02729 void CopyJob::copyNextFile()
02730 {
02731     bool bCopyFile = false;
02732     //kdDebug(7007) << "CopyJob::copyNextFile()" << endl;
02733     // Take the first file in the list
02734     QValueList<CopyInfo>::Iterator it = files.begin();
02735     // Is this URL on the skip list ?
02736     while (it != files.end() && !bCopyFile)
02737     {
02738         bCopyFile = true;
02739         QString destFile = (*it).uDest.path();
02740 
02741         QStringList::Iterator sit = m_skipList.begin();
02742         for( ; sit != m_skipList.end() && bCopyFile; sit++ )
02743             // Is destFile in *sit (or a subdirectory of *sit) ?
02744             if ( *sit == destFile.left( (*sit).length() ) )
02745                 bCopyFile = false; // skip this file
02746 
02747         if (!bCopyFile) {
02748             files.remove( it );
02749             it = files.begin();
02750         }
02751     }
02752 
02753     if (bCopyFile) // any file to create, finally ?
02754     {
02755         // Do we set overwrite ?
02756         bool bOverwrite = m_bOverwriteAll; // yes if overwrite all
02757         QString destFile = (*it).uDest.path();
02758         if ( (*it).uDest == (*it).uSource )
02759             bOverwrite = false;
02760         else
02761         {
02762             // or if on the overwrite list
02763             QStringList::Iterator sit = m_overwriteList.begin();
02764             for( ; sit != m_overwriteList.end() && !bOverwrite; sit++ )
02765                 if ( *sit == destFile.left( (*sit).length() ) )
02766                     bOverwrite = true;
02767         }
02768 
02769         m_bCurrentOperationIsLink = false;
02770         KIO::Job * newjob = 0L;
02771         if ( m_mode == Link )
02772         {
02773             //kdDebug(7007) << "Linking" << endl;
02774             if (
02775                 ((*it).uSource.protocol() == (*it).uDest.protocol()) &&
02776                 ((*it).uSource.host() == (*it).uDest.host()) &&
02777                 ((*it).uSource.port() == (*it).uDest.port()) &&
02778                 ((*it).uSource.user() == (*it).uDest.user()) &&
02779                 ((*it).uSource.pass() == (*it).uDest.pass()) )
02780             {
02781                 // This is the case of creating a real symlink
02782                 KIO::SimpleJob *newJob = KIO::symlink( (*it).uSource.path(), (*it).uDest, bOverwrite, false /*no GUI*/ );
02783                 newjob = newJob;
02784                 Scheduler::scheduleJob(newJob);
02785                 //kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).uSource.path() << " link=" << (*it).uDest.prettyURL() << endl;
02786                 //emit linking( this, (*it).uSource.path(), (*it).uDest );
02787                 m_bCurrentOperationIsLink = true;
02788                 m_currentSrcURL=(*it).uSource;
02789                 m_currentDestURL=(*it).uDest;
02790                 //Observer::self()->slotCopying( this, (*it).uSource, (*it).uDest ); // should be slotLinking perhaps
02791             } else {
02792                 //kdDebug(7007) << "CopyJob::copyNextFile : Linking URL=" << (*it).uSource.prettyURL() << " link=" << (*it).uDest.prettyURL() << endl;
02793                 if ( (*it).uDest.isLocalFile() )
02794                 {
02795                     bool devicesOk=false;
02796 
02797                     // if the source is a devices url, handle it a littlebit special
02798                     if ((*it).uSource.protocol()==QString::fromLatin1("devices"))
02799                     {
02800                        QByteArray data;
02801                        QByteArray param;
02802                        QCString retType;
02803                        QDataStream streamout(param,IO_WriteOnly);
02804                        streamout<<(*it).uSource;
02805                        streamout<<(*it).uDest;
02806                        if ( kapp->dcopClient()->call( "kded",
02807                             "mountwatcher", "createLink(KURL, KURL)", param,retType,data,false ) )
02808                        {
02809                           QDataStream streamin(data,IO_ReadOnly);
02810                           streamin>>devicesOk;
02811                        }
02812                        if (devicesOk)
02813                        {
02814                            files.remove( it );
02815                            m_processedFiles++;
02816                            //emit processedFiles( this, m_processedFiles );
02817                            copyNextFile();
02818                            return;
02819                        }
02820                     }
02821 
02822                     if (!devicesOk)
02823                     {
02824                        QString path = (*it).uDest.path();
02825                        //kdDebug(7007) << "CopyJob::copyNextFile path=" << path << endl;
02826                        QFile f( path );
02827                        if ( f.open( IO_ReadWrite ) )
02828                        {
02829                            f.close();
02830                            KSimpleConfig config( path );
02831                            config.setDesktopGroup();
02832                            config.writePathEntry( QString::fromLatin1("URL"), (*it).uSource.url() );
02833                            config.writeEntry( QString::fromLatin1("Name"), (*it).uSource.url() );
02834                            config.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("Link") );
02835                            QString protocol = (*it).uSource.protocol();
02836                            if ( protocol == QString::fromLatin1("ftp") )
02837                                config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("ftp") );
02838                            else if ( protocol == QString::fromLatin1("http") )
02839                                config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("www") );
02840                            else if ( protocol == QString::fromLatin1("info") )
02841                                config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("info") );
02842                            else if ( protocol == QString::fromLatin1("mailto") )   // sven:
02843                                config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("kmail") ); // added mailto: support
02844                            else
02845                                config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("unknown") );
02846                            config.sync();
02847                            files.remove( it );
02848                            m_processedFiles++;
02849                            //emit processedFiles( this, m_processedFiles );
02850                            copyNextFile();
02851                            return;
02852                        }
02853                        else
02854                        {
02855                            kdDebug(7007) << "CopyJob::copyNextFile ERR_CANNOT_OPEN_FOR_WRITING" << endl;
02856                            m_error = ERR_CANNOT_OPEN_FOR_WRITING;
02857                            m_errorText = (*it).uDest.path();
02858                            emitResult();
02859                            return;
02860                        }
02861                     }
02862                 } else {
02863                     // Todo: not show "link" on remote dirs if the src urls are not from the same protocol+host+...
02864                     m_error = ERR_CANNOT_SYMLINK;
02865                     m_errorText = (*it).uDest.prettyURL();
02866                     emitResult();
02867                     return;
02868                 }
02869             }
02870         }
02871         else if ( !(*it).linkDest.isEmpty() &&
02872                   ((*it).uSource.protocol() == (*it).uDest.protocol()) &&
02873                   ((*it).uSource.host() == (*it).uDest.host()) &&
02874                   ((*it).uSource.port() == (*it).uDest.port()) &&
02875                   ((*it).uSource.user() == (*it).uDest.user()) &&
02876                   ((*it).uSource.pass() == (*it).uDest.pass()))
02877             // Copying a symlink - only on the same protocol/host/etc. (#5601, downloading an FTP file through its link),
02878         {
02879             KIO::SimpleJob *newJob = KIO::symlink( (*it).linkDest, (*it).uDest, bOverwrite, false /*no GUI*/ );
02880             Scheduler::scheduleJob(newJob);
02881             newjob = newJob;
02882             //kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).linkDest << " link=" << (*it).uDest.prettyURL() << endl;
02883             //emit linking( this, (*it).linkDest, (*it).uDest );
02884             m_currentSrcURL=(*it).linkDest;
02885             m_currentDestURL=(*it).uDest;
02886             //Observer::self()->slotCopying( this, (*it).linkDest, (*it).uDest ); // should be slotLinking perhaps
02887             m_bCurrentOperationIsLink = true;
02888             // NOTE: if we are moving stuff, the deletion of the source will be done in slotResultCopyingFiles
02889         } else if (m_mode == Move) // Moving a file
02890         {
02891             KIO::FileCopyJob * moveJob = KIO::file_move( (*it).uSource, (*it).uDest, (*it).permissions, bOverwrite, false, false/*no GUI*/ );
02892             moveJob->setSourceSize64( (*it).size );
02893             newjob = moveJob;
02894             //kdDebug(7007) << "CopyJob::copyNextFile : Moving " << (*it).uSource.prettyURL() << " to " << (*it).uDest.prettyURL() << endl;
02895             //emit moving( this, (*it).uSource, (*it).uDest );
02896             m_currentSrcURL=(*it).uSource;
02897             m_currentDestURL=(*it).uDest;
02898             //Observer::self()->slotMoving( this, (*it).uSource, (*it).uDest );
02899         }
02900         else // Copying a file
02901         {
02902             // If source isn't local and target is local, we ignore the original permissions
02903             // Otherwise, files downloaded from HTTP end up with -r--r--r--
02904             // But for files coming from TAR, we want to preserve permissions -> we use default perms only if from remote
02905             // The real fix would be KProtocolInfo::inputType(protocol) == T_FILESYSTEM, but we can't access ksycoca from here !
02906             bool remoteSource = !(*it).uSource.isLocalFile() && ((*it).uSource.protocol() != "tar"); // HACK
02907             int permissions = ( remoteSource && (*it).uDest.isLocalFile() ) ? -1 : (*it).permissions;
02908             KIO::FileCopyJob * copyJob = KIO::file_copy( (*it).uSource, (*it).uDest, permissions, bOverwrite, false, false/*no GUI*/ );
02909             copyJob->setParentJob( this ); // in case of rename dialog
02910             copyJob->setSourceSize64( (*it).size );
02911             newjob = copyJob;
02912             //kdDebug(7007) << "CopyJob::copyNextFile : Copying " << (*it).uSource.prettyURL() << " to " << (*it).uDest.prettyURL() << endl;
02913             m_currentSrcURL=(*it).uSource;
02914             m_currentDestURL=(*it).uDest;
02915         }
02916         addSubjob(newjob);
02917         connect( newjob, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ),
02918                  this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) );
02919         connect( newjob, SIGNAL( totalSize( KIO::Job*, KIO::filesize_t ) ),
02920                  this, SLOT( slotTotalSize( KIO::Job*, KIO::filesize_t ) ) );
02921     }
02922     else
02923     {
02924         // We're done
02925         //kdDebug(7007) << "copyNextFile finished" << endl;
02926         deleteNextDir();
02927     }
02928 }
02929 
02930 void CopyJob::deleteNextDir()
02931 {
02932     if ( m_mode == Move && !dirsToRemove.isEmpty() ) // some dirs to delete ?
02933     {
02934         state = STATE_DELETING_DIRS;
02935         // Take first dir to delete out of list - last ones first !
02936         KURL::List::Iterator it = dirsToRemove.fromLast();
02937         SimpleJob *job = KIO::rmdir( *it );
02938         Scheduler::scheduleJob(job);
02939         dirsToRemove.remove(it);
02940         addSubjob( job );
02941     }
02942     else
02943     {
02944         // Finished - tell the world
02945         if ( !m_bOnlyRenames )
02946         {
02947             KDirNotify_stub allDirNotify("*", "KDirNotify*");
02948             KURL url( m_dest );
02949             if ( destinationState != DEST_IS_DIR || m_asMethod )
02950                 url.setPath( url.directory() );
02951             //kdDebug(7007) << "KDirNotify'ing FilesAdded " << url.prettyURL() << endl;
02952             allDirNotify.FilesAdded( url );
02953 
02954             if ( m_mode == Move && !m_srcList.isEmpty() )
02955                 allDirNotify.FilesRemoved( m_srcList );
02956         }
02957         if (m_reportTimer!=0)
02958             m_reportTimer->stop();
02959         emitResult();
02960     }
02961 }
02962 
02963 void CopyJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size )
02964 {
02965   //kdDebug(7007) << "CopyJob::slotProcessedSize " << (unsigned long)data_size << endl;
02966   m_fileProcessedSize = data_size;
02967   setProcessedSize(m_processedSize + m_fileProcessedSize);
02968 
02969   if ( m_processedSize + m_fileProcessedSize > m_totalSize )
02970   {
02971     m_totalSize = m_processedSize + m_fileProcessedSize;
02972     //kdDebug(7007) << "Adjusting m_totalSize to " << (unsigned long) m_totalSize << endl;
02973     emit totalSize( this, m_totalSize ); // safety
02974   }
02975   //kdDebug(7007) << "emit processedSize " << (unsigned long) (m_processedSize + m_fileProcessedSize) << endl;
02976   emit processedSize( this, m_processedSize + m_fileProcessedSize );
02977   emitPercent( m_processedSize + m_fileProcessedSize, m_totalSize );
02978 }
02979 
02980 void CopyJob::slotTotalSize( KIO::Job*, KIO::filesize_t size )
02981 {
02982   // Special case for copying a single file
02983   // This is because some protocols don't implement stat properly
02984   // (e.g. HTTP), and don't give us a size in some cases (redirection)
02985   // so we'd rather rely on the size given for the transfer
02986   if ( m_bSingleFileCopy )
02987   {
02988     //kdDebug(7007) << "Single file -> updating totalsize to " << (long)size << endl;
02989     m_totalSize = size;
02990     emit totalSize( this, size );
02991   }
02992 }
02993 
02994 void CopyJob::slotResultDeletingDirs( Job * job )
02995 {
02996     if (job->error())
02997     {
02998         // Couldn't remove directory. Well, perhaps it's not empty
02999         // because the user pressed Skip for a given file in it.
03000         // Let's not display "Could not remove dir ..." for each of those dir !
03001     }
03002     subjobs.remove( job );
03003     assert ( subjobs.isEmpty() );
03004     deleteNextDir();
03005 }
03006 
03007 void CopyJob::slotResult( Job *job )
03008 {
03009     //kdDebug(7007) << "CopyJob::slotResult() state=" << (int) state << endl;
03010     // In each case, what we have to do is :
03011     // 1 - check for errors and treat them
03012     // 2 - subjobs.remove(job);
03013     // 3 - decide what to do next
03014 
03015     switch ( state ) {
03016         case STATE_STATING: // We were trying to stat a src url or the dest
03017             slotResultStating( job );
03018             break;
03019         case STATE_RENAMING: // We were trying to do a direct renaming, before even stat'ing
03020         {
03021             int err = job->error();
03022             subjobs.remove( job );
03023             assert ( subjobs.isEmpty() );
03024             // Determine dest again
03025             KURL dest = m_dest;
03026             if ( destinationState == DEST_IS_DIR && !m_asMethod )
03027                 dest.addPath( m_currentSrcURL.fileName() );
03028             if ( err )
03029             {
03030                 // Direct renaming didn't work. Try renaming to a temp name,
03031                 // this can help e.g. when renaming 'a' to 'A' on a VFAT partition.
03032                 // In that case it's the _same_ dir, we don't want to copy+del (data loss!)
03033                 if ( m_currentSrcURL.isLocalFile() && m_currentSrcURL.url(-1) != dest.url(-1) &&
03034                      m_currentSrcURL.url(-1).lower() == dest.url(-1).lower() &&
03035                      ( err == ERR_FILE_ALREADY_EXIST || err == ERR_DIR_ALREADY_EXIST ) )
03036                 {
03037                     kdDebug(7007) << "Couldn't rename directly, dest already exists. Detected special case of lower/uppercase renaming in same dir, try with 2 rename calls" << endl;
03038                     QCString _src( QFile::encodeName(m_currentSrcURL.path()) );
03039                     QCString _dest( QFile::encodeName(dest.path()) );
03040                     KTempFile tmpFile( m_currentSrcURL.directory(false) );
03041                     QCString _tmp( QFile::encodeName(tmpFile.name()) );
03042                     kdDebug(7007) << "CopyJob::slotResult KTempFile status:" << tmpFile.status() << " using " << _tmp << " as intermediary" << endl;
03043                     tmpFile.unlink();
03044                     if ( ::rename( _src, _tmp ) == 0 )
03045                     {
03046                         if ( !QFile::exists( _dest ) && ::rename( _tmp, _dest ) == 0 )
03047                         {
03048                             kdDebug(7007) << "Success." << endl;
03049                             err = 0;
03050                         }
03051                         else
03052                         {
03053                             // Revert back to original name!
03054                             if ( ::rename( _tmp, _src ) != 0 ) {
03055                                 kdError(7007) << "Couldn't rename " << tmpFile.name() << " back to " << _src << " !" << endl;
03056                                 // Severe error, abort
03057                                 Job::slotResult( job ); // will set the error and emit result(this)
03058                                 return;
03059                             }
03060                         }
03061                     }
03062                 }
03063             }
03064             if ( err )
03065             {
03066                 m_currentSrcURL=*m_currentStatSrc;
03067                 m_currentDestURL=m_dest;
03068 
03069                 if ( err == ERR_DIR_ALREADY_EXIST || err == ERR_FILE_ALREADY_EXIST )
03070                 {
03071                     if (m_reportTimer)
03072                         m_reportTimer->stop();
03073 
03074                     QString newPath;
03075                     RenameDlg_Mode mode = M_SINGLE;
03076                     RenameDlg_Result r = Observer::self()->open_RenameDlg( this,
03077                                          err == ERR_FILE_ALREADY_EXIST ? i18n("File Already Exists") : i18n("Already Exists as Folder"),
03078                                          m_currentSrcURL.prettyURL(0, KURL::StripFileProtocol),
03079                                          m_currentDestURL.prettyURL(0, KURL::StripFileProtocol),
03080                                          mode, newPath );
03081                     if (m_reportTimer)
03082                         m_reportTimer->start(REPORT_TIMEOUT,false);
03083 
03084                     switch ( r )
03085                     {
03086                         case R_CANCEL:
03087                         {
03088                             m_error = ERR_USER_CANCELED;
03089                             emitResult();
03090                             return;
03091                         }
03092                         case R_RENAME:
03093                         {
03094                             m_dest.setPath( newPath );
03095                             KIO::Job* job = KIO::stat( m_dest, false, 2, false );
03096                             state = STATE_STATING;
03097                             destinationState = DEST_NOT_STATED;
03098                             addSubjob(job);
03099                             break;
03100                         }
03101                         default:
03102                             //assert( 0 );
03103                             break;
03104                     }
03105                 }
03106                 else
03107                 {
03108                     kdDebug(7007) << "Couldn't rename, reverting to normal way, starting with stat" << endl;
03109                     //kdDebug(7007) << "KIO::stat on " << m_currentSrcURL.prettyURL() << endl;
03110                     KIO::Job* job = KIO::stat( m_currentSrcURL, true, 2, false );
03111                     state = STATE_STATING;
03112                     addSubjob(job);
03113                     m_bOnlyRenames = false;
03114                 }
03115             }
03116             else
03117             {
03118                 //kdDebug(7007) << "Renaming succeeded, move on" << endl;
03119                 emit copyingDone( this, *m_currentStatSrc, dest, true, true );
03120                 ++m_currentStatSrc;
03121                 statNextSrc();
03122             }
03123         }
03124         break;
03125         case STATE_LISTING: // recursive listing finished
03126             //kdDebug(7007) << "totalSize: " << (unsigned int) m_totalSize << " files: " << files.count() << " dirs: " << dirs.count() << endl;
03127             // Was there an error ?
03128             if (job->error())
03129             {
03130                 Job::slotResult( job ); // will set the error and emit result(this)
03131                 return;
03132             }
03133 
03134             subjobs.remove( job );
03135             assert ( subjobs.isEmpty() );
03136 
03137             ++m_currentStatSrc;
03138             statNextSrc();
03139             break;
03140         case STATE_CREATING_DIRS:
03141             slotResultCreatingDirs( job );
03142             break;
03143         case STATE_CONFLICT_CREATING_DIRS:
03144             slotResultConflictCreatingDirs( job );
03145             break;
03146         case STATE_COPYING_FILES:
03147             slotResultCopyingFiles( job );
03148             break;
03149         case STATE_CONFLICT_COPYING_FILES:
03150             slotResultConflictCopyingFiles( job );
03151             break;
03152         case STATE_DELETING_DIRS:
03153             slotResultDeletingDirs( job );
03154             break;
03155         default:
03156             assert( 0 );
03157     }
03158 }
03159 
03160 CopyJob *KIO::copy(const KURL& src, const KURL& dest, bool showProgressInfo )
03161 {
03162     //kdDebug(7007) << "KIO::copy src=" << src.url() << " dest=" << dest.url() << endl;
03163     KURL::List srcList;
03164     srcList.append( src );
03165     return new CopyJob( srcList, dest, CopyJob::Copy, false, showProgressInfo );
03166 }
03167 
03168 CopyJob *KIO::copyAs(const KURL& src, const KURL& dest, bool showProgressInfo )
03169 {
03170     //kdDebug(7007) << "KIO::copyAs src=" << src.url() << " dest=" << dest.url() << endl;
03171     KURL::List srcList;
03172     srcList.append( src );
03173     return new CopyJob( srcList, dest, CopyJob::Copy, true, showProgressInfo );
03174 }
03175 
03176 CopyJob *KIO::copy( const KURL::List& src, const KURL& dest, bool showProgressInfo )
03177 {
03178     return new CopyJob( src, dest, CopyJob::Copy, false, showProgressInfo );
03179 }
03180 
03181 CopyJob *KIO::move(const KURL& src, const KURL& dest, bool showProgressInfo )
03182 {
03183     KURL::List srcList;
03184     srcList.append( src );
03185     return new CopyJob( srcList, dest, CopyJob::Move, false, showProgressInfo );
03186 }
03187 
03188 CopyJob *KIO::moveAs(const KURL& src, const KURL& dest, bool showProgressInfo )
03189 {
03190     KURL::List srcList;
03191     srcList.append( src );
03192     return new CopyJob( srcList, dest, CopyJob::Move, true, showProgressInfo );
03193 }
03194 
03195 CopyJob *KIO::move( const KURL::List& src, const KURL& dest, bool showProgressInfo )
03196 {
03197     return new CopyJob( src, dest, CopyJob::Move, false, showProgressInfo );
03198 }
03199 
03200 CopyJob *KIO::link(const KURL& src, const KURL& destDir, bool showProgressInfo )
03201 {
03202     KURL::List srcList;
03203     srcList.append( src );
03204     return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
03205 }
03206 
03207 CopyJob *KIO::link(const KURL::List& srcList, const KURL& destDir, bool showProgressInfo )
03208 {
03209     return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
03210 }
03211 
03212 CopyJob *KIO::linkAs(const KURL& src, const KURL& destDir, bool showProgressInfo )
03213 {
03214     KURL::List srcList;
03215     srcList.append( src );
03216     return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
03217 }
03218 
03220 
03221 DeleteJob::DeleteJob( const KURL::List& src, bool shred, bool showProgressInfo )
03222 : Job(showProgressInfo), m_totalSize( 0 ), m_processedSize( 0 ), m_fileProcessedSize( 0 ),
03223   m_processedFiles( 0 ), m_processedDirs( 0 ), m_totalFilesDirs( 0 ),
03224   m_srcList(src), m_currentStat(m_srcList.begin()), m_shred(shred), m_reportTimer(0)
03225 {
03226   if ( showProgressInfo ) {
03227 
03228      connect( this, SIGNAL( totalFiles( KIO::Job*, unsigned long ) ),
03229               Observer::self(), SLOT( slotTotalFiles( KIO::Job*, unsigned long ) ) );
03230 
03231      connect( this, SIGNAL( totalDirs( KIO::Job*, unsigned long ) ),
03232               Observer::self(), SLOT( slotTotalDirs( KIO::Job*, unsigned long ) ) );
03233 
03234      // See slotReport
03235      /*connect( this, SIGNAL( processedFiles( KIO::Job*, unsigned long ) ),
03236       m_observer, SLOT( slotProcessedFiles( KIO::Job*, unsigned long ) ) );
03237 
03238       connect( this, SIGNAL( processedDirs( KIO::Job*, unsigned long ) ),
03239       m_observer, SLOT( slotProcessedDirs( KIO::Job*, unsigned long ) ) );
03240 
03241       connect( this, SIGNAL( deleting( KIO::Job*, const KURL& ) ),
03242       m_observer, SLOT( slotDeleting( KIO::Job*, const KURL& ) ) );*/
03243 
03244      m_reportTimer=new QTimer(this);
03245      connect(m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
03246      //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
03247      m_reportTimer->start(REPORT_TIMEOUT,false);
03248   }
03249 
03250   QTimer::singleShot(0, this, SLOT(slotStart()));
03251 }
03252 
03253 void DeleteJob::slotStart()
03254 {
03255   statNextSrc();
03256 }
03257 
03258 //this is called often, so calling the functions
03259 //from Observer here directly might improve the performance a little bit
03260 //aleXXX
03261 void DeleteJob::slotReport()
03262 {
03263    if (m_progressId==0)
03264       return;
03265 
03266    Observer * observer = Observer::self();
03267 
03268    emit deleting( this, m_currentURL );
03269    observer->slotDeleting(this,m_currentURL);
03270 
03271    switch( state ) {
03272         case STATE_STATING:
03273         case STATE_LISTING:
03274             emit totalSize( this, m_totalSize );
03275             emit totalFiles( this, files.count() );
03276             emit totalDirs( this, dirs.count() );
03277             break;
03278         case STATE_DELETING_DIRS:
03279             emit processedDirs( this, m_processedDirs );
03280             observer->slotProcessedDirs(this,m_processedDirs);
03281             emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
03282             break;
03283         case STATE_DELETING_FILES:
03284             observer->slotProcessedFiles(this,m_processedFiles);
03285             emit processedFiles( this, m_processedFiles );
03286             if (!m_shred)
03287                emitPercent( m_processedFiles, m_totalFilesDirs );
03288             break;
03289    }
03290 }
03291 
03292 
03293 void DeleteJob::slotEntries(KIO::Job* job, const UDSEntryList& list)
03294 {
03295    UDSEntryListConstIterator it = list.begin();
03296    UDSEntryListConstIterator end = list.end();
03297    for (; it != end; ++it)
03298    {
03299       UDSEntry::ConstIterator it2 = (*it).begin();
03300       bool bDir = false;
03301       bool bLink = false;
03302       QString relName;
03303       int atomsFound(0);
03304       for( ; it2 != (*it).end(); it2++ )
03305       {
03306          switch ((*it2).m_uds)
03307          {
03308          case UDS_FILE_TYPE:
03309             bDir = S_ISDIR((*it2).m_long);
03310             atomsFound++;
03311             break;
03312          case UDS_NAME:
03313             relName = ((*it2).m_str);
03314             atomsFound++;
03315             break;
03316          case UDS_LINK_DEST:
03317             bLink = !(*it2).m_str.isEmpty();
03318             atomsFound++;
03319             break;
03320          case UDS_SIZE:
03321             m_totalSize += (KIO::filesize_t)((*it2).m_long);
03322             atomsFound++;
03323             break;
03324          default:
03325             break;
03326          }
03327          if (atomsFound==4) break;
03328       }
03329       assert(!relName.isEmpty());
03330       if (relName != ".." && relName != ".")
03331       {
03332          KURL url = ((SimpleJob *)job)->url(); // assumed to be a dir
03333          url.addPath( relName );
03334          //kdDebug(7007) << "DeleteJob::slotEntries " << relName << " (" << url.prettyURL() << ")" << endl;
03335          if ( bLink )
03336             symlinks.append( url );
03337          else if ( bDir )
03338             dirs.append( url );
03339          else
03340             files.append( url );
03341       }
03342    }
03343 }
03344 
03345 
03346 void DeleteJob::statNextSrc()
03347 {
03348     //kdDebug(7007) << "statNextSrc" << endl;
03349     if ( m_currentStat != m_srcList.end() )
03350     {
03351         m_currentURL = (*m_currentStat);
03352 
03353         // if the file system doesn't support deleting, we do not even stat
03354         if (!KProtocolInfo::supportsDeleting(m_currentURL)) {
03355             KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyURL()));
03356             ++m_currentStat;
03357             statNextSrc(); // we could use a loop instead of a recursive call :)
03358             return;
03359         }
03360         // Stat it
03361         state = STATE_STATING;
03362         KIO::SimpleJob * job = KIO::stat( m_currentURL, true, 1, false );
03363         Scheduler::scheduleJob(job);
03364         //kdDebug(7007) << "KIO::stat (DeleteJob) " << m_currentURL.prettyURL() << endl;
03365         addSubjob(job);
03366         //if ( m_progressId ) // Did we get an ID from the observer ?
03367         //  Observer::self()->slotDeleting( this, *it ); // show asap
03368     } else
03369     {
03370         m_totalFilesDirs = files.count()+symlinks.count() + dirs.count();
03371         slotReport();
03372         // Now we know which dirs hold the files we're going to delete.
03373         // To speed things up and prevent double-notification, we disable KDirWatch
03374         // on those dirs temporarily (using KDirWatch::self, that's the instanced
03375         // used by e.g. kdirlister).
03376         for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it )
03377             KDirWatch::self()->stopDirScan( *it );
03378         state = STATE_DELETING_FILES;
03379     deleteNextFile();
03380     }
03381 }
03382 
03383 void DeleteJob::deleteNextFile()
03384 {
03385     //kdDebug(7007) << "deleteNextFile" << endl;
03386     if ( !files.isEmpty() || !symlinks.isEmpty() )
03387     {
03388         SimpleJob *job;
03389         do {
03390             // Take first file to delete out of list
03391             KURL::List::Iterator it = files.begin();
03392             bool isLink = false;
03393             if ( it == files.end() ) // No more files
03394             {
03395                 it = symlinks.begin(); // Pick up a symlink to delete
03396                 isLink = true;
03397             }
03398             // Use shredding ?
03399             if ( m_shred && (*it).isLocalFile() && !isLink )
03400             {
03401                 // KShred your KTie
03402                 KIO_ARGS << int(3) << (*it).path();
03403                 job = KIO::special(KURL("file:/"), packedArgs, false /*no GUI*/);
03404                 Scheduler::scheduleJob(job);
03405                 m_currentURL=(*it);
03406                 connect( job, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ),
03407                          this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) );
03408             } else
03409             {
03410                 // Normal deletion
03411                 // If local file, try do it directly
03412                 if ( (*it).isLocalFile() && unlink( QFile::encodeName((*it).path()) ) == 0 ) {
03413                     job = 0;
03414                     m_processedFiles++;
03415                     if ( m_processedFiles % 300 == 0 ) { // update progress info every 300 files
03416                         m_currentURL = *it;
03417                         slotReport();
03418                     }
03419                 } else
03420                 { // if remote - or if unlink() failed (we'll use the job's error handling in that case)
03421                     job = KIO::file_delete( *it, false /*no GUI*/);
03422                     Scheduler::scheduleJob(job);
03423                     m_currentURL=(*it);
03424                 }
03425             }
03426             if ( isLink )
03427                 symlinks.remove(it);
03428             else
03429                 files.remove(it);
03430             if ( job ) {
03431                 addSubjob(job);
03432                 return;
03433             }
03434             // loop only if direct deletion worked (job=0) and there is something else to delete
03435         } while (!job && (!files.isEmpty() || !symlinks.isEmpty()));
03436     }
03437     state = STATE_DELETING_DIRS;
03438     deleteNextDir();
03439 }
03440 
03441 void DeleteJob::deleteNextDir()
03442 {
03443     if ( !dirs.isEmpty() ) // some dirs to delete ?
03444     {
03445         do {
03446             // Take first dir to delete out of list - last ones first !
03447             KURL::List::Iterator it = dirs.fromLast();
03448             // If local dir, try to rmdir it directly
03449             if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).path()) ) == 0 ) {
03450 
03451                 m_processedDirs++;
03452                 if ( m_processedDirs % 100 == 0 ) { // update progress info every 100 dirs
03453                     m_currentURL = *it;
03454                     slotReport();
03455                 }
03456             } else
03457             {
03458                 SimpleJob *job = KIO::rmdir( *it );
03459                 Scheduler::scheduleJob(job);
03460                 dirs.remove(it);
03461                 addSubjob( job );
03462                 return;
03463             }
03464             dirs.remove(it);
03465         } while ( !dirs.isEmpty() );
03466     }
03467 
03468     // Re-enable watching on the dirs that held the deleted files
03469     for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it )
03470         KDirWatch::self()->restartDirScan( *it );
03471 
03472     // Finished - tell the world
03473     if ( !m_srcList.isEmpty() )
03474     {
03475         KDirNotify_stub allDirNotify("*", "KDirNotify*");
03476         allDirNotify.FilesRemoved( m_srcList );
03477     }
03478     if (m_reportTimer!=0)
03479        m_reportTimer->stop();
03480     emitResult();
03481 }
03482 
03483 void DeleteJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size )
03484 {
03485    // Note: this is the same implementation as CopyJob::slotProcessedSize but
03486    // it's different from FileCopyJob::slotProcessedSize - which is why this
03487    // is not in Job.
03488 
03489    m_fileProcessedSize = data_size;
03490    setProcessedSize(m_processedSize + m_fileProcessedSize);
03491 
03492    //kdDebug(7007) << "DeleteJob::slotProcessedSize " << (unsigned int) (m_processedSize + m_fileProcessedSize) << endl;
03493 
03494    emit processedSize( this, m_processedSize + m_fileProcessedSize );
03495 
03496    // calculate percents
03497    unsigned long ipercent = m_percent;
03498 
03499    if ( m_totalSize == 0 )
03500       m_percent = 100;
03501    else
03502       m_percent = (unsigned long)(( (float)(m_processedSize + m_fileProcessedSize) / (float)m_totalSize ) * 100.0);
03503 
03504    if ( m_percent > ipercent )
03505    {
03506       emit percent( this, m_percent );
03507       //kdDebug(7007) << "DeleteJob::slotProcessedSize - percent =  " << (unsigned int) m_percent << endl;
03508    }
03509 
03510 }
03511 
03512 void DeleteJob::slotResult( Job *job )
03513 {
03514    switch ( state )
03515    {
03516    case STATE_STATING:
03517       {
03518          // Was there an error while stating ?
03519          if (job->error() )
03520          {
03521             // Probably : doesn't exist
03522             Job::slotResult( job ); // will set the error and emit result(this)
03523             return;
03524          }
03525 
03526          // Is it a file or a dir ?
03527          UDSEntry entry = ((StatJob*)job)->statResult();
03528          bool bDir = false;
03529          bool bLink = false;
03530          KIO::filesize_t size = (KIO::filesize_t)-1;
03531          UDSEntry::ConstIterator it2 = entry.begin();
03532          int atomsFound(0);
03533          for( ; it2 != entry.end(); it2++ )
03534          {
03535             if ( ((*it2).m_uds) == UDS_FILE_TYPE )
03536             {
03537                bDir = S_ISDIR( (mode_t)(*it2).m_long );
03538                atomsFound++;
03539             }
03540             else if ( ((*it2).m_uds) == UDS_LINK_DEST )
03541             {
03542                bLink = !((*it2).m_str.isEmpty());
03543                atomsFound++;
03544             }
03545             else if ( ((*it2).m_uds) == UDS_SIZE )
03546             {
03547                size = (*it2).m_long;
03548                atomsFound++;
03549             };
03550             if (atomsFound==3) break;
03551          }
03552 
03553          KURL url = ((SimpleJob*)job)->url();
03554 
03555          subjobs.remove( job );
03556          assert( subjobs.isEmpty() );
03557 
03558          if (bDir && !bLink)
03559          {
03560             // Add toplevel dir in list of dirs
03561             dirs.append( url );
03562             if ( url.isLocalFile() && !m_parentDirs.contains( url.path(-1) ) )
03563                 m_parentDirs.append( url.path(-1) );
03564 
03565             //kdDebug(7007) << " Target is a directory " << endl;
03566             // List it
03567             state = STATE_LISTING;
03568             ListJob *newjob = listRecursive( url, false );
03569             newjob->setUnrestricted(true); // No KIOSK restrictions
03570             Scheduler::scheduleJob(newjob);
03571             connect(newjob, SIGNAL(entries( KIO::Job *,
03572                                             const KIO::UDSEntryList& )),
03573                     SLOT( slotEntries( KIO::Job*,
03574                                        const KIO::UDSEntryList& )));
03575             addSubjob(newjob);
03576          }
03577          else
03578          {
03579             if ( bLink ) {
03580                 //kdDebug(7007) << " Target is a symlink" << endl;
03581                 symlinks.append( url );
03582             } else {
03583                 //kdDebug(7007) << " Target is a file" << endl;
03584                 files.append( url );
03585             }
03586             if ( url.isLocalFile() && !m_parentDirs.contains( url.directory(-1) ) )
03587                 m_parentDirs.append( url.directory(-1) );
03588             ++m_currentStat;
03589             statNextSrc();
03590          }
03591       }
03592       break;
03593    case STATE_LISTING:
03594       if ( job->error() )
03595       {
03596          // Try deleting nonetheless, it may be empty (and non-listable)
03597       }
03598       subjobs.remove( job );
03599       assert( subjobs.isEmpty() );
03600       ++m_currentStat;
03601       statNextSrc();
03602       break;
03603    case STATE_DELETING_FILES:
03604       if ( job->error() )
03605       {
03606          Job::slotResult( job ); // will set the error and emit result(this)
03607          return;
03608       }
03609       subjobs.remove( job );
03610       assert( subjobs.isEmpty() );
03611       m_processedFiles++;
03612 
03613       deleteNextFile();
03614       break;
03615    case STATE_DELETING_DIRS:
03616       if ( job->error() )
03617       {
03618          Job::slotResult( job ); // will set the error and emit result(this)
03619          return;
03620       }
03621       subjobs.remove( job );
03622       assert( subjobs.isEmpty() );
03623       m_processedDirs++;
03624       //emit processedDirs( this, m_processedDirs );
03625       //if (!m_shred)
03626          //emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
03627 
03628       deleteNextDir();
03629       break;
03630    default:
03631       assert(0);
03632    }
03633 }
03634 
03635 DeleteJob *KIO::del( const KURL& src, bool shred, bool showProgressInfo )
03636 {
03637   KURL::List srcList;
03638   srcList.append( src );
03639   DeleteJob *job = new DeleteJob( srcList, shred, showProgressInfo );
03640   return job;
03641 }
03642 
03643 DeleteJob *KIO::del( const KURL::List& src, bool shred, bool showProgressInfo )
03644 {
03645   DeleteJob *job = new DeleteJob( src, shred, showProgressInfo );
03646   return job;
03647 }
03648 
03649 MultiGetJob::MultiGetJob(const KURL& url,
03650                          bool showProgressInfo)
03651  : TransferJob(url, 0, QByteArray(), QByteArray(), showProgressInfo)
03652 {
03653    m_waitQueue.setAutoDelete(true);
03654    m_activeQueue.setAutoDelete(true);
03655    m_currentEntry = 0;
03656 }
03657 
03658 void MultiGetJob::get(long id, const KURL &url, const MetaData &metaData)
03659 {
03660    GetRequest *entry = new GetRequest(id, url, metaData);
03661    entry->metaData["request-id"] = QString("%1").arg(id);
03662    m_waitQueue.append(entry);
03663 }
03664 
03665 void MultiGetJob::flushQueue(QPtrList<GetRequest> &queue)
03666 {
03667    GetRequest *entry;
03668    // Use multi-get
03669    // Scan all jobs in m_waitQueue
03670    for(entry = m_waitQueue.first(); entry; )
03671    {
03672       if ((m_url.protocol() == entry->url.protocol()) &&
03673           (m_url.host() == entry->url.host()) &&
03674           (m_url.port() == entry->url.port()) &&
03675           (m_url.user() == entry->url.user()))
03676       {
03677          m_waitQueue.take();
03678          queue.append(entry);
03679          entry = m_waitQueue.current();
03680       }
03681       else
03682       {
03683          entry = m_waitQueue.next();
03684       }
03685    }
03686    // Send number of URLs, (URL, metadata)*
03687    KIO_ARGS << (Q_INT32) queue.count();
03688    for(entry = queue.first(); entry; entry = queue.next())
03689    {
03690       stream << entry->url << entry->metaData;
03691    }
03692    m_packedArgs = packedArgs;
03693    m_command = CMD_MULTI_GET;
03694    m_outgoingMetaData.clear();
03695 }
03696 
03697 void MultiGetJob::start(Slave *slave)
03698 {
03699    // Add first job from m_waitQueue and add it to m_activeQueue
03700    GetRequest *entry = m_waitQueue.take(0);
03701    m_activeQueue.append(entry);
03702 
03703    m_url = entry->url;
03704 
03705    if (!entry->url.protocol().startsWith("http"))
03706    {
03707       // Use normal get
03708       KIO_ARGS << entry->url;
03709       m_packedArgs = packedArgs;
03710       m_outgoingMetaData = entry->metaData;
03711       m_command = CMD_GET;
03712       b_multiGetActive = false;
03713    }
03714    else
03715    {
03716       flushQueue(m_activeQueue);
03717       b_multiGetActive = true;
03718    }
03719 
03720    TransferJob::start(slave); // Anything else to do??
03721 }
03722 
03723 bool MultiGetJob::findCurrentEntry()
03724 {
03725    if (b_multiGetActive)
03726    {
03727       long id = m_incomingMetaData["request-id"].toLong();
03728       for(GetRequest *entry = m_activeQueue.first(); entry; entry = m_activeQueue.next())
03729       {
03730          if (entry->id == id)
03731          {
03732             m_currentEntry = entry;
03733             return true;
03734          }
03735       }
03736       m_currentEntry = 0;
03737       return false;
03738    }
03739    else
03740    {
03741       m_currentEntry = m_activeQueue.first();
03742       return (m_currentEntry != 0);
03743    }
03744 }
03745 
03746 void MultiGetJob::slotRedirection( const KURL &url)
03747 {
03748   if (!findCurrentEntry()) return; // Error
03749   if (!kapp->authorizeURLAction("redirect", m_url, url))
03750   {
03751      kdWarning(7007) << "MultiGetJob: Redirection from " << m_currentEntry->url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl;
03752      return;
03753   }
03754   m_redirectionURL = url;
03755   if (m_currentEntry->url.hasUser() && !url.hasUser() && (m_currentEntry->url.host().lower() == url.host().lower()))
03756       m_redirectionURL.setUser(m_currentEntry->url.user()); // Preserve user
03757   get(m_currentEntry->id, m_redirectionURL, m_currentEntry->metaData); // Try again
03758 }
03759 
03760 
03761 void MultiGetJob::slotFinished()
03762 {
03763   if (!findCurrentEntry()) return;
03764   if (m_redirectionURL.isEmpty())
03765   {
03766      // No redirection, tell the world that we are finished.
03767      emit result(m_currentEntry->id);
03768   }
03769   m_redirectionURL = KURL();
03770   m_error = 0;
03771   m_incomingMetaData.clear();
03772   m_activeQueue.removeRef(m_currentEntry);
03773   if (m_activeQueue.count() == 0)
03774   {
03775      if (m_waitQueue.count() == 0)
03776      {
03777         // All done
03778         TransferJob::slotFinished();
03779      }
03780      else
03781      {
03782         // return slave to pool
03783         // fetch new slave for first entry in m_waitQueue and call start
03784         // again.
03785         GetRequest *entry = m_waitQueue.at(0);
03786         m_url = entry->url;
03787         slaveDone();
03788         Scheduler::doJob(this);
03789      }
03790   }
03791 }
03792 
03793 void MultiGetJob::slotData( const QByteArray &_data)
03794 {
03795   if(!m_currentEntry) return;// Error, unknown request!
03796   if(m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error)
03797      emit data(m_currentEntry->id, _data);
03798 }
03799 
03800 void MultiGetJob::slotMimetype( const QString &_mimetype )
03801 {
03802   if (b_multiGetActive)
03803   {
03804      QPtrList<GetRequest> newQueue;
03805      flushQueue(newQueue);
03806      if (!newQueue.isEmpty())
03807      {
03808         while(!newQueue.isEmpty())
03809            m_activeQueue.append(newQueue.take(0));
03810         m_slave->send( m_command, m_packedArgs );
03811      }
03812   }
03813   if (!findCurrentEntry()) return; // Error, unknown request!
03814   emit mimetype(m_currentEntry->id, _mimetype);
03815 }
03816 
03817 MultiGetJob *KIO::multi_get(long id, const KURL &url, const MetaData &metaData)
03818 {
03819     MultiGetJob * job = new MultiGetJob( url, false );
03820     job->get(id, url, metaData);
03821     return job;
03822 }
03823 
03824 
03825 #ifdef CACHE_INFO
03826 CacheInfo::CacheInfo(const KURL &url)
03827 {
03828     m_url = url;
03829 }
03830 
03831 QString CacheInfo::cachedFileName()
03832 {
03833    const QChar separator = '_';
03834 
03835    QString CEF = m_url.path();
03836 
03837    int p = CEF.find('/');
03838 
03839    while(p != -1)
03840    {
03841       CEF[p] = separator;
03842       p = CEF.find('/', p);
03843    }
03844 
03845    QString host = m_url.host().lower();
03846    CEF = host + CEF + '_';
03847 
03848    QString dir = KProtocolManager::cacheDir();
03849    if (dir[dir.length()-1] != '/')
03850       dir += "/";
03851 
03852    int l = m_url.host().length();
03853    for(int i = 0; i < l; i++)
03854    {
03855       if (host[i].isLetter() && (host[i] != 'w'))
03856       {
03857          dir += host[i];
03858          break;
03859       }
03860    }
03861    if (dir[dir.length()-1] == '/')
03862       dir += "0";
03863 
03864    unsigned long hash = 0x00000000;
03865    QCString u = m_url.url().latin1();
03866    for(int i = u.length(); i--;)
03867    {
03868       hash = (hash * 12211 + u[i]) % 2147483563;
03869    }
03870 
03871    QString hashString;
03872    hashString.sprintf("%08lx", hash);
03873 
03874    CEF = CEF + hashString;
03875 
03876    CEF = dir + "/" + CEF;
03877 
03878    return CEF;
03879 }
03880 
03881 QFile *CacheInfo::cachedFile()
03882 {
03883    const char *mode = (readWrite ? "r+" : "r");
03884 
03885    FILE *fs = fopen( CEF.latin1(), mode); // Open for reading and writing
03886    if (!fs)
03887       return 0;
03888 
03889    char buffer[401];
03890    bool ok = true;
03891 
03892   // CacheRevision
03893   if (ok && (!fgets(buffer, 400, fs)))
03894       ok = false;
03895    if (ok && (strcmp(buffer, CACHE_REVISION) != 0))
03896       ok = false;
03897 
03898    time_t date;
03899    time_t currentDate = time(0);
03900 
03901    // URL
03902    if (ok && (!fgets(buffer, 400, fs)))
03903       ok = false;
03904    if (ok)
03905    {
03906       int l = strlen(buffer);
03907       if (l>0)
03908          buffer[l-1] = 0; // Strip newline
03909       if (m_.url.url() != buffer)
03910       {
03911          ok = false; // Hash collision
03912       }
03913    }
03914 
03915    // Creation Date
03916    if (ok && (!fgets(buffer, 400, fs)))
03917       ok = false;
03918    if (ok)
03919    {
03920       date = (time_t) strtoul(buffer, 0, 10);
03921       if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge))
03922       {
03923          m_bMustRevalidate = true;
03924          m_expireDate = currentDate;
03925       }
03926    }
03927 
03928    // Expiration Date
03929    m_cacheExpireDateOffset = ftell(fs);
03930    if (ok && (!fgets(buffer, 400, fs)))
03931       ok = false;
03932    if (ok)
03933    {
03934       if (m_request.cache == CC_Verify)
03935       {
03936          date = (time_t) strtoul(buffer, 0, 10);
03937          // After the expire date we need to revalidate.
03938          if (!date || difftime(currentDate, date) >= 0)
03939             m_bMustRevalidate = true;
03940          m_expireDate = date;
03941       }
03942    }
03943 
03944    // ETag
03945    if (ok && (!fgets(buffer, 400, fs)))
03946       ok = false;
03947    if (ok)
03948    {
03949       m_etag = QString(buffer).stripWhiteSpace();
03950    }
03951 
03952    // Last-Modified
03953    if (ok && (!fgets(buffer, 400, fs)))
03954       ok = false;
03955    if (ok)
03956    {
03957       m_lastModified = QString(buffer).stripWhiteSpace();
03958    }
03959 
03960    fclose(fs);
03961 
03962    if (ok)
03963       return fs;
03964 
03965    unlink( CEF.latin1());
03966    return 0;
03967 
03968 }
03969 
03970 void CacheInfo::flush()
03971 {
03972     cachedFile().remove();
03973 }
03974 
03975 void CacheInfo::touch()
03976 {
03977 
03978 }
03979 void CacheInfo::setExpireDate(int);
03980 void CacheInfo::setExpireTimeout(int);
03981 
03982 
03983 int CacheInfo::creationDate();
03984 int CacheInfo::expireDate();
03985 int CacheInfo::expireTimeout();
03986 #endif
03987 
03988 void Job::virtual_hook( int, void* )
03989 { /*BASE::virtual_hook( id, data );*/ }
03990 
03991 void SimpleJob::virtual_hook( int id, void* data )
03992 { KIO::Job::virtual_hook( id, data ); }
03993 
03994 void StatJob::virtual_hook( int id, void* data )
03995 { SimpleJob::virtual_hook( id, data ); }
03996 
03997 void TransferJob::virtual_hook( int id, void* data )
03998 { SimpleJob::virtual_hook( id, data ); }
03999 
04000 void MultiGetJob::virtual_hook( int id, void* data )
04001 { TransferJob::virtual_hook( id, data ); }
04002 
04003 void MimetypeJob::virtual_hook( int id, void* data )
04004 { TransferJob::virtual_hook( id, data ); }
04005 
04006 void FileCopyJob::virtual_hook( int id, void* data )
04007 { Job::virtual_hook( id, data ); }
04008 
04009 void ListJob::virtual_hook( int id, void* data )
04010 { SimpleJob::virtual_hook( id, data ); }
04011 
04012 void CopyJob::virtual_hook( int id, void* data )
04013 { Job::virtual_hook( id, data ); }
04014 
04015 void DeleteJob::virtual_hook( int id, void* data )
04016 { Job::virtual_hook( id, data ); }
04017 
04018 
04019 #include "jobclasses.moc"
KDE Logo
This file is part of the documentation for kio Library Version 3.2.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Feb 4 12:35:02 2004 by doxygen 1.2.18 written by Dimitri van Heesch, © 1997-2003