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