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