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