• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

copyjob.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002     Copyright 2000       Stephan Kulow <coolo@kde.org>
00003     Copyright 2000-2006  David Faure <faure@kde.org>
00004     Copyright 2000       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 "copyjob.h"
00023 #include "deletejob.h"
00024 
00025 #include <klocale.h>
00026 #include <kdesktopfile.h>
00027 #include <kdebug.h>
00028 #include <kde_file.h>
00029 
00030 #include "slave.h"
00031 #include "scheduler.h"
00032 #include "kdirwatch.h"
00033 #include "kprotocolmanager.h"
00034 
00035 #include "jobuidelegate.h"
00036 
00037 #include <kdirnotify.h>
00038 #include <ktemporaryfile.h>
00039 #include <kuiserverjobtracker.h>
00040 
00041 #ifdef Q_OS_UNIX
00042 #include <utime.h>
00043 #endif
00044 #include <assert.h>
00045 
00046 #include <QtCore/QTimer>
00047 #include <QtCore/QFile>
00048 #include <sys/stat.h> // mode_t
00049 #include <QPointer>
00050 
00051 #include "job_p.h"
00052 
00053 using namespace KIO;
00054 
00055 //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
00056 #define REPORT_TIMEOUT 200
00057 
00058 #define KIO_ARGS QByteArray packedArgs; QDataStream stream( &packedArgs, QIODevice::WriteOnly ); stream
00059 
00060 enum DestinationState {
00061     DEST_NOT_STATED,
00062     DEST_IS_DIR,
00063     DEST_IS_FILE,
00064     DEST_DOESNT_EXIST
00065 };
00066 
00081 enum CopyJobState {
00082     STATE_STATING,
00083     STATE_RENAMING,
00084     STATE_LISTING,
00085     STATE_CREATING_DIRS,
00086     STATE_CONFLICT_CREATING_DIRS,
00087     STATE_COPYING_FILES,
00088     STATE_CONFLICT_COPYING_FILES,
00089     STATE_DELETING_DIRS,
00090     STATE_SETTING_DIR_ATTRIBUTES
00091 };
00092 
00094 class KIO::CopyJobPrivate: public KIO::JobPrivate
00095 {
00096 public:
00097     CopyJobPrivate(const KUrl::List& src, const KUrl& dest,
00098                    CopyJob::CopyMode mode, bool asMethod)
00099         : m_globalDest(dest)
00100         , m_globalDestinationState(DEST_NOT_STATED)
00101         , m_defaultPermissions(false)
00102         , m_bURLDirty(false)
00103         , m_mode(mode)
00104         , m_asMethod(asMethod)
00105         , destinationState(DEST_NOT_STATED)
00106         , state(STATE_STATING)
00107         , m_totalSize(0)
00108         , m_processedSize(0)
00109         , m_fileProcessedSize(0)
00110         , m_processedFiles(0)
00111         , m_processedDirs(0)
00112         , m_srcList(src)
00113         , m_currentStatSrc(m_srcList.constBegin())
00114         , m_bCurrentOperationIsLink(false)
00115         , m_bSingleFileCopy(false)
00116         , m_bOnlyRenames(mode==CopyJob::Move)
00117         , m_dest(dest)
00118         , m_bAutoSkip( false )
00119         , m_bOverwriteAll( false )
00120         , m_conflictError(0)
00121         , m_reportTimer(0)
00122     {
00123     }
00124 
00125     // This is the dest URL that was initially given to CopyJob
00126     // It is copied into m_dest, which can be changed for a given src URL
00127     // (when using the RENAME dialog in slotResult),
00128     // and which will be reset for the next src URL.
00129     KUrl m_globalDest;
00130     // The state info about that global dest
00131     DestinationState m_globalDestinationState;
00132     // See setDefaultPermissions
00133     bool m_defaultPermissions;
00134     // Whether URLs changed (and need to be emitted by the next slotReport call)
00135     bool m_bURLDirty;
00136     // Used after copying all the files into the dirs, to set mtime (TODO: and permissions?)
00137     // after the copy is done
00138     QLinkedList<CopyInfo> m_directoriesCopied;
00139     QLinkedList<CopyInfo>::const_iterator m_directoriesCopiedIterator;
00140 
00141     CopyJob::CopyMode m_mode;
00142     bool m_asMethod;
00143     DestinationState destinationState;
00144     CopyJobState state;
00145     KIO::filesize_t m_totalSize;
00146     KIO::filesize_t m_processedSize;
00147     KIO::filesize_t m_fileProcessedSize;
00148     int m_processedFiles;
00149     int m_processedDirs;
00150     QList<CopyInfo> files;
00151     QList<CopyInfo> dirs;
00152     KUrl::List dirsToRemove;
00153     KUrl::List m_srcList;
00154     KUrl::List m_skippedSourceUrls;
00155     KUrl::List::const_iterator m_currentStatSrc;
00156     bool m_bCurrentSrcIsDir;
00157     bool m_bCurrentOperationIsLink;
00158     bool m_bSingleFileCopy;
00159     bool m_bOnlyRenames;
00160     KUrl m_dest;
00161     KUrl m_currentDest;
00162     //
00163     QStringList m_skipList;
00164     QStringList m_overwriteList;
00165     bool m_bAutoSkip;
00166     bool m_bOverwriteAll;
00167     int m_conflictError;
00168 
00169     QTimer *m_reportTimer;
00170     //these both are used for progress dialog reporting
00171     KUrl m_currentSrcURL;
00172     KUrl m_currentDestURL;
00173 
00174     void statCurrentSrc();
00175     void statNextSrc();
00176 
00177     // Those aren't slots but submethods for slotResult.
00178     void slotResultStating( KJob * job );
00179     void startListing( const KUrl & src );
00180     void slotResultCreatingDirs( KJob * job );
00181     void slotResultConflictCreatingDirs( KJob * job );
00182     void createNextDir();
00183     void slotResultCopyingFiles( KJob * job );
00184     void slotResultConflictCopyingFiles( KJob * job );
00185 //     KIO::Job* linkNextFile( const KUrl& uSource, const KUrl& uDest, bool overwrite );
00186     KIO::Job* linkNextFile( const KUrl& uSource, const KUrl& uDest, JobFlags flags );
00187     void copyNextFile();
00188     void slotResultDeletingDirs( KJob * job );
00189     void deleteNextDir();
00190     void skip( const KUrl & sourceURL );
00191     void slotResultRenaming( KJob * job );
00192     void slotResultSettingDirAttributes( KJob * job );
00193     void setNextDirAttribute();
00194 
00195     void startRenameJob(const KUrl &slave_url);
00196     bool shouldOverwrite( const QString& path ) const;
00197     bool shouldSkip( const QString& path ) const;
00198     void skipSrc();
00199 
00200     void slotStart();
00201     void slotEntries( KIO::Job*, const KIO::UDSEntryList& list );
00205     void slotProcessedSize( KJob*, qulonglong data_size );
00210     void slotTotalSize( KJob*, qulonglong size );
00211 
00212     void slotReport();
00213 
00214     Q_DECLARE_PUBLIC(CopyJob)
00215 
00216     static inline CopyJob *newJob(const KUrl::List& src, const KUrl& dest,
00217                                   CopyJob::CopyMode mode, bool asMethod, JobFlags flags)
00218     {
00219         CopyJob *job = new CopyJob(*new CopyJobPrivate(src,dest,mode,asMethod));
00220         job->setUiDelegate(new JobUiDelegate);
00221         if (!(flags & HideProgressInfo))
00222             KIO::getJobTracker()->registerJob(job);
00223         return job;
00224     }
00225 };
00226 
00227 CopyJob::CopyJob(CopyJobPrivate &dd)
00228     : Job(dd)
00229 {
00230     QTimer::singleShot(0, this, SLOT(slotStart()));
00231 }
00232 
00233 CopyJob::~CopyJob()
00234 {
00235 }
00236 
00237 KUrl::List CopyJob::srcUrls() const
00238 {
00239     return d_func()->m_srcList;
00240 }
00241 
00242 KUrl CopyJob::destUrl() const
00243 {
00244     return d_func()->m_dest;
00245 }
00246 
00247 void CopyJobPrivate::slotStart()
00248 {
00249     Q_Q(CopyJob);
00255     m_reportTimer = new QTimer(q);
00256 
00257     q->connect(m_reportTimer,SIGNAL(timeout()),q,SLOT(slotReport()));
00258     m_reportTimer->start(REPORT_TIMEOUT);
00259 
00260     // Stat the dest
00261     KIO::Job * job = KIO::stat( m_dest, StatJob::DestinationSide, 2, KIO::HideProgressInfo );
00262     //kDebug(7007) << "CopyJob:stating the dest " << d->m_dest;
00263     q->addSubjob(job);
00264 }
00265 
00266 // For unit test purposes
00267 KIO_EXPORT bool kio_resolve_local_urls = true;
00268 
00269 void CopyJobPrivate::slotResultStating( KJob *job )
00270 {
00271     Q_Q(CopyJob);
00272     //kDebug(7007);
00273     // Was there an error while stating the src ?
00274     if (job->error() && destinationState != DEST_NOT_STATED )
00275     {
00276         KUrl srcurl = ((SimpleJob*)job)->url();
00277         if ( !srcurl.isLocalFile() )
00278         {
00279             // Probably : src doesn't exist. Well, over some protocols (e.g. FTP)
00280             // this info isn't really reliable (thanks to MS FTP servers).
00281             // We'll assume a file, and try to download anyway.
00282             kDebug(7007) << "Error while stating source. Activating hack";
00283             q->removeSubjob( job );
00284             assert ( !q->hasSubjobs() ); // We should have only one job at a time ...
00285             struct CopyInfo info;
00286             info.permissions = (mode_t) -1;
00287             info.mtime = (time_t) -1;
00288             info.ctime = (time_t) -1;
00289             info.size = (KIO::filesize_t)-1;
00290             info.uSource = srcurl;
00291             info.uDest = m_dest;
00292             // Append filename or dirname to destination URL, if allowed
00293             if ( destinationState == DEST_IS_DIR && !m_asMethod )
00294                 info.uDest.addPath( srcurl.fileName() );
00295 
00296             files.append( info );
00297             statNextSrc();
00298             return;
00299         }
00300         // Local file. If stat fails, the file definitely doesn't exist.
00301         // yes, q->Job::, because we don't want to call our override
00302         q->Job::slotResult( job ); // will set the error and emit result(this)
00303         return;
00304     }
00305 
00306     // Keep copy of the stat result
00307     const UDSEntry entry = static_cast<StatJob*>(job)->statResult();
00308     const QString sLocalPath = entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
00309     const bool isDir = entry.isDir();
00310 
00311     if ( destinationState == DEST_NOT_STATED )
00312         // we were stating the dest
00313     {
00314         if (job->error())
00315             destinationState = DEST_DOESNT_EXIST;
00316         else {
00317             // Treat symlinks to dirs as dirs here, so no test on isLink
00318             destinationState = isDir ? DEST_IS_DIR : DEST_IS_FILE;
00319             //kDebug(7007) << "dest is dir:" << bDir;
00320         }
00321         const bool isGlobalDest = m_dest == m_globalDest;
00322         if ( isGlobalDest )
00323             m_globalDestinationState = destinationState;
00324 
00325         if ( !sLocalPath.isEmpty() && kio_resolve_local_urls ) {
00326             m_dest = KUrl();
00327             m_dest.setPath(sLocalPath);
00328             if ( isGlobalDest )
00329                 m_globalDest = m_dest;
00330         }
00331 
00332         q->removeSubjob( job );
00333         assert ( !q->hasSubjobs() );
00334 
00335         // After knowing what the dest is, we can start stat'ing the first src.
00336         statCurrentSrc();
00337         return;
00338     }
00339 
00340     // Is it a file or a dir ?
00341     const QString sName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
00342 
00343     // We were stating the current source URL
00344     m_currentDest = m_dest; // used by slotEntries
00345     // Create a dummy list with it, for slotEntries
00346     UDSEntryList lst;
00347     lst.append(entry);
00348 
00349     // There 6 cases, and all end up calling slotEntries(job, lst) first :
00350     // 1 - src is a dir, destination is a directory,
00351     // slotEntries will append the source-dir-name to the destination
00352     // 2 - src is a dir, destination is a file, ERROR (done later on)
00353     // 3 - src is a dir, destination doesn't exist, then it's the destination dirname,
00354     // so slotEntries will use it as destination.
00355 
00356     // 4 - src is a file, destination is a directory,
00357     // slotEntries will append the filename to the destination.
00358     // 5 - src is a file, destination is a file, m_dest is the exact destination name
00359     // 6 - src is a file, destination doesn't exist, m_dest is the exact destination name
00360     // Tell slotEntries not to alter the src url
00361     m_bCurrentSrcIsDir = false;
00362     slotEntries(static_cast<KIO::Job*>( job ), lst);
00363 
00364     KUrl srcurl;
00365     if (!sLocalPath.isEmpty())
00366         srcurl.setPath(sLocalPath);
00367     else
00368         srcurl = ((SimpleJob*)job)->url();
00369 
00370     q->removeSubjob( job );
00371     assert ( !q->hasSubjobs() ); // We should have only one job at a time ...
00372 
00373     if ( isDir
00374          // treat symlinks as files (no recursion)
00375          && !entry.isLink()
00376          && m_mode != CopyJob::Link ) // No recursion in Link mode either.
00377     {
00378         //kDebug(7007) << "Source is a directory";
00379 
00380         m_bCurrentSrcIsDir = true; // used by slotEntries
00381         if ( destinationState == DEST_IS_DIR ) // (case 1)
00382         {
00383             if ( !m_asMethod )
00384             {
00385                 // Use <desturl>/<directory_copied> as destination, from now on
00386                 QString directory = srcurl.fileName();
00387                 if ( !sName.isEmpty() && KProtocolManager::fileNameUsedForCopying( srcurl ) == KProtocolInfo::Name )
00388                 {
00389                     directory = sName;
00390                 }
00391                 m_currentDest.addPath( directory );
00392             }
00393         }
00394         else if ( destinationState == DEST_IS_FILE ) // (case 2)
00395         {
00396             q->setError( ERR_IS_FILE );
00397             q->setErrorText( m_dest.prettyUrl() );
00398             q->emitResult();
00399             return;
00400         }
00401         else // (case 3)
00402         {
00403             // otherwise dest is new name for toplevel dir
00404             // so the destination exists, in fact, from now on.
00405             // (This even works with other src urls in the list, since the
00406             //  dir has effectively been created)
00407             destinationState = DEST_IS_DIR;
00408             if ( m_dest == m_globalDest )
00409                 m_globalDestinationState = destinationState;
00410         }
00411 
00412         startListing( srcurl );
00413     }
00414     else
00415     {
00416         //kDebug(7007) << "Source is a file (or a symlink), or we are linking -> no recursive listing";
00417         statNextSrc();
00418     }
00419 }
00420 
00421 bool CopyJob::doSuspend()
00422 {
00423     Q_D(CopyJob);
00424     d->slotReport();
00425     return Job::doSuspend();
00426 }
00427 
00428 void CopyJobPrivate::slotReport()
00429 {
00430     Q_Q(CopyJob);
00431     if ( q->isSuspended() )
00432         return;
00433     // If showProgressInfo was set, progressId() is > 0.
00434     switch (state) {
00435         case STATE_RENAMING:
00436             q->setTotalAmount(KJob::Files, m_srcList.count());
00437             // fall-through intended
00438         case STATE_COPYING_FILES:
00439             q->setProcessedAmount( KJob::Files, m_processedFiles );
00440             if (m_bURLDirty)
00441             {
00442                 // Only emit urls when they changed. This saves time, and fixes #66281
00443                 m_bURLDirty = false;
00444                 if (m_mode==CopyJob::Move)
00445                 {
00446                     emitMoving(q, m_currentSrcURL, m_currentDestURL);
00447                     emit q->moving( q, m_currentSrcURL, m_currentDestURL);
00448                 }
00449                 else if (m_mode==CopyJob::Link)
00450                 {
00451                     emitCopying( q, m_currentSrcURL, m_currentDestURL ); // we don't have a delegate->linking
00452                     emit q->linking( q, m_currentSrcURL.path(), m_currentDestURL );
00453                 }
00454                 else
00455                 {
00456                     emitCopying( q, m_currentSrcURL, m_currentDestURL );
00457                     emit q->copying( q, m_currentSrcURL, m_currentDestURL );
00458                 }
00459             }
00460             break;
00461 
00462         case STATE_CREATING_DIRS:
00463             q->setProcessedAmount( KJob::Directories, m_processedDirs );
00464             if (m_bURLDirty)
00465             {
00466                 m_bURLDirty = false;
00467                 emit q->creatingDir( q, m_currentDestURL );
00468                 emitCreatingDir( q, m_currentDestURL );
00469             }
00470             break;
00471 
00472         case STATE_STATING:
00473         case STATE_LISTING:
00474             if (m_bURLDirty)
00475             {
00476                 m_bURLDirty = false;
00477                 if (m_mode==CopyJob::Move)
00478                 {
00479                     emitMoving( q, m_currentSrcURL, m_currentDestURL );
00480                 }
00481                 else
00482                 {
00483                     emitCopying( q, m_currentSrcURL, m_currentDestURL );
00484                 }
00485             }
00486             q->setTotalAmount(KJob::Bytes, m_totalSize);
00487             q->setTotalAmount(KJob::Files, files.count());
00488             q->setTotalAmount(KJob::Directories, dirs.count());
00489             break;
00490 
00491         default:
00492             break;
00493     }
00494 }
00495 
00496 void CopyJobPrivate::slotEntries(KIO::Job* job, const UDSEntryList& list)
00497 {
00498     //Q_Q(CopyJob);
00499     UDSEntryList::ConstIterator it = list.constBegin();
00500     UDSEntryList::ConstIterator end = list.constEnd();
00501     for (; it != end; ++it) {
00502         const UDSEntry& entry = *it;
00503         struct CopyInfo info;
00504         info.permissions = entry.numberValue( KIO::UDSEntry::UDS_ACCESS, -1 );
00505         info.mtime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 );
00506         info.ctime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_CREATION_TIME, -1 );
00507         info.size = (KIO::filesize_t) entry.numberValue( KIO::UDSEntry::UDS_SIZE, -1 );
00508         if ( info.size != (KIO::filesize_t) -1 )
00509             m_totalSize += info.size;
00510 
00511         // recursive listing, displayName can be a/b/c/d
00512         const QString displayName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
00513         const QString urlStr = entry.stringValue( KIO::UDSEntry::UDS_URL );
00514         KUrl url;
00515         if ( !urlStr.isEmpty() )
00516             url = urlStr;
00517         QString localPath = entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
00518         const bool isDir = entry.isDir();
00519         info.linkDest = entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST );
00520 
00521         if (displayName != ".." && displayName != ".")
00522         {
00523             bool hasCustomURL = !url.isEmpty() || !localPath.isEmpty();
00524             if( !hasCustomURL ) {
00525                 // Make URL from displayName
00526                 url = static_cast<SimpleJob *>(job)->url();
00527                 if ( m_bCurrentSrcIsDir ) { // Only if src is a directory. Otherwise uSource is fine as is
00528                     //kDebug(7007) << "adding path " << displayName;
00529                     url.addPath( displayName );
00530                 }
00531             }
00532             //kDebug(7007) << "displayName=" << displayName << "url=" << url;
00533             if (!localPath.isEmpty() && kio_resolve_local_urls) {
00534                 url = KUrl();
00535                 url.setPath(localPath);
00536             }
00537 
00538             info.uSource = url;
00539             info.uDest = m_currentDest;
00540             //kDebug(7007) << "uSource=" << info.uSource << "uDest(1)=" << info.uDest;
00541             // Append filename or dirname to destination URL, if allowed
00542             if ( destinationState == DEST_IS_DIR &&
00543                  // "copy/move as <foo>" means 'foo' is the dest for the base srcurl
00544                  // (passed here during stating) but not its children (during listing)
00545                  ( ! ( m_asMethod && state == STATE_STATING ) ) )
00546             {
00547                 QString destFileName;
00548                 if ( hasCustomURL &&
00549                      KProtocolManager::fileNameUsedForCopying( url ) == KProtocolInfo::FromUrl ) {
00550                     //destFileName = url.fileName(); // Doesn't work for recursive listing
00551                     // Count the number of prefixes used by the recursive listjob
00552                     int numberOfSlashes = displayName.count( '/' ); // don't make this a find()!
00553                     QString path = url.path();
00554                     int pos = 0;
00555                     for ( int n = 0; n < numberOfSlashes + 1; ++n ) {
00556                         pos = path.lastIndexOf( '/', pos - 1 );
00557                         if ( pos == -1 ) { // error
00558                             kWarning(7007) << "kioslave bug: not enough slashes in UDS_URL" << path << "- looking for" << numberOfSlashes << "slashes";
00559                             break;
00560                         }
00561                     }
00562                     if ( pos >= 0 ) {
00563                         destFileName = path.mid( pos + 1 );
00564                     }
00565 
00566                 } else { // destination filename taken from UDS_NAME
00567                     destFileName = displayName;
00568                 }
00569 
00570                 // Here we _really_ have to add some filename to the dest.
00571                 // Otherwise, we end up with e.g. dest=..../Desktop/ itself.
00572                 // (This can happen when dropping a link to a webpage with no path)
00573                 if ( destFileName.isEmpty() )
00574                     destFileName = KIO::encodeFileName( info.uSource.prettyUrl() );
00575 
00576                 //kDebug(7007) << " adding destFileName=" << destFileName;
00577                 info.uDest.addPath( destFileName );
00578             }
00579             //kDebug(7007) << " uDest(2)=" << info.uDest;
00580             //kDebug(7007) << " " << info.uSource << "->" << info.uDest;
00581             if ( info.linkDest.isEmpty() && isDir && m_mode != CopyJob::Link ) // Dir
00582             {
00583                 dirs.append( info ); // Directories
00584                 if (m_mode == CopyJob::Move)
00585                     dirsToRemove.append( info.uSource );
00586             }
00587             else {
00588                 files.append( info ); // Files and any symlinks
00589             }
00590         }
00591     }
00592 }
00593 
00594 void CopyJobPrivate::skipSrc()
00595 {
00596     m_dest = m_globalDest;
00597     destinationState = m_globalDestinationState;
00598     ++m_currentStatSrc;
00599     skip( m_currentSrcURL );
00600     statCurrentSrc();
00601 }
00602 
00603 void CopyJobPrivate::statNextSrc()
00604 {
00605     /* Revert to the global destination, the one that applies to all source urls.
00606      * Imagine you copy the items a b and c into /d, but /d/b exists so the user uses "Rename" to put it in /foo/b instead.
00607      * d->m_dest is /foo/b for b, but we have to revert to /d for item c and following.
00608      */
00609     m_dest = m_globalDest;
00610     destinationState = m_globalDestinationState;
00611     ++m_currentStatSrc;
00612     statCurrentSrc();
00613 }
00614 
00615 void CopyJobPrivate::statCurrentSrc()
00616 {
00617     Q_Q(CopyJob);
00618     if ( m_currentStatSrc != m_srcList.constEnd() )
00619     {
00620         m_currentSrcURL = (*m_currentStatSrc);
00621         m_bURLDirty = true;
00622         if ( m_mode == CopyJob::Link )
00623         {
00624             // Skip the "stating the source" stage, we don't need it for linking
00625             m_currentDest = m_dest;
00626             struct CopyInfo info;
00627             info.permissions = -1;
00628             info.mtime = (time_t) -1;
00629             info.ctime = (time_t) -1;
00630             info.size = (KIO::filesize_t)-1;
00631             info.uSource = m_currentSrcURL;
00632             info.uDest = m_currentDest;
00633             // Append filename or dirname to destination URL, if allowed
00634             if ( destinationState == DEST_IS_DIR && !m_asMethod )
00635             {
00636                 if (
00637                     (m_currentSrcURL.protocol() == info.uDest.protocol()) &&
00638                     (m_currentSrcURL.host() == info.uDest.host()) &&
00639                     (m_currentSrcURL.port() == info.uDest.port()) &&
00640                     (m_currentSrcURL.user() == info.uDest.user()) &&
00641                     (m_currentSrcURL.pass() == info.uDest.pass()) )
00642                 {
00643                     // This is the case of creating a real symlink
00644                     info.uDest.addPath( m_currentSrcURL.fileName() );
00645                 }
00646                 else
00647                 {
00648                     // Different protocols, we'll create a .desktop file
00649                     // We have to change the extension anyway, so while we're at it,
00650                     // name the file like the URL
00651                     info.uDest.addPath( KIO::encodeFileName( m_currentSrcURL.prettyUrl() )+".desktop" );
00652                 }
00653             }
00654             files.append( info ); // Files and any symlinks
00655             statNextSrc(); // we could use a loop instead of a recursive call :)
00656             return;
00657         }
00658         else if ( m_mode == CopyJob::Move && (
00659                 // Don't go renaming right away if we need a stat() to find out the destination filename
00660                 KProtocolManager::fileNameUsedForCopying( m_currentSrcURL ) == KProtocolInfo::FromUrl ||
00661                 destinationState != DEST_IS_DIR || m_asMethod )
00662             )
00663         {
00664            // If moving, before going for the full stat+[list+]copy+del thing, try to rename
00665            // The logic is pretty similar to FileCopyJobPrivate::slotStart()
00666            if ( (m_currentSrcURL.protocol() == m_dest.protocol()) &&
00667               (m_currentSrcURL.host() == m_dest.host()) &&
00668               (m_currentSrcURL.port() == m_dest.port()) &&
00669               (m_currentSrcURL.user() == m_dest.user()) &&
00670               (m_currentSrcURL.pass() == m_dest.pass()) )
00671            {
00672               startRenameJob( m_currentSrcURL );
00673               return;
00674            }
00675            else if ( m_currentSrcURL.isLocalFile() && KProtocolManager::canRenameFromFile( m_dest ) )
00676            {
00677               startRenameJob( m_dest );
00678               return;
00679            }
00680            else if ( m_dest.isLocalFile() && KProtocolManager::canRenameToFile( m_currentSrcURL ) )
00681            {
00682               startRenameJob( m_currentSrcURL );
00683               return;
00684            }
00685         }
00686 
00687         // if the file system doesn't support deleting, we do not even stat
00688         if (m_mode == CopyJob::Move && !KProtocolManager::supportsDeleting(m_currentSrcURL)) {
00689             QPointer<CopyJob> that = q;
00690             emit q->warning( q, buildErrorString(ERR_CANNOT_DELETE, m_currentSrcURL.prettyUrl()) );
00691             if (that)
00692                 statNextSrc(); // we could use a loop instead of a recursive call :)
00693             return;
00694         }
00695 
00696         // Stat the next src url
00697         Job * job = KIO::stat( m_currentSrcURL, StatJob::SourceSide, 2, KIO::HideProgressInfo );
00698         //kDebug(7007) << "KIO::stat on " << m_currentSrcURL;
00699         state = STATE_STATING;
00700         q->addSubjob(job);
00701         m_currentDestURL=m_dest;
00702         m_bOnlyRenames = false;
00703         m_bURLDirty = true;
00704     }
00705     else
00706     {
00707         // Finished the stat'ing phase
00708         // First make sure that the totals were correctly emitted
00709         state = STATE_STATING;
00710         m_bURLDirty = true;
00711         slotReport();
00712         if (!dirs.isEmpty())
00713            emit q->aboutToCreate( q, dirs );
00714         if (!files.isEmpty())
00715            emit q->aboutToCreate( q, files );
00716         // Check if we are copying a single file
00717         m_bSingleFileCopy = ( files.count() == 1 && dirs.isEmpty() );
00718         // Then start copying things
00719         state = STATE_CREATING_DIRS;
00720         createNextDir();
00721     }
00722 }
00723 
00724 void CopyJobPrivate::startRenameJob( const KUrl& slave_url )
00725 {
00726     Q_Q(CopyJob);
00727     KUrl dest = m_dest;
00728     // Append filename or dirname to destination URL, if allowed
00729     if ( destinationState == DEST_IS_DIR && !m_asMethod )
00730         dest.addPath( m_currentSrcURL.fileName() );
00731     m_currentDestURL = dest;
00732     kDebug(7007) << "This seems to be a suitable case for trying to rename before stat+[list+]copy+del";
00733     state = STATE_RENAMING;
00734 
00735     struct CopyInfo info;
00736     info.permissions = -1;
00737     info.mtime = (time_t) -1;
00738     info.ctime = (time_t) -1;
00739     info.size = (KIO::filesize_t)-1;
00740     info.uSource = m_currentSrcURL;
00741     info.uDest = dest;
00742     QList<CopyInfo> files;
00743     files.append(info);
00744     emit q->aboutToCreate( q, files );
00745 
00746     KIO_ARGS << m_currentSrcURL << dest << (qint8) false /*no overwrite*/;
00747     SimpleJob * newJob = SimpleJobPrivate::newJob(slave_url, CMD_RENAME, packedArgs);
00748     newJob->setUiDelegate(new JobUiDelegate());
00749     Scheduler::scheduleJob(newJob);
00750     q->addSubjob( newJob );
00751     if ( m_currentSrcURL.directory() != dest.directory() ) // For the user, moving isn't renaming. Only renaming is.
00752         m_bOnlyRenames = false;
00753 }
00754 
00755 void CopyJobPrivate::startListing( const KUrl & src )
00756 {
00757     Q_Q(CopyJob);
00758     state = STATE_LISTING;
00759     m_bURLDirty = true;
00760     ListJob * newjob = listRecursive(src, KIO::HideProgressInfo);
00761     newjob->setUiDelegate(new JobUiDelegate());
00762     newjob->setUnrestricted(true);
00763     q->connect(newjob, SIGNAL(entries( KIO::Job *,const KIO::UDSEntryList& )),
00764                SLOT( slotEntries( KIO::Job*, const KIO::UDSEntryList& )));
00765     q->addSubjob( newjob );
00766 }
00767 
00768 void CopyJobPrivate::skip( const KUrl & sourceUrl )
00769 {
00770     // If this is one if toplevel sources,
00771     // remove it from d->m_srcList, for a correct FilesRemoved() signal
00772     // But don't do it right away, we have iterators into that list (#157601)
00773     m_skippedSourceUrls.append( sourceUrl );
00774     dirsToRemove.removeAll( sourceUrl );
00775 }
00776 
00777 bool CopyJobPrivate::shouldOverwrite( const QString& path ) const
00778 {
00779     if ( m_bOverwriteAll )
00780         return true;
00781     Q_FOREACH(const QString& overwritePath, m_overwriteList) {
00782         if ( path.startsWith(overwritePath) )
00783             return true;
00784     }
00785     return false;
00786 }
00787 
00788 bool CopyJobPrivate::shouldSkip( const QString& path ) const
00789 {
00790     Q_FOREACH(const QString& skipPath, m_skipList) {
00791         if ( path.startsWith(skipPath) )
00792             return true;
00793     }
00794     return false;
00795 }
00796 
00797 void CopyJobPrivate::slotResultCreatingDirs( KJob * job )
00798 {
00799     Q_Q(CopyJob);
00800     // The dir we are trying to create:
00801     QList<CopyInfo>::Iterator it = dirs.begin();
00802     // Was there an error creating a dir ?
00803     if ( job->error() )
00804     {
00805         m_conflictError = job->error();
00806         if ( (m_conflictError == ERR_DIR_ALREADY_EXIST)
00807              || (m_conflictError == ERR_FILE_ALREADY_EXIST) ) // can't happen?
00808         {
00809             KUrl oldURL = ((SimpleJob*)job)->url();
00810             // Should we skip automatically ?
00811             if ( m_bAutoSkip ) {
00812                 // We don't want to copy files in this directory, so we put it on the skip list
00813                 m_skipList.append( oldURL.path( KUrl::AddTrailingSlash ) );
00814                 skip( oldURL );
00815                 dirs.erase( it ); // Move on to next dir
00816             } else {
00817                 // Did the user choose to overwrite already?
00818                 const QString destFile = (*it).uDest.path();
00819                 if ( shouldOverwrite( destFile ) ) { // overwrite => just skip
00820                     emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true /* directory */, false /* renamed */ );
00821                     dirs.erase( it ); // Move on to next dir
00822                 } else {
00823                     if ( !q->isInteractive() ) {
00824                         q->Job::slotResult( job ); // will set the error and emit result(this)
00825                         return;
00826                     }
00827 
00828                     assert( ((SimpleJob*)job)->url().url() == (*it).uDest.url() );
00829                     q->removeSubjob( job );
00830                     assert ( !q->hasSubjobs() ); // We should have only one job at a time ...
00831 
00832                     // We need to stat the existing dir, to get its last-modification time
00833                     KUrl existingDest( (*it).uDest );
00834                     SimpleJob * newJob = KIO::stat( existingDest, StatJob::DestinationSide, 2, KIO::HideProgressInfo );
00835                     Scheduler::scheduleJob(newJob);
00836                     kDebug(7007) << "KIO::stat for resolving conflict on " << existingDest;
00837                     state = STATE_CONFLICT_CREATING_DIRS;
00838                     q->addSubjob(newJob);
00839                     return; // Don't move to next dir yet !
00840                 }
00841             }
00842         }
00843         else
00844         {
00845             // Severe error, abort
00846             q->Job::slotResult( job ); // will set the error and emit result(this)
00847             return;
00848         }
00849     }
00850     else // no error : remove from list, to move on to next dir
00851     {
00852         //this is required for the undo feature
00853         emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true, false );
00854         m_directoriesCopied.append( *it );
00855         dirs.erase( it );
00856     }
00857 
00858     m_processedDirs++;
00859     //emit processedAmount( this, KJob::Directories, m_processedDirs );
00860     q->removeSubjob( job );
00861     assert( !q->hasSubjobs() ); // We should have only one job at a time ...
00862     createNextDir();
00863 }
00864 
00865 void CopyJobPrivate::slotResultConflictCreatingDirs( KJob * job )
00866 {
00867     Q_Q(CopyJob);
00868     // We come here after a conflict has been detected and we've stated the existing dir
00869 
00870     // The dir we were trying to create:
00871     QList<CopyInfo>::Iterator it = dirs.begin();
00872 
00873     const UDSEntry entry = ((KIO::StatJob*)job)->statResult();
00874 
00875     // Its modification time:
00876     const time_t destmtime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 );
00877     const time_t destctime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_CREATION_TIME, -1 );
00878 
00879     const KIO::filesize_t destsize = entry.numberValue( KIO::UDSEntry::UDS_SIZE );
00880     const QString linkDest = entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST );
00881 
00882     q->removeSubjob( job );
00883     assert ( !q->hasSubjobs() ); // We should have only one job at a time ...
00884 
00885     // Always multi and skip (since there are files after that)
00886     RenameDialog_Mode mode = (RenameDialog_Mode)( M_MULTI | M_SKIP );
00887     // Overwrite only if the existing thing is a dir (no chance with a file)
00888     if ( m_conflictError == ERR_DIR_ALREADY_EXIST )
00889     {
00890         if( (*it).uSource == (*it).uDest ||
00891             ((*it).uSource.protocol() == (*it).uDest.protocol() &&
00892               (*it).uSource.path( KUrl::RemoveTrailingSlash ) == linkDest) )
00893           mode = (RenameDialog_Mode)( mode | M_OVERWRITE_ITSELF);
00894         else
00895           mode = (RenameDialog_Mode)( mode | M_OVERWRITE );
00896     }
00897 
00898     QString existingDest = (*it).uDest.path();
00899     QString newPath;
00900     if (m_reportTimer)
00901         m_reportTimer->stop();
00902     RenameDialog_Result r = q->ui()->askFileRename( q, i18n("Folder Already Exists"),
00903                                          (*it).uSource.url(),
00904                                          (*it).uDest.url(),
00905                                          mode, newPath,
00906                                          (*it).size, destsize,
00907                                          (*it).ctime, destctime,
00908                                          (*it).mtime, destmtime );
00909     if (m_reportTimer)
00910         m_reportTimer->start(REPORT_TIMEOUT);
00911     switch ( r ) {
00912         case R_CANCEL:
00913             q->setError( ERR_USER_CANCELED );
00914             q->emitResult();
00915             return;
00916         case R_RENAME:
00917         {
00918           QString oldPath = (*it).uDest.path( KUrl::AddTrailingSlash );
00919             KUrl newUrl( (*it).uDest );
00920             newUrl.setPath( newPath );
00921             emit q->renamed( q, (*it).uDest, newUrl ); // for e.g. kpropsdlg
00922 
00923             // Change the current one and strip the trailing '/'
00924             (*it).uDest.setPath( newUrl.path( KUrl::RemoveTrailingSlash ) );
00925             newPath = newUrl.path( KUrl::AddTrailingSlash ); // With trailing slash
00926             QList<CopyInfo>::Iterator renamedirit = it;
00927             ++renamedirit;
00928             // Change the name of subdirectories inside the directory
00929             for( ; renamedirit != dirs.end() ; ++renamedirit )
00930             {
00931                 QString path = (*renamedirit).uDest.path();
00932                 if ( path.startsWith( oldPath ) ) {
00933                     QString n = path;
00934                     n.replace( 0, oldPath.length(), newPath );
00935                     kDebug(7007) << "dirs list:" << (*renamedirit).uSource.path()
00936                                   << "was going to be" << path
00937                                   << ", changed into" << n;
00938                     (*renamedirit).uDest.setPath( n );
00939                 }
00940             }
00941             // Change filenames inside the directory
00942             QList<CopyInfo>::Iterator renamefileit = files.begin();
00943             for( ; renamefileit != files.end() ; ++renamefileit )
00944             {
00945                 QString path = (*renamefileit).uDest.path();
00946                 if ( path.startsWith( oldPath ) ) {
00947                     QString n = path;
00948                     n.replace( 0, oldPath.length(), newPath );
00949                     kDebug(7007) << "files list:" << (*renamefileit).uSource.path()
00950                                   << "was going to be" << path
00951                                   << ", changed into" << n;
00952                     (*renamefileit).uDest.setPath( n );
00953                 }
00954             }
00955             if (!dirs.isEmpty())
00956                 emit q->aboutToCreate( q, dirs );
00957             if (!files.isEmpty())
00958                 emit q->aboutToCreate( q, files );
00959         }
00960         break;
00961         case R_AUTO_SKIP:
00962             m_bAutoSkip = true;
00963             // fall through
00964         case R_SKIP:
00965             m_skipList.append( existingDest );
00966             skip( (*it).uSource );
00967             // Move on to next dir
00968             dirs.erase( it );
00969             m_processedDirs++;
00970             break;
00971         case R_OVERWRITE:
00972             m_overwriteList.append( existingDest );
00973             emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true /* directory */, false /* renamed */ );
00974             // Move on to next dir
00975             dirs.erase( it );
00976             m_processedDirs++;
00977             break;
00978         case R_OVERWRITE_ALL:
00979             m_bOverwriteAll = true;
00980             emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true /* directory */, false /* renamed */ );
00981             // Move on to next dir
00982             dirs.erase( it );
00983             m_processedDirs++;
00984             break;
00985         default:
00986             assert( 0 );
00987     }
00988     state = STATE_CREATING_DIRS;
00989     //emit processedAmount( this, KJob::Directories, m_processedDirs );
00990     createNextDir();
00991 }
00992 
00993 void CopyJobPrivate::createNextDir()
00994 {
00995     Q_Q(CopyJob);
00996     KUrl udir;
00997     if ( !dirs.isEmpty() )
00998     {
00999         // Take first dir to create out of list
01000         QList<CopyInfo>::Iterator it = dirs.begin();
01001         // Is this URL on the skip list or the overwrite list ?
01002         while( it != dirs.end() && udir.isEmpty() )
01003         {
01004             const QString dir = (*it).uDest.path();
01005             if ( shouldSkip( dir ) ) {
01006                 dirs.erase( it );
01007                 it = dirs.begin();
01008             } else
01009                 udir = (*it).uDest;
01010         }
01011     }
01012     if ( !udir.isEmpty() ) // any dir to create, finally ?
01013     {
01014         // Create the directory - with default permissions so that we can put files into it
01015         // TODO : change permissions once all is finished; but for stuff coming from CDROM it sucks...
01016         KIO::SimpleJob *newjob = KIO::mkdir( udir, -1 );
01017         Scheduler::scheduleJob(newjob);
01018 
01019         m_currentDestURL = udir;
01020         m_bURLDirty = true;
01021 
01022         q->addSubjob(newjob);
01023         return;
01024     }
01025     else // we have finished creating dirs
01026     {
01027         q->setProcessedAmount( KJob::Directories, m_processedDirs ); // make sure final number appears
01028 
01029         state = STATE_COPYING_FILES;
01030         m_processedFiles++; // Ralf wants it to start at 1, not 0
01031         copyNextFile();
01032     }
01033 }
01034 
01035 void CopyJobPrivate::slotResultCopyingFiles( KJob * job )
01036 {
01037     Q_Q(CopyJob);
01038     // The file we were trying to copy:
01039     QList<CopyInfo>::Iterator it = files.begin();
01040     if ( job->error() )
01041     {
01042         // Should we skip automatically ?
01043         if ( m_bAutoSkip )
01044         {
01045             skip( (*it).uSource );
01046             m_fileProcessedSize = (*it).size;
01047             files.erase( it ); // Move on to next file
01048         }
01049         else
01050         {
01051             if ( !q->isInteractive() ) {
01052                 q->Job::slotResult( job ); // will set the error and emit result(this)
01053                 return;
01054             }
01055 
01056             m_conflictError = job->error(); // save for later
01057             // Existing dest ?
01058             if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
01059                  || ( m_conflictError == ERR_DIR_ALREADY_EXIST )
01060                  || ( m_conflictError == ERR_IDENTICAL_FILES ) )
01061             {
01062                 q->removeSubjob( job );
01063                 assert ( !q->hasSubjobs() );
01064                 // We need to stat the existing file, to get its last-modification time
01065                 KUrl existingFile( (*it).uDest );
01066                 SimpleJob * newJob = KIO::stat( existingFile, StatJob::DestinationSide, 2, KIO::HideProgressInfo );
01067                 Scheduler::scheduleJob(newJob);
01068                 kDebug(7007) << "KIO::stat for resolving conflict on " << existingFile;
01069                 state = STATE_CONFLICT_COPYING_FILES;
01070                 q->addSubjob(newJob);
01071                 return; // Don't move to next file yet !
01072             }
01073             else
01074             {
01075                 if ( m_bCurrentOperationIsLink && qobject_cast<KIO::DeleteJob*>( job ) )
01076                 {
01077                     // Very special case, see a few lines below
01078                     // We are deleting the source of a symlink we successfully moved... ignore error
01079                     m_fileProcessedSize = (*it).size;
01080                     files.erase( it );
01081                 } else {
01082                     // Go directly to the conflict resolution, there is nothing to stat
01083                     slotResultConflictCopyingFiles( job );
01084                     return;
01085                 }
01086             }
01087         }
01088     } else // no error
01089     {
01090         // Special case for moving links. That operation needs two jobs, unlike others.
01091         if ( m_bCurrentOperationIsLink && m_mode == CopyJob::Move
01092              && !qobject_cast<KIO::DeleteJob *>( job ) // Deleting source not already done
01093              )
01094         {
01095             q->removeSubjob( job );
01096             assert ( !q->hasSubjobs() );
01097             // The only problem with this trick is that the error handling for this del operation
01098             // is not going to be right... see 'Very special case' above.
01099             KIO::Job * newjob = KIO::del( (*it).uSource, HideProgressInfo );
01100             q->addSubjob( newjob );
01101             return; // Don't move to next file yet !
01102         }
01103 
01104         if ( m_bCurrentOperationIsLink )
01105         {
01106             QString target = ( m_mode == CopyJob::Link ? (*it).uSource.path() : (*it).linkDest );
01107             //required for the undo feature
01108             emit q->copyingLinkDone( q, (*it).uSource, target, (*it).uDest );
01109         }
01110         else {
01111             //required for the undo feature
01112             emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, false, false );
01113             if (m_mode == CopyJob::Move)
01114                 org::kde::KDirNotify::emitFileMoved( (*it).uSource.url(), (*it).uDest.url() );
01115         }
01116         // remove from list, to move on to next file
01117         files.erase( it );
01118     }
01119     m_processedFiles++;
01120 
01121     // clear processed size for last file and add it to overall processed size
01122     m_processedSize += m_fileProcessedSize;
01123     m_fileProcessedSize = 0;
01124 
01125     //kDebug(7007) << files.count() << "files remaining";
01126 
01127     // Merge metadata from subjob
01128     KIO::Job* kiojob = dynamic_cast<KIO::Job*>(job);
01129     Q_ASSERT(kiojob);
01130     m_incomingMetaData += kiojob->metaData();
01131     q->removeSubjob( job );
01132     assert( !q->hasSubjobs() ); // We should have only one job at a time ...
01133     copyNextFile();
01134 }
01135 
01136 void CopyJobPrivate::slotResultConflictCopyingFiles( KJob * job )
01137 {
01138     Q_Q(CopyJob);
01139     // We come here after a conflict has been detected and we've stated the existing file
01140     // The file we were trying to create:
01141     QList<CopyInfo>::Iterator it = files.begin();
01142 
01143     RenameDialog_Result res;
01144     QString newPath;
01145 
01146     if (m_reportTimer)
01147         m_reportTimer->stop();
01148 
01149     if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
01150          || ( m_conflictError == ERR_DIR_ALREADY_EXIST )
01151          || ( m_conflictError == ERR_IDENTICAL_FILES ) )
01152     {
01153         // Its modification time:
01154         const UDSEntry entry = ((KIO::StatJob*)job)->statResult();
01155 
01156         const time_t destmtime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 );
01157         const time_t destctime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_CREATION_TIME, -1 );
01158         const KIO::filesize_t destsize = entry.numberValue( KIO::UDSEntry::UDS_SIZE );
01159         const QString linkDest = entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST );
01160 
01161         // Offer overwrite only if the existing thing is a file
01162         // If src==dest, use "overwrite-itself"
01163         RenameDialog_Mode mode;
01164         bool isDir = true;
01165 
01166         if( m_conflictError == ERR_DIR_ALREADY_EXIST )
01167             mode = (RenameDialog_Mode) 0;
01168         else
01169         {
01170             if ( (*it).uSource == (*it).uDest  ||
01171                  ((*it).uSource.protocol() == (*it).uDest.protocol() &&
01172                    (*it).uSource.path( KUrl::RemoveTrailingSlash ) == linkDest) )
01173                 mode = M_OVERWRITE_ITSELF;
01174             else
01175                 mode = M_OVERWRITE;
01176             isDir = false;
01177         }
01178 
01179         if ( m_bSingleFileCopy )
01180             mode = (RenameDialog_Mode) ( mode | M_SINGLE );
01181         else
01182             mode = (RenameDialog_Mode) ( mode | M_MULTI | M_SKIP );
01183 
01184         res = q->ui()->askFileRename( q, !isDir ?
01185                                    i18n("File Already Exists") : i18n("Already Exists as Folder"),
01186                                    (*it).uSource.url(),
01187                                    (*it).uDest.url(),
01188                                    mode, newPath,
01189                                    (*it).size, destsize,
01190                                    (*it).ctime, destctime,
01191                                    (*it).mtime, destmtime );
01192 
01193     }
01194     else
01195     {
01196         if ( job->error() == ERR_USER_CANCELED )
01197             res = R_CANCEL;
01198         else if ( !q->isInteractive() ) {
01199             q->Job::slotResult( job ); // will set the error and emit result(this)
01200             return;
01201         }
01202         else
01203         {
01204             SkipDialog_Result skipResult = q->ui()->askSkip( q, files.count() > 1,
01205                                                           job->errorString() );
01206 
01207             // Convert the return code from SkipDialog into a RenameDialog code
01208             res = ( skipResult == S_SKIP ) ? R_SKIP :
01209                          ( skipResult == S_AUTO_SKIP ) ? R_AUTO_SKIP :
01210                                         R_CANCEL;
01211         }
01212     }
01213 
01214     if (m_reportTimer)
01215         m_reportTimer->start(REPORT_TIMEOUT);
01216 
01217     q->removeSubjob( job );
01218     assert ( !q->hasSubjobs() );
01219     switch ( res ) {
01220         case R_CANCEL:
01221             q->setError( ERR_USER_CANCELED );
01222             q->emitResult();
01223             return;
01224         case R_RENAME:
01225         {
01226             KUrl newUrl( (*it).uDest );
01227             newUrl.setPath( newPath );
01228             emit q->renamed( q, (*it).uDest, newUrl ); // for e.g. kpropsdlg
01229             (*it).uDest = newUrl;
01230 
01231             QList<CopyInfo> files;
01232             files.append(*it);
01233             emit q->aboutToCreate( q, files );
01234         }
01235         break;
01236         case R_AUTO_SKIP:
01237             m_bAutoSkip = true;
01238             // fall through
01239         case R_SKIP:
01240             // Move on to next file
01241             skip( (*it).uSource );
01242             m_processedSize += (*it).size;
01243             files.erase( it );
01244             m_processedFiles++;
01245             break;
01246        case R_OVERWRITE_ALL:
01247             m_bOverwriteAll = true;
01248             break;
01249         case R_OVERWRITE:
01250             // Add to overwrite list, so that copyNextFile knows to overwrite
01251             m_overwriteList.append( (*it).uDest.path() );
01252             break;
01253         default:
01254             assert( 0 );
01255     }
01256     state = STATE_COPYING_FILES;
01257     copyNextFile();
01258 }
01259 
01260 KIO::Job* CopyJobPrivate::linkNextFile( const KUrl& uSource, const KUrl& uDest, JobFlags flags )
01261 {
01262     //kDebug(7007) << "Linking";
01263     if (
01264         (uSource.protocol() == uDest.protocol()) &&
01265         (uSource.host() == uDest.host()) &&
01266         (uSource.port() == uDest.port()) &&
01267         (uSource.user() == uDest.user()) &&
01268         (uSource.pass() == uDest.pass()) )
01269     {
01270         // This is the case of creating a real symlink
01271         KIO::SimpleJob *newJob = KIO::symlink( uSource.path(), uDest, flags|HideProgressInfo /*no GUI*/ );
01272         Scheduler::scheduleJob(newJob);
01273         //kDebug(7007) << "Linking target=" << uSource.path() << "link=" << uDest;
01274         //emit linking( this, uSource.path(), uDest );
01275         m_bCurrentOperationIsLink = true;
01276         m_currentSrcURL=uSource;
01277         m_currentDestURL=uDest;
01278         m_bURLDirty = true;
01279         //Observer::self()->slotCopying( this, uSource, uDest ); // should be slotLinking perhaps
01280         return newJob;
01281     } else {
01282         Q_Q(CopyJob);
01283         //kDebug(7007) << "Linking URL=" << uSource << "link=" << uDest;
01284         if ( uDest.isLocalFile() ) {
01285             // if the source is a devices url, handle it a littlebit special
01286 
01287             QString path = uDest.path();
01288             //kDebug(7007) << "path=" << path;
01289             QFile f( path );
01290             if ( f.open( QIODevice::ReadWrite ) )
01291             {
01292                 f.close();
01293                 KDesktopFile desktopFile( path );
01294                 KConfigGroup config = desktopFile.desktopGroup();
01295                 KUrl url = uSource;
01296                 url.setPass( "" );
01297                 config.writePathEntry( "URL", url.url() );
01298                 config.writeEntry( "Name", url.url() );
01299                 config.writeEntry( "Type", QString::fromLatin1("Link") );
01300                 QString protocol = uSource.protocol();
01301                 if ( protocol == QLatin1String("ftp") )
01302                     config.writeEntry( "Icon", QString::fromLatin1("folder-remote") );
01303                 else if ( protocol == QLatin1String("http") )
01304                     config.writeEntry( "Icon", QString::fromLatin1("text-html") );
01305                 else if ( protocol == QLatin1String("info") )
01306                     config.writeEntry( "Icon", QString::fromLatin1("text-x-texinfo") );
01307                 else if ( protocol == QLatin1String("mailto") )   // sven:
01308                     config.writeEntry( "Icon", QString::fromLatin1("internet-mail") ); // added mailto: support
01309                 else
01310                     config.writeEntry( "Icon", QString::fromLatin1("unknown") );
01311                 config.sync();
01312                 files.erase( files.begin() ); // done with this one, move on
01313                 m_processedFiles++;
01314                 //emit processedAmount( this, KJob::Files, m_processedFiles );
01315                 copyNextFile();
01316                 return 0;
01317             }
01318             else
01319             {
01320                 kDebug(7007) << "ERR_CANNOT_OPEN_FOR_WRITING";
01321                 q->setError( ERR_CANNOT_OPEN_FOR_WRITING );
01322                 q->setErrorText( uDest.path() );
01323                 q->emitResult();
01324                 return 0;
01325             }
01326         } else {
01327             // Todo: not show "link" on remote dirs if the src urls are not from the same protocol+host+...
01328             q->setError( ERR_CANNOT_SYMLINK );
01329             q->setErrorText( uDest.prettyUrl() );
01330             q->emitResult();
01331             return 0;
01332         }
01333     }
01334 }
01335 
01336 void CopyJobPrivate::copyNextFile()
01337 {
01338     Q_Q(CopyJob);
01339     bool bCopyFile = false;
01340     //kDebug(7007);
01341     // Take the first file in the list
01342     QList<CopyInfo>::Iterator it = files.begin();
01343     // Is this URL on the skip list ?
01344     while (it != files.end() && !bCopyFile)
01345     {
01346         const QString destFile = (*it).uDest.path();
01347         bCopyFile = !shouldSkip( destFile );
01348         if ( !bCopyFile ) {
01349             files.erase( it );
01350             it = files.begin();
01351         }
01352     }
01353 
01354     if (bCopyFile) // any file to create, finally ?
01355     {
01356         const KUrl& uSource = (*it).uSource;
01357         const KUrl& uDest = (*it).uDest;
01358         // Do we set overwrite ?
01359         bool bOverwrite;
01360         const QString destFile = uDest.path();
01361         kDebug(7007) << "copying " << destFile;
01362         if ( uDest == uSource )
01363             bOverwrite = false;
01364         else
01365             bOverwrite = shouldOverwrite( destFile );
01366 
01367         m_bCurrentOperationIsLink = false;
01368         KIO::Job * newjob = 0;
01369         if ( m_mode == CopyJob::Link ) {
01370             // User requested that a symlink be made
01371           JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
01372             newjob = linkNextFile(uSource, uDest, flags);
01373             if (!newjob)
01374                 return;
01375         } else if ( !(*it).linkDest.isEmpty() &&
01376                   (uSource.protocol() == uDest.protocol()) &&
01377                   (uSource.host() == uDest.host()) &&
01378                   (uSource.port() == uDest.port()) &&
01379                   (uSource.user() == uDest.user()) &&
01380                   (uSource.pass() == uDest.pass()))
01381             // Copying a symlink - only on the same protocol/host/etc. (#5601, downloading an FTP file through its link),
01382         {
01383             JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
01384             KIO::SimpleJob *newJob = KIO::symlink( (*it).linkDest, uDest, flags | HideProgressInfo /*no GUI*/ );
01385             Scheduler::scheduleJob(newJob);
01386             newjob = newJob;
01387             //kDebug(7007) << "Linking target=" << (*it).linkDest << "link=" << uDest;
01388             m_currentSrcURL = KUrl( (*it).linkDest );
01389             m_currentDestURL = uDest;
01390             m_bURLDirty = true;
01391             //emit linking( this, (*it).linkDest, uDest );
01392             //Observer::self()->slotCopying( this, m_currentSrcURL, uDest ); // should be slotLinking perhaps
01393             m_bCurrentOperationIsLink = true;
01394             // NOTE: if we are moving stuff, the deletion of the source will be done in slotResultCopyingFiles
01395         } else if (m_mode == CopyJob::Move) // Moving a file
01396         {
01397             JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
01398             KIO::FileCopyJob * moveJob = KIO::file_move( uSource, uDest, (*it).permissions, flags | HideProgressInfo/*no GUI*/ );
01399             moveJob->setSourceSize( (*it).size );
01400             newjob = moveJob;
01401             //kDebug(7007) << "Moving" << uSource << "to" << uDest;
01402             //emit moving( this, uSource, uDest );
01403             m_currentSrcURL=uSource;
01404             m_currentDestURL=uDest;
01405             m_bURLDirty = true;
01406             //Observer::self()->slotMoving( this, uSource, uDest );
01407         }
01408         else // Copying a file
01409         {
01410             // If source isn't local and target is local, we ignore the original permissions
01411             // Otherwise, files downloaded from HTTP end up with -r--r--r--
01412             bool remoteSource = !KProtocolManager::supportsListing(uSource);
01413             int permissions = (*it).permissions;
01414             if ( m_defaultPermissions || ( remoteSource && uDest.isLocalFile() ) )
01415                 permissions = -1;
01416             JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
01417             KIO::FileCopyJob * copyJob = KIO::file_copy( uSource, uDest, permissions, flags | HideProgressInfo/*no GUI*/ );
01418             copyJob->setParentJob( q ); // in case of rename dialog
01419             copyJob->setSourceSize( (*it).size );
01420             if ((*it).mtime != -1) {
01421                 QDateTime dt; dt.setTime_t( (*it).mtime );
01422                 copyJob->setModificationTime( dt );
01423             }
01424             newjob = copyJob;
01425             //kDebug(7007) << "Copying" << uSource << "to" << uDest;
01426             m_currentSrcURL=uSource;
01427             m_currentDestURL=uDest;
01428             m_bURLDirty = true;
01429         }
01430         q->addSubjob(newjob);
01431         q->connect( newjob, SIGNAL( processedSize( KJob*, qulonglong ) ),
01432                     SLOT( slotProcessedSize( KJob*, qulonglong ) ) );
01433         q->connect( newjob, SIGNAL( totalSize( KJob*, qulonglong ) ),
01434                     SLOT( slotTotalSize( KJob*, qulonglong ) ) );
01435     }
01436     else
01437     {
01438         // We're done
01439         //kDebug(7007) << "copyNextFile finished";
01440         deleteNextDir();
01441     }
01442 }
01443 
01444 void CopyJobPrivate::deleteNextDir()
01445 {
01446     Q_Q(CopyJob);
01447     if ( m_mode == CopyJob::Move && !dirsToRemove.isEmpty() ) // some dirs to delete ?
01448     {
01449         state = STATE_DELETING_DIRS;
01450         m_bURLDirty = true;
01451         // Take first dir to delete out of list - last ones first !
01452         KUrl::List::Iterator it = --dirsToRemove.end();
01453         SimpleJob *job = KIO::rmdir( *it );
01454         Scheduler::scheduleJob(job);
01455         dirsToRemove.erase(it);
01456         q->addSubjob( job );
01457     }
01458     else
01459     {
01460         // This step is done, move on
01461         state = STATE_SETTING_DIR_ATTRIBUTES;
01462         m_directoriesCopiedIterator = m_directoriesCopied.constBegin();
01463         setNextDirAttribute();
01464     }
01465 }
01466 
01467 void CopyJobPrivate::setNextDirAttribute()
01468 {
01469     Q_Q(CopyJob);
01470     while (m_directoriesCopiedIterator != m_directoriesCopied.constEnd() &&
01471            (*m_directoriesCopiedIterator).mtime == -1) {
01472         ++m_directoriesCopiedIterator;
01473     }
01474     if ( m_directoriesCopiedIterator != m_directoriesCopied.constEnd() ) {
01475         const KUrl url = (*m_directoriesCopiedIterator).uDest;
01476         const time_t mtime = (*m_directoriesCopiedIterator).mtime;
01477         const QDateTime dt = QDateTime::fromTime_t(mtime);
01478         ++m_directoriesCopiedIterator;
01479 
01480         KIO::SimpleJob *job = KIO::setModificationTime( url, dt );
01481         Scheduler::scheduleJob(job);
01482         q->addSubjob( job );
01483 
01484 
01485 #if 0 // ifdef Q_OS_UNIX
01486         // TODO: can be removed now. Or reintroduced as a fast path for local files
01487         // if launching even more jobs as done above is a performance problem.
01488         //
01489         QLinkedList<CopyInfo>::const_iterator it = m_directoriesCopied.constBegin();
01490         for ( ; it != m_directoriesCopied.constEnd() ; ++it ) {
01491             const KUrl& url = (*it).uDest;
01492             if ( url.isLocalFile() && (*it).mtime != (time_t)-1 ) {
01493                 const QByteArray path = QFile::encodeName( url.path() );
01494                 KDE_struct_stat statbuf;
01495                 if (KDE_lstat(path, &statbuf) == 0) {
01496                     struct utimbuf utbuf;
01497                     utbuf.actime = statbuf.st_atime; // access time, unchanged
01498                     utbuf.modtime = (*it).mtime; // modification time
01499                     utime( path, &utbuf );
01500                 }
01501 
01502             }
01503         }
01504         m_directoriesCopied.clear();
01505         // but then we need to jump to the else part below. Maybe with a recursive call?
01506 #endif
01507     } else {
01508         // Finished - tell the world
01509         if ( !m_bOnlyRenames )
01510         {
01511             KUrl url( m_globalDest );
01512             if ( m_globalDestinationState != DEST_IS_DIR || m_asMethod )
01513                 url.setPath( url.directory() );
01514             //kDebug(7007) << "KDirNotify'ing FilesAdded " << url;
01515             org::kde::KDirNotify::emitFilesAdded( url.url() );
01516 
01517             Q_FOREACH(const KUrl& url, m_skippedSourceUrls)
01518                 m_srcList.removeAll(url);
01519 
01520             if ( m_mode == CopyJob::Move && !m_srcList.isEmpty() ) {
01521                 //kDebug(7007) << "KDirNotify'ing FilesRemoved " << m_srcList.toStringList();
01522                 org::kde::KDirNotify::emitFilesRemoved( m_srcList.toStringList() );
01523             }
01524         }
01525         if (m_reportTimer)
01526             m_reportTimer->stop();
01527         --m_processedFiles; // undo the "start at 1" hack
01528         slotReport(); // display final numbers, important if progress dialog stays up
01529 
01530         q->emitResult();
01531     }
01532 }
01533 
01534 void CopyJobPrivate::slotProcessedSize( KJob*, qulonglong data_size )
01535 {
01536   Q_Q(CopyJob);
01537   //kDebug(7007) << data_size;
01538   m_fileProcessedSize = data_size;
01539   q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
01540 
01541   if ( m_processedSize + m_fileProcessedSize > m_totalSize )
01542   {
01543     // Example: download any attachment from bugs.kde.org
01544     m_totalSize = m_processedSize + m_fileProcessedSize;
01545     //kDebug(7007) << "Adjusting m_totalSize to " << m_totalSize;
01546     q->setTotalAmount(KJob::Bytes, m_totalSize); // safety
01547   }
01548   //kDebug(7007) << "emit processedSize " << (unsigned long) (m_processedSize + m_fileProcessedSize);
01549   q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
01550 }
01551 
01552 void CopyJobPrivate::slotTotalSize( KJob*, qulonglong size )
01553 {
01554   Q_Q(CopyJob);
01555   //kDebug(7007) << "slotTotalSize: " << size;
01556   // Special case for copying a single file
01557   // This is because some protocols don't implement stat properly
01558   // (e.g. HTTP), and don't give us a size in some cases (redirection)
01559   // so we'd rather rely on the size given for the transfer
01560   if ( m_bSingleFileCopy && size > m_totalSize)
01561   {
01562     //kDebug(7007) << "slotTotalSize: updating totalsize to " << size;
01563     m_totalSize = size;
01564     q->setTotalAmount(KJob::Bytes, size);
01565   }
01566 }
01567 
01568 void CopyJobPrivate::slotResultDeletingDirs( KJob * job )
01569 {
01570     Q_Q(CopyJob);
01571     if (job->error())
01572     {
01573         // Couldn't remove directory. Well, perhaps it's not empty
01574         // because the user pressed Skip for a given file in it.
01575         // Let's not display "Could not remove dir ..." for each of those dir !
01576     }
01577     q->removeSubjob( job );
01578     assert( !q->hasSubjobs() );
01579     deleteNextDir();
01580 }
01581 
01582 void CopyJobPrivate::slotResultSettingDirAttributes( KJob * job )
01583 {
01584     Q_Q(CopyJob);
01585     if (job->error())
01586     {
01587         // Couldn't set directory attributes. Ignore the error, it can happen
01588         // with inferior file systems like VFAT.
01589         // Let's not display warnings for each dir like "cp -a" does.
01590     }
01591     q->removeSubjob( job );
01592     assert( !q->hasSubjobs() );
01593     setNextDirAttribute();
01594 }
01595 
01596 // We were trying to do a direct renaming, before even stat'ing
01597 void CopyJobPrivate::slotResultRenaming( KJob* job )
01598 {
01599     Q_Q(CopyJob);
01600     int err = job->error();
01601     const QString errText = job->errorText();
01602     // Merge metadata from subjob
01603     KIO::Job* kiojob = dynamic_cast<KIO::Job*>(job);
01604     Q_ASSERT(kiojob);
01605     m_incomingMetaData += kiojob->metaData();
01606     q->removeSubjob( job );
01607     assert ( !q->hasSubjobs() );
01608     // Determine dest again
01609     KUrl dest = m_dest;
01610     if ( destinationState == DEST_IS_DIR && !m_asMethod )
01611         dest.addPath( m_currentSrcURL.fileName() );
01612     if ( err )
01613     {
01614         // Direct renaming didn't work. Try renaming to a temp name,
01615         // this can help e.g. when renaming 'a' to 'A' on a VFAT partition.
01616         // In that case it's the _same_ dir, we don't want to copy+del (data loss!)
01617       if ( m_currentSrcURL.isLocalFile() && m_currentSrcURL.url(KUrl::RemoveTrailingSlash) != dest.url(KUrl::RemoveTrailingSlash) &&
01618            m_currentSrcURL.url(KUrl::RemoveTrailingSlash).toLower() == dest.url(KUrl::RemoveTrailingSlash).toLower() &&
01619              ( err == ERR_FILE_ALREADY_EXIST ||
01620                err == ERR_DIR_ALREADY_EXIST ||
01621                err == ERR_IDENTICAL_FILES ) )
01622         {
01623             kDebug(7007) << "Couldn't rename directly, dest already exists. Detected special case of lower/uppercase renaming in same dir, try with 2 rename calls";
01624             QByteArray _src( QFile::encodeName(m_currentSrcURL.path()) );
01625             QByteArray _dest( QFile::encodeName(dest.path()) );
01626             KTemporaryFile tmpFile;
01627             tmpFile.setPrefix(m_currentSrcURL.directory(KUrl::ObeyTrailingSlash));
01628             tmpFile.setAutoRemove(false);
01629             tmpFile.open();
01630             QByteArray _tmp( QFile::encodeName(tmpFile.fileName()) );
01631             kDebug(7007) << "KTemporaryFile using" << _tmp << "as intermediary";
01632             if ( KDE_rename( _src, _tmp ) == 0 )
01633             {
01634                 if ( !QFile::exists( _dest ) && KDE_rename( _tmp, _dest ) == 0 )
01635                 {
01636                     kDebug(7007) << "Success.";
01637                     err = 0;
01638                 }
01639                 else
01640                 {
01641                     // Revert back to original name!
01642                     if ( KDE_rename( _tmp, _src ) != 0 ) {
01643                         kError(7007) << "Couldn't rename" << tmpFile.fileName() << "back to" << _src << '!';
01644                         // Severe error, abort
01645                         q->Job::slotResult( job ); // will set the error and emit result(this)
01646                         return;
01647                     }
01648                 }
01649             }
01650         }
01651     }
01652     if ( err )
01653     {
01654         // This code is similar to CopyJobPrivate::slotResultConflictCopyingFiles
01655         // but here it's about the base src url being moved/renamed
01656         // (*m_currentStatSrc) and its dest (m_dest), not about a single file.
01657         // It also means we already stated the dest, here.
01658         // On the other hand we haven't stated the src yet (we skipped doing it
01659         // to save time, since it's not necessary to rename directly!)...
01660 
01661         Q_ASSERT( m_currentSrcURL == *m_currentStatSrc );
01662 
01663         // Existing dest?
01664         if ( err == ERR_DIR_ALREADY_EXIST ||
01665              err == ERR_FILE_ALREADY_EXIST ||
01666              err == ERR_IDENTICAL_FILES )
01667         {
01668             if (m_reportTimer)
01669                 m_reportTimer->stop();
01670 
01671             // Should we skip automatically ?
01672             if ( m_bAutoSkip ) {
01673                 // Move on to next file
01674                 skipSrc();
01675                 return;
01676             } else if ( m_bOverwriteAll ) {
01677                 ; // nothing to do, stat+copy+del will overwrite
01678             } else if ( q->isInteractive() ) {
01679                 QString newPath;
01680                 // If src==dest, use "overwrite-itself"
01681                 RenameDialog_Mode mode = (RenameDialog_Mode)
01682                                       ( ( m_currentSrcURL == dest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE );
01683 
01684                 if ( m_srcList.count() > 1 )
01685                     mode = (RenameDialog_Mode) ( mode | M_MULTI | M_SKIP );
01686                 else
01687                     mode = (RenameDialog_Mode) ( mode | M_SINGLE );
01688 
01689                 // we lack mtime info for both the src (not stated)
01690                 // and the dest (stated but this info wasn't stored)
01691                 // Let's do it for local files, at least
01692                 KIO::filesize_t sizeSrc = (KIO::filesize_t) -1;
01693                 KIO::filesize_t sizeDest = (KIO::filesize_t) -1;
01694                 time_t ctimeSrc = (time_t) -1;
01695                 time_t ctimeDest = (time_t) -1;
01696                 time_t mtimeSrc = (time_t) -1;
01697                 time_t mtimeDest = (time_t) -1;
01698 
01699                 KDE_struct_stat stat_buf;
01700                 if ( m_currentSrcURL.isLocalFile() &&
01701                     KDE_stat(QFile::encodeName(m_currentSrcURL.path()), &stat_buf) == 0 ) {
01702                     sizeSrc = stat_buf.st_size;
01703                     ctimeSrc = stat_buf.st_ctime;
01704                     mtimeSrc = stat_buf.st_mtime;
01705                 }
01706                 if ( dest.isLocalFile() &&
01707                     KDE_stat(QFile::encodeName(dest.path()), &stat_buf) == 0 ) {
01708                     sizeDest = stat_buf.st_size;
01709                     ctimeDest = stat_buf.st_ctime;
01710                     mtimeDest = stat_buf.st_mtime;
01711                 }
01712 
01713                 RenameDialog_Result r = q->ui()->askFileRename(
01714                     q,
01715                     err != ERR_DIR_ALREADY_EXIST ? i18n("File Already Exists") : i18n("Already Exists as Folder"),
01716                     m_currentSrcURL.url(),
01717                     dest.url(),
01718                     mode, newPath,
01719                     sizeSrc, sizeDest,
01720                     ctimeSrc, ctimeDest,
01721                     mtimeSrc, mtimeDest );
01722                 if (m_reportTimer)
01723                     m_reportTimer->start(REPORT_TIMEOUT);
01724 
01725                 switch ( r )
01726                 {
01727                 case R_CANCEL:
01728                 {
01729                     q->setError( ERR_USER_CANCELED );
01730                     q->emitResult();
01731                     return;
01732                 }
01733                 case R_RENAME:
01734                 {
01735                     // Set m_dest to the chosen destination
01736                     // This is only for this src url; the next one will revert to m_globalDest
01737                     m_dest.setPath( newPath );
01738                     KIO::Job* job = KIO::stat( m_dest, StatJob::DestinationSide, 2, KIO::HideProgressInfo );
01739                     state = STATE_STATING;
01740                     destinationState = DEST_NOT_STATED;
01741                     q->addSubjob(job);
01742                     return;
01743                 }
01744                 case R_AUTO_SKIP:
01745                     m_bAutoSkip = true;
01746                     // fall through
01747                 case R_SKIP:
01748                     // Move on to next file
01749                     skipSrc();
01750                     return;
01751                 case R_OVERWRITE_ALL:
01752                     m_bOverwriteAll = true;
01753                     break;
01754                 case R_OVERWRITE:
01755                     // Add to overwrite list
01756                     // Note that we add dest, not m_dest.
01757                     // This ensures that when moving several urls into a dir (m_dest),
01758                     // we only overwrite for the current one, not for all.
01759                     // When renaming a single file (m_asMethod), it makes no difference.
01760                     kDebug(7007) << "adding to overwrite list: " << dest.path();
01761                     m_overwriteList.append( dest.path() );
01762                     break;
01763                 default:
01764                     //assert( 0 );
01765                     break;
01766                 }
01767             } else if ( err != KIO::ERR_UNSUPPORTED_ACTION ) {
01768                 // Dest already exists, and job is not interactive -> abort with error
01769                 q->setError( err );
01770                 q->setErrorText( errText );
01771                 q->emitResult();
01772                 return;
01773             }
01774         } else if ( err != KIO::ERR_UNSUPPORTED_ACTION ) {
01775             kDebug(7007) << "Couldn't rename" << m_currentSrcURL << "to" << dest << ", aborting";
01776             q->setError( err );
01777             q->setErrorText( errText );
01778             q->emitResult();
01779             return;
01780         }
01781         kDebug(7007) << "Couldn't rename" << m_currentSrcURL << "to" << dest << ", reverting to normal way, starting with stat";
01782         //kDebug(7007) << "KIO::stat on " << m_currentSrcURL;
01783         KIO::Job* job = KIO::stat( m_currentSrcURL, StatJob::SourceSide, 2, KIO::HideProgressInfo );
01784         state = STATE_STATING;
01785         q->addSubjob(job);
01786         m_bOnlyRenames = false;
01787     }
01788     else
01789     {
01790         //kDebug(7007) << "Renaming succeeded, move on";
01791         ++m_processedFiles;
01792         emit q->copyingDone( q, *m_currentStatSrc, dest, -1 /*mtime unknown, and not needed*/, true, true );
01793         statNextSrc();
01794     }
01795 }
01796 
01797 void CopyJob::slotResult( KJob *job )
01798 {
01799     Q_D(CopyJob);
01800     //kDebug(7007) << "d->state=" << (int) d->state;
01801     // In each case, what we have to do is :
01802     // 1 - check for errors and treat them
01803     // 2 - removeSubjob(job);
01804     // 3 - decide what to do next
01805 
01806     switch ( d->state ) {
01807         case STATE_STATING: // We were trying to stat a src url or the dest
01808             d->slotResultStating( job );
01809             break;
01810         case STATE_RENAMING: // We were trying to do a direct renaming, before even stat'ing
01811         {
01812             d->slotResultRenaming( job );
01813             break;
01814         }
01815         case STATE_LISTING: // recursive listing finished
01816             //kDebug(7007) << "totalSize:" << (unsigned int) d->m_totalSize << "files:" << d->files.count() << "d->dirs:" << d->dirs.count();
01817             // Was there an error ?
01818             if (job->error())
01819             {
01820                 Job::slotResult( job ); // will set the error and emit result(this)
01821                 return;
01822             }
01823 
01824             removeSubjob( job );
01825             assert ( !hasSubjobs() );
01826 
01827             d->statNextSrc();
01828             break;
01829         case STATE_CREATING_DIRS:
01830             d->slotResultCreatingDirs( job );
01831             break;
01832         case STATE_CONFLICT_CREATING_DIRS:
01833             d->slotResultConflictCreatingDirs( job );
01834             break;
01835         case STATE_COPYING_FILES:
01836             d->slotResultCopyingFiles( job );
01837             break;
01838         case STATE_CONFLICT_COPYING_FILES:
01839             d->slotResultConflictCopyingFiles( job );
01840             break;
01841         case STATE_DELETING_DIRS:
01842             d->slotResultDeletingDirs( job );
01843             break;
01844         case STATE_SETTING_DIR_ATTRIBUTES:
01845             d->slotResultSettingDirAttributes( job );
01846             break;
01847         default:
01848             assert( 0 );
01849     }
01850 }
01851 
01852 void KIO::CopyJob::setDefaultPermissions( bool b )
01853 {
01854     d_func()->m_defaultPermissions = b;
01855 }
01856 
01857 KIO::CopyJob::CopyMode KIO::CopyJob::operationMode() const
01858 {
01859     return d_func()->m_mode;
01860 }
01861 
01862 CopyJob *KIO::copy(const KUrl& src, const KUrl& dest, JobFlags flags)
01863 {
01864     //kDebug(7007) << "src=" << src << "dest=" << dest;
01865     KUrl::List srcList;
01866     srcList.append( src );
01867     return CopyJobPrivate::newJob(srcList, dest, CopyJob::Copy, false, flags);
01868 }
01869 
01870 CopyJob *KIO::copyAs(const KUrl& src, const KUrl& dest, JobFlags flags)
01871 {
01872     //kDebug(7007) << "src=" << src << "dest=" << dest;
01873     KUrl::List srcList;
01874     srcList.append( src );
01875     return CopyJobPrivate::newJob(srcList, dest, CopyJob::Copy, true, flags);
01876 }
01877 
01878 CopyJob *KIO::copy( const KUrl::List& src, const KUrl& dest, JobFlags flags )
01879 {
01880     //kDebug(7007) << src << dest;
01881     return CopyJobPrivate::newJob(src, dest, CopyJob::Copy, false, flags);
01882 }
01883 
01884 CopyJob *KIO::move(const KUrl& src, const KUrl& dest, JobFlags flags)
01885 {
01886     //kDebug(7007) << src << dest;
01887     KUrl::List srcList;
01888     srcList.append( src );
01889     return CopyJobPrivate::newJob(srcList, dest, CopyJob::Move, false, flags);
01890 }
01891 
01892 CopyJob *KIO::moveAs(const KUrl& src, const KUrl& dest, JobFlags flags)
01893 {
01894     //kDebug(7007) << src << dest;
01895     KUrl::List srcList;
01896     srcList.append( src );
01897     return CopyJobPrivate::newJob(srcList, dest, CopyJob::Move, true, flags);
01898 }
01899 
01900 CopyJob *KIO::move( const KUrl::List& src, const KUrl& dest, JobFlags flags)
01901 {
01902     //kDebug(7007) << src << dest;
01903     return CopyJobPrivate::newJob(src, dest, CopyJob::Move, false, flags);
01904 }
01905 
01906 CopyJob *KIO::link(const KUrl& src, const KUrl& destDir, JobFlags flags)
01907 {
01908     KUrl::List srcList;
01909     srcList.append( src );
01910     return CopyJobPrivate::newJob(srcList, destDir, CopyJob::Link, false, flags);
01911 }
01912 
01913 CopyJob *KIO::link(const KUrl::List& srcList, const KUrl& destDir, JobFlags flags)
01914 {
01915     return CopyJobPrivate::newJob(srcList, destDir, CopyJob::Link, false, flags);
01916 }
01917 
01918 CopyJob *KIO::linkAs(const KUrl& src, const KUrl& destDir, JobFlags flags )
01919 {
01920     KUrl::List srcList;
01921     srcList.append( src );
01922     return CopyJobPrivate::newJob(srcList, destDir, CopyJob::Link, false, flags);
01923 }
01924 
01925 CopyJob *KIO::trash(const KUrl& src, JobFlags flags)
01926 {
01927     KUrl::List srcList;
01928     srcList.append( src );
01929     return CopyJobPrivate::newJob(srcList, KUrl( "trash:/" ), CopyJob::Move, false, flags);
01930 }
01931 
01932 CopyJob *KIO::trash(const KUrl::List& srcList, JobFlags flags)
01933 {
01934     return CopyJobPrivate::newJob(srcList, KUrl( "trash:/" ), CopyJob::Move, false, flags);
01935 }
01936 
01937 #include "copyjob.moc"

KIO

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.6
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal