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