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