00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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>
00049 #include <QPointer>
00050
00051 #include "job_p.h"
00052
00053 using namespace KIO;
00054
00055
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_bAutoSkipFiles( false )
00119 , m_bAutoSkipDirs( false )
00120 , m_bOverwriteAllFiles( false )
00121 , m_bOverwriteAllDirs( false )
00122 , m_conflictError(0)
00123 , m_reportTimer(0)
00124 {
00125 }
00126
00127
00128
00129
00130
00131 KUrl m_globalDest;
00132
00133 DestinationState m_globalDestinationState;
00134
00135 bool m_defaultPermissions;
00136
00137 bool m_bURLDirty;
00138
00139
00140 QLinkedList<CopyInfo> m_directoriesCopied;
00141 QLinkedList<CopyInfo>::const_iterator m_directoriesCopiedIterator;
00142
00143 CopyJob::CopyMode m_mode;
00144 bool m_asMethod;
00145 DestinationState destinationState;
00146 CopyJobState state;
00147 KIO::filesize_t m_totalSize;
00148 KIO::filesize_t m_processedSize;
00149 KIO::filesize_t m_fileProcessedSize;
00150 int m_processedFiles;
00151 int m_processedDirs;
00152 QList<CopyInfo> files;
00153 QList<CopyInfo> dirs;
00154 KUrl::List dirsToRemove;
00155 KUrl::List m_srcList;
00156 KUrl::List m_skippedSourceUrls;
00157 KUrl::List::const_iterator m_currentStatSrc;
00158 bool m_bCurrentSrcIsDir;
00159 bool m_bCurrentOperationIsLink;
00160 bool m_bSingleFileCopy;
00161 bool m_bOnlyRenames;
00162 KUrl m_dest;
00163 KUrl m_currentDest;
00164
00165 QStringList m_skipList;
00166 QStringList m_overwriteList;
00167 bool m_bAutoSkipFiles;
00168 bool m_bAutoSkipDirs;
00169 bool m_bOverwriteAllFiles;
00170 bool m_bOverwriteAllDirs;
00171 int m_conflictError;
00172
00173 QTimer *m_reportTimer;
00174
00175 KUrl m_currentSrcURL;
00176 KUrl m_currentDestURL;
00177
00178 void statCurrentSrc();
00179 void statNextSrc();
00180
00181
00182 void slotResultStating( KJob * job );
00183 void startListing( const KUrl & src );
00184 void slotResultCreatingDirs( KJob * job );
00185 void slotResultConflictCreatingDirs( KJob * job );
00186 void createNextDir();
00187 void slotResultCopyingFiles( KJob * job );
00188 void slotResultConflictCopyingFiles( KJob * job );
00189
00190 KIO::Job* linkNextFile( const KUrl& uSource, const KUrl& uDest, JobFlags flags );
00191 void copyNextFile();
00192 void slotResultDeletingDirs( KJob * job );
00193 void deleteNextDir();
00194 void skip( const KUrl & sourceURL );
00195 void slotResultRenaming( KJob * job );
00196 void slotResultSettingDirAttributes( KJob * job );
00197 void setNextDirAttribute();
00198
00199 void startRenameJob(const KUrl &slave_url);
00200 bool shouldOverwriteDir( const QString& path ) const;
00201 bool shouldOverwriteFile( const QString& path ) const;
00202 bool shouldSkip( const QString& path ) const;
00203 void skipSrc();
00204
00205 void slotStart();
00206 void slotEntries( KIO::Job*, const KIO::UDSEntryList& list );
00210 void slotProcessedSize( KJob*, qulonglong data_size );
00215 void slotTotalSize( KJob*, qulonglong size );
00216
00217 void slotReport();
00218
00219 Q_DECLARE_PUBLIC(CopyJob)
00220
00221 static inline CopyJob *newJob(const KUrl::List& src, const KUrl& dest,
00222 CopyJob::CopyMode mode, bool asMethod, JobFlags flags)
00223 {
00224 CopyJob *job = new CopyJob(*new CopyJobPrivate(src,dest,mode,asMethod));
00225 job->setUiDelegate(new JobUiDelegate);
00226 if (!(flags & HideProgressInfo))
00227 KIO::getJobTracker()->registerJob(job);
00228 return job;
00229 }
00230 };
00231
00232 CopyJob::CopyJob(CopyJobPrivate &dd)
00233 : Job(dd)
00234 {
00235 QTimer::singleShot(0, this, SLOT(slotStart()));
00236 }
00237
00238 CopyJob::~CopyJob()
00239 {
00240 }
00241
00242 KUrl::List CopyJob::srcUrls() const
00243 {
00244 return d_func()->m_srcList;
00245 }
00246
00247 KUrl CopyJob::destUrl() const
00248 {
00249 return d_func()->m_dest;
00250 }
00251
00252 void CopyJobPrivate::slotStart()
00253 {
00254 Q_Q(CopyJob);
00260 m_reportTimer = new QTimer(q);
00261
00262 q->connect(m_reportTimer,SIGNAL(timeout()),q,SLOT(slotReport()));
00263 m_reportTimer->start(REPORT_TIMEOUT);
00264
00265
00266 KIO::Job * job = KIO::stat( m_dest, StatJob::DestinationSide, 2, KIO::HideProgressInfo );
00267
00268 q->addSubjob(job);
00269 }
00270
00271
00272 KIO_EXPORT bool kio_resolve_local_urls = true;
00273
00274 void CopyJobPrivate::slotResultStating( KJob *job )
00275 {
00276 Q_Q(CopyJob);
00277
00278
00279 if (job->error() && destinationState != DEST_NOT_STATED )
00280 {
00281 KUrl srcurl = ((SimpleJob*)job)->url();
00282 if ( !srcurl.isLocalFile() )
00283 {
00284
00285
00286
00287 kDebug(7007) << "Error while stating source. Activating hack";
00288 q->removeSubjob( job );
00289 assert ( !q->hasSubjobs() );
00290 struct CopyInfo info;
00291 info.permissions = (mode_t) -1;
00292 info.mtime = (time_t) -1;
00293 info.ctime = (time_t) -1;
00294 info.size = (KIO::filesize_t)-1;
00295 info.uSource = srcurl;
00296 info.uDest = m_dest;
00297
00298 if ( destinationState == DEST_IS_DIR && !m_asMethod )
00299 info.uDest.addPath( srcurl.fileName() );
00300
00301 files.append( info );
00302 statNextSrc();
00303 return;
00304 }
00305
00306
00307 q->Job::slotResult( job );
00308 return;
00309 }
00310
00311
00312 const UDSEntry entry = static_cast<StatJob*>(job)->statResult();
00313 const QString sLocalPath = entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
00314 const bool isDir = entry.isDir();
00315
00316 if ( destinationState == DEST_NOT_STATED )
00317
00318 {
00319 if (job->error())
00320 destinationState = DEST_DOESNT_EXIST;
00321 else {
00322
00323 destinationState = isDir ? DEST_IS_DIR : DEST_IS_FILE;
00324
00325 }
00326 const bool isGlobalDest = m_dest == m_globalDest;
00327 if ( isGlobalDest )
00328 m_globalDestinationState = destinationState;
00329
00330 if ( !sLocalPath.isEmpty() && kio_resolve_local_urls ) {
00331 m_dest = KUrl();
00332 m_dest.setPath(sLocalPath);
00333 if ( isGlobalDest )
00334 m_globalDest = m_dest;
00335 }
00336
00337 q->removeSubjob( job );
00338 assert ( !q->hasSubjobs() );
00339
00340
00341 statCurrentSrc();
00342 return;
00343 }
00344
00345
00346 const QString sName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
00347
00348
00349 m_currentDest = m_dest;
00350
00351 UDSEntryList lst;
00352 lst.append(entry);
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366 m_bCurrentSrcIsDir = false;
00367 slotEntries(static_cast<KIO::Job*>( job ), lst);
00368
00369 KUrl srcurl;
00370 if (!sLocalPath.isEmpty())
00371 srcurl.setPath(sLocalPath);
00372 else
00373 srcurl = ((SimpleJob*)job)->url();
00374
00375 q->removeSubjob( job );
00376 assert ( !q->hasSubjobs() );
00377
00378 if ( isDir
00379
00380 && !entry.isLink()
00381 && m_mode != CopyJob::Link )
00382 {
00383
00384
00385 m_bCurrentSrcIsDir = true;
00386 if ( destinationState == DEST_IS_DIR )
00387 {
00388 if ( !m_asMethod )
00389 {
00390
00391 QString directory = srcurl.fileName();
00392 if ( !sName.isEmpty() && KProtocolManager::fileNameUsedForCopying( srcurl ) == KProtocolInfo::Name )
00393 {
00394 directory = sName;
00395 }
00396 m_currentDest.addPath( directory );
00397 }
00398 }
00399 else if ( destinationState == DEST_IS_FILE )
00400 {
00401 q->setError( ERR_IS_FILE );
00402 q->setErrorText( m_dest.prettyUrl() );
00403 q->emitResult();
00404 return;
00405 }
00406 else
00407 {
00408
00409
00410
00411
00412 destinationState = DEST_IS_DIR;
00413 if ( m_dest == m_globalDest )
00414 m_globalDestinationState = destinationState;
00415 }
00416
00417 startListing( srcurl );
00418 }
00419 else
00420 {
00421
00422 statNextSrc();
00423 }
00424 }
00425
00426 bool CopyJob::doSuspend()
00427 {
00428 Q_D(CopyJob);
00429 d->slotReport();
00430 return Job::doSuspend();
00431 }
00432
00433 void CopyJobPrivate::slotReport()
00434 {
00435 Q_Q(CopyJob);
00436 if ( q->isSuspended() )
00437 return;
00438
00439 switch (state) {
00440 case STATE_RENAMING:
00441 q->setTotalAmount(KJob::Files, m_srcList.count());
00442
00443 case STATE_COPYING_FILES:
00444 q->setProcessedAmount( KJob::Files, m_processedFiles );
00445 if (m_bURLDirty)
00446 {
00447
00448 m_bURLDirty = false;
00449 if (m_mode==CopyJob::Move)
00450 {
00451 emitMoving(q, m_currentSrcURL, m_currentDestURL);
00452 emit q->moving( q, m_currentSrcURL, m_currentDestURL);
00453 }
00454 else if (m_mode==CopyJob::Link)
00455 {
00456 emitCopying( q, m_currentSrcURL, m_currentDestURL );
00457 emit q->linking( q, m_currentSrcURL.path(), m_currentDestURL );
00458 }
00459 else
00460 {
00461 emitCopying( q, m_currentSrcURL, m_currentDestURL );
00462 emit q->copying( q, m_currentSrcURL, m_currentDestURL );
00463 }
00464 }
00465 break;
00466
00467 case STATE_CREATING_DIRS:
00468 q->setProcessedAmount( KJob::Directories, m_processedDirs );
00469 if (m_bURLDirty)
00470 {
00471 m_bURLDirty = false;
00472 emit q->creatingDir( q, m_currentDestURL );
00473 emitCreatingDir( q, m_currentDestURL );
00474 }
00475 break;
00476
00477 case STATE_STATING:
00478 case STATE_LISTING:
00479 if (m_bURLDirty)
00480 {
00481 m_bURLDirty = false;
00482 if (m_mode==CopyJob::Move)
00483 {
00484 emitMoving( q, m_currentSrcURL, m_currentDestURL );
00485 }
00486 else
00487 {
00488 emitCopying( q, m_currentSrcURL, m_currentDestURL );
00489 }
00490 }
00491 q->setTotalAmount(KJob::Bytes, m_totalSize);
00492 q->setTotalAmount(KJob::Files, files.count());
00493 q->setTotalAmount(KJob::Directories, dirs.count());
00494 break;
00495
00496 default:
00497 break;
00498 }
00499 }
00500
00501 void CopyJobPrivate::slotEntries(KIO::Job* job, const UDSEntryList& list)
00502 {
00503
00504 UDSEntryList::ConstIterator it = list.constBegin();
00505 UDSEntryList::ConstIterator end = list.constEnd();
00506 for (; it != end; ++it) {
00507 const UDSEntry& entry = *it;
00508 struct CopyInfo info;
00509 info.permissions = entry.numberValue( KIO::UDSEntry::UDS_ACCESS, -1 );
00510 info.mtime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 );
00511 info.ctime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_CREATION_TIME, -1 );
00512 info.size = (KIO::filesize_t) entry.numberValue( KIO::UDSEntry::UDS_SIZE, -1 );
00513 if ( info.size != (KIO::filesize_t) -1 )
00514 m_totalSize += info.size;
00515
00516
00517 const QString displayName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
00518 const QString urlStr = entry.stringValue( KIO::UDSEntry::UDS_URL );
00519 KUrl url;
00520 if ( !urlStr.isEmpty() )
00521 url = urlStr;
00522 QString localPath = entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
00523 const bool isDir = entry.isDir();
00524 info.linkDest = entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST );
00525
00526 if (displayName != ".." && displayName != ".")
00527 {
00528 bool hasCustomURL = !url.isEmpty() || !localPath.isEmpty();
00529 if( !hasCustomURL ) {
00530
00531 url = static_cast<SimpleJob *>(job)->url();
00532 if ( m_bCurrentSrcIsDir ) {
00533
00534 url.addPath( displayName );
00535 }
00536 }
00537
00538 if (!localPath.isEmpty() && kio_resolve_local_urls) {
00539 url = KUrl();
00540 url.setPath(localPath);
00541 }
00542
00543 info.uSource = url;
00544 info.uDest = m_currentDest;
00545
00546
00547 if ( destinationState == DEST_IS_DIR &&
00548
00549
00550 ( ! ( m_asMethod && state == STATE_STATING ) ) )
00551 {
00552 QString destFileName;
00553 if ( hasCustomURL &&
00554 KProtocolManager::fileNameUsedForCopying( url ) == KProtocolInfo::FromUrl ) {
00555
00556
00557 int numberOfSlashes = displayName.count( '/' );
00558 QString path = url.path();
00559 int pos = 0;
00560 for ( int n = 0; n < numberOfSlashes + 1; ++n ) {
00561 pos = path.lastIndexOf( '/', pos - 1 );
00562 if ( pos == -1 ) {
00563 kWarning(7007) << "kioslave bug: not enough slashes in UDS_URL" << path << "- looking for" << numberOfSlashes << "slashes";
00564 break;
00565 }
00566 }
00567 if ( pos >= 0 ) {
00568 destFileName = path.mid( pos + 1 );
00569 }
00570
00571 } else {
00572 destFileName = displayName;
00573 }
00574
00575
00576
00577
00578 if ( destFileName.isEmpty() )
00579 destFileName = KIO::encodeFileName( info.uSource.prettyUrl() );
00580
00581
00582 info.uDest.addPath( destFileName );
00583 }
00584
00585
00586 if ( info.linkDest.isEmpty() && isDir && m_mode != CopyJob::Link )
00587 {
00588 dirs.append( info );
00589 if (m_mode == CopyJob::Move)
00590 dirsToRemove.append( info.uSource );
00591 }
00592 else {
00593 files.append( info );
00594 }
00595 }
00596 }
00597 }
00598
00599 void CopyJobPrivate::skipSrc()
00600 {
00601 m_dest = m_globalDest;
00602 destinationState = m_globalDestinationState;
00603 ++m_currentStatSrc;
00604 skip( m_currentSrcURL );
00605 statCurrentSrc();
00606 }
00607
00608 void CopyJobPrivate::statNextSrc()
00609 {
00610
00611
00612
00613
00614 m_dest = m_globalDest;
00615 destinationState = m_globalDestinationState;
00616 ++m_currentStatSrc;
00617 statCurrentSrc();
00618 }
00619
00620 void CopyJobPrivate::statCurrentSrc()
00621 {
00622 Q_Q(CopyJob);
00623 if ( m_currentStatSrc != m_srcList.constEnd() )
00624 {
00625 m_currentSrcURL = (*m_currentStatSrc);
00626 m_bURLDirty = true;
00627 if ( m_mode == CopyJob::Link )
00628 {
00629
00630 m_currentDest = m_dest;
00631 struct CopyInfo info;
00632 info.permissions = -1;
00633 info.mtime = (time_t) -1;
00634 info.ctime = (time_t) -1;
00635 info.size = (KIO::filesize_t)-1;
00636 info.uSource = m_currentSrcURL;
00637 info.uDest = m_currentDest;
00638
00639 if ( destinationState == DEST_IS_DIR && !m_asMethod )
00640 {
00641 if (
00642 (m_currentSrcURL.protocol() == info.uDest.protocol()) &&
00643 (m_currentSrcURL.host() == info.uDest.host()) &&
00644 (m_currentSrcURL.port() == info.uDest.port()) &&
00645 (m_currentSrcURL.user() == info.uDest.user()) &&
00646 (m_currentSrcURL.pass() == info.uDest.pass()) )
00647 {
00648
00649 info.uDest.addPath( m_currentSrcURL.fileName() );
00650 }
00651 else
00652 {
00653
00654
00655
00656 info.uDest.addPath( KIO::encodeFileName( m_currentSrcURL.prettyUrl() )+".desktop" );
00657 }
00658 }
00659 files.append( info );
00660 statNextSrc();
00661 return;
00662 }
00663 else if ( m_mode == CopyJob::Move && (
00664
00665 KProtocolManager::fileNameUsedForCopying( m_currentSrcURL ) == KProtocolInfo::FromUrl ||
00666 destinationState != DEST_IS_DIR || m_asMethod )
00667 )
00668 {
00669
00670
00671 if ( (m_currentSrcURL.protocol() == m_dest.protocol()) &&
00672 (m_currentSrcURL.host() == m_dest.host()) &&
00673 (m_currentSrcURL.port() == m_dest.port()) &&
00674 (m_currentSrcURL.user() == m_dest.user()) &&
00675 (m_currentSrcURL.pass() == m_dest.pass()) )
00676 {
00677 startRenameJob( m_currentSrcURL );
00678 return;
00679 }
00680 else if ( m_currentSrcURL.isLocalFile() && KProtocolManager::canRenameFromFile( m_dest ) )
00681 {
00682 startRenameJob( m_dest );
00683 return;
00684 }
00685 else if ( m_dest.isLocalFile() && KProtocolManager::canRenameToFile( m_currentSrcURL ) )
00686 {
00687 startRenameJob( m_currentSrcURL );
00688 return;
00689 }
00690 }
00691
00692
00693 if (m_mode == CopyJob::Move && !KProtocolManager::supportsDeleting(m_currentSrcURL)) {
00694 QPointer<CopyJob> that = q;
00695 emit q->warning( q, buildErrorString(ERR_CANNOT_DELETE, m_currentSrcURL.prettyUrl()) );
00696 if (that)
00697 statNextSrc();
00698 return;
00699 }
00700
00701
00702 Job * job = KIO::stat( m_currentSrcURL, StatJob::SourceSide, 2, KIO::HideProgressInfo );
00703
00704 state = STATE_STATING;
00705 q->addSubjob(job);
00706 m_currentDestURL=m_dest;
00707 m_bOnlyRenames = false;
00708 m_bURLDirty = true;
00709 }
00710 else
00711 {
00712
00713
00714 state = STATE_STATING;
00715 m_bURLDirty = true;
00716 slotReport();
00717 if (!dirs.isEmpty())
00718 emit q->aboutToCreate( q, dirs );
00719 if (!files.isEmpty())
00720 emit q->aboutToCreate( q, files );
00721
00722 m_bSingleFileCopy = ( files.count() == 1 && dirs.isEmpty() );
00723
00724 state = STATE_CREATING_DIRS;
00725 createNextDir();
00726 }
00727 }
00728
00729 void CopyJobPrivate::startRenameJob( const KUrl& slave_url )
00730 {
00731 Q_Q(CopyJob);
00732 KUrl dest = m_dest;
00733
00734 if ( destinationState == DEST_IS_DIR && !m_asMethod )
00735 dest.addPath( m_currentSrcURL.fileName() );
00736 m_currentDestURL = dest;
00737 kDebug(7007) << "This seems to be a suitable case for trying to rename before stat+[list+]copy+del";
00738 state = STATE_RENAMING;
00739
00740 struct CopyInfo info;
00741 info.permissions = -1;
00742 info.mtime = (time_t) -1;
00743 info.ctime = (time_t) -1;
00744 info.size = (KIO::filesize_t)-1;
00745 info.uSource = m_currentSrcURL;
00746 info.uDest = dest;
00747 QList<CopyInfo> files;
00748 files.append(info);
00749 emit q->aboutToCreate( q, files );
00750
00751 KIO_ARGS << m_currentSrcURL << dest << (qint8) false ;
00752 SimpleJob * newJob = SimpleJobPrivate::newJobNoUi(slave_url, CMD_RENAME, packedArgs);
00753 Scheduler::scheduleJob(newJob);
00754 q->addSubjob( newJob );
00755 if ( m_currentSrcURL.directory() != dest.directory() )
00756 m_bOnlyRenames = false;
00757 }
00758
00759 void CopyJobPrivate::startListing( const KUrl & src )
00760 {
00761 Q_Q(CopyJob);
00762 state = STATE_LISTING;
00763 m_bURLDirty = true;
00764 ListJob * newjob = listRecursive(src, KIO::HideProgressInfo);
00765 newjob->setUnrestricted(true);
00766 q->connect(newjob, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
00767 SLOT(slotEntries(KIO::Job*,KIO::UDSEntryList)));
00768 q->addSubjob( newjob );
00769 }
00770
00771 void CopyJobPrivate::skip( const KUrl & sourceUrl )
00772 {
00773
00774
00775
00776 m_skippedSourceUrls.append( sourceUrl );
00777 dirsToRemove.removeAll( sourceUrl );
00778 }
00779
00780 bool CopyJobPrivate::shouldOverwriteDir( const QString& path ) const
00781 {
00782 if ( m_bOverwriteAllDirs )
00783 return true;
00784 return m_overwriteList.contains(path);
00785 }
00786
00787 bool CopyJobPrivate::shouldOverwriteFile( const QString& path ) const
00788 {
00789 if ( m_bOverwriteAllFiles )
00790 return true;
00791 return m_overwriteList.contains(path);
00792 }
00793
00794 bool CopyJobPrivate::shouldSkip( const QString& path ) const
00795 {
00796 Q_FOREACH(const QString& skipPath, m_skipList) {
00797 if ( path.startsWith(skipPath) )
00798 return true;
00799 }
00800 return false;
00801 }
00802
00803 void CopyJobPrivate::slotResultCreatingDirs( KJob * job )
00804 {
00805 Q_Q(CopyJob);
00806
00807 QList<CopyInfo>::Iterator it = dirs.begin();
00808
00809 if ( job->error() )
00810 {
00811 m_conflictError = job->error();
00812 if ( (m_conflictError == ERR_DIR_ALREADY_EXIST)
00813 || (m_conflictError == ERR_FILE_ALREADY_EXIST) )
00814 {
00815 KUrl oldURL = ((SimpleJob*)job)->url();
00816
00817 if ( m_bAutoSkipDirs ) {
00818
00819 m_skipList.append( oldURL.path( KUrl::AddTrailingSlash ) );
00820 skip( oldURL );
00821 dirs.erase( it );
00822 } else {
00823
00824 const QString destDir = (*it).uDest.path();
00825 if ( shouldOverwriteDir( destDir ) ) {
00826 emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true , false );
00827 dirs.erase( it );
00828 } else {
00829 if ( !q->isInteractive() ) {
00830 q->Job::slotResult( job );
00831 return;
00832 }
00833
00834 assert( ((SimpleJob*)job)->url().url() == (*it).uDest.url() );
00835 q->removeSubjob( job );
00836 assert ( !q->hasSubjobs() );
00837
00838
00839 KUrl existingDest( (*it).uDest );
00840 SimpleJob * newJob = KIO::stat( existingDest, StatJob::DestinationSide, 2, KIO::HideProgressInfo );
00841 Scheduler::scheduleJob(newJob);
00842 kDebug(7007) << "KIO::stat for resolving conflict on " << existingDest;
00843 state = STATE_CONFLICT_CREATING_DIRS;
00844 q->addSubjob(newJob);
00845 return;
00846 }
00847 }
00848 }
00849 else
00850 {
00851
00852 q->Job::slotResult( job );
00853 return;
00854 }
00855 }
00856 else
00857 {
00858
00859 emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true, false );
00860 m_directoriesCopied.append( *it );
00861 dirs.erase( it );
00862 }
00863
00864 m_processedDirs++;
00865
00866 q->removeSubjob( job );
00867 assert( !q->hasSubjobs() );
00868 createNextDir();
00869 }
00870
00871 void CopyJobPrivate::slotResultConflictCreatingDirs( KJob * job )
00872 {
00873 Q_Q(CopyJob);
00874
00875
00876
00877 QList<CopyInfo>::Iterator it = dirs.begin();
00878
00879 const UDSEntry entry = ((KIO::StatJob*)job)->statResult();
00880
00881
00882 const time_t destmtime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 );
00883 const time_t destctime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_CREATION_TIME, -1 );
00884
00885 const KIO::filesize_t destsize = entry.numberValue( KIO::UDSEntry::UDS_SIZE );
00886 const QString linkDest = entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST );
00887
00888 q->removeSubjob( job );
00889 assert ( !q->hasSubjobs() );
00890
00891
00892 RenameDialog_Mode mode = (RenameDialog_Mode)( M_MULTI | M_SKIP | M_ISDIR );
00893
00894 if ( m_conflictError == ERR_DIR_ALREADY_EXIST )
00895 {
00896 if( (*it).uSource == (*it).uDest ||
00897 ((*it).uSource.protocol() == (*it).uDest.protocol() &&
00898 (*it).uSource.path( KUrl::RemoveTrailingSlash ) == linkDest) )
00899 mode = (RenameDialog_Mode)( mode | M_OVERWRITE_ITSELF);
00900 else
00901 mode = (RenameDialog_Mode)( mode | M_OVERWRITE );
00902 }
00903
00904 QString existingDest = (*it).uDest.path();
00905 QString newPath;
00906 if (m_reportTimer)
00907 m_reportTimer->stop();
00908 RenameDialog_Result r = q->ui()->askFileRename( q, i18n("Folder Already Exists"),
00909 (*it).uSource.url(),
00910 (*it).uDest.url(),
00911 mode, newPath,
00912 (*it).size, destsize,
00913 (*it).ctime, destctime,
00914 (*it).mtime, destmtime );
00915 if (m_reportTimer)
00916 m_reportTimer->start(REPORT_TIMEOUT);
00917 switch ( r ) {
00918 case R_CANCEL:
00919 q->setError( ERR_USER_CANCELED );
00920 q->emitResult();
00921 return;
00922 case R_RENAME:
00923 {
00924 QString oldPath = (*it).uDest.path( KUrl::AddTrailingSlash );
00925 KUrl newUrl( (*it).uDest );
00926 newUrl.setPath( newPath );
00927 emit q->renamed( q, (*it).uDest, newUrl );
00928
00929
00930 (*it).uDest.setPath( newUrl.path( KUrl::RemoveTrailingSlash ) );
00931 newPath = newUrl.path( KUrl::AddTrailingSlash );
00932 QList<CopyInfo>::Iterator renamedirit = it;
00933 ++renamedirit;
00934
00935 for( ; renamedirit != dirs.end() ; ++renamedirit )
00936 {
00937 QString path = (*renamedirit).uDest.path();
00938 if ( path.startsWith( oldPath ) ) {
00939 QString n = path;
00940 n.replace( 0, oldPath.length(), newPath );
00941 kDebug(7007) << "dirs list:" << (*renamedirit).uSource.path()
00942 << "was going to be" << path
00943 << ", changed into" << n;
00944 (*renamedirit).uDest.setPath( n );
00945 }
00946 }
00947
00948 QList<CopyInfo>::Iterator renamefileit = files.begin();
00949 for( ; renamefileit != files.end() ; ++renamefileit )
00950 {
00951 QString path = (*renamefileit).uDest.path();
00952 if ( path.startsWith( oldPath ) ) {
00953 QString n = path;
00954 n.replace( 0, oldPath.length(), newPath );
00955 kDebug(7007) << "files list:" << (*renamefileit).uSource.path()
00956 << "was going to be" << path
00957 << ", changed into" << n;
00958 (*renamefileit).uDest.setPath( n );
00959 }
00960 }
00961 if (!dirs.isEmpty())
00962 emit q->aboutToCreate( q, dirs );
00963 if (!files.isEmpty())
00964 emit q->aboutToCreate( q, files );
00965 }
00966 break;
00967 case R_AUTO_SKIP:
00968 m_bAutoSkipDirs = true;
00969
00970 case R_SKIP:
00971 m_skipList.append( existingDest );
00972 skip( (*it).uSource );
00973
00974 dirs.erase( it );
00975 m_processedDirs++;
00976 break;
00977 case R_OVERWRITE:
00978 m_overwriteList.append( existingDest );
00979 emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true , false );
00980
00981 dirs.erase( it );
00982 m_processedDirs++;
00983 break;
00984 case R_OVERWRITE_ALL:
00985 m_bOverwriteAllDirs = true;
00986 emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true , false );
00987
00988 dirs.erase( it );
00989 m_processedDirs++;
00990 break;
00991 default:
00992 assert( 0 );
00993 }
00994 state = STATE_CREATING_DIRS;
00995
00996 createNextDir();
00997 }
00998
00999 void CopyJobPrivate::createNextDir()
01000 {
01001 Q_Q(CopyJob);
01002 KUrl udir;
01003 if ( !dirs.isEmpty() )
01004 {
01005
01006 QList<CopyInfo>::Iterator it = dirs.begin();
01007
01008 while( it != dirs.end() && udir.isEmpty() )
01009 {
01010 const QString dir = (*it).uDest.path();
01011 if ( shouldSkip( dir ) ) {
01012 dirs.erase( it );
01013 it = dirs.begin();
01014 } else
01015 udir = (*it).uDest;
01016 }
01017 }
01018 if ( !udir.isEmpty() )
01019 {
01020
01021
01022 KIO::SimpleJob *newjob = KIO::mkdir( udir, -1 );
01023 Scheduler::scheduleJob(newjob);
01024
01025 m_currentDestURL = udir;
01026 m_bURLDirty = true;
01027
01028 q->addSubjob(newjob);
01029 return;
01030 }
01031 else
01032 {
01033 q->setProcessedAmount( KJob::Directories, m_processedDirs );
01034
01035 state = STATE_COPYING_FILES;
01036 m_processedFiles++;
01037 copyNextFile();
01038 }
01039 }
01040
01041 void CopyJobPrivate::slotResultCopyingFiles( KJob * job )
01042 {
01043 Q_Q(CopyJob);
01044
01045 QList<CopyInfo>::Iterator it = files.begin();
01046 if ( job->error() )
01047 {
01048
01049 if ( m_bAutoSkipFiles )
01050 {
01051 skip( (*it).uSource );
01052 m_fileProcessedSize = (*it).size;
01053 files.erase( it );
01054 }
01055 else
01056 {
01057 if ( !q->isInteractive() ) {
01058 q->Job::slotResult( job );
01059 return;
01060 }
01061
01062 m_conflictError = job->error();
01063
01064 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
01065 || ( m_conflictError == ERR_DIR_ALREADY_EXIST )
01066 || ( m_conflictError == ERR_IDENTICAL_FILES ) )
01067 {
01068 q->removeSubjob( job );
01069 assert ( !q->hasSubjobs() );
01070
01071 KUrl existingFile( (*it).uDest );
01072 SimpleJob * newJob = KIO::stat( existingFile, StatJob::DestinationSide, 2, KIO::HideProgressInfo );
01073 Scheduler::scheduleJob(newJob);
01074 kDebug(7007) << "KIO::stat for resolving conflict on " << existingFile;
01075 state = STATE_CONFLICT_COPYING_FILES;
01076 q->addSubjob(newJob);
01077 return;
01078 }
01079 else
01080 {
01081 if ( m_bCurrentOperationIsLink && qobject_cast<KIO::DeleteJob*>( job ) )
01082 {
01083
01084
01085 m_fileProcessedSize = (*it).size;
01086 files.erase( it );
01087 } else {
01088
01089 slotResultConflictCopyingFiles( job );
01090 return;
01091 }
01092 }
01093 }
01094 } else
01095 {
01096
01097 if ( m_bCurrentOperationIsLink && m_mode == CopyJob::Move
01098 && !qobject_cast<KIO::DeleteJob *>( job )
01099 )
01100 {
01101 q->removeSubjob( job );
01102 assert ( !q->hasSubjobs() );
01103
01104
01105 KIO::Job * newjob = KIO::del( (*it).uSource, HideProgressInfo );
01106 q->addSubjob( newjob );
01107 return;
01108 }
01109
01110 if ( m_bCurrentOperationIsLink )
01111 {
01112 QString target = ( m_mode == CopyJob::Link ? (*it).uSource.path() : (*it).linkDest );
01113
01114 emit q->copyingLinkDone( q, (*it).uSource, target, (*it).uDest );
01115 }
01116 else {
01117
01118 emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, false, false );
01119 if (m_mode == CopyJob::Move)
01120 org::kde::KDirNotify::emitFileMoved( (*it).uSource.url(), (*it).uDest.url() );
01121 }
01122
01123 files.erase( it );
01124 }
01125 m_processedFiles++;
01126
01127
01128 m_processedSize += m_fileProcessedSize;
01129 m_fileProcessedSize = 0;
01130
01131
01132
01133
01134 KIO::Job* kiojob = dynamic_cast<KIO::Job*>(job);
01135 Q_ASSERT(kiojob);
01136 m_incomingMetaData += kiojob->metaData();
01137 q->removeSubjob( job );
01138 assert( !q->hasSubjobs() );
01139 copyNextFile();
01140 }
01141
01142 void CopyJobPrivate::slotResultConflictCopyingFiles( KJob * job )
01143 {
01144 Q_Q(CopyJob);
01145
01146
01147 QList<CopyInfo>::Iterator it = files.begin();
01148
01149 RenameDialog_Result res;
01150 QString newPath;
01151
01152 if (m_reportTimer)
01153 m_reportTimer->stop();
01154
01155 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
01156 || ( m_conflictError == ERR_DIR_ALREADY_EXIST )
01157 || ( m_conflictError == ERR_IDENTICAL_FILES ) )
01158 {
01159
01160 const UDSEntry entry = ((KIO::StatJob*)job)->statResult();
01161
01162 const time_t destmtime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 );
01163 const time_t destctime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_CREATION_TIME, -1 );
01164 const KIO::filesize_t destsize = entry.numberValue( KIO::UDSEntry::UDS_SIZE );
01165 const QString linkDest = entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST );
01166
01167
01168
01169 RenameDialog_Mode mode;
01170 bool isDir = true;
01171
01172 if( m_conflictError == ERR_DIR_ALREADY_EXIST )
01173 mode = M_ISDIR;
01174 else
01175 {
01176 if ( (*it).uSource == (*it).uDest ||
01177 ((*it).uSource.protocol() == (*it).uDest.protocol() &&
01178 (*it).uSource.path( KUrl::RemoveTrailingSlash ) == linkDest) )
01179 mode = M_OVERWRITE_ITSELF;
01180 else
01181 mode = M_OVERWRITE;
01182 isDir = false;
01183 }
01184
01185 if ( !m_bSingleFileCopy )
01186 mode = (RenameDialog_Mode) ( mode | M_MULTI | M_SKIP );
01187
01188 res = q->ui()->askFileRename( q, !isDir ?
01189 i18n("File Already Exists") : i18n("Already Exists as Folder"),
01190 (*it).uSource.url(),
01191 (*it).uDest.url(),
01192 mode, newPath,
01193 (*it).size, destsize,
01194 (*it).ctime, destctime,
01195 (*it).mtime, destmtime );
01196
01197 }
01198 else
01199 {
01200 if ( job->error() == ERR_USER_CANCELED )
01201 res = R_CANCEL;
01202 else if ( !q->isInteractive() ) {
01203 q->Job::slotResult( job );
01204 return;
01205 }
01206 else
01207 {
01208 SkipDialog_Result skipResult = q->ui()->askSkip( q, files.count() > 1,
01209 job->errorString() );
01210
01211
01212 res = ( skipResult == S_SKIP ) ? R_SKIP :
01213 ( skipResult == S_AUTO_SKIP ) ? R_AUTO_SKIP :
01214 R_CANCEL;
01215 }
01216 }
01217
01218 if (m_reportTimer)
01219 m_reportTimer->start(REPORT_TIMEOUT);
01220
01221 q->removeSubjob( job );
01222 assert ( !q->hasSubjobs() );
01223 switch ( res ) {
01224 case R_CANCEL:
01225 q->setError( ERR_USER_CANCELED );
01226 q->emitResult();
01227 return;
01228 case R_RENAME:
01229 {
01230 KUrl newUrl( (*it).uDest );
01231 newUrl.setPath( newPath );
01232 emit q->renamed( q, (*it).uDest, newUrl );
01233 (*it).uDest = newUrl;
01234
01235 QList<CopyInfo> files;
01236 files.append(*it);
01237 emit q->aboutToCreate( q, files );
01238 }
01239 break;
01240 case R_AUTO_SKIP:
01241 m_bAutoSkipFiles = true;
01242
01243 case R_SKIP:
01244
01245 skip( (*it).uSource );
01246 m_processedSize += (*it).size;
01247 files.erase( it );
01248 m_processedFiles++;
01249 break;
01250 case R_OVERWRITE_ALL:
01251 m_bOverwriteAllFiles = true;
01252 break;
01253 case R_OVERWRITE:
01254
01255 m_overwriteList.append( (*it).uDest.path() );
01256 break;
01257 default:
01258 assert( 0 );
01259 }
01260 state = STATE_COPYING_FILES;
01261 copyNextFile();
01262 }
01263
01264 KIO::Job* CopyJobPrivate::linkNextFile( const KUrl& uSource, const KUrl& uDest, JobFlags flags )
01265 {
01266
01267 if (
01268 (uSource.protocol() == uDest.protocol()) &&
01269 (uSource.host() == uDest.host()) &&
01270 (uSource.port() == uDest.port()) &&
01271 (uSource.user() == uDest.user()) &&
01272 (uSource.pass() == uDest.pass()) )
01273 {
01274
01275 KIO::SimpleJob *newJob = KIO::symlink( uSource.path(), uDest, flags|HideProgressInfo );
01276 Scheduler::scheduleJob(newJob);
01277
01278
01279 m_bCurrentOperationIsLink = true;
01280 m_currentSrcURL=uSource;
01281 m_currentDestURL=uDest;
01282 m_bURLDirty = true;
01283
01284 return newJob;
01285 } else {
01286 Q_Q(CopyJob);
01287
01288 if ( uDest.isLocalFile() ) {
01289
01290
01291 QString path = uDest.path();
01292
01293 QFile f( path );
01294 if ( f.open( QIODevice::ReadWrite ) )
01295 {
01296 f.close();
01297 KDesktopFile desktopFile( path );
01298 KConfigGroup config = desktopFile.desktopGroup();
01299 KUrl url = uSource;
01300 url.setPass( "" );
01301 config.writePathEntry( "URL", url.url() );
01302 config.writeEntry( "Name", url.url() );
01303 config.writeEntry( "Type", QString::fromLatin1("Link") );
01304 QString protocol = uSource.protocol();
01305 if ( protocol == QLatin1String("ftp") )
01306 config.writeEntry( "Icon", QString::fromLatin1("folder-remote") );
01307 else if ( protocol == QLatin1String("http") )
01308 config.writeEntry( "Icon", QString::fromLatin1("text-html") );
01309 else if ( protocol == QLatin1String("info") )
01310 config.writeEntry( "Icon", QString::fromLatin1("text-x-texinfo") );
01311 else if ( protocol == QLatin1String("mailto") )
01312 config.writeEntry( "Icon", QString::fromLatin1("internet-mail") );
01313 else
01314 config.writeEntry( "Icon", QString::fromLatin1("unknown") );
01315 config.sync();
01316 files.erase( files.begin() );
01317 m_processedFiles++;
01318
01319 copyNextFile();
01320 return 0;
01321 }
01322 else
01323 {
01324 kDebug(7007) << "ERR_CANNOT_OPEN_FOR_WRITING";
01325 q->setError( ERR_CANNOT_OPEN_FOR_WRITING );
01326 q->setErrorText( uDest.path() );
01327 q->emitResult();
01328 return 0;
01329 }
01330 } else {
01331
01332 q->setError( ERR_CANNOT_SYMLINK );
01333 q->setErrorText( uDest.prettyUrl() );
01334 q->emitResult();
01335 return 0;
01336 }
01337 }
01338 }
01339
01340 void CopyJobPrivate::copyNextFile()
01341 {
01342 Q_Q(CopyJob);
01343 bool bCopyFile = false;
01344
01345
01346 QList<CopyInfo>::Iterator it = files.begin();
01347
01348 while (it != files.end() && !bCopyFile)
01349 {
01350 const QString destFile = (*it).uDest.path();
01351 bCopyFile = !shouldSkip( destFile );
01352 if ( !bCopyFile ) {
01353 files.erase( it );
01354 it = files.begin();
01355 }
01356 }
01357
01358 if (bCopyFile)
01359 {
01360 const KUrl& uSource = (*it).uSource;
01361 const KUrl& uDest = (*it).uDest;
01362
01363 bool bOverwrite;
01364 const QString destFile = uDest.path();
01365
01366 if ( uDest == uSource )
01367 bOverwrite = false;
01368 else
01369 bOverwrite = shouldOverwriteFile( destFile );
01370
01371 m_bCurrentOperationIsLink = false;
01372 KIO::Job * newjob = 0;
01373 if ( m_mode == CopyJob::Link ) {
01374
01375 JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
01376 newjob = linkNextFile(uSource, uDest, flags);
01377 if (!newjob)
01378 return;
01379 } else if ( !(*it).linkDest.isEmpty() &&
01380 (uSource.protocol() == uDest.protocol()) &&
01381 (uSource.host() == uDest.host()) &&
01382 (uSource.port() == uDest.port()) &&
01383 (uSource.user() == uDest.user()) &&
01384 (uSource.pass() == uDest.pass()))
01385
01386 {
01387 JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
01388 KIO::SimpleJob *newJob = KIO::symlink( (*it).linkDest, uDest, flags | HideProgressInfo );
01389 Scheduler::scheduleJob(newJob);
01390 newjob = newJob;
01391
01392 m_currentSrcURL = KUrl( (*it).linkDest );
01393 m_currentDestURL = uDest;
01394 m_bURLDirty = true;
01395
01396
01397 m_bCurrentOperationIsLink = true;
01398
01399 } else if (m_mode == CopyJob::Move)
01400 {
01401 JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
01402 KIO::FileCopyJob * moveJob = KIO::file_move( uSource, uDest, (*it).permissions, flags | HideProgressInfo );
01403 moveJob->setSourceSize( (*it).size );
01404 newjob = moveJob;
01405
01406
01407 m_currentSrcURL=uSource;
01408 m_currentDestURL=uDest;
01409 m_bURLDirty = true;
01410
01411 }
01412 else
01413 {
01414
01415
01416 bool remoteSource = !KProtocolManager::supportsListing(uSource);
01417 int permissions = (*it).permissions;
01418 if ( m_defaultPermissions || ( remoteSource && uDest.isLocalFile() ) )
01419 permissions = -1;
01420 JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
01421 KIO::FileCopyJob * copyJob = KIO::file_copy( uSource, uDest, permissions, flags | HideProgressInfo );
01422 copyJob->setParentJob( q );
01423 copyJob->setSourceSize( (*it).size );
01424 if ((*it).mtime != -1) {
01425 QDateTime dt; dt.setTime_t( (*it).mtime );
01426 copyJob->setModificationTime( dt );
01427 }
01428 newjob = copyJob;
01429
01430 m_currentSrcURL=uSource;
01431 m_currentDestURL=uDest;
01432 m_bURLDirty = true;
01433 }
01434 q->addSubjob(newjob);
01435 q->connect( newjob, SIGNAL( processedSize( KJob*, qulonglong ) ),
01436 SLOT( slotProcessedSize( KJob*, qulonglong ) ) );
01437 q->connect( newjob, SIGNAL( totalSize( KJob*, qulonglong ) ),
01438 SLOT( slotTotalSize( KJob*, qulonglong ) ) );
01439 }
01440 else
01441 {
01442
01443
01444 deleteNextDir();
01445 }
01446 }
01447
01448 void CopyJobPrivate::deleteNextDir()
01449 {
01450 Q_Q(CopyJob);
01451 if ( m_mode == CopyJob::Move && !dirsToRemove.isEmpty() )
01452 {
01453 state = STATE_DELETING_DIRS;
01454 m_bURLDirty = true;
01455
01456 KUrl::List::Iterator it = --dirsToRemove.end();
01457 SimpleJob *job = KIO::rmdir( *it );
01458 Scheduler::scheduleJob(job);
01459 dirsToRemove.erase(it);
01460 q->addSubjob( job );
01461 }
01462 else
01463 {
01464
01465 state = STATE_SETTING_DIR_ATTRIBUTES;
01466 m_directoriesCopiedIterator = m_directoriesCopied.constBegin();
01467 setNextDirAttribute();
01468 }
01469 }
01470
01471 void CopyJobPrivate::setNextDirAttribute()
01472 {
01473 Q_Q(CopyJob);
01474 while (m_directoriesCopiedIterator != m_directoriesCopied.constEnd() &&
01475 (*m_directoriesCopiedIterator).mtime == -1) {
01476 ++m_directoriesCopiedIterator;
01477 }
01478 if ( m_directoriesCopiedIterator != m_directoriesCopied.constEnd() ) {
01479 const KUrl url = (*m_directoriesCopiedIterator).uDest;
01480 const time_t mtime = (*m_directoriesCopiedIterator).mtime;
01481 const QDateTime dt = QDateTime::fromTime_t(mtime);
01482 ++m_directoriesCopiedIterator;
01483
01484 KIO::SimpleJob *job = KIO::setModificationTime( url, dt );
01485 Scheduler::scheduleJob(job);
01486 q->addSubjob( job );
01487
01488
01489 #if 0 // ifdef Q_OS_UNIX
01490
01491
01492
01493 QLinkedList<CopyInfo>::const_iterator it = m_directoriesCopied.constBegin();
01494 for ( ; it != m_directoriesCopied.constEnd() ; ++it ) {
01495 const KUrl& url = (*it).uDest;
01496 if ( url.isLocalFile() && (*it).mtime != (time_t)-1 ) {
01497 const QByteArray path = QFile::encodeName( url.path() );
01498 KDE_struct_stat statbuf;
01499 if (KDE_lstat(path, &statbuf) == 0) {
01500 struct utimbuf utbuf;
01501 utbuf.actime = statbuf.st_atime;
01502 utbuf.modtime = (*it).mtime;
01503 utime( path, &utbuf );
01504 }
01505
01506 }
01507 }
01508 m_directoriesCopied.clear();
01509
01510 #endif
01511 } else {
01512
01513 if ( !m_bOnlyRenames )
01514 {
01515 KUrl url( m_globalDest );
01516 if ( m_globalDestinationState != DEST_IS_DIR || m_asMethod )
01517 url.setPath( url.directory() );
01518
01519 org::kde::KDirNotify::emitFilesAdded( url.url() );
01520
01521 Q_FOREACH(const KUrl& url, m_skippedSourceUrls)
01522 m_srcList.removeAll(url);
01523
01524 if ( m_mode == CopyJob::Move && !m_srcList.isEmpty() ) {
01525
01526 org::kde::KDirNotify::emitFilesRemoved( m_srcList.toStringList() );
01527 }
01528 }
01529 if (m_reportTimer)
01530 m_reportTimer->stop();
01531 --m_processedFiles;
01532 slotReport();
01533
01534 q->emitResult();
01535 }
01536 }
01537
01538 void CopyJobPrivate::slotProcessedSize( KJob*, qulonglong data_size )
01539 {
01540 Q_Q(CopyJob);
01541
01542 m_fileProcessedSize = data_size;
01543 q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
01544
01545 if ( m_processedSize + m_fileProcessedSize > m_totalSize )
01546 {
01547
01548 m_totalSize = m_processedSize + m_fileProcessedSize;
01549
01550 q->setTotalAmount(KJob::Bytes, m_totalSize);
01551 }
01552
01553 q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
01554 }
01555
01556 void CopyJobPrivate::slotTotalSize( KJob*, qulonglong size )
01557 {
01558 Q_Q(CopyJob);
01559
01560
01561
01562
01563
01564 if ( m_bSingleFileCopy && size > m_totalSize)
01565 {
01566
01567 m_totalSize = size;
01568 q->setTotalAmount(KJob::Bytes, size);
01569 }
01570 }
01571
01572 void CopyJobPrivate::slotResultDeletingDirs( KJob * job )
01573 {
01574 Q_Q(CopyJob);
01575 if (job->error())
01576 {
01577
01578
01579
01580 }
01581 q->removeSubjob( job );
01582 assert( !q->hasSubjobs() );
01583 deleteNextDir();
01584 }
01585
01586 void CopyJobPrivate::slotResultSettingDirAttributes( KJob * job )
01587 {
01588 Q_Q(CopyJob);
01589 if (job->error())
01590 {
01591
01592
01593
01594 }
01595 q->removeSubjob( job );
01596 assert( !q->hasSubjobs() );
01597 setNextDirAttribute();
01598 }
01599
01600
01601 void CopyJobPrivate::slotResultRenaming( KJob* job )
01602 {
01603 Q_Q(CopyJob);
01604 int err = job->error();
01605 const QString errText = job->errorText();
01606
01607 KIO::Job* kiojob = dynamic_cast<KIO::Job*>(job);
01608 Q_ASSERT(kiojob);
01609 m_incomingMetaData += kiojob->metaData();
01610 q->removeSubjob( job );
01611 assert ( !q->hasSubjobs() );
01612
01613 KUrl dest = m_dest;
01614 if ( destinationState == DEST_IS_DIR && !m_asMethod )
01615 dest.addPath( m_currentSrcURL.fileName() );
01616 if ( err )
01617 {
01618
01619
01620
01621 if ( m_currentSrcURL.isLocalFile() && m_currentSrcURL.url(KUrl::RemoveTrailingSlash) != dest.url(KUrl::RemoveTrailingSlash) &&
01622 m_currentSrcURL.url(KUrl::RemoveTrailingSlash).toLower() == dest.url(KUrl::RemoveTrailingSlash).toLower() &&
01623 ( err == ERR_FILE_ALREADY_EXIST ||
01624 err == ERR_DIR_ALREADY_EXIST ||
01625 err == ERR_IDENTICAL_FILES ) )
01626 {
01627 kDebug(7007) << "Couldn't rename directly, dest already exists. Detected special case of lower/uppercase renaming in same dir, try with 2 rename calls";
01628 QByteArray _src( QFile::encodeName(m_currentSrcURL.path()) );
01629 QByteArray _dest( QFile::encodeName(dest.path()) );
01630 KTemporaryFile tmpFile;
01631 tmpFile.setPrefix(m_currentSrcURL.directory(KUrl::ObeyTrailingSlash));
01632 tmpFile.setAutoRemove(false);
01633 tmpFile.open();
01634 QByteArray _tmp( QFile::encodeName(tmpFile.fileName()) );
01635 kDebug(7007) << "KTemporaryFile using" << _tmp << "as intermediary";
01636 if ( KDE_rename( _src, _tmp ) == 0 )
01637 {
01638 if ( !QFile::exists( _dest ) && KDE_rename( _tmp, _dest ) == 0 )
01639 {
01640 kDebug(7007) << "Success.";
01641 err = 0;
01642 }
01643 else
01644 {
01645
01646 if ( KDE_rename( _tmp, _src ) != 0 ) {
01647 kError(7007) << "Couldn't rename" << tmpFile.fileName() << "back to" << _src << '!';
01648
01649 q->Job::slotResult( job );
01650 return;
01651 }
01652 }
01653 }
01654 }
01655 }
01656 if ( err )
01657 {
01658
01659
01660
01661
01662
01663
01664
01665 Q_ASSERT( m_currentSrcURL == *m_currentStatSrc );
01666
01667
01668 if ( err == ERR_DIR_ALREADY_EXIST ||
01669 err == ERR_FILE_ALREADY_EXIST ||
01670 err == ERR_IDENTICAL_FILES )
01671 {
01672 if (m_reportTimer)
01673 m_reportTimer->stop();
01674
01675
01676 bool isDir = (err == ERR_DIR_ALREADY_EXIST);
01677 if ((isDir && m_bAutoSkipDirs) || (!isDir && m_bAutoSkipFiles)) {
01678
01679 skipSrc();
01680 return;
01681 } else if ((isDir && m_bOverwriteAllDirs) || (!isDir && m_bOverwriteAllFiles)) {
01682 ;
01683 } else if ( q->isInteractive() ) {
01684 QString newPath;
01685
01686
01687
01688 KIO::filesize_t sizeSrc = (KIO::filesize_t) -1;
01689 KIO::filesize_t sizeDest = (KIO::filesize_t) -1;
01690 time_t ctimeSrc = (time_t) -1;
01691 time_t ctimeDest = (time_t) -1;
01692 time_t mtimeSrc = (time_t) -1;
01693 time_t mtimeDest = (time_t) -1;
01694
01695 KDE_struct_stat stat_buf;
01696 if ( m_currentSrcURL.isLocalFile() &&
01697 KDE_stat(QFile::encodeName(m_currentSrcURL.path()), &stat_buf) == 0 ) {
01698 sizeSrc = stat_buf.st_size;
01699 ctimeSrc = stat_buf.st_ctime;
01700 mtimeSrc = stat_buf.st_mtime;
01701 isDir = S_ISDIR(stat_buf.st_mode);
01702 }
01703 if ( dest.isLocalFile() &&
01704 KDE_stat(QFile::encodeName(dest.path()), &stat_buf) == 0 ) {
01705 sizeDest = stat_buf.st_size;
01706 ctimeDest = stat_buf.st_ctime;
01707 mtimeDest = stat_buf.st_mtime;
01708 }
01709
01710
01711 RenameDialog_Mode mode = ( m_currentSrcURL == dest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE;
01712
01713 if ( m_srcList.count() > 1 )
01714 mode = (RenameDialog_Mode) ( mode | M_MULTI | M_SKIP );
01715 if ( isDir )
01716 mode = (RenameDialog_Mode) ( mode | M_ISDIR );
01717
01718 RenameDialog_Result r = q->ui()->askFileRename(
01719 q,
01720 err != ERR_DIR_ALREADY_EXIST ? i18n("File Already Exists") : i18n("Already Exists as Folder"),
01721 m_currentSrcURL.url(),
01722 dest.url(),
01723 mode, newPath,
01724 sizeSrc, sizeDest,
01725 ctimeSrc, ctimeDest,
01726 mtimeSrc, mtimeDest );
01727 if (m_reportTimer)
01728 m_reportTimer->start(REPORT_TIMEOUT);
01729
01730 switch ( r )
01731 {
01732 case R_CANCEL:
01733 {
01734 q->setError( ERR_USER_CANCELED );
01735 q->emitResult();
01736 return;
01737 }
01738 case R_RENAME:
01739 {
01740
01741
01742 m_dest.setPath( newPath );
01743 KIO::Job* job = KIO::stat( m_dest, StatJob::DestinationSide, 2, KIO::HideProgressInfo );
01744 state = STATE_STATING;
01745 destinationState = DEST_NOT_STATED;
01746 q->addSubjob(job);
01747 return;
01748 }
01749 case R_AUTO_SKIP:
01750 if (isDir)
01751 m_bAutoSkipDirs = true;
01752 else
01753 m_bAutoSkipFiles = true;
01754
01755 case R_SKIP:
01756
01757 skipSrc();
01758 return;
01759 case R_OVERWRITE_ALL:
01760 if (isDir)
01761 m_bOverwriteAllDirs = true;
01762 else
01763 m_bOverwriteAllFiles = true;
01764 break;
01765 case R_OVERWRITE:
01766
01767
01768
01769
01770
01771 kDebug(7007) << "adding to overwrite list: " << dest.path();
01772 m_overwriteList.append( dest.path() );
01773 break;
01774 default:
01775
01776 break;
01777 }
01778 } else if ( err != KIO::ERR_UNSUPPORTED_ACTION ) {
01779
01780 q->setError( err );
01781 q->setErrorText( errText );
01782 q->emitResult();
01783 return;
01784 }
01785 } else if ( err != KIO::ERR_UNSUPPORTED_ACTION ) {
01786 kDebug(7007) << "Couldn't rename" << m_currentSrcURL << "to" << dest << ", aborting";
01787 q->setError( err );
01788 q->setErrorText( errText );
01789 q->emitResult();
01790 return;
01791 }
01792 kDebug(7007) << "Couldn't rename" << m_currentSrcURL << "to" << dest << ", reverting to normal way, starting with stat";
01793
01794 KIO::Job* job = KIO::stat( m_currentSrcURL, StatJob::SourceSide, 2, KIO::HideProgressInfo );
01795 state = STATE_STATING;
01796 q->addSubjob(job);
01797 m_bOnlyRenames = false;
01798 }
01799 else
01800 {
01801 kDebug(7007) << "Renaming succeeded, move on";
01802 ++m_processedFiles;
01803 emit q->copyingDone( q, *m_currentStatSrc, dest, -1 , true, true );
01804 statNextSrc();
01805 }
01806 }
01807
01808 void CopyJob::slotResult( KJob *job )
01809 {
01810 Q_D(CopyJob);
01811
01812
01813
01814
01815
01816
01817 switch ( d->state ) {
01818 case STATE_STATING:
01819 d->slotResultStating( job );
01820 break;
01821 case STATE_RENAMING:
01822 {
01823 d->slotResultRenaming( job );
01824 break;
01825 }
01826 case STATE_LISTING:
01827
01828
01829 if (job->error())
01830 {
01831 Job::slotResult( job );
01832 return;
01833 }
01834
01835 removeSubjob( job );
01836 assert ( !hasSubjobs() );
01837
01838 d->statNextSrc();
01839 break;
01840 case STATE_CREATING_DIRS:
01841 d->slotResultCreatingDirs( job );
01842 break;
01843 case STATE_CONFLICT_CREATING_DIRS:
01844 d->slotResultConflictCreatingDirs( job );
01845 break;
01846 case STATE_COPYING_FILES:
01847 d->slotResultCopyingFiles( job );
01848 break;
01849 case STATE_CONFLICT_COPYING_FILES:
01850 d->slotResultConflictCopyingFiles( job );
01851 break;
01852 case STATE_DELETING_DIRS:
01853 d->slotResultDeletingDirs( job );
01854 break;
01855 case STATE_SETTING_DIR_ATTRIBUTES:
01856 d->slotResultSettingDirAttributes( job );
01857 break;
01858 default:
01859 assert( 0 );
01860 }
01861 }
01862
01863 void KIO::CopyJob::setDefaultPermissions( bool b )
01864 {
01865 d_func()->m_defaultPermissions = b;
01866 }
01867
01868 KIO::CopyJob::CopyMode KIO::CopyJob::operationMode() const
01869 {
01870 return d_func()->m_mode;
01871 }
01872
01873 void KIO::CopyJob::setAutoSkip(bool autoSkip)
01874 {
01875 d_func()->m_bAutoSkipFiles = autoSkip;
01876 d_func()->m_bAutoSkipDirs = autoSkip;
01877 }
01878
01879 void KIO::CopyJob::setWriteIntoExistingDirectories(bool overwriteAll)
01880 {
01881 d_func()->m_bOverwriteAllDirs = overwriteAll;
01882 }
01883
01884 CopyJob *KIO::copy(const KUrl& src, const KUrl& dest, JobFlags flags)
01885 {
01886
01887 KUrl::List srcList;
01888 srcList.append( src );
01889 return CopyJobPrivate::newJob(srcList, dest, CopyJob::Copy, false, flags);
01890 }
01891
01892 CopyJob *KIO::copyAs(const KUrl& src, const KUrl& dest, JobFlags flags)
01893 {
01894
01895 KUrl::List srcList;
01896 srcList.append( src );
01897 return CopyJobPrivate::newJob(srcList, dest, CopyJob::Copy, true, flags);
01898 }
01899
01900 CopyJob *KIO::copy( const KUrl::List& src, const KUrl& dest, JobFlags flags )
01901 {
01902
01903 return CopyJobPrivate::newJob(src, dest, CopyJob::Copy, false, flags);
01904 }
01905
01906 CopyJob *KIO::move(const KUrl& src, const KUrl& dest, JobFlags flags)
01907 {
01908
01909 KUrl::List srcList;
01910 srcList.append( src );
01911 return CopyJobPrivate::newJob(srcList, dest, CopyJob::Move, false, flags);
01912 }
01913
01914 CopyJob *KIO::moveAs(const KUrl& src, const KUrl& dest, JobFlags flags)
01915 {
01916
01917 KUrl::List srcList;
01918 srcList.append( src );
01919 return CopyJobPrivate::newJob(srcList, dest, CopyJob::Move, true, flags);
01920 }
01921
01922 CopyJob *KIO::move( const KUrl::List& src, const KUrl& dest, JobFlags flags)
01923 {
01924
01925 return CopyJobPrivate::newJob(src, dest, CopyJob::Move, false, flags);
01926 }
01927
01928 CopyJob *KIO::link(const KUrl& src, const KUrl& destDir, JobFlags flags)
01929 {
01930 KUrl::List srcList;
01931 srcList.append( src );
01932 return CopyJobPrivate::newJob(srcList, destDir, CopyJob::Link, false, flags);
01933 }
01934
01935 CopyJob *KIO::link(const KUrl::List& srcList, const KUrl& destDir, JobFlags flags)
01936 {
01937 return CopyJobPrivate::newJob(srcList, destDir, CopyJob::Link, false, flags);
01938 }
01939
01940 CopyJob *KIO::linkAs(const KUrl& src, const KUrl& destDir, JobFlags flags )
01941 {
01942 KUrl::List srcList;
01943 srcList.append( src );
01944 return CopyJobPrivate::newJob(srcList, destDir, CopyJob::Link, false, flags);
01945 }
01946
01947 CopyJob *KIO::trash(const KUrl& src, JobFlags flags)
01948 {
01949 KUrl::List srcList;
01950 srcList.append( src );
01951 return CopyJobPrivate::newJob(srcList, KUrl( "trash:/" ), CopyJob::Move, false, flags);
01952 }
01953
01954 CopyJob *KIO::trash(const KUrl::List& srcList, JobFlags flags)
01955 {
01956 return CopyJobPrivate::newJob(srcList, KUrl( "trash:/" ), CopyJob::Move, false, flags);
01957 }
01958
01959 #include "copyjob.moc"