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